分布式系統中一定會遇到的一個問題:服務雪崩效應或者叫級聯效應
什么是服務雪崩效應呢?
在一個高度服務化的系統中,我們實現的一個業務邏輯通常會依賴多個服務,比如:
商品詳情展示服務會依賴商品服務, 價格服務, 商品評論服務. 調用三個依賴服務會共享商品詳情服務的線程池. 如果其中的商品評論服務不可用, 就會出現線程池里所有線程都因等待響應而被阻塞, 從而造成服務雪崩. 如圖所示:
簡單理解: 就是商品評論服務耗時假如是15S,那么在高並發的時候,其他服務很快就做出響應並把線程回收但是商品評論服務需要10S。在這10S內100個線程都會集中被消耗在商品評論服務,就造成商品評論服務沒有線程對外提供服務了。
服務雪崩效應:因服務提供者的不可用導致服務調用者的不可用,並將不可用逐漸放大的過
程,就叫服務雪崩效應
導致服務不可用的原因有幾點: 程序Bug,大流量請求,硬件故障,緩存擊穿
【大流量請求】:在秒殺和大促開始前,如果准備不充分,瞬間大量請求會造成服務提供者的不可用.
【硬件故障】:可能為硬件損壞造成的服務器主機宕機, 網絡硬件故障造成的服務提供者的不可訪問.
【緩存擊穿】:一般發生在緩存應用重啟, 緩存失效時高並發, 所有緩存被清空時,以及短時間內大量緩存失效時. 大量的緩存不命中, 使請求直擊后端,造成服務提供者超負荷運行,引起服務不可用。
用戶重試/代碼邏輯重試,用戶重試:在服務提供者不可用后, 用戶由於忍受不了界面上長時間的等待,會不斷刷新頁面甚至提交表單,或者是代碼有重試策略等等
那么,歸根結底導致雪崩效應的最根本原因是:大量請求線程同步等待造成的資源耗盡
解決方案
1. 超時機制
2. 服務限流
3. 服務熔斷
4. 服務降級
先啟動Eureka 注冊中心,代碼就不上了,截圖:

order微服務工程

pom.xml :
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.tuling.cloud</groupId>
<artifactId>microservice-consumer-order-ribbon-hystrix-fallback</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>06-ms-consumer-order-ribbon-hystrix-fallback</name>
<!-- 引入spring boot的依賴 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- <artifactId>spring-cloud-starter-eureka</artifactId> 依賴了 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> -->
<!-- 此包包含了eurekaclient,ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- hystrix 依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- feign 依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<!-- 引入spring cloud的依賴 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 添加spring-boot的maven插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
OrderController.java:
package com.jiagoushi.cloud.study.user.controller;
import com.jiagoushi.cloud.study.user.entity.User;
import com.jiagoushi.cloud.study.user.feign.UserFeignClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
/**
* 演示 user服務掛了超時和執行請求超時
*/
@RestController
public class OrderController {
private static final Logger logger = LoggerFactory.getLogger(OrderController.class);
@Autowired
private UserFeignClient userFeignClient;
@Autowired
private RestTemplate restTemplate;
/**
* Hystrix調用接口默認1秒超時,超時后會自動執行降級方法,可在文件配置,配置文件配置是全局的,
* @HystrixCommand(fallbackMethod = "findByIdFallback") 注解也可以配置超時
*/
@HystrixCommand(fallbackMethod = "findByIdFallback") // 基於注解的hystrix 推薦使用
@GetMapping("/user/{id}")
public User findById(@PathVariable Long id) {
logger.info("================請求用戶中心接口==============");
return this.restTemplate.getForObject("http://microservice-provider-user/" + id, User.class);
}
// 降級的方法
public User findByIdFallback(Long id) {
User user = new User();
user.setId(-1L);
user.setName("降級用戶");
return user;
}
}
ConsumerOrderApplication.java:
package com.jiagoushi.cloud.study;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
@EnableCircuitBreaker//開啟斷路器功能
public class ConsumerOrderApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerOrderApplication.class, args);
}
}
user微服務:
package com.tuling.cloud.study.controller;
import java.util.Random;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.tuling.cloud.study.entity.User;
import com.tuling.cloud.study.repository.UserRepository;
@RestController
public class UserController {
private final Logger logger = Logger.getLogger(getClass());
@Autowired
private UserRepository userRepository;
@Autowired
private Registration registration;
@GetMapping("/{id}")
public User findById(@PathVariable Long id) throws Exception {
logger.info("用戶中心接口:查詢用戶"+ id +"信息");
//模擬系統執行速度很慢的情況
Thread.sleep(5000);
User findOne = userRepository.findOne(id);
return findOne;
}
@GetMapping("/getIpAndPort")
public String findById() {
return registration.getHost() + ":" + registration.getPort();
}
}
user微服務在停止或者阻塞5s 的時候,order服務會走降級方法

如何想修改接口超時間時間可以在order調用方修改yml 文件:
server: port: 9010 spring: application: name: microservice-consumer-order eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ instance: prefer-ip-address: true hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 3000 #命令執行超時時間,默認1000ms,就是調接口的響應時間超過3S就執行降級,不管提供者是否掛機還是延遲超過時間就走降級


