Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Spring Cloud Hystrix的请求合并

Spring Cloud Hystrix的请求合并

作者头像
程序猿DD
发布于 2018-02-01 11:12:28
发布于 2018-02-01 11:12:28
82100
代码可运行
举报
文章被收录于专栏:程序猿DD程序猿DD
运行总次数:0
代码可运行

通常微服务架构中的依赖通过远程调用实现,而远程调用中最常见的问题就是通信消耗与连接数占用。在高并发的情况之下,因通信次数的增加,总的通信时间消耗将会变的不那么理想。同时,因为对依赖服务的线程池资源有限,将出现排队等待与响应延迟的情况。为了优化这两个问题,Hystrix提供了HystrixCollapser来实现请求的合并,以减少通信消耗和线程数的占用。

HystrixCollapser实现了在HystrixCommand之前放置一个合并处理器,它将处于一个很短时间窗(默认10毫秒)内对同一依赖服务的多个请求进行整合并以批量方式发起请求的功能(服务提供方也需要提供相应的批量实现接口)。通过HystrixCollapser的封装,开发者不需要去关注线程合并的细节过程,只需要关注批量化服务和处理。下面我们从HystrixCollapser的使用实例,对其合并请求的过程一探究竟。

Hystrix的请求合并示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class HystrixCollapser<BatchReturnType, ResponseType, RequestArgumentType> implements 
        HystrixExecutable<ResponseType>, HystrixObservable<ResponseType> {
    ...
    public abstract RequestArgumentType getRequestArgument();

    protected abstract HystrixCommand<BatchReturnType> createCommand(Collection<CollapsedRequest<ResponseType, RequestArgumentType>> requests);

    protected abstract void mapResponseToRequests(BatchReturnType batchResponse, Collection<CollapsedRequest<ResponseType, RequestArgumentType>> requests);
    ...
}

HystrixCollapser抽象类的定义中可以看到,它指定了三个不同的类型:

  • BatchReturnType:合并后批量请求的返回类型
  • ResponseType:单个请求返回的类型
  • RequestArgumentType:请求参数类型

而对于这三个类型的使用可以在它的三个抽象方法中看到:

  • RequestArgumentTypegetRequestArgument():该函数用来定义获取请求参数的方法。
  • HystrixCommand<BatchReturnType>createCommand(Collection<CollapsedRequest<ResponseType,RequestArgumentType>>requests):合并请求产生批量命令的具体实现方法。
  • mapResponseToRequests(BatchReturnTypebatchResponse,Collection<CollapsedRequest<ResponseType,RequestArgumentType>>requests):批量命令结果返回后的处理,这里需要实现将批量结果拆分并传递给合并前的各个原子请求命令的逻辑。

接下来,我们通过一个简单的示例来直观的理解实现请求合并的过程。

假设,当前微服务 USER-SERVICE提供了两个获取 User的接口:

  • /users/{id}:根据id返回User对象的GET请求接口。
  • /users?ids={ids}:根据ids参数返回User对象列表的GET请求接口,其中ids为以逗号分割的id集合。

而在服务消费端,为这两个远程接口已经通过 RestTemplate实现了简单的调用,具体如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public User find(Long id) {
        return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id);
    }

    @Override
    public List<User> findAll(List<Long> ids) {
        return restTemplate.getForObject("http://USER-SERVICE/users?ids={1}", List.class, StringUtils.join(ids, ","));
    }

}

接着,我们来实现将短时间内多个获取单一User对象的请求命令进行合并的实现:

  • 第一步:为请求合并的实现准备一个批量请求命令的实现,具体如下:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class UserBatchCommand extends HystrixCommand<List<User>> {

    UserService userService;
    List<Long> userIds;

    public UserBatchCommand(UserService userService, List<Long> userIds) {
        super(Setter.withGroupKey(asKey("userServiceCommand")));
        this.userIds = userIds;
        this.userService = userService;
    }

    @Override
    protected List<User> run() throws Exception {
        return userService.findAll(userIds);
    }

}

