HystrixCollapser 请求合并

一、背景

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

    HystrixCollapser 实现了在 HystrixCommand 之前放置一个合并处理器:

    • 将处于一个很短时间窗(默认10毫秒)内对同一依赖服务的多个请求进行整合并以批量方式发起请求的功能(服务提供方也需要提供相应的批量实现接口)
    • 通过 HystrixCollapser 的封装,开发者不需要去关注线程合并的细节过程,只需要关注批量化服务和处理

二、实例

1. 部分源码

/**
 * BatchReturnType:合并后批量请求的返回类型
 * ResponseType:单个请求返回的类型
 * RequestArgumentType:请求参数类型
 */
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);
    ...
}

2. 简单的示例

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

  • /users/{id}:根据 id 返回 User 对象的 GET 请求接口
  • /users?ids={ids}:根据 ids 参数返回 User 对象列表的 GET 请求接口

    ids 以逗号分割的 id 集合

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

@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 对象的请求命令进行合并的实现:

  • 第一步:为请求合并的实现准备一个批量请求命令的实现

    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 实现请求合并器

    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);
        	}
    	}
    }
    
    • getRequestArgument:返回给定的单个请求参数 userId
    • createCommand:collapsedRequests 参数保存延迟时间窗中收集到的所有获取单个 User 的请求

      通过获取这些请求的参数来组织准备的批量请求命令 UserBatchCommand 实例

    • mapResponseToRequests:在批量命令 UserBatchCommand 实例被触发执行完成之后,该方法开始执行
      • batchResponse 参数保存了 createCommand 中组织的批量请求命令的返回结果
      • collapsedRequests 参数代表了每个被合并的请求

    通过遍历批量结果 batchResponse 对象,为 collapsedRequests 中每个合并前的单个请求设置返回结果,以此完成批量结果到单个请求结果的转换

推荐文章: Spring Cloud Hystrix的请求合并

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值