1、 什么是熔断
一般是某个服务故障或者是异常引起的,类似现实世界中的‘保险丝’,当某个异常条件被触发,直接熔断整个服务,而不是一直等到此服务超时,为了防止防止整个系统的故障,而采用了一些保护措施。过载保护。
比如A服务的X功能依赖B服务的某个接口,当B服务接口响应很慢时,A服务X功能的响应也会被拖慢,进一步导致了A服务的线程都卡在了X功能上,A服务的其它功能也会卡主或拖慢。此时就需要熔断机制,即A服务不在请求B这个接口,而可以直接进行降级处理。
2、 什么是降级
服务器当压力剧增的时候,根据当前业务情况及流量,对一些服务和页面进行有策略的降级。以此缓解服务器资源的的压力,以保证核心业务的正常运行,同时也保持了客户和大部分客户的得到正确的相应。
3、熔断和降级异同
相同点:
1.从可用性和可靠性触发,为了防止系统崩溃
2.最终让用户体验到的是某些功能暂时不能用
不同点:
1.服务熔断一般是下游服务故障导致的,而服务降级一般是从整体系统负荷考虑,由调用方控制。
4、Netflix开源组件断路器Hystrix
文档地址:
https://github.com/Netflix/Hystrix
为什么用Hystrix
在一个分布式系统里,一个服务依赖多个服务,可能存在某个服务调用失败,比如超时、异常等,如何能够保证在一个依赖出问题的情况下,不会导致整体服务失败,通过Hystrix就可以解决。
Hystrix是如何实现它的目标的
(1)通过HystrixCommand或者HystrixObservableCommand来封装对外部依赖的访问请求,这个访问请求一般会运行在独立的线程中,资源隔离
(2)对于超出我们设定阈值的服务调用,直接进行超时,不允许其耗费过长时间阻塞住。这个超时时间默认是99.5%的访问时间,但是一般我们可以自己设置一下
(3)为每一个依赖服务维护一个独立的线程池,或者是semaphore,当线程池已满时,直接拒绝对这个服务的调用
(4)对依赖服务的调用的成功次数,失败次数,拒绝次数,超时次数,进行统计
(5)如果对一个依赖服务的调用失败次数超过了一定的阈值,自动进行熔断,在一定时间内对该服务的调用直接降级,一段时间后再自动尝试恢复
(6)当一个服务调用出现失败,被拒绝,超时,短路等异常情况时,自动调用fallback降级机制
(7)对属性和配置的修改提供近实时的支持
5、Hystrix实战
使用到的组件包括:Eureka、Feign包括以下三个项目:
(1)Eureka-server: 注册中心
(2)product-service :商品微服务
(3)order-service: 订单微服务
前面文章已经搭建过 这里不做赘述 这里只写order-service微服务
pom.xml 添加依赖
<!--hystrix依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
application.yml
server:
port: 8781
#指定注册中心地址
eureka:
client:
serviceUrl:
defaultZone: http://localhost:7001/eureka/
#服务的名称
spring:
application:
name: order-service
#开启feign支持hystrix (注意,一定要开启,旧版本默认支持,新版本默认关闭)
# #修改调用超时时间(默认是1秒就算超时)
feign:
hystrix:
enabled: true
client:
config:
default:
connectTimeout: 2000
readTimeout: 2000
#hystrix超时时间调整(默认是一秒)
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 2000
SpringBoot启动类
@SpringBootApplication
@EnableFeignClients
//添加熔断降级注解
@EnableCircuitBreaker
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
ProductClient
/**
* 商品服务客户端
* name = "product-service"是你调用服务端名称
* fallback = ProductClientFallback.class,后面是你自定义的降级处理类,降级类一定要实现ProductClient
*/
@FeignClient(name = "product-service",fallback = ProductClientFallback.class)
public interface ProductClient {
//这样组合就相当于http://product-service/api/v1/product/find
@GetMapping("/api/v1/product/find")
String findById(@RequestParam(value = "id") int id);
}
ProductClientFallback降级处理类
/**
* 针对商品服务,错降级处理
*/
@Component
public class ProductClientFallback implements ProductClient {
@Override
public String findById(int id) {
System.out.println("ProductClientFallback中的降级方法");
//这对gai该接口进行一些逻辑降级处理........
return null;
}
}
OrderController类
注意:fallbackMethod = "saveOrderFail"中的saveOrderFail方法中的参数类型,个数,顺序要和save一模一样,否则会报找不到saveOrderFail方法。
@RestController
@RequestMapping("api/v1/order")
public class OrderController {
@Autowired
private ProductOrderService productOrderService;
@RequestMapping("save")
//当调用微服务出现异常会降级到saveOrderFail方法中
@HystrixCommand(fallbackMethod = "saveOrderFail")
public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id") int productId){
return productOrderService.save(userId, productId);
}
//注意,方法签名一定要要和api方法一致
private Object saveOrderFail(int userId, int productId){
System.out.println("controller中的降级方法");
Map<String, Object> msg = new HashMap<>();
msg.put("code", -1);
msg.put("msg", "抢购人数太多,您被挤出来了,稍等重试");
return msg;
}
}
测试
异常情况
6、结合redis模拟熔断降级服务异常报警通知实战
主要是完善服务熔断处理,报警机制完善结合redis进行模拟短信通知用户下单失败。
pom.xml
<!--springboot整合redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
application.yml
#redis
spring:
application:
name: order-service
redis:
database: 0
host: 127.0.0.1
port: 6379
timeout: 2000
OrderController类
主要看降级方法的不同
@RestController
@RequestMapping("api/v1/order")
public class OrderController {
@Autowired
private ProductOrderService productOrderService;
//添加bean
@Autowired
private StringRedisTemplate redisTemplate;
@RequestMapping("save")
//当调用微服务出现异常会降级到saveOrderFail方法中
@HystrixCommand(fallbackMethod = "saveOrderFail")
public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id") int productId,HttpServletRequest request){
return productOrderService.save(userId, productId);
}
//注意,方法签名一定要要和api方法一致
private Object saveOrderFail(int userId, int productId, HttpServletRequest request){
//监控报警
String saveOrderKye = "save-order";
//有数据代表20秒内已经发过
String sendValue = redisTemplate.opsForValue().get(saveOrderKye);
final String ip = request.getRemoteAddr();
//新启动一个线程进行业务逻辑处理
new Thread( ()->{
if (StringUtils.isBlank(sendValue)) {
System.out.println("紧急短信,用户下单失败,请离开查找原因,ip地址是="+ip);
//发送一个http请求,调用短信服务 TODO
redisTemplate.opsForValue().set(saveOrderKye, "save-order-fail", 20, TimeUnit.SECONDS);
}else{
System.out.println("已经发送过短信,20秒内不重复发送");
}
}).start();
Map<String, Object> msg = new HashMap<>();
msg.put("code", -1);
msg.put("msg", "抢购人数太多,您被挤出来了,稍等重试");
return msg;
}
}
测试
7、 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、访问入口
http://localhost:8781/hystrix
Hystrix Dashboard输入: http://localhost:8781/actuator/hystrix.stream