SpringCloud(六):Hystrix的请求合并【Greenwich 版】

上一篇我们说到了Hystrix的请求熔断和服务降级,接下来我们说说Hystrix的请求合并。

请求合并:把重复的请求批量的用一个HystrixCommand命令去执行,以减少通信消耗和线程数的占用,默认合并10ms内的请求
优点:减少了通信开销,
缺点:请求延迟增加

步骤:

  1. 自定义类继承HystrixCollapser类,去合并请求,合并成一个集合,并且返回各自的结果
  2. 继承HystrixCommand类,执行合并后的请求
  3. 初始化HystrixRequestContext,创建自定义类,传递查询条件

我们在进行请求合并之前先改造一下之前的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());
    }
}

启动程序之后访问该接口,返回结果
SpringCloud(六):Hystrix的请求合并【Greenwich 版】_第1张图片

注解的方式进行请求合并:

@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());
}

启动程序之后访问该接口,返回结果
SpringCloud(六):Hystrix的请求合并【Greenwich 版】_第2张图片
当关闭8003服务,会调用降级方法
SpringCloud(六):Hystrix的请求合并【Greenwich 版】_第3张图片

相关阅读

项目代码
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 版】

你可能感兴趣的:(SpringCloud)