一、概念
- Hystrix为每个依赖服务调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队,加速失败判定时间
- 用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,或者请求超时,则会进行降级处理(服务降级:优先保证核心服务,而非核心服务不可用或弱可用。)
用户的请求故障时,不会被阻塞,更不会无休止的等待或者看到系统崩溃,至少可以看到一个执行结果(例如返回友好的提示信息)
服务降级虽然会导致请求失败,但是不会导致阻塞,而且最多会影响这个依赖服务对应的线程池中的资源,对其它服务没有影响
触发Hystrix服务降级的情况:
- 线程池已满
- 请求超时
二、开发步骤
1、引入依赖
在consumer-demo消费端系统的pom.xml文件添加如下依赖:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency>
2、开启熔断
在启动类ConsumerApplication上添加注解: @EnableCircuitBreaker
@SpringBootApplication@EnableDiscoveryclient@EnableCircuitBreakerpublic class ConsumerApplication {}
可以看到,我们类上的注解越来越多,在微服务中,经常会引入上面的三个注解,于是Spring就提供了一 个组合注解: @SpringCloudApplication
@Target(ElementType.TYPE)@Retention( RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootApplication@EnableDiscoveryClient@EnableCircuitBreakerpublic @interface SpringCloudApplication {}
因此,我们可以使用这个组合注解来代替之前的3个注解。
@SpringCloudApplicationpublic class ConsumerApplication {}
3、编写降级逻辑
当目标服务的调用出现故障,我们希望快速失败,给用户一个友好提示。因此需要提前编写好失败时的降级处理逻辑,要使用HystrixCommand来完成。
改造consumer-demo\src\main\java\com\itheima\consumer\controller\Consumercontroller.java处理器
类,如下:
package com.itheima.consumer.controller;import com.itheima.consumer.pojo.User;import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;import lombok.extern.slf4j.s1f4j ;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.Discoveryclient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.Pathvariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;import java.util.List;@RestController@RequestMapping("/consumer")@s1f4jpublic class Consumercontroller {@Autowiredprivate RestTemplate restTemplate ;@Autowiredprivate Discoveryclient discoveryclient;@GetMapping("{id}")@HystrixCommand(fallbackMethod = "queryByIdFallback")public string queryById(@Pathvariable Long id) {string url = "http://localhost:9091/user/" + id;// 获取eureka中注册的user-service实例列表//List<ServiceInstance> serviceInstanceList = discoveryclient.getInstances("user-service");//ServiceInstance serviceInstance = serviceInstanceList.get(0);//url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort()+ "/user/" + id;url = "http://user-service/user/" + id;return restTemplate.getForobject(url,String.class);}public String queryByIdFallback (Long id){log.error("查询用户信息失败。id: {}", id);return "对不起,网络太拥挤了! ";}}
要注意;因为熔断的降级逻辑方法必须跟正常逻辑方法保证:相同的参数列表和返回值声明。 失败逻辑中返回User对象没有太大意义,一般会返回友好提示。所以把queryByld的方法改造为返回String, 反正也是son数据。这样失败逻辑中返回一个错误说明,会比较方便。
说明:
- @HystrixCommand(fallbackMethod = “queryByldFallBack”):用来声明一个降级逻辑的方法
测试:
当user-service正常提供服务时,访问与以前一致。但是当将user-service停机时,会发现页面返回了降级处理信息:
4、默认的FallBack
刚才把fallback写在了某个业务方法上,如果这样的方法很多,那岂不是要写很多。所以可以把Fallback配置加在类上,实现默认fallback;
再次改造consumer-demo\src\main\java\com\itheima\consumer\controller\Consumercontroller.java
package com.itheima.consumer.controller;import com.itheima.consumer.pojo.User;import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;import lombok.extern.slf4j.s1f4j ;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.Discoveryclient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.Pathvariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;import java.util.List;@RestController@RequestMapping("/consumer")@s1f4j@DefaultProperties(defaultFallback = "defaultFallback")public class Consumercontroller {@Autowiredprivate RestTemplate restTemplate ;@Autowiredprivate Discoveryclient discoveryclient;@GetMapping("{id}")//@HystrixCommand(fallbackMethod = "queryByIdFallback")@HystrixCommandpublic string queryById(@Pathvariable Long id) {string url = "http://localhost:9091/user/" + id;// 获取eureka中注册的user-service实例列表//List<ServiceInstance> serviceInstanceList = discoveryclient.getInstances("user-service");//ServiceInstance serviceInstance = serviceInstanceList.get(0);//url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort()+ "/user/" + id;url = "http://user-service/user/" + id;return restTemplate.getForobject(url,String.class);}public String queryByIdFallback (Long id){log.error("查询用户信息失败。id: {}", id);return "对不起,网络太拥挤了! ";}public String defaultFallback() {return "默认提示:对不起,网络太拥挤了! ";}}
- @DefaultProperties(defaultFallback = “defaultFallBack”):在类上指明统i一的失败降级方法;该类中所有方法返回类型要与处理失败的方法的返回类型一致。
5、超时设置
在之前的案例中,请求在超过1秒后都会返回错误信息,这是因为Hystrix的默认超时时长为1,我们可以通过配置修改这个值;修改consumer-demo\src\main\resources\application.yml添加如下配置:
hystrix:command :default:execution:isolation:thread:timeoutInMilliseconds: 2000
这个配置会作用于全局所有方法。为了方便复制到ymI配置文件中,可以复制hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=2000到yml文件中会自动格式化后再进行修改。
为了触发超时,可以在user-service\src\main\java\com\itheima\user\service\userService.java的方法中休眠2秒;
@Servicepublic class UserService {@Autowiredprivate UserMapper userMapper;public User queryById(Long id) {try {Thread.sleep(2000) ;} catch (InterruptedException e) {e.printStackTrace();}return userMapper.selectByPrimaryKey(id) ;}}
测试:
可以发现,请求的时长已经到了2s+,证明配置生效了。如果把修改时间修改到2秒以下,又可以正常访问。
