hystrix源码之请求合并
请求合并
使用HystrixObservableCollapser可以将参数不同,但执行过程相同的调用合并执行。当调用observe、toObservable方法时,会向RequestCollapser提交getRequestArgument方法获取的参数。用户需要实现getRequestArgument方法来设定请求参数,RequestCollapser是执行批量的类。
public Observable<ResponseType> toObservable(Scheduler observeOn) {
return Observable.defer(new Func0<Observable<ResponseType>>() {
@Override
public Observable<ResponseType> call() {
final boolean isRequestCacheEnabled = getProperties().requestCacheEnabled().get();
//是否开启缓存,如果开启缓存并且实现了getCacheKey方法,则首先从HystrixRequestCache获取结果数据
if (isRequestCacheEnabled) {
HystrixCachedObservable<ResponseType> fromCache = requestCache.get(getCacheKey());
if (fromCache != null) {
metrics.markResponseFromCache();
return fromCache.toObservable();
}
}
//获取合并请求对象,提交请求
RequestCollapser<BatchReturnType, ResponseType, RequestArgumentType> requestCollapser = collapserFactory.getRequestCollapser(collapserInstanceWrapper);
Observable<ResponseType> response = requestCollapser.submitRequest(getRequestArgument());
metrics.markRequestBatched();
// 是否开启缓存,如果开启缓存并且实现了getCacheKey方法,将结果放到HystrixRequestCache
if (isRequestCacheEnabled) {
HystrixCachedObservable<ResponseType> toCache = HystrixCachedObservable.from(response);
HystrixCachedObservable<ResponseType> fromCache = requestCache.putIfAbsent(getCacheKey(), toCache);
if (fromCache == null) {
return toCache.toObservable();
} else {
return fromCache.toObservable();
}
}
return response;
}
});
}
RequestCollapserFactory
创建RequestCollapser,如果是GLOBAL作用域,会创建一个RequestCollapser,并且使用一个static的ConcurrentHashMap作为一个全局的缓存。如果是REQUEST作用域,会创建一个RequestCollapser,使用HystrixRequestVariableHolder来存储。

private static ConcurrentHashMap<String, RequestCollapser<?, ?, ?>> globalScopedCollapsers = new ConcurrentHashMap<String, RequestCollapser<?, ?, ?>>();
private static ConcurrentHashMap<String, HystrixRequestVariableHolder<RequestCollapser<?, ?, ?>>> requestScopedCollapsers = new ConcurrentHashMap<String, HystrixRequestVariableHolder<RequestCollapser<?, ?, ?>>>(); public RequestCollapser<BatchReturnType, ResponseType, RequestArgumentType> getRequestCollapser(HystrixCollapserBridge<BatchReturnType, ResponseType, RequestArgumentType> commandCollapser) {
if (Scopes.REQUEST == Scopes.valueOf(getScope().name())) {
return getCollapserForUserRequest(commandCollapser);
} else if (Scopes.GLOBAL == Scopes.valueOf(getScope().name())) {
return getCollapserForGlobalScope(commandCollapser);
} else {
logger.warn("Invalid Scope: {} Defaulting to REQUEST scope.", getScope());
return getCollapserForUserRequest(commandCollapser);
}
}