批量请求命令实际上就是一个简单的HystrixCommand实现,从上面的实现中可以看到它通过调用 userService.findAll方法来访问 /users?ids={ids}接口以返回User的列表结果。

  • 第二步,通过继承 HystrixCollapser实现请求合并器:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class UserCollapseCommand extends HystrixCollapser<List<User>, User, Long> {

    private UserService userService;
    private Long userId;

    public UserCollapseCommand(UserService userService, Long userId) {
        super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("userCollapseCommand")).andCollapserPropertiesDefaults(
                HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(100)));
        this.userService = userService;
        this.userId = userId;
    }

    @Override
    public Long getRequestArgument() {
        return userId;
    }

    @Override
    protected HystrixCommand<List<User>> createCommand(Collection<CollapsedRequest<User, Long>> collapsedRequests) {
        List<Long> userIds = new ArrayList<>(collapsedRequests.size());
        userIds.addAll(collapsedRequests.stream().map(CollapsedRequest::getArgument).collect(Collectors.toList()));
        return new UserBatchCommand(userService, userIds);
    }

    @Override
    protected void mapResponseToRequests(List<User> batchResponse, Collection<CollapsedRequest<User, Long>> collapsedRequests) {
        int count = 0;
        for (CollapsedRequest<User, Long> collapsedRequest : collapsedRequests) {
            User user = batchResponse.get(count++);
            collapsedRequest.setResponse(user);
        }
    }

}

在上面的构造函数中,我们为请求合并器设置了时间延迟属性,合并器会在该时间窗内收集获取单个User的请求并在时间窗结束时进行合并组装成单个批量请求。下面 getRequestArgument方法返回给定的单个请求参数userId,而 createCommandmapResponseToRequests是请求合并器的两个核心:

  • createCommand:该方法的 collapsedRequests参数中保存了延迟时间窗中收集到的所有获取单个User的请求。通过获取这些请求的参数来组织上面我们准备的批量请求命令 UserBatchCommand实例。
  • mapResponseToRequests:在批量命令 UserBatchCommand实例被触发执行完成之后,该方法开始执行,其中 batchResponse参数保存了 createCommand中组织的批量请求命令的返回结果,而 collapsedRequests参数则代表了每个被合并的请求。在这里我们通过遍历批量结果 batchResponse对象,为 collapsedRequests中每个合并前的单个请求设置返回结果,以此完成批量结果到单个请求结果的转换。

请求合并的原理分析

下图展示了在未使用 HystrixCollapser请求合并器之前的线程使用情况。可以看到当服务消费者同时对 USER-SERVICE/users/{id}接口发起了五个请求时,会向该依赖服务的独立线程池中申请五个线程来完成各自的请求操作。

而在使用了 HystrixCollapser请求合并器之后,相同情况下的线程占用如下图所示。由于同一时间发生的五个请求处于请求合并器的一个时间窗内,这些发向 /users/{id}接口的请求被请求合并器拦截下来,并在合并器中进行组合,然后将这些请求合并成一个请求发向 USER-SERVICE的批量接口 /users?ids={ids},在获取到批量请求结果之后,通过请求合并器再将批量结果拆分并分配给每个被合并的请求。从图中我们可以看到以来,通过使用请求合并器有效地减少了对线程池中资源的占用。所以在资源有效并且在短时间内会产生高并发请求的时候,为避免连接不够用而引起的延迟可以考虑使用请求合并器的方式来处理和优化。

使用注解实现请求合并器

在快速入门的例子中,我们使用 @HystrixCommand注解优雅地实现了 HystrixCommand的定义,那么对于请求合并器是否也可以通过注解来定义呢?答案是肯定!

以上面实现的请求合并器为例,也可以通过如下方式实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class UserService {

    @Autowired
    private RestTemplate restTemplate;

    @HystrixCollapser(batchMethod = "findAll", collapserProperties = {
            @HystrixProperty(name="timerDelayInMilliseconds", value = "100")
    })
    public User find(Long id) {
        return null;
    }

    @HystrixCommand
    public List<User> findAll(List<Long> ids) {
        return restTemplate.getForObject("http://USER-SERVICE/users?ids={1}", List.class, StringUtils.join(ids, ","));
    }
}

@HystrixCommand我们之前已经介绍过了,可以看到这里通过它定义了两个Hystrix命令,一个用于请求 /users/{id}接口,一个用于请求 /users?ids={ids}接口。而在请求 /users/{id}接口的方法上通过 @HystrixCollapser注解为其创建了合并请求器,通过 batchMethod属性指定了批量请求的实现方法为 findAll方法(即:请求 /users?ids={ids}接口的命令),同时通过 collapserProperties属性为合并请求器设置相关属性,这里使用 @HystrixProperty(name="timerDelayInMilliseconds",value="100")将合并时间窗设置为100毫秒。这样通过 @HystrixCollapser注解简单而又优雅地实现了在 /users/{id}依赖服务之前设置了一个批量请求合并器。

