一、雪崩效應(yīng)和解決方案
1.什么是災(zāi)難性的雪崩效應(yīng) ?
在微服務(wù)架構(gòu)中,一個(gè)請(qǐng)求需要調(diào)用多個(gè)服務(wù)是非常常見(jiàn)的。若有大量的請(qǐng)求涌入,容器的線(xiàn)程資源會(huì)被消耗完畢,導(dǎo)致服務(wù)癱瘓。服務(wù)與服務(wù)之間的依賴(lài)性,故障會(huì)傳播,造成連鎖反應(yīng),會(huì)對(duì)整個(gè)微服務(wù)系統(tǒng)造成災(zāi)難性的嚴(yán)重后果,這就是服務(wù)故障的“雪崩”效應(yīng)。



2.出現(xiàn)雪崩的原因:
(1)服務(wù)提供者不可用(硬件故障,程序bug,緩存擊穿,用戶(hù)的大量請(qǐng)求);
(2)重試加大流量(用戶(hù)重試,代碼邏輯重試);
(3)服務(wù)調(diào)用者不可用(同步等待造成的資源耗盡);
最終的結(jié)果就是一個(gè)服務(wù)不可用,導(dǎo)致一系列服務(wù)的不可用,而往往這種后果是無(wú)法預(yù)料的。
3.如何解決雪崩效應(yīng):
- 降級(jí) :
超時(shí)降級(jí)、資源不足時(shí)(線(xiàn)程或信號(hào)量)降級(jí),降級(jí)后可以配合降級(jí)接口返回托底數(shù)據(jù)。 實(shí)現(xiàn)一個(gè) fallback 方法, 當(dāng)請(qǐng)求后端服務(wù)出現(xiàn)異常的時(shí)候, 可以使用 fallback 方法返回的值. 隔離(線(xiàn)程池隔離和信號(hào)量隔離) 限制調(diào)用分布式服務(wù)的資源使用,某一個(gè)調(diào)用的服務(wù)出現(xiàn)問(wèn)題不會(huì)影響其他服務(wù)調(diào)用。
- 熔斷:
當(dāng)失敗率(如因網(wǎng)絡(luò)故障/超時(shí)造成的失敗率高)達(dá)到閥值自動(dòng)觸發(fā)降級(jí),熔斷器觸發(fā)的快 速失敗會(huì)進(jìn)行快速恢復(fù)。
- 緩存:
提供了請(qǐng)求緩存。
- 請(qǐng)求合并:
提供請(qǐng)求合并。
二、服務(wù)降級(jí)
1.什么是服務(wù)降級(jí)?
就是對(duì)不怎么重要的服務(wù)進(jìn)行低優(yōu)先級(jí)的處理。盡可能的把系統(tǒng)資源讓給優(yōu)先級(jí)高的服務(wù)。資源有限,而請(qǐng)求是無(wú)限的。如果在并發(fā)高峰期,不做服務(wù)降級(jí)處理,一方面肯定會(huì)影響整體服務(wù)的性能,嚴(yán)重的話(huà)可能會(huì)導(dǎo)致宕機(jī)某些重要的服務(wù)不可用。
- 什么時(shí)候會(huì)觸發(fā)getFallback調(diào)用:
(1) 方法拋出非 HystrixBadRequestException 異常。
(2) 方法調(diào)用超時(shí)。
(3) 熔斷器開(kāi)啟攔截調(diào)用。
(4) 線(xiàn)程池/隊(duì)列/信號(hào)量是否跑滿(mǎn)。
2.如何使用服務(wù)降級(jí):
- 創(chuàng)建簡(jiǎn)單測(cè)試降級(jí)實(shí)現(xiàn):
使用Eureka注冊(cè)中心注冊(cè)服務(wù),下面測(cè)試都使用這個(gè)Provider提供服務(wù)。


1.1Consumer-hystrix:
- 修改 pom 文件添加 hystrix 的坐標(biāo):
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 修改全局配置文件:
spring.application.name=product-provider-hystix
server.port=9010
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
- 添加實(shí)體類(lèi):
public class Product {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Product(int id, String name) {
super();
this.id = id;
this.name = name;
}
public Product() {
super();
}
}
- 編寫(xiě)啟動(dòng)類(lèi)開(kāi)啟熔斷器:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableCircuitBreaker // 開(kāi)啟熔斷器 斷路器
@EnableEurekaClient
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
- 編寫(xiě)ProductService:
@Service
public class ProductService {
@Autowired
private LoadBalancerClient loadBalancerClient;// ribbon負(fù)載均衡器
@HystrixCommand(fallbackMethod = "fallback")//當(dāng)下面方法服務(wù)出現(xiàn)異常停止會(huì)返回托底數(shù)據(jù)
public List<Product> getProduct() {
// 選擇調(diào)用的服務(wù)的名稱(chēng)
// ServiceInstance 封裝了服務(wù)的基本信息,如 IP,端口
ServiceInstance si = this.loadBalancerClient.choose("product-provider-hystix");
// 拼接訪(fǎng)問(wèn)服務(wù)的URL
StringBuffer sb = new StringBuffer();
// http://localhost:9001/product/findAll
sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll");
System.out.println(sb.toString());
// springMVC RestTemplate
RestTemplate rt = new RestTemplate();
ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {
};
// ResponseEntity:封裝了返回值信息
ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);
List<Product> list = response.getBody();
return list;
}
//返回托底數(shù)據(jù)的方法
public List<Product> fallback(){
List<Product> list = new ArrayList<>();
list.add(new Product(-1,"托底數(shù)據(jù)!"));
return list;
}
}
- 編寫(xiě)Controller:
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/list")
public List<Product> list(){
return productService.getProduct();
}
}
1.2Service-hystrix:
- 修改POM文件:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
- 創(chuàng)建實(shí)體類(lèi):
public class Product {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 編寫(xiě)Service:
@RequestMapping("/product")
public interface ProductService {
// 查詢(xún)所有
@RequestMapping(value = "/findAll", method = RequestMethod.GET)
public List<Product> findAll();
// ID查詢(xún)
@RequestMapping(value = "/findById", method = RequestMethod.GET)
public Product getProductById(@RequestParam("id") Integer id);
// 傳遞多個(gè)參數(shù) 方式一 :GET方式
@RequestMapping(value = "/add", method = RequestMethod.GET)
public Product addProduct(@RequestParam("id") Integer id, @RequestParam("name") String name);
// ----------------------Httpclient----------------------------------------------
//傳遞多個(gè)參數(shù) 方式二 :POST方式
@RequestMapping(value = "/add2", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public Product addProduct2(@RequestBody Product product);
// 使用HttpClient工具 傳遞多個(gè)參數(shù) :基于GET方式
@RequestMapping(value = "/add3", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE)
public Product addProduct3(Product product);
}
1.3Provider- hystrix:
- 修改POM文件:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- 添加product-service坐標(biāo) -->
<dependency>
<groupId>com.zlw</groupId>
<artifactId>springcloud-eureka-ribbon-hystrix-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 修改全局配置文件:
spring.application.name=product-provider-hystix
server.port=9011
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
- 編寫(xiě)啟動(dòng)類(lèi):
@EnableEurekaClient
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
- 編寫(xiě)ProviderController:
/**
* Product-Provider服務(wù)
* @author zhang
*
*/
@RestController
public class ProductController implements ProductService{
@Override
public List<Product> findAll() {
List<Product> list = new ArrayList<Product>();
list.add(new Product(1,"手機(jī)"));
list.add(new Product(2,"電腦"));
list.add(new Product(3,"電視"));
return list;
}
@Override
public Product getProductById(Integer id) {
return new Product(id,"Product Provider");
}
@Override
public Product addProduct(Integer id, String name) {
return new Product(id,name);
}
@Override
public Product addProduct2(@RequestBody Product product) {
return product;
}
@Override
public Product addProduct3(@RequestBody Product product) {
return product;
}
}
-
測(cè)試:
示例
示例
三、請(qǐng)求緩存
Hystrix 為了降低訪(fǎng)問(wèn)服務(wù)的頻率,支持將一個(gè)請(qǐng)求與返回結(jié)果做緩存處理。如果再次 請(qǐng)求的 URL 沒(méi)有變化,那么 Hystrix 不會(huì)請(qǐng)求服務(wù),而是直接從緩存中將結(jié)果返回。這樣可 以大大降低訪(fǎng)問(wèn)服務(wù)的壓力。
- Hystrix 自帶緩存。有兩個(gè)缺點(diǎn):
(1)是一個(gè)本地緩存。在集群情況下緩存是不能同步的。
(2)不支持第三方緩存容器。Redis,memcache 不支持的。 可以使用 spring 的 cache。
1.使用Redis
1.1創(chuàng)建Consumer-cache:

- 修改POM文件添加 springCache 坐標(biāo) :
<!-- springCache Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 修改配置文件添加redis鏈接信息:
spring.application.name=product-consumer-redis
server.port=9012
#設(shè)置服務(wù)注冊(cè)中心地址,指向另一個(gè)注冊(cè)中心
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
#Redis
spring.redis.datasource=0
#Redis服務(wù)器地址
spring.redis.host=192.168.226.130
#Reids服務(wù)器連接的端口
spring.redis.port=6379
#Redis 服務(wù)器連接密碼(默認(rèn)為空)
spring.redis.password=
#連接池最大連接數(shù)(負(fù)值表示沒(méi)有限制)
spring.redis.pool.max-active=100
#連接池最大阻塞等待時(shí)間(負(fù)值表示沒(méi)有限制)
spring.redis.pool.max-wait=3000
#連接池最大空閉連接數(shù)
spring.redis.pool.max-idle=200
#連接漢最小空閑連接數(shù)
spring.redis.pool.min-idle=50
#連接超時(shí)時(shí)間(毫秒)
spring.redis.pool.timeout=600
- 修改啟動(dòng)類(lèi)開(kāi)啟緩存:
@EnableCaching
@EnableEurekaClient
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
- 創(chuàng)建實(shí)體類(lèi)(get和set方法):
private int id;
private String name;
- 修改ProductService:
@CacheConfig(cacheNames = { "product.consumer" })
@Service
public class ProductService {
@Autowired
private LoadBalancerClient loadBalancerClient;// ribbon負(fù)載均衡器
public List<Product> getProduct() {
// 選擇調(diào)用的服務(wù)的名稱(chēng)
// ServiceInstance 封裝了服務(wù)的基本信息,如 IP,端口
ServiceInstance si = this.loadBalancerClient.choose("product-provider-hystix");
// 拼接訪(fǎng)問(wèn)服務(wù)的URL
StringBuffer sb = new StringBuffer();
// http://localhost:9001/product/findAll
sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll");
System.out.println(sb.toString());
// springMVC RestTemplate
RestTemplate rt = new RestTemplate();
ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {
};
// ResponseEntity:封裝了返回值信息
ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);
List<Product> list = response.getBody();
return list;
}
// 根據(jù)ID查詢(xún)
@Cacheable(key = "'product'+#id")
public Product findById(Integer id) {
System.out.println("------Find------" + id);
return new Product(id, "Product Redis");
}
// 根據(jù)ID刪除
@CacheEvict(key = "'product'+#id")
public void delProduct(Integer id) {
System.out.println("-------Delete--------"+id);
}
}
-
測(cè)試:
控制臺(tái)打印


四、請(qǐng)求合并
就是把請(qǐng)求合并起來(lái)一起發(fā)送給服務(wù)端(一次性批量發(fā)送請(qǐng)求),服務(wù)端批量處理完成后再返回給客戶(hù)端。
請(qǐng)求合并的功能是客戶(hù)端來(lái)控制的,由客戶(hù)端整合發(fā)起,然后由服務(wù)端統(tǒng)一處理,但是需要服務(wù)端支持批量處理請(qǐng)求的能力。
1.什么情況下使用請(qǐng)求合并:
在微服務(wù)架構(gòu)中,我們將一個(gè)項(xiàng)目拆分成很多個(gè)獨(dú)立的模塊,這些獨(dú)立的模塊通過(guò)遠(yuǎn)程 調(diào)用來(lái)互相配合工作,但是,在高并發(fā)情況下,通信次數(shù)的增加會(huì)導(dǎo)致總的通信時(shí)間增加, 同時(shí),線(xiàn)程池的資源也是有限的,高并發(fā)環(huán)境會(huì)導(dǎo)致有大量的線(xiàn)程處于等待狀態(tài),進(jìn)而導(dǎo)致 響應(yīng)延遲,為了解決這些問(wèn)題,我們需要來(lái)了解 Hystrix 的請(qǐng)求合并。
- 請(qǐng)求合并的缺點(diǎn):
設(shè)置請(qǐng)求合并之后,本來(lái)一個(gè)請(qǐng)求可能 5ms 就搞定了,但是現(xiàn)在必須再等 10ms 看看還 有沒(méi)有其他的請(qǐng)求一起的,這樣一個(gè)請(qǐng)求的耗時(shí)就從 5ms 增加到 15ms 了,不過(guò),如果我們 要發(fā)起的命令本身就是一個(gè)高延遲的命令,那么這個(gè)時(shí)候就可以使用請(qǐng)求合并了,因?yàn)檫@個(gè) 時(shí)候時(shí)間窗的時(shí)間消耗就顯得微不足道了,另外高并發(fā)也是請(qǐng)求合并的一個(gè)非常重要的場(chǎng) 景。
2.創(chuàng)建Consumer-batch測(cè)試:

- 修改POM文件:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
- 修改配置文件:
spring.application.name=product-provider-batch
server.port=9009
#設(shè)置服務(wù)注冊(cè)中心地址,指向另一個(gè)注冊(cè)中心
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
- 創(chuàng)建實(shí)體類(lèi):
private Integer id;
private String name;
- 修改啟動(dòng)類(lèi):
@EnableCircuitBreaker
@EnableEurekaClient
@SpringBootApplication
public class ProviductApplication {
public static void main(String[] args) {
SpringApplication.run(ProviductApplication.class, args);
}
}
- 修改ProductService:
@Service
public class ProductService {
// 使用hystrix進(jìn)行合并請(qǐng)求
@HystrixCollapser(batchMethod = "productMethod", scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL, collapserProperties = {
// 請(qǐng)求時(shí)間間隔在 20ms 之內(nèi)的請(qǐng)求會(huì)被合并為一個(gè)請(qǐng)求,默認(rèn) 為 10ms
@HystrixProperty(name = "timerDelayInMilliseconds", value = "20"),
// 設(shè)置觸發(fā)批處理執(zhí)行之前,在批處理中允許許的最大請(qǐng)求數(shù)。
@HystrixProperty(name = "maxRequestsInBatch", value = "3")
})
// consumer的Controller調(diào)用的方法,該方法返回值必須要返回 Future類(lèi)型
public Future<Product> getFuture(Integer id) {
System.out.println("-------" + id + "---------");
return null;
}
//調(diào)用Provider服務(wù)的方法
@HystrixCommand
public List<Product> productMethod(List<Integer> id){
System.out.println("---------productMethod---------");
for (Integer num : id) {
System.out.println("========id:"+num);
}
List<Product> list = new ArrayList<Product>();
list.add(new Product(1,"手機(jī)"));
list.add(new Product(2,"電腦"));
list.add(new Product(3,"電視"));
list.add(new Product(4,"音箱"));
list.add(new Product(99,"...."));
System.out.println("=================");
return list;
}
}
- 修改Controller:
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/list")
public void list() throws Exception {
Future<Product> future = productService.getFuture(1);
Future<Product> future2 = productService.getFuture(2);
Future<Product> future3 = productService.getFuture(4);
Future<Product> future4 = productService.getFuture(3);
System.out.println(future.get().toString());
System.out.println(future2.get().toString());
System.out.println(future3.get().toString());
System.out.println(future4.get().toString());
}
}
-
測(cè)試:
示例
五、服務(wù)熔斷
熔斷機(jī)制相當(dāng)于電路的跳閘功能。

