当HystrixCommand在调用期间发生异常时会导致服务降级,此时会转入指定的fallback方法中执行降级逻辑
@HystrixCommand(fallbackMethod = "fallback",ignoreExceptions = NullPointerException.class)
public User findUser(Integer id){
return restTemplate.getForObject("http://hello-service/user/{1}",User.class,id);
}
//注意降级策略的返回值必须与可能触发降级策略的调用相同
public User fallback(Integer id,Throwable t){//使用参数t获取造成服务降级的异常
log.info(t.getMessage());
User defaultUser = new User(3,"xjf");
return defaultUser;
}
可以指定注解中的ignoreExceptions属性指定异常直接抛出而不是执行降级逻辑,需要注意降级方法必须与原方法保持相同的返回值
Hystrix默认使用@HystrixCommand注解的方法名作为命令名称,可以对命令进行分组,默认同一组的命令使用相同的线程池。若存在业务上属于同组但执行环境需要隔离的情况,则可以指定线程划分。
@HystrixCommand(fallbackMethod = "fallback",ignoreExceptions = NullPointerException.class,
commandKey = "findUser",groupKey = "userGroup",threadPoolKey = "***")
Hystrix也可以缓存对同一服务的请求结果以减轻服务的负载压力,但需要注意这个缓存仅在同一个用户的请求上下文中有效。
开启HystrixContext
1.在主方法上加入注解@ServletComponentScan
2.创建HystrixRequestContextServletFilter
package com.cfh.eurekaconsumer.filter;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(filterName = "hystrixRequestContextServletFilter",urlPatterns = "/*",asyncSupported = true)
public class HystrixRequestContextServletFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//初始化Hystrix请求上下文
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
//请求正常通过
chain.doFilter(request, response);
} finally {
//关闭Hystrix请求上下文
context.shutdown();
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
使用@CacheResult注解开启缓存,指定cacheKeyMethod为缓存key值的生成策略。若不指定则默认使用HystrixCommand的所有参数的组合作为缓存的key
//使用ignoreExceptions可以让指定的异常不触发降级策略而是直接抛出
//使用commandKey与groupkey参数指定命令名与命令组名(默认同组的命令使用同一个线程池)
@HystrixCommand(fallbackMethod = "fallback",ignoreExceptions = NullPointerException.class,
commandKey = "findUser",groupKey = "userGroup")
@CacheResult(cacheKeyMethod = "getCacheKey")//开启缓存
public User findUser(Integer id){//指定缓存的key
return restTemplate.getForObject("http://hello-service/user/{1}",User.class,id);
}
指定id的值为缓存的key
public String getCacheKey(Integer id){
return String.valueOf(id);
}
当调用可能导致缓存失效的命令时需要使用@CacheRemove注解清空缓存,同时必须要指定生成相应缓存的CommandKey
@HystrixCommand
@CacheRemove(commandKey = "findUser")//由于更新user会导致缓存失效所以需要使用@CacheRemove注解清缓存
public void updateUser(Integer id){
log.info("清空失效缓存");
User updateUser = new User(id,"newName");
restTemplate.postForEntity("http://hello-service/user/update/{1}",updateUser,void.class,id);
}
4.聚合请求
hystrix提供了在一个请求窗口内对同一个服务的聚合请求的功能(需要服务放提供对聚合请求处理的服务),以减轻因为通信次数的增加导致的线程消耗与通信资源消耗。
通过@HystrixCollapser注解开启请求合并功能,并指定合并方法(合并方法必须是HystrixCommand)以及相关属性(如这里指定了一个请求窗口的大小)。
//开启请求合并器,设置合并窗口时间为100ms
@HystrixCollapser(batchMethod = "findAll",collapserProperties = {@HystrixProperty(name ="timerDelayInMilliseconds",value = "100")})
public User findUser2(Integer id){//指定缓存的key
return null;
}
聚合之后的请求
@HystrixCommand
//聚合之后的请求
public List<User> findAll(List<Integer> ids){
log.info(Thread.currentThread().getName());
ParameterizedTypeReference<List<User>> responseType = new ParameterizedTypeReference<List<User>>() {
};
//服务方提供了处理聚合请求的能力
return restTemplate.exchange("http://hello-service/users?ids={1}",HttpMethod.GET,
null,responseType,StringUtils.join(ids,",")).getBody();
}
Hystrix会将聚合请求的结果分用到各个单独请求的结果上,调用者的感知就好像调用了一个独立的方法。
因为聚合本身会带来一定的开销,因此建议只对高并发与高延迟的服务开启请求聚合功能
相关代码请参考:
https://github.com/Tinysakura/studyhistrix/commits/master