什么是请求合并?我们先来看两张图:

上方的两张图中,第二张可以看出服务端只执行了一次响应,这就是请求合并。客户端新增的请求合并模块,内部存在一个等待的时间窗口,将一定时间段内满足条件的请求进行合并,以此降低服务端的请求响应压力。

可以看出,请求合并是在客户端中实现的,接下来我们通过代码来实践一下。

首先给出服务端的代码,这里打印了入参ids,后续我们将通过这个入参打印的情况来对请求合并的情况进行观察。

  1. @RequestMapping(value = "/mergeTest", method = RequestMethod.GET)
  2. public List<Test> mergeTest(String ids) {
  3.  
  4. System.out.println("ids:{" + ids + "}");
  5. String[] strs = ids.split(",");
  6.  
  7. List<Test> lstResult = new ArrayList<Test>();
  8. for (int i = 0; i < strs.length; i++) {
  9. lstResult.add(new Test(i, Integer.parseInt(strs[i])));
  10. }
  11. return lstResult;
  12. }

这里补充说明一点,对于测试用的Test类必须存在空构造函数,否则在客户端收到应答后反序列化时可能会抛出如下异常:

  1. com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `hystrix.dto.Test` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
  2. at [Source: (PushbackInputStream); line: 1, column: 3] (through reference chain: java.lang.Object[][0])
  3. at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67) ~[jackson-databind-2.9.10.6.jar:2.9.10.6]
  4. at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1452) ~[jackson-databind-2.9.10.6.jar:2.9.10.6]
  5. at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1028) ~[jackson-databind-2.9.10.6.jar:2.9.10.6]
  6. at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1297) ~[jackson-databind-2.9.10.6.jar:2.9.10.6]
  7. at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326) ~[jackson-databind-2.9.10.6.jar:2.9.10.6]
  8. at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159) ~[jackson-databind-2.9.10.6.jar:2.9.10.6]
  9. at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:195) ~[jackson-databind-2.9.10.6.jar:2.9.10.6]
  10. at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:21) ~[jackson-databind-2.9.10.6.jar:2.9.10.6]
  11. at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4014) ~[jackson-databind-2.9.10.6.jar:2.9.10.6]
  12. at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3085) ~[jackson-databind-2.9.10.6.jar:2.9.10.6]
  13. at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:271) ~[spring-web-5.1.19.RELEASE.jar:5.1.19.RELEASE]
  14. at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:240) ~[spring-web-5.1.19.RELEASE.jar:5.1.19.RELEASE]
  15. at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:102) ~[spring-web-5.1.19.RELEASE.jar:5.1.19.RELEASE]
  16. at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:733) ~[spring-web-5.1.19.RELEASE.jar:5.1.19.RELEASE]
  17. at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:666) ~[spring-web-5.1.19.RELEASE.jar:5.1.19.RELEASE]
  18. at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:307) ~[spring-web-5.1.19.RELEASE.jar:5.1.19.RELEASE]
  19. at hystrix.service.TestService.mergeTest(TestService.java:53) ~[classes/:na]
  20. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
  21. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
  22. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
  23. at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]

接下来再看下客户端请求合并逻辑的实现,这里我们采用注解的方式来完成:

  1. @Service
  2. public class TestService {
  3.  
  4. @Autowired
  5. private RestTemplate restTemplate;
  6.  
  7. @Autowired
  8. private DiscoveryClient discoveryClient;
  9.  
  10. @HystrixCollapser(batchMethod = "mergeTest", collapserProperties = {
  11. @HystrixProperty(name = "timerDelayInMilliseconds", value = "3000")
  12. })
  13. public Future<Test> singleTest(Integer id) {
  14. return null;
  15. }
  16.  
  17. @HystrixCommand
  18. public List<Test> mergeTest(List<Integer> ids) {
  19.  
  20. ServiceInstance instance = discoveryClient.getInstances("spring-cloud-service-provider").get(0);
  21. String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/mergeTest";
  22. Test[] tests = restTemplate.getForObject(url + "?ids={1}", Test[].class, StringUtils.join(ids, ","));
  23. return Arrays.asList(tests);
  24. }
  25. }

singleTest通过@HystrixCollapser注解实现请求合并,并指定了合并后调用的方法为mergeTest(),同时设置了时间窗口的大小为3000毫秒。

最后就是测试代码:

  1. @GetMapping("/hystrix/test")
  2. public String helloHystrix() throws ExecutionException, InterruptedException {
  3.  
  4. HystrixRequestContext context = HystrixRequestContext.initializeContext();
  5. Future<Test> f1 = service.singleTest(101);
  6. Future<Test> f2 = service.singleTest(102);
  7. Future<Test> f3 = service.singleTest(103);
  8. Test t1 = f1.get();
  9. Test t2 = f2.get();
  10. Test t3 = f3.get();
  11. Thread.sleep(3000);
  12. Future<Test> f4 = service.singleTest(104);
  13. Test t4 = f4.get();
  14. context.close();
  15.  
  16. return t1.getIndex() + ", " + t2.getIndex() + ", " + t3.getIndex() + ", " + t4.getIndex();
  17. }

通过postman进行请求后,服务端打印信息如下:

可以看到前三条请求合并为一条汇总请求调用到服务端,由于第四条请求是在sleep(3000)之后进行发起,并未和前三条请求进入同一个时间窗口,因此单独调用到了服务端。

参考资料:

https://segmentfault.com/a/1190000011468804

https://www.cnblogs.com/yb-ken/p/15068392.html

Spring Cloud Hystrix 学习(三)请求合并的更多相关文章

  1. spring cloud: Hystrix(三):健康指数 health Indicator

    spring cloud: Hystrix(三):健康指数 health Indicator ribbon+hystrix 当使用Hystrix时(spring-cloud-starter-hystr ...

  2. Spring Cloud Hystrix 学习(一)

    在学习Hystrix之前,首先引入一个问题场景,服务雪崩.如下图所示: 可以看到,三个入口服务A.B.C最终都会请求到服务T.当服务T的请求过载,打满CPU都无法匹配请求的频率时,同步调用的上级服务就 ...

  3. spring cloud深入学习(三)-----服务消费

    在上一篇博文中简单实现了eureka-server以及eureka-provider,后面会实现eureka-cosumer,现在针对eureka做进一步的详解. 微服务整体架构 文字再美也没有图片直 ...

  4. Spring Cloud Hystrix 学习(二)熔断与降级

    今天来看下Hystrix的熔断与降级. 首先什么是降级?当请求超时.资源不足等情况发生时进行服务降级处理,不调用真实服务逻辑,而是使用快速失败(fallback)方式直接返回一个托底数据,保证服务链条 ...

  5. spring cloud深入学习(四)-----eureka源码解析、ribbon解析、声明式调用feign

    基本概念 1.Registe 一一服务注册当eureka Client向Eureka Server注册时,Eureka Client提供自身的元数据,比如IP地址.端口.运行状况指标的Uri.主页地址 ...

  6. 笔记:Spring Cloud Hystrix 异常处理、缓存和请求合并

    异常处理 在 HystrixCommand 实现的run方法中抛出异常,除了 HystrixBadRequestException之外,其他异常均会被Hystrix 认为命令执行失败并触发服务降级处理 ...

  7. 《Spring Cloud》学习(三) 容错保护!

    在微服务架构中,我们将系统拆分成了很多服务单元,各单元的应用间互相依赖.由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身间题出现调用故障或延迟,而 ...

  8. Spring Cloud学习 之 Spring Cloud Hystrix(基础知识铺垫)

    Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 文章目录 前述: 快速入门: 命令模式: RxJava: 前述: ​ 在微服务架构中, ...

  9. 第五章 服务容错保护:Spring Cloud Hystrix

    在微服务架构中,我们将系统拆分为很多个服务,各个服务之间通过注册与订阅的方式相互依赖,由于各个服务都是在各自的进程中运行,就有可能由于网络原因或者服务自身的问题导致调用故障或延迟,随着服务的积压,可能 ...

随机推荐

  1. NOIP 模拟 $34\; \rm Rectangle$

    题解 \(by\;zj\varphi\) 对于没有在同一行或同一列的情况,直接枚举右边界,左边界从大到小,用树状数组维护上下边界即可. 而对于有多个在一列或一行的情况,这些点将左右分成了几个区间,枚举 ...

  2. NOIP 模拟 7 考试总结

    T1 超级大水题,用 \(kmp\) 和 \(hash\) 均能过,但都忘了,结果只打了个暴力.难受.板子题,题解就不放了 Code #include<bits/stdc++.h> #de ...

  3. SpringBoot自定义参数验证器

    前要 之前我们介绍了JSR-303验证方式,十分的方便Spring都帮我们封装好了,但是对一些复杂的验证,还是需要更加灵活的验证器的. JSR-303验证器传送门:https://www.jiansh ...

  4. 依赖注入@Autowired@Primary@Quelifier使用

    @Autowired 注入声明的SpringBean对象,根据一定的规则首先按照注入的类型去查找,如果没有找到安装注入的名称去匹配你要注入的属性名称,如果都没有找到启动项目时抛出异常,@Autowir ...

  5. WPF 中的DataTemplate 的嵌套

    <Window x:Class="WPF.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xa ...

  6. springboot中添加事务注解

    1.首先在service层中的方法前添加@Transactional @Service public class UserService { @Autowired private UserMapper ...

  7. Android WorkManager 定时任务

    App有时可能需要定期运行某些工作.例如,可能要定期备份数据.上传信息到服务器,定期获取新的内容等等. 在app运行期间,我们使用Handler也可以完成定期的功能.在这里我们介绍WorkManage ...

  8. springboot @value无法赋值

    1解决方式在类上在加@Compent @Component@EnableBinding(Sink.class)public class ReceiveMessageListenerController ...

  9. n个容器取油问题再探

    在 韩信分油问题的拓展分析 里,最后给出了一般性的结论,即: 用 n (n > 1) 个不规则无刻度的容器从一个无穷大的油桶里取油,这些容器容量都为整数升,分别记为 a1, a2, ..., a ...

  10. python--接口自动化经常用到的pytest框架

    pytest常用的方法和原理 1.pytest的原理 pytest插件基于pluggy模块:pluggy有三个重要概念:HookspecMarker(用来定义hook函数),HookimplMarke ...