简介
主页:链接: https://github.com/Netflix/Hystrix/
Hystix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务,防止出现级联失败。
雪崩问题
微服务中,服务间的调用关系错综复杂,一个请求,可能要调用多个微服务才能实现,会形成非常复杂的调用链路:
如图所示,一次业务请求调用了A、H、I、P四个服务,这四个服务还可能去调用其他服务,若果这时,某个服务出现了异常。
如图,例如此时I服务出现了异常,请求阻塞,用户请求就不会得到响应,则tomcat的这个线程就不会释放,随后越来越多的请求到来,就会导致越来越多的请求阻塞:
服务器支持的线程和并发数有限,请求一直被阻塞,就会导致服务器资源被耗尽,从而导致其他服务都不可用,形成雪崩效应。
而Hystrix就可以有效地解决雪崩问题,Hystrix解决雪崩问题的手段主要包括:
- 线程隔离
- 服务降级
线程隔离
原理
线程隔离示意图:
解读:
Hystrix为每个依赖服务调用分配了一个小的线程池,当线程池已满调用将被立即拒绝,默认不使用排队,加速失败判定时间。
用户请求不再直接访问服务,而是通过线程池中的空闲线程访问,如果线程池已满或者请求超时,则会进行降级处理。
服务降级
服务降级:可以优先保证核心服务。
用户的请求故障时,不会被阻塞,更不会无休止地等待或者看到系统的崩溃,至少能看到一个执行结果(例如一条友好提示)。
服务降级虽然会导致请求失败,但是不会导致请求阻塞,而且最多影响这个依赖服务对应的线程池中的资源,对其他服务没有影响。
会触发Hystrix服务降级的情况:
- 线程池已满
- 请求超时
代码实现
服务降级:及时返回服务调用失败的结果,让线程不因为等待服务而阻塞。
1.引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.开启熔断
在启动器类上添加注解:@EnableCircuitBreaker
package com.lt.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient //开启eureka客户端发现功能
@EnableCircuitBreaker //开启熔断器
//@SpringCloudApplication可用于替代上述三个注解
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
3.编写降级逻辑
当目标服务调用失败时,我们希望快速失败,给用户一个友好提示。因此需要提前编写好失败时的降级处理逻辑,要使用HystrixCommand来完成。
如下:
package com.lt.consumer.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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;
@RestController
@RequestMapping("/consumer")
@Slf4j
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{id}")
@HystrixCommand(fallbackMethod = "queryByIdFallBack")
public String queryById(@PathVariable("id") Long id){
if (id==2){
throw new RuntimeException("太忙了");
}
String url = "http://user-server/user/"+id;
return restTemplate.getForObject(url,String.class);
}
public String queryByIdFallback(Long id){
log.error("查询用户信息失败.id:{}",id);
return "对不起,网络太拥挤了";
}
}
注意:
- 熔断的降级逻辑方法必须和正常逻辑方法保证有相同的参数列表和返回值声明。
- 由于要返回友好提示,所以一般返回值声明为String类型
说明:
@HystrixCommand(fallbackMethod = “queryByIdFallBack”):用来声明一个降级逻辑的方法
启动测试:
如图所示,当服务正常运行时,我们可以正常访问,当服务停机时,就会进行服务降级,返回友好提示。
4.默认的fallback
当有很多方式时,给每一个方法都添加fallback会显得非常麻烦,此时,我们就可以把fallback写在类上,实现默认的fallback。
代码如下:
package com.lt.consumer.controller;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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;
@RestController
@RequestMapping("/consumer")
@Slf4j
@DefaultProperties(defaultFallback = "defaultFallback")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{id}")
@HystrixCommand
public String queryById(@PathVariable("id") Long id){
if (id==2){
throw new RuntimeException("太忙了");
}
String url = "http://user-server/user/"+id;
return restTemplate.getForObject(url,String.class);
}
public String defaultFallback(){
return "默认提示:对不起,网络太拥挤了!";
}
}
注意:在类上指明统一的失败降级方法,那么该类中所有方法返回值类型都要与处理失败的方法返回值类型一致
5.超时设置
Hystrix的默认超时时长为1s,我们可以通过修改配置修改这个值,如下:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 2000
注意单位是毫秒。
这个配置会作用于全局所有方法,此时只要当访问超时2s,就会触发服务降级,为了方便测试,可以给服务添加2s的睡眠时间来达到超时2s的目的。