上一篇我们说到了Hystrix的请求熔断和服务降级,接下来我们说说Hystrix的请求合并。
请求合并:把重复的请求批量的用一个HystrixCommand命令去执行,以减少通信消耗和线程数的占用,默认合并10ms内的请求
优点:减少了通信开销,
缺点:请求延迟增加
步骤:
我们在进行请求合并之前先改造一下之前的eureka-client的代码,添加下面的方法,用来模拟请求合并。这里我们创建一个list集合来模拟合并请求返回的结果,后面会合并三个请求,所以这里封装三个数据。
@RequestMapping("/merge")
public List<String> demo(String id){
System.out.println("合并之后的请求参数:"+id);
System.out.println("这里应该拿到单个的请求参数,分别去数据库中查询,为了方便我就用集合模拟了请求结果");
List<String> list = new ArrayList<>();
list.add("模拟请求结果:result1");
list.add("模拟请求结果:result2");
list.add("模拟请求结果:result3");
return list;
}
第一步:自定义类继承HystrixCollapser类
点进源码我们可以看到三个泛型分别是批处理的返回类型,单个请求的返回类型,请求参数类型
HystrixCollapser
public class MyHystrixCollapser extends HystrixCollapser<List<String>,String, Integer> {
private Integer id;
private RestTemplate restTemplate;
public MyHystrixCollapser(Integer id, RestTemplate restTemplate) {
//合并100ms内的请求,withTimerDelayInMilliseconds默认是10ms
super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("MyHystrixCollapser"))
.andCollapserPropertiesDefaults(HystrixCollapserProperties.Setter().
withTimerDelayInMilliseconds(100)));
this.id = id;
this.restTemplate = restTemplate;
}
/**
* 获取参数
* @return 每一个参数
*/
@Override
public Integer getRequestArgument() {
return id;
}
/**
* 创建HystrixCommand
* @param collection 合并之后的参数
* @return
*/
@Override
protected HystrixCommand<List<String>> createCommand(Collection<CollapsedRequest<String, Integer>> collection) {
List<Integer> ids = new ArrayList<>(collection.size());
for (CollapsedRequest<String, Integer> request : collection){
//将请求参数封装到list集合中
ids.add(request.getArgument());
}
//将封装过的参数传入HystrixCommand类,由HystrixCommand类执行
return new MyHystrixCommandMerge(restTemplate,ids);
}
/**
* 合并请求拿到结果,将请求结果按请求顺序分发给各个请求
* @param results 返回的结果集
* @param collection 合并的请求集
*/
@Override
protected void mapResponseToRequests(List<String> results, Collection<CollapsedRequest<String, Integer>> collection) {
int count = 0;
for (CollapsedRequest<String, Integer> request : collection) {
//分别拿到每个请求的结果
String result = results.get(count++);
//用该请求响应该结果(请求和结果对应)
request.setResponse(result);
}
}
}
第二步:继承HystrixCommand类,执行合并后的请求
public class MyHystrixCommandMerge extends HystrixCommand<List<String>> {
private RestTemplate restTemplate;
private List<Integer> ids;
protected MyHystrixCommandMerge(RestTemplate restTemplate, List<Integer> ids) {
super(HystrixCommandGroupKey.Factory.asKey("MyHystrixCommandMerge"));
this.restTemplate = restTemplate;
this.ids = ids;
}
@Override
protected List<String> run() throws Exception {
System.out.println("参数为:" + ids.toString() + "--" + Thread.currentThread().getName());
String[] result = restTemplate.getForEntity("http://eureka-client/merge?id={1}", String[].class, StringUtils.join(ids, ",")).getBody();
return Arrays.asList(result);
}
@Override
protected List<String> getFallback() {
List<String> list = new ArrayList<>();
list.add("合并请求出错");
return list;
}
}
controller层面:
@RestController
public class MyHystrixMergeController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/merge")
public void merge() throws ExecutionException, InterruptedException {
//进行初始化操作,不加这个会报错
HystrixRequestContext context = HystrixRequestContext.initializeContext();
MyHystrixCollapser mergeCollapser1 = new MyHystrixCollapser(1,restTemplate);
MyHystrixCollapser mergeCollapser2 = new MyHystrixCollapser(2,restTemplate);
MyHystrixCollapser mergeCollapser3 = new MyHystrixCollapser(3,restTemplate);
//这里必须使用异步请求,否则不会合并
Future<String> future1 = mergeCollapser1.queue();
Future<String> future2 = mergeCollapser2.queue();
Future<String> future3 = mergeCollapser3.queue();
System.out.println(future1.get());
System.out.println(future2.get());
System.out.println(future3.get());
}
}
@Service
public class MyHystrixMergeService {
@Autowired
private RestTemplate restTemplate;
//指定批处理的方法,设置合并200ms之内的请求
@HystrixCollapser(batchMethod = "getMerge", collapserProperties = {@HystrixProperty(name = "timerDelayInMilliseconds", value = "200")})
public Future<String> merge(Integer id){
//不会进入这个方法体
return null;
}
@HystrixCommand(fallbackMethod = "fallback")
public List<String> getMerge(List<Integer> ids){
System.out.println("合并的请求:"+ids.toString());
String[] result = restTemplate.getForEntity("http://eureka-client/merge?id={1}",String[].class, StringUtils.join(ids, ",")).getBody();
System.out.println("合并后的结果"+result);
return Arrays.asList(result);
}
}
/**
* 降级方法的参数,返回值类型,返回值数量要和上面的方法对应
* @param ids
* @return
*/
public List<String> fallback(List<Integer> ids){
List<String> list = new ArrayList<>();
list.add("请求合并失败-1");
list.add("请求合并失败-2");
list.add("请求合并失败-3");
return list;
}
controller中调用
@Autowired
private MyHystrixMergeService myHystrixMergeService;
@RequestMapping("/merge2")
public void merge3() throws ExecutionException, InterruptedException {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
Future<String> future1 = myHystrixMergeService.merge(1);
Future<String> future2 = myHystrixMergeService.merge(2);
Future<String> future3 = myHystrixMergeService.merge(3);
System.out.println(future1.get());
System.out.println(future2.get());
System.out.println(future3.get());
}
启动程序之后访问该接口,返回结果
当关闭8003服务,会调用降级方法
项目代码
SpringCloud 汇总【Greenwich 版】
SpringCloud(一):Eureka注册中心【Greenwich 版】
SpringCloud(二):Ribbon负载均衡【Greenwich 版】
SpringCloud(三):Feign声明式服务调用【Greenwich 版】
SpringCloud(四):Hystrix熔断器介绍【Greenwich 版】
SpringCloud(五):Hystrix的请求熔断与服务降级【Greenwich 版】
SpringCloud(六):Hystrix的请求合并【Greenwich 版】
SpringCloud(七):Hystrix仪表盘与Turbine集群监控【Greenwich 版】
SpringCloud(八):Zuul网关【Greenwich 版】
SpringCloud(九):Config配置中心【Greenwich 版】
SpringCloud(十):Bus消息总线【Greenwich 版】
SpringCloud(十一):Stream消息驱动 + RabbitMQ【Greenwich 版】
SpringCloud(十二):Sleuth链路跟踪【Greenwich 版】