请求合并的额外开销

虽然通过请求合并可以减少请求的数量以缓解依赖服务线程池的资源,但是在使用的时候也需要注意它所带来的额外开销:用于请求合并的延迟时间窗会使得依赖服务的请求延迟增高。比如:某个请求在不通过请求合并器访问的平均耗时为5ms,请求合并的延迟时间窗为10ms(默认值),那么当该请求的设置了请求合并器之后,最坏情况下(在延迟时间窗结束时才发起请求)该请求需要15ms才能完成。

由于请求合并器的延迟时间窗会带来额外开销,所以我们是否使用请求合并器需要根据依赖服务调用的实际情况来选择,主要考虑下面两个方面:

  • 请求命令本身的延迟。如果依赖服务的请求命令本身是一个高延迟的命令,那么可以使用请求合并器,因为延迟时间窗的时间消耗就显得莫不足道了。
  • 延迟时间窗内的并发量。如果一个时间窗内只有1-2个请求,那么这样的依赖服务不适合使用请求合并器,这种情况下不但不能提升系统性能,反而会成为系统瓶颈,因为每个请求都需要多消耗一个时间窗才响应。相反,如果一个时间窗内具有很高的并发量,并且服务提供方也实现了批量处理接口,那么使用请求合并器可以有效的减少网络连接数量并极大地提升系统吞吐量,此时延迟时间窗所增加的消耗就可以忽略不计了。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-11-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序猿DD 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Spring Cloud中Hystrix的请求合并
在微服务架构中,我们将一个项目拆分成很多个独立的模块,这些独立的模块通过远程调用来互相配合工作,但是,在高并发情况下,通信次数的增加会导致总的通信时间增加,同时,线程池的资源也是有限的,高并发环境会导致有大量的线程处于等待状态,进而导致响应延迟,为了解决这些问题,我们需要来了解Hystrix的请求合并 ---- Hystrix中的请求合并,就是利用一个合并处理器,将对同一个服务发起的连续请求合并成一个请求进行处理(这些连续请求的时间窗默认为10ms),在这个过程中涉及到的一个核心类就是HystrixColl
江南一点雨
2018/04/02
1.4K0
Spring Cloud中Hystrix的请求合并
Spring Cloud Hystrix:服务容错保护
在微服务架构中,服务与服务之间通过远程调用的方式进行通信,一旦某个被调用的服务发生了故障,其依赖服务也会发生故障,此时就会发生故障的蔓延,最终导致系统瘫痪。Hystrix实现了断路器模式,当某个服务发生故障时,通过断路器的监控,给调用方返回一个错误响应,而不是长时间的等待,这样就不会使得调用方由于长时间得不到响应而占用线程,从而防止故障的蔓延。Hystrix具备服务降级、服务熔断、线程隔离、请求缓存、请求合并及服务监控等强大功能。
macrozheng
2019/09/19
5540
Spring Cloud Hystrix:服务容错保护
Hystrix注解的使用(二)
@HystrixCollapser注解用于实现请求合并功能,将多个请求合并成一个请求,从而减少网络开销。该注解必须与@HystrixCommand注解一起使用,通常使用在获取批量数据的场景中。
堕落飞鸟
2023/04/08
4320
Spring Cloud 之 Hystrix.
 在微服务架构中,我们将系统拆分成了很多服务单元,各单元的应用间通过服务注册与订阅的方式互相依赖。由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身间题出现调用故障或延迟,而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加,最后就会因等待出现故障的依赖方响应形成任务积压,最终导致自身服务的瘫痪。
JMCui
2019/07/15
5780
Spring Cloud 之 Hystrix.
SpringCloud-Hystrix【解决灾难性雪崩-请求合并】
  没有合并的场景中,对于provider的调用会非常的频繁,容易造成处理不过来的情况
