1.前言
经过上一篇博客的Ribbon和OpenFeign学习后,接下来我们正式开始熔断机制的学习,目前常用的服务间调用组件有Hystrix,这里我们就重点来学习一下这个组件的使用。
2.Hystrix整合Ribbon
我们知道大量请求会阻塞在Tomcat服务器上,影响其它整个服务.在复杂的分布式架构的应用程序有很多的依赖,都会不可避免地在某些时候失败.高并发的依赖失败时如果没有隔离措施,当前应用服务就有被拖垮的风险。
Spring Cloud Netflix Hystrix就是隔离措施的一种实现,可以设置在某种超时或者失败情形下断开依赖调用或者返回指定逻辑,从而提高分布式系统的稳定性。
生活中举个例子,如电力过载保护器,当电流过大的的时候,出问题,过载器会自动断开,从而保护电器不受烧坏。因此Hystrix请求熔断的机制跟电力过载保护器的原理很类似。
比如:订单系统请求库存系统,结果一个请求过去,因为各种原因,网络超时,在规定几秒内没反应,或者服务本身就挂了,这时候更多的请求来了,不断的请求库存服务,不断的创建线程,因为没有返回,也就资源没有释放。
Hystrix特性:
- 请求熔断
当请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN)。 - 服务降级
当请求后端服务出现异常的时候, 服务降级就是用来提供一个基础服务,以便告诉后面的请求不要再来了。 - 依赖隔离
在Hystrix中, 主要通过线程池来实现资源隔离. 通常在使用的时候我们会根据调用的远程服务划分出多个线程池.比如说,一个服务调用两外两个服务,你如果调用两个服务都用一个线程池,那么如果一个服务卡在哪里,资源没被释放后面的请求又来了,导致后面的请求都卡在哪里等待,导致你依赖的A服务把你卡在哪里,耗尽了资源,也导致了你另外一个B服务也不可用了。这时如果依赖隔离,某一个服务调用A B两个服务,如果这时我有100个线程可用,我给A服务分配50个,给B服务分配50个,这样就算A服务挂了,我的B服务依然可以用。 - 请求缓存
比如一个请求过来请求我userId=1的数据,你后面的请求也过来请求同样的数据,这时我不会继续走原来的那条请求链路了,而是把第一次请求缓存过了,把第一次的请求结果返回给后面的请求。 - 请求合并
我依赖于某一个服务,我要调用N次,比如说查数据库的时候,我发了N条请求发了N条SQL然后拿到一堆结果,这时候我们可以把多个请求合并成一个请求,发送一个查询多条数据的SQL的请求,这样我们只需查询一次数据库,提升了效率。
事实上,Hystrix一般需要与Ribbon和OpenFeign配合使用,因为OpenFeign的内部已集成Hystrix,只需要调用相应api即可,所以这里首先演示Ribbon与OpenFeign的配合使用。
- 像之前一样建立名为hystrix-client的模块,同样记得在选择依赖时选择Spring Web、Eureka Discovery、Ribbon,Hystrix,如图所示:
- 修改配置文件application.yml,代码如下:
server:
port: 18766
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://${eureka.instance.hostname}:18761/eureka/
spring:
application:
name: service-hystrix
- 修改启动类,使用注解
@EnableCircuitBreaker
开启断路器的支持。代码如下:
package com.springclouddemo.hystrixclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class HystrixClientApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixClientApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
- 接下来,创建一个HiService类,表示要远程调用的接口服务,注意这里的uri名字要和调用的
application-name
相同,即上一篇博客中eureka-client
中配置过的。除此之外,使用注解@HystrixCommand
来提供服务降级,注解参数fallbackMethod
提供降级服务,代码如下:
package com.springclouddemo.hystrixclient.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
/**
* @author 莫息涛
* @Description: Ribbon的测试服务类
* @date 2020/2/24 11:54
*/
@Service
public class HiService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "hiServiceError")
public String hiService() {
return restTemplate.getForObject("http://SERVICE-HI/hi",String.class); //注册的服务名称
}
public String hiServiceError(){
return "Error";
}
}
- 创建一个HiController,调用HiService的方法,代码如下:
package com.springclouddemo.ribbonclient.controller;
import com.springclouddemo.ribbonclient.service.HiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 莫息涛
* @Description: Ribbon的对外测试接口
* @date 2020/2/24 11:54
*/
@RestController
public class HiController {
@Autowired
HiService hiService;
@RequestMapping(value = "/hi")
public String hi(){
return hiService.hiService();
}
}
- 按照顺序依次启动
eureka-server
,eureka-client
,hystrix-client
,访问注册中心页面,可以看到整合了hystrix的ribbon模块已经启动,如图所示:
- 访问
http://localhost:18766/hi
,可以看到如下显示结果,说明整合了hystrix的ribbon配置成功,如图所示:
- 关闭
eureka-client
,然后再次访问http://localhost:18766/hi
,可以看到如下显示结果,说明hystrix配置的降级服务成功,如图所示:
3.Hystrix整合OpenFeign
我们讲完了Ribbon与Hystrix的结合,那么接下来看看Feign中又是怎么使用Hystrix的。
因为OpenFeign内置了Hystrix的功能,所以我们不需要引入特殊的包。我们先来创建一个降级的服务。
用这篇博客写好的OpenFeign样例在上面进行扩展
- 新建一个fallback包,用于存放降级服务的相应方法,再新建HiFallback,作为降级服务的HiInterface 实现类,代码如下:
package com.springclouddemo.openfeignclient.fallback;
import com.springclouddemo.openfeignclient.Interface.HiInterface;
import org.springframework.stereotype.Component;
/**
* @author 莫息涛
* @Description: 测试的降级服务
* @date 2020/2/24 18:28
*/
@Component
public class HiFallback implements HiInterface {
@Override
public String hi() {
return "Error";
}
}
- 修改HiInterface,添加降级服务的
fallback
属性,代码如下:
package com.springclouddemo.openfeignclient.Interface;
import com.springclouddemo.openfeignclient.fallback.HiFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author 莫息涛
* @Description: Feign的测试接口
* @date 2020/2/24 10:48
*/
@FeignClient(value = "service-hi",fallback = HiFallback.class)
public interface HiInterface {
@RequestMapping(value = "/hi",method = RequestMethod.GET)
String hi();
}
- 修改application.yaml,由于Feign默认的Hystrix是关闭的,我们需要在配置文件中开启,代码如下:
server:
port: 18764
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://${eureka.instance.hostname}:18761/eureka/
spring:
application:
name: service-openfeign
feign:
hystrix:
enabled: true
- 按照顺序依次启动
eureka-server
,eureka-client
,openfeign-client
,访问注册中心页面,可以看到整合了hystrix的openfeign模块已经启动,如图所示:
- 访问
http://localhost:18764/hi
,可以看到如下显示结果,说明整合了hystrix的openfeign配置成功,如图所示:
- 关闭
eureka-client
,然后再次访问http://localhost:18764/hi
,可以看到如下显示结果,说明hystrix配置的降级服务成功,如图所示: