灾难性雪崩效应
如何解决灾难性雪崩效应
降级
超时降级、资源不足时
(
线程或信号量
)
降级,降级后可以配合降级接口返回
托底数据
。实现
一
个
fallback
方法
,
当请求后端服务出现异常的时候
,
可以使用
fallback
方法返回的值
.
隔离(线程池隔离和信号量隔离)
限制调用分布式服务的资源使用,某
一
个调用的服务出现问题不会影响其他服务调用。
熔断
当失败率
(
如因网络故障
/
超时造成的失败率高
)
达到阀值自动触发降级,熔断器触发的快
速失败会进行快速恢复。
缓存
提供了请求缓存。
请求合并
提供请求合并。
降级
对服务做降级处理
添加pom文件(hystix坐标)
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.11.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.bjsxt</groupId>
<artifactId>spring-boot-hystrix-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-hystrix-consumer</name>
<description>spring-boot-hystrix-consumer</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR4</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--导入service-->
<dependency>
<groupId>com.bjsxt</groupId>
<artifactId>spring-boot-feign-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
修改启动类开启熔断器
package com.bjsxt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@EnableCircuitBreaker
@SpringBootApplication
public class SpringBootHystrixConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHystrixConsumerApplication.class, args);
}
}
修改
ProductService
@HystrixCommand(fallbackMethod=
"
fallback
"
)
package com.bjsxt.service;
import com.bjsxt.pojo.Product;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
@Service
public class ProductService {
@Autowired
private LoadBalancerClient loadBalancerClient;//ribbon负载均衡器
@HystrixCommand(fallbackMethod = "fallback")
public List<Product> getUser(){
//选择调用的服务的名称
// ServiceInstance 封装了服务的基本信息,如 IP,端口
ServiceInstance choose = loadBalancerClient.choose("yxfProductProvider");
StringBuffer stringBuffer=new StringBuffer();
stringBuffer.append("http://").append(choose.getHost()).append(":").append(choose.getPort()).append("/product/findall");
System.out.println(stringBuffer.toString());
RestTemplate rt=new RestTemplate();
ParameterizedTypeReference<List<Product>> typeReference=new ParameterizedTypeReference<List<Product>>() {};
//ResponseEntity:封装了返回值信息
ResponseEntity<List<Product>> response = rt.exchange(stringBuffer.toString(), HttpMethod.GET, null, typeReference);
List<Product> list = response.getBody();
return list;
}
/*返回托底数据*/
public List<Product> fallback(){
List<Product> list=new ArrayList<>();
list.add(new Product(-1,"抱歉,服务提供者给你放鸽子了~~~~"));
return list;
}
}
以下四种情况将触发
getFallback
调用
(1)
方法抛出非
HystrixBadRequestException
异常。
(2)
方法调用超时
(3)
熔断器开启拦截调用
(4)
线程池
/
队列
/
信号量是否跑满
请求缓存
Hystrix
为了降低访问服务的频率,支持将一
个请求与返回结果做缓存处理。如果再次
请求的
URL
没有变化,那么
Hystrix
不会请求服务,而是直接从缓存中将结果返回。这样可
以大大降低访问服务的压力。
Hystrix
自带缓存。有两个缺点:
1.是
一
个本地缓存。在集群情况下缓存是不能同步的。
2.不支持第三方缓存容器。
Redis
,
memcache
不支持的。
可以使用
spring
的
cache
。
安装
Redis
修改
pom 文件添加
springCache
坐标
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.11.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.bjsxt</groupId>
<artifactId>spring-boot-hystrix-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-hystrix-consumer</name>
<description>spring-boot-hystrix-consumer</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR4</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--导入service-->
<dependency>
<groupId>com.bjsxt</groupId>
<artifactId>spring-boot-feign-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>
<!-- springCache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在配置文件中配置
redis
链接信息
spring.application.name=yxfProductConsumer
server.port=9002
#设置服务注册中心地址,指向另一个注册中心
eureka.client.service-url.defaultZone=http://admin:1234@192.168.41.242:5050/eureka/,http://admin:1234@192.168.41.242:5051/eureka/
# Redis
spring.redis.database=0
#Redis 服务器地址
spring.redis.host=192.168.181.130
#Redis 服务器连接端口
spring.redis.port=6379
#Redis 服务器连接密码(默认为空)
spring.redis.password=
#连接池最大连接数(负值表示没有限制)
spring.redis.jedis.pool.max-active=100
#连接池最大阻塞等待时间(负值表示没有限制)
spring.redis.jedis.pool.max-wait=3000
#连接池最大空闭连接数
spring.redis.jedis.pool.max-idle=200
#连接汉最小空闲连接数
spring.redis.jedis.pool.min-idle=50
#连接超时时间(毫秒)
spring.redis.pool.timeout=600
修改启动类开启缓存
package com.bjsxt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
@EnableCaching
public class SpringBootHystrixConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHystrixConsumerApplication.class, args);
}
}
修改
ProductService
package com.bjsxt.service;
import com.bjsxt.pojo.Product;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
@CacheConfig(cacheNames = {"com.bjsxt.pojo.Product"})
@Service
public class ProductService {
@Autowired
private LoadBalancerClient loadBalancerClient;//ribbon负载均衡器
@HystrixCommand(fallbackMethod = "fallback")
public List<Product> getUser(){
//选择调用的服务的名称
// ServiceInstance 封装了服务的基本信息,如 IP,端口
ServiceInstance choose = loadBalancerClient.choose("yxfProductProvider");
StringBuffer stringBuffer=new StringBuffer();
stringBuffer.append("http://").append(choose.getHost()).append(":").append(choose.getPort()).append("/product/findall");
System.out.println(stringBuffer.toString());
RestTemplate rt=new RestTemplate();
ParameterizedTypeReference<List<Product>> typeReference=new ParameterizedTypeReference<List<Product>>() {};
//ResponseEntity:封装了返回值信息
ResponseEntity<List<Product>> response = rt.exchange(stringBuffer.toString(), HttpMethod.GET, null, typeReference);
List<Product> list = response.getBody();
return list;
}
/*返回托底数据*/
public List<Product> fallback(){
List<Product> list=new ArrayList<>();
list.add(new Product(-1,"抱歉,服务提供者给你放鸽子了~~~~"));
return list;
}
//根据ID查询商品
@Cacheable(key = "'product'+#id")
public Product getProductById(Integer id){
System.out.println("========GET======"+id);
return new Product(id,"新的商品");
}
/*根据ID*/
@Cacheable(key = "'product'+#id")
public void delProductById(Integer id){
System.out.println("========DEL======"+id);
}
}
修改
ProductController
package com.bjsxt.controller;
import com.bjsxt.pojo.Product;
import com.bjsxt.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class ProductConsumerController {
@Autowired
private ProductService us;
@RequestMapping("/consumer")
public List<Product> getUser(){
List<Product> list = us.getUser();
return list;
}
@RequestMapping(value = "/get",method = RequestMethod.GET)
public Product getPro(Integer id){
Product product = us.getProductById(id);
return product;
}
@RequestMapping(value = "/del",method = RequestMethod.GET)
public void delPro(Integer id){
us.delProductById(id);
}
}
请求合并
没合并的请求
请求合并
什么情况下使用请求合并
在微服务架构中,我们将一
个项目拆分成很多个独立的模块,这些独立的模块通过远程
调用来互相配合工作,但是,
在高并发情况下
,通信次数的增加会导致总的通信时间增加,
同时,
线程池的资源也是有限的
,高并发环境会导致有
大量的线程处于等待状态
,进而导致
响应延迟
,为了解决这些问题,我们需要来了解
Hystrix
的请求合并。
请求合并的缺点
设置请求合并之后,本来一
个请求可能
5ms
就搞定了,但是现在必须再等
10ms
看看还
有没有其他的请求
一
起的,这样
一
个请求的耗时就从
5ms
增加到
15ms
了,不过,如果我们
要发起的命令本身就是
一
个高延迟的命令,那么这个时候就可以使用请合并了,因为这个
时候时间窗的时间消耗就显得微不足道了,
另外高并发也是请求合并的
一个非常重要的场
景
修改
pom
文件添加
hystrix
坐标
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.11.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.bjsxt</groupId>
<artifactId>spring-boot-hystrix-batch-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-hystrix-batch-consumer</name>
<description>spring-boot-hystrix-batch-consumer</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR4</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--导入service-->
<dependency>
<groupId>com.bjsxt</groupId>
<artifactId>spring-boot-feign-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
修改
ProductService
package com.bjsxt.service;
import com.bjsxt.pojo.Product;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
@Service
public class ProductService {
/*利用hystrix合并请求*/
@HystrixCollapser(batchMethod = "batchProduct",scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
collapserProperties = {
//请求时间间隔在20ms之内的请求会被合并为一个请求,默认为10ms
@HystrixProperty(name = "timerDelayInMilliseconds",value = "20"),
//设置触发批处理执行之前,在批处理中允许的最大请求数.
@HystrixProperty(name ="maxRequestsInBatch",value = "200"),
})
public Future<Product> getProduct(Integer id){
System.out.println("======="+id+"======");
return null;
}
@HystrixCommand
/*调用provider服务方法*/
public List<Product> batchProduct(List<Integer> ids ){
for (Integer id : ids) {
System.out.println(id);
}
//假设是调用provider服务后返回的list
List<Product> list = new ArrayList<>();
list.add(new Product(1, "电视"));
list.add(new Product(2, "电脑"));
list.add(new Product(3, "冰箱"));
list.add(new Product(4, "手电筒"));
list.add(new Product(100,"list............"));
System.out.println("ddddddddddddddddddddddd");
return list;
}
}
修改
Controller
package com.bjsxt.controller;
import com.bjsxt.pojo.Product;
import com.bjsxt.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.Future;
@RestController
public class ProductConsumerController {
@Autowired
private ProductService us;
@RequestMapping("/consumer")
public void getUsers() throws Exception{
Future<Product> p1 = this.us.getProduct(1);
Future<Product> p2 = this.us.getProduct(2);
Future<Product> p3 = this.us.getProduct(3);
System.out.println(p1.get().toString());
System.out.println(p2.get().toString());
System.out.println(p3.get().toString());
}
}
请求合并参数介绍
服务熔断
修改启动类
package com.bjsxt;
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 //开启熔断器 断路器
@EnableEurekaClient
@SpringBootApplication
public class SpringBootHystrixBreakerConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHystrixBreakerConsumerApplication.class, args);
}
}
修改
ProductService
package com.bjsxt.service;
import com.bjsxt.pojo.Product;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
@Service
public class ProductService {
@Autowired
private LoadBalancerClient loadBalancerClient;// ribbon负载均衡器
@HystrixCommand(fallbackMethod = "fallback",
commandProperties = {
//默认20个;10s内请求数大于20个时就启动熔断器,当请求符合熔断条件时将触发getFallback()。
@HystrixProperty(name= HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value="10"),
//请求错误率大于50%时就熔断,然后for循环发起请求,当请求符合熔断条件时将触发getFallback()。
@HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value="50"),
//默认5秒;熔断多少秒后去尝试请求
@HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value="5000"),
})
public List<Product> getUsers(int flag) {
System.out.println(flag);
if (flag==1){
throw new RuntimeException();
}
// 选择调用的服务的名称
// ServiceInstance 封装了服务的基本信息,如 IP,端口
ServiceInstance si = loadBalancerClient.choose("yxfProductProvider");
// 拼接访问服务的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<>();
list.add(new Product(-1, "我是托底数据"));
return list;
}
}
修改
ProductController
package com.bjsxt.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.bjsxt.pojo.Product;
import com.bjsxt.service.ProductService;
@RestController
public class ProductController {
@Autowired
private ProductService userService;
@RequestMapping("/consumer")
public List<Product> getUsers(@RequestParam("flag") Integer flag){
return this.userService.getUsers(flag);
}
}
熔断参数
隔离
1
线程池隔离