用户4919348
2019/06/13
7610
项目自从用了接口请求合并,效率直接加倍!
假设我们3个用户(用户id分别是1、2、3),现在他们都要查询自己的基本信息,请求到服务器,服务器端请求数据库,发出3次请求。我们都知道数据库连接资源是相当宝贵的,那么我们怎么尽可能节省连接资源呢?
码猿技术专栏
2023/05/01
4360
项目自从用了接口请求合并,效率直接加倍!
熔断器 Hystrix 源码解析 —— 命令合并执行
本文主要基于 Hystrix 1.5.X 版本 1. 概述 2. HystrixCollapser 2.1 构造方法 2.2 执行命令方式 2.3 核心方法 3. RequestCollapserFactory 4. RequestCollapser 4.1 构造方法 4.2 RequestBatch 4.3 #submitRequest(arg) 4.4 #createNewBatchAndExecutePreviousIfNeeded(previousBatch) 5. CollapserTimer 5
芋道源码
2018/03/02
1.3K0
熔断器 Hystrix 源码解析 —— 命令合并执行
微服务弹性框架hystrix-javanica详解(下)
续上集。我们之所以向你介绍有关javanica的内容,很大一部分原因,是因为spring cloud中集成了javanica,我们都知道spring cloud使用了大量的注解,javanica基于注解的风格和spring cloud不谋而合,在最新的spring cloud中集成了1.5.6的hystrix-javanica。 本集主要介绍请求缓存以及配置和批处理。 微服务弹性框架hystrix-javanica详解(上) Request Cache 请求缓存 Javanica提供特定的注解,以便启
ImportSource
2018/04/03
1.2K0
Spring Cloud Ribbon:负载均衡的服务调用
在微服务架构中,很多服务都会部署多个,其他服务去调用该服务的时候,如何保证负载均衡是个不得不去考虑的问题。负载均衡可以增加系统的可用性和扩展性,当我们使用RestTemplate来调用其他服务时,Ribbon可以很方便的实现负载均衡功能。
macrozheng
2019/09/17
8190
SpringCloud-Hystrix原理
Hystrix官网的原理介绍以及使用介绍非常详细,非常建议看一遍,地址见参考文档部分。
爱撸猫的杰
2020/02/14
1.4K0
SpringCloud中Hystrix容错保护原理及配置,给力!
> 公众号:[Java小咖秀](https://t.1yb.co/jwkk),网站:[javaxks.com](https://www.javaxks.com)
Java小咖秀
2021/04/02
9520
Hystrix:HystrixCollapser请求合并
偶尔在spring4all,看到DiDi关于hystrix请求合并的一篇文章 Spring Cloud Hystrix的请求合并,查阅资料又整理了一下。 具体业务概念,什么是请求合并?请求合并优缺点?可以参考DiDi的文章,然后我把我使用过程中的问题及解决方法写出来 代码 合并请求服务实现 @HystrixCollapser(batchMethod = "testAll", collapserProperties = { @HystrixProperty(name = "timerDela
冷冷
2018/02/08
1.3K0
Hystrix:HystrixCollapser请求合并
Spring全家桶之SpringCloud——高级阶段(上)
SpringCloud高级阶段上 (当前所在位置) 第一章 负载均衡Ribbon 第二章 声明式调用Feign 第三章 服务容错保护Hystrix
时间静止不是简史
2020/07/27
2.8K0
Spring全家桶之SpringCloud——高级阶段(上)
Spring Cloud 之 Hystrix
不管是业务方法指定的降级方法还是全局降级方法,它们都必须和业务方法在同一个类中才能生效,业务逻辑与降级逻辑耦合度极高。
一只
2024/07/01
1580
Spring Cloud 之 Hystrix
Hystrix
Hystix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败。
暴躁的程序猿
2022/03/24
5620
Hystrix
快速学习-熔断器Hystix
Hystix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败。
cwl_java
2020/02/11
3550
Hystrix使用分析
在启动类上加入@EnableCircuitBreaker注解,并在调用到另一个微服务的方法上加入一些配置
zhaozhen
2021/07/15
4340
Hystrix使用分析
请求合并哪家强
工作中,我们常见的请求模型都是请求-应答式,即一次请求中,服务给请求分配一个独立的线程,一块独立的内存空间,所有的操作都是独立的,包括资源和系统运算。我们也知道,在请求中处理一次系统 I/O 的消耗是非常大的,如果有非常多的请求都进行同一类 I/O 操作,那么是否可以将这些 I/O 操作都合并到一起,进行一次 I/O 操作,是否可以大大降低下游资源服务器的负担呢?
枕边书
2018/08/20
1K0
Spring Cloud Hystrix简单实用
Hystrix,英文意思是豪猪,全身是刺,刺是一种保护机制。Hystrix也是Netflflix公司的一款组件。
ha_lydms
2023/08/10
1790
Spring Cloud Hystrix简单实用
Hystrix请求合并的使用(二)
接下来,我们将创建一个名为“GetDataCollapserExecutor”的类,该类用于执行Hystrix请求合并器:
堕落飞鸟
2023/04/08
1.3K1
相关推荐
Spring Cloud中Hystrix的请求合并
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验