Spring Cloud Hystrix断路器
分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。
服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”.
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
一般情况对于服务依赖的保护主要有3中解决方案:熔断模式、隔离模式、限流模式
Spring Cloud Netflix实现了断路器,它是Hystrix
首先在服务端添加maven依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
服务熔断
熔断机制是应对雪崩效应的一种微服务链路保护机制。
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回"错误"的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand
这里我有3个服务端实例:
分别在3个实例启动类添加注解:@EnableCircuitBreaker
拿8001来说:
其他2个服务端一样
修改controller代码使用Hystris熔断器:
package com.ypp.springcloud.web.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.ypp.springcloud.entites.Dept;
import com.ypp.springcloud.service.DeptService;
@RestController
public class DeptController {
@Autowired
private DeptService service;
@Autowired
private DiscoveryClient client;
@RequestMapping(value = "/dept/add", method = RequestMethod.POST)
public boolean add(@RequestBody Dept dept) {
return service.addDept(dept);
}
@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
// 一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
@HystrixCommand(fallbackMethod = "fallBackGet")
public Dept get(@PathVariable("id") Long id) {
Dept dept = service.findById(id);
if (dept == null) {
throw new NullPointerException();
}
return dept;
}
public Dept fallBackGet(@PathVariable("id") Long id) {
return new Dept().setId(id).setDeptName("没有对应id:" + id + "的信息[email protected]")
.setDbSource("This data does not exist in mysql by 8001");
}
@RequestMapping(value = "/dept/list", method = RequestMethod.GET)
public List<Dept> list() {
return service.findAll();
}
}
其他2个服务端一样,只是用了8002/8003座区分,先查有id的1,再查数据库没有id的99.测试:
可以看到3个数据库都没有id为99的信息,然后给了提示语,使用了fallbackMethod指定的方法,但是现在是get,如果是其他方法呢?update、delete、insert、list.......,那不是每个都要写一个@HystrixCommand?
服务降级: 整体资源快不够了,忍痛将某些服务先关掉,待渡过难关,再开启回来。服务降级处理是在客户端实现完成的,与服务端没有关系
在之前用过Feign,api向外部暴露了服务接口DeptClientService.java
根据已经有的DeptClientService接口新建一个实现FallbackFactory接口的类DeptClientServiceFallbackFactory,记住,一定要加注解@Component
DeptClientServiceFallbackFactory.java:
package com.ypp.springcloud.service;
import java.util.List;
import org.springframework.stereotype.Component;
import com.ypp.springcloud.entites.Dept;
import feign.hystrix.FallbackFactory;
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService>{
@Override
public DeptClientService create(Throwable arg0) {
return new DeptClientService() {
@Override
public boolean insert(Dept dept) {
return false;
}
@Override
public Dept find(long id) {
return new Dept().setId(id).setDeptName("当前服务已经停止")
.setDbSource("no server");
}
@Override
public List<Dept> all() {
return null;
}
};
}
}
DeptClientService添加Hystrix熔断器备选响应的类,即当此服务不可用了,还是可以给予外部一些相应。就是上面的DeptClientServiceFallbackFactory
改造后的DeptClientService.java:
package com.ypp.springcloud.service;
import java.util.List;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.ypp.springcloud.entites.Dept;
@FeignClient(name = "YPPCLOUD-DEPT", fallbackFactory = DeptClientServiceFallbackFactory.class)
public interface DeptClientService {
// 访问的是8001 8002 8003里面的服务 http://YPPCLOUD-DEPT/dept/get/{id} 自带负载均衡
@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
public Dept find(@PathVariable("id") long id);
@RequestMapping(value = "/dept/list", method = RequestMethod.GET)
public List<Dept> all();
@RequestMapping(value = "/dept/add", method = RequestMethod.POST)
public boolean insert(Dept dept);
}
上面2个类的意思就是,当此服务不可用之后,fallbackFactory(备选响应)选择DeptClientServiceFallbackFactory来做,而DeptClientServiceFallbackFactory 必须要实现fallbackFactory它,里面帮DeptClientService 做一系列的备选响应。也就是里面的方法自己要处理的逻辑。还能实现解耦,之前@HystrixCommand的代码既可以不要了
等下把服务给停了,看还能不能给予外部一些友好的响应,就像银行中午有窗口停止了服务,但是窗口前放置了一个牌子:暂停服务
上面说的:服务降级处理是在客户端实现完成的,与服务端没有关系,所以再客户端80的application.yml添加如下配置开启服务降级处理:
feign:
hystrix:
enabled: true
访问测试.把所有的提供服务都关闭
这就是服务降级,当服务不可用,当渡过难关然后开启恢复,在服务降级的时也可以进行自己一系列逻辑业务的处理。并不一定非要给外部一些提示信息,这样的话感觉就没必要服务降级了
源码地址:https://pan.baidu.com/s/14zbcRLP8EJKpO51S9_tbqA 提取码:ikqh