1、分布式核心知识之熔断、降级讲解
简介:系统负载过高,突发流量或者网络等各种异常情况介绍,常用的解决方案
1、熔断:
保险丝,熔断服务,为了防止整个系统故障,包含子和下游服务
有了熔断之后(将服务停掉了,其他服务不会调用,否则一直等待结果)
下单服务 -》商品服务
-》用户服务 (出现异常-》熔断)
2、降级:
抛弃一些非核心的接口和数据
旅行箱的例子:只带核心的物品,抛弃非核心的,等有条件的时候再去携带这些物品
3、熔断和降级互相交集
相同点:
1)从可用性和可靠性触发,为了防止系统崩溃
2)最终让用户体验到的是某些功能暂时不能用
不同点
1)服务熔断一般是下游服务故障导致的,而服务降级一般是从整体系统负荷考虑,由调用方控制
2、Netflix开源组件断路器Hystrix介绍
简介:介绍Hystrix基础知识和使用场景
文档地址:
https://github.com/Netflix/Hystrix
https://github.com/Netflix/Hystrix/wiki
1、什么是Hystrix?
1)hystrix对应的中文名字是“豪猪”
2)hystrix 英[hɪst'rɪks] 美[hɪst'rɪks]
2、为什么要用?
在一个分布式系统里,一个服务依赖多个服务,可能存在某个服务调用失败,
比如超时、异常等,如何能够保证在一个依赖出问题的情况下,不会导致整体服务失败,
通过Hystrix就可以解决
http://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#_circuit_breaker_hystrix_clients
3、提供了熔断、隔离、Fallback、cache、监控等功能
4、熔断后怎么处理?
出现错误之后可以 fallback 错误的处理信息
兜底数据
3、Feign结合Hystrix断路器开发实战《上》
简介:讲解SpringCloud整合断路器的使用,用户服务异常情况
1、加入依赖
注意:网上新旧版本问题,所以要以官网为主,不然部分注解会丢失
最新版本 2.0
1 <dependency> 2 <groupId>org.springframework.cloud</groupId> 3 <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> 4 </dependency>
2、增加注解
启动类里面增加注解
@EnableCircuitBreaker
注解越来越多-》 SpringCloudApplication注解
3、API接口编码实战
熔断-》降级
1)最外层api使用,好比异常处理(网络异常,参数或者内部调用问题)
api方法上增加 @HystrixCommand(fallbackMethod = "saveOrderFail")
order-service的controller方法里面
1 package com.po.order_service.controller; 2 3 import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 4 import com.po.order_service.service.ProductOrderService; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.web.bind.annotation.RequestMapping; 7 import org.springframework.web.bind.annotation.RequestParam; 8 import org.springframework.web.bind.annotation.RestController; 9 10 import java.util.HashMap; 11 import java.util.Map; 12 13 @RestController 14 @RequestMapping("api/vi/order") 15 public class ProductOrderController { 16 @Autowired 17 private ProductOrderService productOrderService; 18 @RequestMapping("save") 19 @HystrixCommand(fallbackMethod = "saveOrderFail")//调用save方法失败则执行saveOrderFail的方法 20 public Object save(@RequestParam("user_id")int userId,@RequestParam("product_id")int productId){ 21 Map<String, Object> data = new HashMap<>(); 22 data.put("code",0); 23 data.put("data",productOrderService.save(userId,productId)); 24 return data; 25 } 26 //编写fallback方法实现,方法签名一定要和api方法签名一致(注意点!!!) 27 private Object saveOrderFail(int userId,int productId){ 28 Map<String, Object> msg = new HashMap<>(); 29 msg.put("code",-1); 30 msg.put("msg","访问人数过多,您被挤出来了,请稍后访问!"); 31 return msg; 32 } 33 34 }
编写fallback方法实现,方法签名一定要和api方法签名一致(注意点!!!)
这样我们在调用save方法失败的时候,返回msg信息回去
访问:
http://localhost:8781/api/vi/order/save?product_id=3&user_id=5
{"code":0,"data":{"id":0,"productName":"\"iPhone6data from port = 8771\"","tradeNo":"b2969831-a249-4407-8626-6b33de4ac34d","price":6999,"createTime":"2018-12-28T13:50:26.434+0000","userId":5,"userName":null}}
补充: 修改maven仓库地址
1 pom.xml中修改 2 3 <repositories> 4 <repository> 5 <id>nexus-aliyun</id> 6 <name>Nexus aliyun</name> 7 <layout>default</layout> 8 <url>http://maven.aliyun.com/nexus/content/groups/public</url> 9 <snapshots> 10 <enabled>false</enabled> 11 </snapshots> 12 <releases> 13 <enabled>true</enabled> 14 </releases> 15 </repository> 16 </repositories>
4、Feign结合Hystrix断路器开发实战《下》
简介:讲解SpringCloud整合断路器的使用,用户服务异常情况
先看@FeignClient(name="product-service")
在包import org.springframework.cloud.openfeign.FeignClient;中,
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
注意:虽然spring-cloud-starter-openfeign依赖下面有hystrix的但是这个不支持@HystrixCommand注解的
1 <dependency> 2 <groupId>io.github.openfeign</groupId> 3 <artifactId>feign-hystrix</artifactId> 4 </dependency>
1、feign结合Hystrix
1)开启feign支持hystrix (注意,一定要开启,旧版本默认支持,新版本默认关闭)
1 server: 2 port: 8781 3 4 5 #指定注册中心地址 6 eureka: 7 client: 8 serviceUrl: 9 defaultZone: http://localhost:8761/eureka/ 10 11 #服务的名称 12 spring: 13 application: 14 name: order-service 15 16 #自定义负载均衡策略 17 product-service: 18 ribbon: 19 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule 20 21 22 #修改调用超时时间 注意hystrix和client同级 23 feign: 24 hystrix: 25 enabled: true 26 client: 27 config: 28 default: 29 connectTimeout: 2000 30 readTimeout: 2000
2)建自定义的异常包(回滚)fallback 建立ProductClientFallback,该类必须实现ProductClient接口(FeignClient源码有说明)
1 package com.po.order_service.fallback; 2 3 import com.po.order_service.service.ProductClient; 4 import org.springframework.stereotype.Component; 5 /* 6 针对商品服务,做降级处理 7 */ 8 @Component 9 public class ProductClientFallback implements ProductClient { 10 11 @Override 12 public String findById(int id) { 13 System.out.println("feign调用product-service 的findById出现异常"); 14 return null; 15 } 16 }
3)FeignClient(name="xxx", fallback=xxx.class ), class需要继承当前FeignClient的类
1 package com.po.order_service.service; 2 3 import com.po.order_service.fallback.ProductClientFallback; 4 import org.springframework.cloud.openfeign.FeignClient; 5 import org.springframework.web.bind.annotation.GetMapping; 6 import org.springframework.web.bind.annotation.RequestParam; 7 8 /* 9 商品服务客户端 10 */ 11 @FeignClient(name="product-service" ,fallback = ProductClientFallback.class) 12 public interface ProductClient {//ProductClientFallback.class就是我们自定义的异常类 13 //注意这里GetMapping的路径和product-service服务里面的findById方法的路径一致,id也是一样的 14 @GetMapping("/api/vi/product/findById") 15 String findById(@RequestParam(value ="id")int id); 16 }
此时假如product-service停止了
则访问时
http://localhost:8781/api/vi/order/save?product_id=2&user_id=5
{"msg":"访问人数过多,您被挤出来了,请稍后访问!","code":-1}
控制台
feign调用product-service 的findById出现异常
说明程序调用到了ProductClientFallback方法
5、熔断降级服务异常报警通知实战
简介:完善服务熔断处理,报警机制完善
1、加入redis依赖
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-data-redis</artifactId> 4 </dependency>
2、配置redis链接信息
1 server: 2 port: 8781 3 4 5 #指定注册中心地址 6 eureka: 7 client: 8 serviceUrl: 9 defaultZone: http://localhost:8761/eureka/ 10 11 #服务的名称 12 spring: 13 application: 14 name: order-service 15 redis: 16 database: 0 17 host: 127.0.0.1 18 port: 6379 19 timeout: 2000 20 #自定义负载均衡策略 21 product-service: 22 ribbon: 23 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule 24 25 26 #修改调用超时时间 27 feign: 28 hystrix: 29 enabled: true 30 client: 31 config: 32 default: 33 connectTimeout: 2000 34 readTimeout: 2000
3、使用
1 package com.po.order_service.controller; 2 3 import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 4 import com.po.order_service.service.ProductOrderService; 5 import com.sun.org.apache.xml.internal.security.keys.storage.implementations.CertsInFilesystemDirectoryResolver; 6 import io.netty.util.internal.StringUtil; 7 import org.apache.commons.lang.StringUtils; 8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.data.redis.core.StringRedisTemplate; 10 import org.springframework.web.bind.annotation.RequestMapping; 11 import org.springframework.web.bind.annotation.RequestParam; 12 import org.springframework.web.bind.annotation.RestController; 13 14 import javax.servlet.http.HttpServletRequest; 15 import java.util.HashMap; 16 import java.util.Map; 17 import java.util.concurrent.TimeUnit; 18 19 @RestController 20 @RequestMapping("api/vi/order") 21 public class ProductOrderController { 22 @Autowired 23 private ProductOrderService productOrderService; 24 @Autowired 25 private StringRedisTemplate redisTemplate; 26 @RequestMapping("save") 27 @HystrixCommand(fallbackMethod = "saveOrderFail")//调用save方法失败则执行saveOrderFail的方法 28 public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id")int productId, HttpServletRequest request){ 29 Map<String, Object> data = new HashMap<>(); 30 data.put("code",0); 31 data.put("data",productOrderService.save(userId,productId)); 32 return data; 33 } 34 //编写fallback方法实现,方法签名一定要和api方法签名一致(注意点!!!) 35 private Object saveOrderFail(int userId,int productId,HttpServletRequest request){ 36 //监控报警 37 String saveOrderKey="save-order"; 38 String sendValue = redisTemplate.opsForValue().get(saveOrderKey); 39 final String ip = request.getRemoteAddr(); 40 41 //发送短信应该是异步的所以应该开启一个线程,就不会阻塞 42 new Thread(()->{ 43 if(StringUtils.isBlank(sendValue)){//第二次别的服务调用时sendValue部位空 44 System.out.println("紧急短信,用户下单失败,请离开查找原因,ip地址是="+ip); 45 redisTemplate.opsForValue().set(saveOrderKey,"save-order-fail",20, TimeUnit.SECONDS); 46 }else { 47 System.out.println("已经发送过短信,20秒内不重复发送"); 48 } 49 50 }).start(); 51 52 Map<String, Object> msg = new HashMap<>(); 53 msg.put("code",-1); 54 msg.put("msg","访问人数过多,您被挤出来了,请稍后访问!"); 55 return msg; 56 } 57 58 }
知道ip 这样的话我们就知道哪个服务出了问题
当product-service服务停了,我们访问
http://localhost:8781/api/vi/order/save?product_id=2&user_id=5
{"msg":"访问人数过多,您被挤出来了,请稍后访问!","code":-1}
看控制台
feign调用product-service 的findById出现异常 紧急短信,用户下单失败,请离开查找原因,ip地址是=0:0:0:0:0:0:0:1 feign调用product-service 的findById出现异常 已经发送过短信,20秒内不重复发送 feign调用product-service 的findById出现异常 已经发送过短信,20秒内不重复发送 feign调用product-service 的findById出现异常 已经发送过短信,20秒内不重复发送 2018-12-30 11:54:59.098 INFO 8116 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver : Resolving eureka endpoints via configuration 紧急短信,用户下单失败,请离开查找原因,ip地址是=0:0:0:0:0:0:0:1 //20之后访问,此时sendValue为null,又发送短信
feign调用product-service 的findById出现异常
选redis的原因:当其他服务也会从redis里面拿数据,起到共享的作用,防止多次发送短信
6、高级篇幅之深入源码剖析Hystrix降级策略和调整
简介:源码分析Hystrix降级策略和调整
问题:当我们调用product-service服务的findById时候,让它休眠2s
1 @RequestMapping("findById") 2 public Product findById(@RequestParam("id") int id){ 3 try {//2s 4 TimeUnit.SECONDS.sleep(2); 5 } catch (Exception e) { 6 e.printStackTrace(); 7 } 8 Product product = productService.findProduct(id); 9 Product reslut = new Product(); 10 //将product复制到result中 11 BeanUtils.copyProperties(product,reslut); 12 reslut.setName(reslut.getName()+"data from port = "+port); 13 return reslut; 14 }
1)此时通过order-service服务调用findById方法
http://localhost:8781/api/vi/order/save?product_id=2&user_id=5
2)控制台,由于我们处理了异常这是我们看到的情况,实际这个异常就是超时
feign调用product-service 的findById出现异常 2018-12-30 13:24:07.361 INFO 8472 --- [ HystrixTimer-1] io.lettuce.core.EpollProvider : Starting without optional epoll library 2018-12-30 13:24:07.363 INFO 8472 --- [ HystrixTimer-1] io.lettuce.core.KqueueProvider : Starting without optional kqueue library 紧急短信,用户下单失败,请离开查找原因,ip地址是=0:0:0:0:0:0:0:1
3)我们修改feign的异常超时时间为4s
1 feign: 2 hystrix: 3 enabled: true 4 client: 5 config: 6 default: 7 connectTimeout: 4000 8 readTimeout: 4000
4)再访问,还是同样的问题,实际上这里还有一个超时没有配就是Hystrix的超时没有配置
方法一:不建议实际开中超时肯定是设置的
1 #把hystrix超时时间禁用 2 hystrix: 3 command: 4 default: 5 execution: 6 timeout: 7 enabled: false
http://localhost:8781/api/vi/order/save?product_id=2&user_id=5 {"code":0,"data":{"id":0,"productName":"\"iPhone7data from port = 8771\"","tradeNo":"e5717768-eb75-421e-8229-46637020acc2","price":7999,"createTime":"2018-12-30T05:39:03.618+0000","userId":5,"userName":null}}
方法二:超时时间调整(推荐)
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 4000
1、查看默认讲解策略 HystrixCommandProperties(搜索HystrixCommand类》》右边列表HystrixCommandProperties)
1)execution.isolation.strategy 隔离策略
THREAD 线程池隔离 (默认)
防止某一个服务过多占用服务器的线程,导致其他服务处于等待最后整个服务雪崩的。(Hystrix的作用)
SEMAPHORE 信号量
信号量适用于接口并发量高的情况,如每秒数千次调用的情况,导致的线程开销过高,通常只适用于非网络调用,执行速度快
2)execution.isolation.thread.timeoutInMilliseconds(HystrixCommandProperties源码)
超时时间
默认 1000毫秒
3)execution.timeout.enabled 是否开启超时限制 (一定不要禁用)
4)execution.isolation.semaphore.maxConcurrentRequests 隔离策略为 信号量的时候,如果达到最大并发数时,后续请求会被拒绝,默认是10
官方文档:
https://github.com/Netflix/Hystrix/wiki/Configuration#execution.isolation.strategy
7、断路器Dashboard监控仪表盘实战(开发中用的少)
简介:讲解断路器Dashboard基础使用和查看
1、加入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
2、启动类增加注解
@EnableHystrixDashboard
3、配置文件增加endpoint
management:
endpoints:
web:
exposure:
include: "*"
4、访问入口
访问前将超时改一下
server:
port: 8781
#指定注册中心地址
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
#服务的名称
spring:
application:
name: order-service
redis:
database: 0
host: 127.0.0.1
port: 6379
timeout: 2000
#自定义负载均衡策略
product-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
#修改调用超时时间
feign:
hystrix:
enabled: true
client:
config:
default:
connectTimeout: 2000
readTimeout: 1000
#把hystrix超时时间禁用
#hystrix:
# command:
# default:
# execution:
# timeout:
# enabled: false
#properties的格式
#execution.isolation.thread.timeoutInMilliseconds=4000
#设置超时时间yml格式
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 1000
management:
endpoints:
web:
exposure:
include: "*"
控制台:
Proxy opening connection to: http://localhost:8781/actuator/hystrix.stream
参考资料
默认开启监控配置
https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-security-actuator
配置文件类:
spring-configuration-metadata.json
8、断路器监控仪表参数讲解和模拟熔断
简介:讲解 断路器监控仪表盘参数和模拟熔断
浏览器输入:
http://localhost:8781/hystrix
Hystrix Dashboard输入: http://localhost:8781/actuator/hystrix.stream
监控仪表参数页面
1、sse server-send-event推送到前端
资料:https://github.com/Netflix/Hystrix/wiki/Dashboard