RequestCollapser
内部有一个RequestBatch对象,代表当前的一批请求,当定时器到达一定间隔,会执行该批量请求对象中所有的请求,然后新建一个RequestBatch对象。
...
private final AtomicReference<RequestBatch<BatchReturnType, ResponseType, RequestArgumentType>> batch = new AtomicReference<RequestBatch<BatchReturnType, ResponseType, RequestArgumentType>>();
....
private class CollapsedTask implements TimerListener {
final Callable<Void> callableWithContextOfParent;
CollapsedTask() {
// this gets executed from the context of a HystrixCommand parent thread (such as a Tomcat thread)
// so we create the callable now where we can capture the thread context
callableWithContextOfParent = new HystrixContextCallable<Void>(concurrencyStrategy, new Callable<Void>() {
// the wrapCallable call allows a strategy to capture thread-context if desired
@Override
public Void call() throws Exception {
try {
// we fetch current so that when multiple threads race
// we can do compareAndSet with the expected/new to ensure only one happens
RequestBatch<BatchReturnType, ResponseType, RequestArgumentType> currentBatch = batch.get();
// 1) it can be null if it got shutdown
// 2) we don't execute this batch if it has no requests and let it wait until next tick to be executed
if (currentBatch != null && currentBatch.getSize() > 0) {
// do execution within context of wrapped Callable
createNewBatchAndExecutePreviousIfNeeded(currentBatch);
}
} catch (Throwable t) {
logger.error("Error occurred trying to execute the batch.", t);
t.printStackTrace();
// ignore error so we don't kill the Timer mainLoop and prevent further items from being scheduled
}
return null;
}
});
}
@Override
public void tick() {
try {
callableWithContextOfParent.call();
} catch (Exception e) {
logger.error("Error occurred trying to execute callable inside CollapsedTask from Timer.", e);
e.printStackTrace();
}
}
@Override
public int getIntervalTimeInMilliseconds() {
return properties.timerDelayInMilliseconds().get();
}
}
使用HystrixContextCallable是为了batch执行共享父亲HystrixRequestContext。
@Override
public K call() throws Exception {
HystrixRequestContext existingState = HystrixRequestContext.getContextForCurrentThread();
try {
// set the state of this thread to that of its parent
HystrixRequestContext.setContextOnCurrentThread(parentThreadState);
// execute actual Callable with the state of the parent
return actual.call();
} finally {
// restore this thread back to its original state
HystrixRequestContext.setContextOnCurrentThread(existingState);
}
}
RequestBatch
内部有一个ConcurrentMap来存储请求对象。key为:请求参数对象,value为CollapsedRequest对象。
private final ConcurrentMap<RequestArgumentType, CollapsedRequest<ResponseType, RequestArgumentType>> argumentMap =
new ConcurrentHashMap<RequestArgumentType, CollapsedRequest<ResponseType, RequestArgumentType>>();
当执行批量请求方法时,遍历存储的CollapsedRequest对象,通过commandCollapser的createObservableCommand方法创建执行批量请求的命令,通过commandCollapser的mapResponseToRequests方法将执行结果和请求进行匹配。
try {
// shard batches
Collection<Collection<CollapsedRequest<ResponseType, RequestArgumentType>>> shards = commandCollapser.shardRequests(argumentMap.values());
// for each shard execute its requests
for (final Collection<CollapsedRequest<ResponseType, RequestArgumentType>> shardRequests : shards) {
try {
// create a new command to handle this batch of requests
Observable<BatchReturnType> o = commandCollapser.createObservableCommand(shardRequests);
commandCollapser.mapResponseToRequests(o, shardRequests)
...
hystrix源码之请求合并的更多相关文章
- hystrix源码之请求缓存
HystrixRequestCache 请求缓存.内部是一个静态ConcurrentHashMap存储各个命令的缓存器,RequestCacheKey为key,HystrixRequestCache为 ...
- 第三篇:白话tornado源码之请求来了
上一篇<白话tornado源码之待请求阶段>中介绍了tornado框架在客户端请求之前所做的准备(下图1.2部分),本质上就是创建了一个socket服务端,并进行了IP和端口的绑定,但是未 ...
- 【一起学源码-微服务】Hystrix 源码一:Hystrix基础原理与Demo搭建
说明 原创不易,如若转载 请标明来源! 欢迎关注本人微信公众号:壹枝花算不算浪漫 更多内容也可查看本人博客:一枝花算不算浪漫 前言 前情回顾 上一个系列文章讲解了Feign的源码,主要是Feign动态 ...
- Hystrix源码解析
1. Hystrix源码解析 1.1. @HystrixCommand原理 直接通过Aspect切面来做的 1.2. feign hystrix原理 它的本质原理就是对HystrixCommand的动 ...
- Tomcat源码分析——请求原理分析(下)
前言 本文继续讲解TOMCAT的请求原理分析,建议朋友们阅读本文时首先阅读过<TOMCAT源码分析——请求原理分析(上)>和<TOMCAT源码分析——请求原理分析(中)>.在& ...
- Tomcat源码分析——请求原理分析(中)
前言 在<TOMCAT源码分析——请求原理分析(上)>一文中已经介绍了关于Tomcat7.0处理请求前作的初始化和准备工作,请读者在阅读本文前确保掌握<TOMCAT源码分析——请求原 ...
- Tomcat源码分析——请求原理分析(上)
前言 谈起Tomcat的诞生,最早可以追溯到1995年.近20年来,Tomcat始终是使用最广泛的Web服务器,由于其使用Java语言开发,所以广为Java程序员所熟悉.很多人早期的J2EE项目,由程 ...
- 浅谈flask源码之请求过程
更新时间:2018年07月26日 09:51:36 作者:Dear. 我要评论 这篇文章主要介绍了浅谈flask源码之请求过程,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随 ...
- hystrix源码之概述
概述 hystrix核心原理是通过代理执行用户命令,记录命令执行的metrics信息,通过这些metrics信息进行降级和熔断. 源码结构包括一下几个部分: 熔断器 熔断器就是hystrix用来判断调 ...
随机推荐
- centos7上借助于xargs快速查询并卸载rpm软件
在centos上卸载某些软件的时候,如果查询的软件包比较多,可以考虑使用xargs,边查询边卸载 如:下面在查询mysql包时候,将查询结果通过管道传送给xargs,然后使用rpm -e --node ...
- 【论文总结】MapReduce论文
摘要: MR是啥:编程模型,用户只需编写Map,Reduce两个函数,系统完成分布式计算 MR系统是啥:在大量普通计算机上实现并行化计算,系统只关心如何分割数据.大规模集群的调度.集群容错.集群通信 ...
- python数据处理工具 -- pandas(序列与数据框的构造)
Pandas模块的核心操作对象就是对序列(Series)和数据框(Dataframe).序列可以理解为数据集中的一个字段,数据框是值包含至少两个字段(或序列) 的数据集. 构造序列 1.通过同质的列表 ...
- Java数据结构——树、二叉树的理论知识汇总
通用树的理论知识 一.树的定义 由一个或多个(n>=0)节点组成的有限集合T,有且仅有一个节点称为根(root),当n>1时,其7余的节点为m(m>=0)个互不相交的有限集合T1,T ...
- vue项目中遇到的问题(包含兼容性问题)
一.由于懒加载,使用elementUI里面的dialog的时候dialog还未被渲染成DOM元素,调用清空表单验证的方法会报错this.$refs[formName].resetFields(); 解 ...
- 欢迎来到 C# 9.0(Welcome to C# 9.0)【纯手工翻译】
翻译自 Mads Torgersen 2020年5月20日的博文<Welcome to C# 9.0>,Mads Torgersen 是微软 C# 语言的首席设计师,也是微软 .NET 团 ...
- openCV - 1. 加载、修改、保存图像
加载 cv::imread 修改 cv::cvtColor 保存 cv::imwrite 加载图像cv::imread imread功能是加载图像文件成为一个Mat对象,其中第一个参数表示图像文件名称 ...
- 力扣Leetcode 11. 盛最多水的容器
盛最多水的容器 给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找 ...
- windows快速安装linux虚拟机
1. 场景描述 因测试中需要linux集群,目前的服务器不太方便部署,需要本机(windows7)启动多个linux虚拟机,记录下,希望能帮到需要的朋友. 2. 解决方案 2.1 软件准备 (1)使用 ...
- 修改linux操作系统的时间可以使用date指令 运维系统工程师必会技术
修改linux的时间可以使用date指令 修改日期: 时间设定成2009年5月10日的命令如下: date -s 05/10/2009 修改时间: 将系统时间设定成上午10点18分0秒的命令如下. d ...