SpringCloud入门案例-GitHub(含本文代码)
断路器的引入就是SpringCloud对分布式的不信任。现实中确实是这样的,分布式系统是不完全可靠的,总会出现服务断开、延迟等情况。以下两张截图均来自官方网站
使用spring-cloud-starter-hystrix入门。
涉及模块如下图
首先一定要弄清楚Hystrix作用于服务提供者。
所以有了第一个版本:
只需要在服务提供者的Controller中使用@HystrixCommand就可以实现服务熔断,防止服务雪崩。
@RestController
public class DeptController {
@Autowired
private DeptServiceImpl deptService;
@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
//一旦调用服务方法失败并抛出异常信息后,会自动调用fallbackMethod指定的方法
//这里的代码有很大的两个问题:1.每个方法都跟随一个fallbackMethod导致这个类方法膨胀;2.违背了Spring切面编程的思想,业务处理不纯粹了
//在api项目中使用的feign使用了接口编程的方式,利用AOP思想将hystrix熔断机制提取出去统一到了接口级别
@HystrixCommand(fallbackMethod = "processHystrix_Get")
public Dept get(@PathVariable("id") Long id){
Dept dept = this.deptService.get(id);
if (null == dept){
throw new RuntimeException("该ID:" + id + "没有对应的数据");
}
return dept;
}
//在api项目中已经将熔断提出到了接口上,这里就不需要这段代码,使此业务类能够纯粹的处理业务
public Dept processHystrix_Get(@PathVariable("id") Long id){
return new Dept().setDeptno(id).setDname("该ID" + id + "没有对应的数据").setDb_source("no this DataBase in Mysql");
}
}
在启动类上必须加@EnableCircuitBreaker注解
@SpringBootApplication
@EnableEurekaClient//本服务会自动注册进Eureka注册中心中
@EnableDiscoveryClient
@EnableCircuitBreaker//开启对熔断器的支持
public class DeptServiceMainApplicationHystrix8001 {
public static void main(String[] args) {
SpringApplication.run(DeptServiceMainApplicationHystrix8001.class, args);
}
}
上面的代码结果是查询数据中存在数据的时候会显示正常结果,但是一旦查询不到不会立马报错,而是立马返回我们期望返回的数据
如上面的代码截图框起来的描述,我们便发现了一个问题,这个服务类不”纯粹“了,违背了Spring的切面编程的思想,导致服务类变得很沉重。
所以我们尊重AOP的思想,必须将非业务代码移除出去。
在api公共服务项目中创建一个专门用于写Hystrix的类,专门实现服务熔断以及降级。
这里就说说何为降级,它与熔断是有何区别?熔断是服务出现异常,直接返回期望数据;而降级呢,是服务是正常的,只是此服务在集群中属于优先级较低的服务,当集群资源出现紧张的情况,此服务会暂时关闭,将资源留给更需要资源的服务(舍己为人)。
- 公共服务项目
@Component //千万不要忘记加Component注解
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public boolean add(Dept dept) {
return false;
}
@Override
public Dept get(Long id) {
return new Dept().setDeptno(id).setDname("该ID:" + id + "没有对应的数据,Consumer客户端提供的降级信息,此服务Provider已经停止" +
"关闭").setDb_source("no this DataBase in the Mysql");
}
@Override
public List<Dept> list() {
return null;
}
};
}
}
DeptClientService
//这里将hystrix服务提取到了接口层面
@FeignClient(value = "MICROSERVICECLOUD-PROVIDER-DEPT", fallbackFactory = DeptClientServiceFallbackFactory.class)
//@FeignClient(value = "MICROSERVICECLOUD-PROVIDER-DEPT")
public interface DeptClientService {
// @GetMapping("/consumer/dept/add")这里写客户端的访问地址是错误的,因为我们是给服务端做熔断器,所以使用服务端的RequestMapping,
//否则客户端所有请求都会执行服务降级,导致服务不可用
@RequestMapping(value = "/dept/add", method = RequestMethod.GET)
public boolean add(Dept dept);
// @GetMapping("/dept/get/{id}")
@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
public Dept get(@PathVariable("id") Long id);
@SuppressWarnings("unchecked")
// @GetMapping("/dept/list")
@RequestMapping(value = "/dept/list", method = RequestMethod.GET)
public List<Dept> list();
}
此时服务提供者中的代码就简化了
- 服务提供者
@RestController
public class DeptController {
@Autowired
private DeptServiceImpl deptService;
@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
public Dept get(@PathVariable("id") Long id){
Dept dept = this.deptService.get(id);
if (null == dept){
throw new RuntimeException("该ID:" + id + "没有对应的数据");
}
return dept;
}
}
服务提供者就很”纯粹“了。
- 服务提供者
由于配合使用了feign,所以服务消费者也有所改动。主要体现在它需要知道配置熔断器的类
@RestController
public class DeptController_Consumer_Feign {
//在之前使用了feign,所以使用了DeptClientService作为代理处理请求
@Autowired
private DeptClientService service;
//方便测试都使用的是HTTP的Get方法
//注意:这里不能使用GetMapping,会报404错误的
// @GetMapping("/consumer/dept/add")
@RequestMapping(value = "/consumer/dept/add", method = RequestMethod.GET)
public boolean add(Dept dept){
return service.add(dept);
}
// @GetMapping("/consumer/dept/get/{id}")
@RequestMapping(value = "/consumer/dept/get/{id}", method = RequestMethod.GET)
public Dept get(@PathVariable("id") Long id){
return service.get(id);
}
@SuppressWarnings("unchecked")
@RequestMapping(value = "/consumer/dept/list", method = RequestMethod.GET)
public List<Dept> list(){
return service.list();
}
}
启动类:
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.ittx.service.dept.feign"})
//这个注解非常重要
@ComponentScan({"com.ittx.springcloud","com.ittx.service.dept.hystrix"})
public class DeptConsumerFeignMainApplication {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerFeignMainApplication.class, args);
}
}
这样既实现了服务熔断和降级
服务熔断与之前效果一样。
模拟服务降级,将服务提供者关闭。
服务就算关闭了,此时也没有报错,而是返回我们期望的数据。
以上就是简单的hystrix入门案例。
代码不全面,需要全部代码后期会将GitHub链接奉上。
好了,案例总结完毕,就要总结启动服务消费者的时候报No fallbackFactory instance of type class错误的解决办法。 首先要明确这个是没有fallbackFactory实例,我们的项目中的DeptClientServiceFallbackFactory就是实现了FallbackFactory接口,也就是说这个类没有被实例化,没有进入IOC容器。
- 检查DeptClientServiceFallbackFactory类上有没有@Component注解,这个一定不要忘记
- 由于服务消费者使用的DeptClientService作为代理访问注册中心的服务,所以一定要检查FallbackFactory传入的泛型是否正确
- 在DeptClientService类上的@FeignClient注解中fallbackFactory属性值写正确否
- 在服务消费者的启动类中是否将FallbackFactory实现类扫描进IOC容器
这里就要说一下@ComponentScan和@SpringBootApplication一起使用的时候,@SpringBootApplication中的扫描注解将失效,所以在@Component注解中需要将本项目需要扫描和引入的其他项目需要扫描的包都要写进去才行。