1.創(chuàng)建Consumer

- 修改POM文件:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
</dependencies>
- 修改全局配置文件:
spring.application.name=product-provider-fuse
server.port=9008
#設(shè)置服務(wù)注冊(cè)中心地址,指向另一個(gè)注冊(cè)中心
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
- 編寫(xiě)啟動(dòng)類(lèi):
@EnableCircuitBreaker//開(kāi)啟熔斷器,斷路器
@EnableEurekaClient
@SpringBootApplication
public class FuseApplication {
public static void main(String[] args) {
SpringApplication.run(FuseApplication.class, args);
}
}
- 創(chuàng)建實(shí)體類(lèi):
public class Product {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Product() {
super();
}
public Product(int id, String name) {
super();
this.id = id;
this.name = name;
}
}
- 編寫(xiě)Service:
@Service
public class ProductService {
@Autowired
private LoadBalancerClient loadBalancerClient;
@HystrixCommand(fallbackMethod = "fallback", commandProperties = {
// 默認(rèn) 20 個(gè);10s 內(nèi)請(qǐng)求數(shù)大于 20 個(gè)時(shí)就啟動(dòng)熔斷器,當(dāng)請(qǐng) 求符合熔斷條件時(shí)將觸發(fā) getFallback()
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10"),
// 默認(rèn) 20 個(gè);10s 內(nèi)請(qǐng)求數(shù)大于 20 個(gè)時(shí)就啟動(dòng)熔斷器,當(dāng)請(qǐng) 求符合熔斷條件時(shí)將觸發(fā) getFallback()
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
// 默認(rèn) 5 秒,熔斷多少秒后去嘗試請(qǐng)求
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5000") })
public List<Product> list(int flag) {
System.out.println("fuse---productService---list:"+flag);
if (flag == 1) {
throw new RuntimeException();
}
// 選擇調(diào)用的服務(wù)的名稱(chēng)
// ServiceInstance 封裝了服務(wù)的基本信息,如 IP,端口
ServiceInstance si = this.loadBalancerClient.choose("eureka-provider");
// 拼接訪(fǎng)問(wèn)服務(wù)的 URL
StringBuffer sb = new StringBuffer();
// http://localhost:9001/product/findAll
sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll");
System.out.println(sb.toString());
// springMVC RestTemplate
RestTemplate rt = new RestTemplate();
ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {
};
// ResponseEntity:封裝了返回值信息
ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);
List<Product> list = response.getBody();
return list;
}
public List<Product> fallback(int flag) {
List<Product> list = new ArrayList<Product>();
list.add(new Product(-1, "托底數(shù)據(jù)!"));
return list;
}
}
- Controller:
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/list")
public List<Product> list(@RequestParam("flag") Integer flag) {
return productService.list(flag);
}
}
-
測(cè)試:
示例
示例
六、線(xiàn)程池隔離
什么是線(xiàn)程隔離,例如Customer通過(guò)線(xiàn)程池,訪(fǎng)問(wèn)服務(wù)接口,接口A(yíng)面對(duì)的10次的請(qǐng)求量,接口B是10次的訪(fǎng)問(wèn)量,比例是1:10000,此時(shí)接口A(yíng)和接口B的連接是在同一個(gè)的線(xiàn)程池中,如果接口A(yíng)因?yàn)榈脑L(fǎng)問(wèn)量過(guò)大的原因出現(xiàn)問(wèn)題,勢(shì)必影響線(xiàn)程池的效率,而線(xiàn)程池中的其他線(xiàn)程也會(huì)受到影響,從而造成雪崩效應(yīng)。

