一、概念
- 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
@EnableCircuitBreaker
public class ConsumerApplication {}
可以看到,我们类上的注解越来越多,在微服务中,经常会引入上面的三个注解,于是Spring就提供了一 个组合注解: @SpringCloudApplication
@Target(ElementType.TYPE)
@Retention( RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {}
因此,我们可以使用这个组合注解来代替之前的3个注解。
@SpringCloudApplication
public 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")
@s1f4j
public class Consumercontroller {
@Autowired
private RestTemplate restTemplate ;
@Autowired
private 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 {
@Autowired
private RestTemplate restTemplate ;
@Autowired
private Discoveryclient discoveryclient;
@GetMapping("{id}")
//@HystrixCommand(fallbackMethod = "queryByIdFallback")
@HystrixCommand
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 "对不起,网络太拥挤了! ";
}
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秒;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User queryById(Long id) {
try {
Thread.sleep(2000) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
return userMapper.selectByPrimaryKey(id) ;
}
}
测试:
可以发现,请求的时长已经到了2s+,证明配置生效了。如果把修改时间修改到2秒以下,又可以正常访问。