1.如何使用線(xiàn)程隔離解決:
那就要用到線(xiàn)程的的隔離技術(shù)了。把可能出現(xiàn)問(wèn)題的服務(wù)獨(dú)立的運(yùn)行在一個(gè)獨(dú)立的線(xiàn)程池中。

線(xiàn)程池的隔離如下圖中兩個(gè)線(xiàn)程池我們將服務(wù)量大的請(qǐng)求單獨(dú)的運(yùn)行在一個(gè)的獨(dú)立的線(xiàn)程池中,兩個(gè)線(xiàn)程池相互之間是不影響的。可以使用線(xiàn)程池隔離的技術(shù)將的線(xiàn)程池內(nèi)的可能預(yù)估出現(xiàn)問(wèn)題的線(xiàn)程和其他的線(xiàn)程的隔開(kāi)運(yùn)行在一個(gè)獨(dú)立的線(xiàn)程池中,一旦此線(xiàn)程出現(xiàn)問(wèn)題,不會(huì)影響到其他線(xiàn)程的運(yùn)行,解決雪崩效應(yīng)的產(chǎn)生。

- 使用線(xiàn)程隔離的優(yōu)缺點(diǎn):
(1)優(yōu)點(diǎn):
使用線(xiàn)程隔離可以完全隔離依賴(lài)的服務(wù)。
當(dāng)線(xiàn)程池出現(xiàn)問(wèn)題時(shí),線(xiàn)程池隔離是獨(dú)立的,不影響其他服務(wù)和接口。
當(dāng)失敗的服務(wù)再次可用時(shí),線(xiàn)程池將清理并可立即恢復(fù),不需要長(zhǎng)時(shí)間的恢復(fù)。
獨(dú)立的線(xiàn)程池提供了并發(fā)性。
(2)缺點(diǎn):
線(xiàn)程池隔離的主要缺點(diǎn)是他們?cè)黾佑?jì)算開(kāi)銷(xiāo),每個(gè)命令的執(zhí)行涉及到排隊(duì),調(diào)度和上下文切換都是在一個(gè)單獨(dú)的線(xiàn)程上運(yùn)行的。
2.創(chuàng)建Consumer測(cè)試:

- 修改POM文件:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
- 修改配置文件:
spring.application.name=product-provider-threadpool
server.port=9007
#設(shè)置服務(wù)注冊(cè)中心地址,指向另一個(gè)注冊(cè)中心
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
- 修改啟動(dòng)類(lèi):
@EnableCircuitBreaker //開(kāi)啟熔斷器 斷路器
@EnableEurekaClient
@SpringBootApplication public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
- 修改ProductService:
@Service
public class ProductService {
@Autowired
private LoadBalancerClient loadBalancerClient;// ribbon 負(fù) 載均衡器
@HystrixCommand(groupKey="ego-product-provider", commandKey = "getUsers",threadPoolKey="ego-product-provider",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"),
//線(xiàn)程池大小
@HystrixProperty(name = "maxQueueSize", value = "100"),
//最大隊(duì)列長(zhǎng)度
@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
//線(xiàn)程存活時(shí)間
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "15")//拒絕請(qǐng)求
},
fallbackMethod = "fallback")
public List<Product> getUsers() {
System.out.println(Thread.currentThread().getName());
// 選擇調(diào)用的服務(wù)的名稱(chēng)
// ServiceInstance 封裝了服務(wù)的基本信息,如 IP,端口
ServiceInstance si = this.loadBalancerClient.choose("ego-product-provider");
// 拼接訪(fǎng)問(wèn)服務(wù)的 URL
StringBuffer sb = new StringBuffer();
// http://localhost:9001/product/findAll
sb.append("http://").append(si.getHost()).append(":").appen d(si.getPort()).append("/product/findAll");
System.out.println(sb.toString());
// springMVC RestTemplate
RestTemplate rt = new RestTemplate();
ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() { };
// ResponseEntity:封裝了返回值信息
ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);
List<Product> list = response.getBody();
return list; } // 返回托底數(shù)據(jù)的方法
public List<Product> fallback() {
System.out.println(Thread.currentThread().getName());
List<Product> list = new ArrayList<>();
list.add(new Product(-1, "我是托底數(shù)據(jù)"));
return list;
}
public void showThread() {
System.out.println(Thread.currentThread().getName());
}
}
- 修改ProductController:
@RestController
public class ProductController {
@Autowired
private ProductService userService;
@RequestMapping("/consumer")
public List<Product> getUsers() {
return this.userService.getUsers();
}
@RequestMapping("/consumer1")
public void getUsers1() {
this.userService.showThread();
}
}
七、使用的注解
1.降級(jí)中使用的注解:
| 名稱(chēng) | 作用 |
|---|---|
| @HystrixCommand | 當(dāng)下面方法服務(wù)出現(xiàn)異常停止會(huì)返回托底數(shù)據(jù) |
| @EnableCircuitBreaker | 開(kāi)啟熔斷器,斷路器 |
2.請(qǐng)求緩存中使用的注解:
| 名稱(chēng) | 作用 |
|---|---|
| @CacheConfig | 一個(gè)類(lèi)中可能會(huì)有多個(gè)緩存操作,而這些緩存操作可能是重復(fù)的。這個(gè)時(shí)候可以使用@CacheConfig |
| @Cacheable | 根據(jù)一定條件對(duì)緩存添加數(shù)據(jù) |
| @CacheEvict | 主要針對(duì)方法配置,能夠根據(jù)一定的條件對(duì)緩存進(jìn)行清空。 |
| @EnableCaching | 注解是spring framework中的注解驅(qū)動(dòng)的緩存管理功能,開(kāi)啟緩存。 |
3.請(qǐng)求合并:
@HystrixCollapser:利用 hystrix 實(shí)現(xiàn)請(qǐng)求合并。
| 屬性名稱(chēng) | 作用 |
|---|---|
| batchMethod | 設(shè)置合并請(qǐng)求的方法 |
| scope | 設(shè)置請(qǐng)求的方式 |
@HystrixProperty:Hystrix 的命令屬性是由@HystrixProperty 注解數(shù)組構(gòu)成的,HystrixProperty 由 name 和 value 兩個(gè)屬性,數(shù)據(jù)類(lèi)型都是字符串。
| 屬性名稱(chēng) | 作用 |
|---|---|
| timerDelayInMilliseconds | 請(qǐng)求時(shí)間間隔默認(rèn) 為 10ms之內(nèi)的請(qǐng)求會(huì)被合并為一個(gè)請(qǐng)求 |
| maxRequestsInBatch | 設(shè)置觸發(fā)批處理執(zhí)行之前,在批處理中允許許的最大請(qǐng)求數(shù)。 |
4.服務(wù)熔斷:
@HystrixProperty中的屬性HystrixPropertiesManager參數(shù)的作用。
| 參數(shù)名稱(chēng) | 作用 |
|---|---|
| circuitBreaker.requestVolumeThreshold | 一個(gè)統(tǒng)計(jì)窗口內(nèi)熔斷觸發(fā)的最小個(gè)數(shù)。 |
| circuitBreaker.enabled | 是否開(kāi)啟熔斷。 |
| circuitBreaker.sleepWindowInMiliseconds | 熔斷多少秒后去嘗試請(qǐng)求。 |
| circuitBreaker.errorThresholdPercentage | 失敗率達(dá)到多少百分比后熔斷。 |
| circuitBreaker.forceOpen | 是否強(qiáng)制開(kāi)啟熔斷。 |
| circuitBreaker.forceClosed | 是否強(qiáng)制關(guān)閉熔斷。 |
5.線(xiàn)程隔離:
(1) @HystrixCommand注解中的threadPoolProperties屬性的作用配置與線(xiàn)程池相關(guān)的屬性。
| 參數(shù)名稱(chēng) | 作用 |
|---|---|
| groupKey | 設(shè)置服務(wù)名(相同的服務(wù)用一個(gè)名稱(chēng)),默認(rèn)值getClass().getSimpleName() |
| commandKey | 設(shè)置服務(wù)下面的接口,默認(rèn)當(dāng)前執(zhí)行方法名 |
| threadPoolKey | 設(shè)置線(xiàn)程池的名稱(chēng),全局唯一標(biāo)識(shí)線(xiàn)程池的名稱(chēng),相同線(xiàn)程池名稱(chēng)的線(xiàn)程池是同一個(gè) |
| coreSize | 設(shè)置線(xiàn)程池大小 |
| maxQueueSize | 設(shè)置最大隊(duì)列長(zhǎng)度 |
| queueSizeRejectionThreshold | 設(shè)置拒絕請(qǐng)求的臨界值 |
| keepAliveTimeMinutes | 設(shè)置線(xiàn)程存活時(shí)間,默認(rèn)為1min |





