dubbo是如何控制并发数和限流的?
ExecuteLimitFilter
ExecuteLimitFilter ,在服务提供者,通过 <dubbo:service /> 的 "executes" 统一配置项开启:
表示每服务的每方法最大可并行执行请求数。
ExecuteLimitFilter是通过信号量来实现的对服务端的并发数的控制。
ExecuteLimitFilter执行流程:
- 首先会去获得服务提供者每服务每方法最大可并行执行请求数
- 如果每服务每方法最大可并行执行请求数大于零,那么就基于基于服务 URL + 方法维度获取一个RpcStatus实例
- 通过RpcStatus实例获取一个信号量,若果获取的这个信号量调用tryAcquire返回false,则抛出异常
- 如果没有抛异常,那么久调用RpcStatus静态方法beginCount,给这个 URL + 方法维度开始计数
- 调用服务
- 调用结束后计数调用RpcStatus静态方法endCount,计数结束
- 释放信号量
ExecuteLimitFilter
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
URL url = invoker.getUrl();
String methodName = invocation.getMethodName();
Semaphore executesLimit = null;
boolean acquireResult = false;
int max = url.getMethodParameter(methodName, Constants.EXECUTES_KEY, 0);
if (max > 0) {
RpcStatus count = RpcStatus.getStatus(url, invocation.getMethodName());
// if (count.getActive() >= max) {
/**
* http://manzhizhen.iteye.com/blog/2386408
* use semaphore for concurrency control (to limit thread number)
*/
executesLimit = count.getSemaphore(max);
if(executesLimit != null && !(acquireResult = executesLimit.tryAcquire())) {
throw new RpcException("Failed to invoke method " + invocation.getMethodName() + " in provider " + url + ", cause: The service using threads greater than <dubbo:service executes=\"" + max + "\" /> limited.");
}
}
long begin = System.currentTimeMillis();
boolean isSuccess = true;
RpcStatus.beginCount(url, methodName);
try {
Result result = invoker.invoke(invocation);
return result;
} catch (Throwable t) {
isSuccess = false;
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new RpcException("unexpected exception when ExecuteLimitFilter", t);
}
} finally {
RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, isSuccess);
if(acquireResult) {
executesLimit.release();
}
}
}
我们接下来看看RpcStatus这个类
private static final ConcurrentMap<String, ConcurrentMap<String, RpcStatus>> METHOD_STATISTICS = new ConcurrentHashMap<String, ConcurrentMap<String, RpcStatus>>();
public static RpcStatus getStatus(URL url, String methodName) {
String uri = url.toIdentityString();
ConcurrentMap<String, RpcStatus> map = METHOD_STATISTICS.get(uri);
if (map == null) {
METHOD_STATISTICS.putIfAbsent(uri, new ConcurrentHashMap<String, RpcStatus>());
map = METHOD_STATISTICS.get(uri);
}
RpcStatus status = map.get(methodName);
if (status == null) {
map.putIfAbsent(methodName, new RpcStatus());
status = map.get(methodName);
}
return status;
}
这个方法很简单,大概就是给RpcStatus这个类里面的静态属性METHOD_STATISTICS里面设值。外层的map是以url为key,里层的map是以方法名为key。
private volatile int executesPermits;
public Semaphore getSemaphore(int maxThreadNum) {
if(maxThreadNum <= 0) {
return null;
}
if (executesLimit == null || executesPermits != maxThreadNum) {
synchronized (this) {
if (executesLimit == null || executesPermits != maxThreadNum) {
executesLimit = new Semaphore(maxThreadNum);
executesPermits = maxThreadNum;
}
}
}
return executesLimit;
}
这个方法是获取信号量,如果这个实例里面的信号量是空的,那么就添加一个,如果不是空的就返回。
TPSLimiter
TpsLimitFilter 过滤器,用于服务提供者中,提供限流的功能。
配置方式:
- 通过 <dubbo:parameter key="tps" value="" /> 配置项,添加到 <dubbo:service /> 或 <dubbo:provider /> 或 <dubbo:protocol /> 中开启,例如:
dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoServiceImpl" protocol="injvm" >
<dubbo:parameter key="tps" value="100" />
</dubbo:service>
- 通过 <dubbo:parameter key="tps.interval" value="" /> 配置项,设置 TPS 周期。
源码分析
TpsLimitFilter
private final TPSLimiter tpsLimiter = new DefaultTPSLimiter();
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
if (!tpsLimiter.isAllowable(invoker.getUrl(), invocation)) {
throw new RpcException(
"Failed to invoke service " +
invoker.getInterface().getName() +
"." +
invocation.getMethodName() +
" because exceed max service tps.");
}
return invoker.invoke(invocation);
}
invoke方法调用了DefaultTPSLimiter的isAllowable,我们进入到isAllowable方法看一下
DefaultTPSLimiter
private final ConcurrentMap<String, StatItem> stats
= new ConcurrentHashMap<String, StatItem>();
@Override
public boolean isAllowable(URL url, Invocation invocation) {
//获取tps这个参数设置的大小
int rate = url.getParameter(Constants.TPS_LIMIT_RATE_KEY, -1);
//获取tps.interval这个参数设置的大小,默认60秒
long interval = url.getParameter(Constants.TPS_LIMIT_INTERVAL_KEY,
Constants.DEFAULT_TPS_LIMIT_INTERVAL);
String serviceKey = url.getServiceKey();
if (rate > 0) {
StatItem statItem = stats.get(serviceKey);
if (statItem == null) {
stats.putIfAbsent(serviceKey,
new StatItem(serviceKey, rate, interval));
statItem = stats.get(serviceKey);
}
return statItem.isAllowable();
} else {
StatItem statItem = stats.get(serviceKey);
if (statItem != null) {
stats.remove(serviceKey);
}
}
return true;
}
若要限流,调用 StatItem#isAllowable(url, invocation) 方法,根据 TPS 限流规则判断是否限制此次调用。
StatItem
private long lastResetTime;
private long interval;
private AtomicInteger token;
private int rate;
public boolean isAllowable() {
long now = System.currentTimeMillis();
// 若到达下一个周期,恢复可用种子数,设置最后重置时间。
if (now > lastResetTime + interval) {
token.set(rate);// 回复可用种子数
lastResetTime = now;// 最后重置时间
}
// CAS ,直到或得到一个种子,或者没有足够种子
int value = token.get();
boolean flag = false;
while (value > 0 && !flag) {
flag = token.compareAndSet(value, value - 1);
value = token.get();
}
return flag;
}
dubbo是如何控制并发数和限流的?的更多相关文章
- WCF之并发,吞吐量和限流
并发 Single重入模式.对于每一个服务实例,同一时刻只能处理一个请求,其他对该实例的请求被排队. PerCall,每一线程会分配一个新的服务实例上.不会有并发性问题.不影响吞吐量. PerSess ...
- spring控制并发数的工具类ConcurrencyThrottleSupport和ConcurrencyThrottleInterceptor
官方文档: /** * Support class for throttling concurrent access to a specific resource. * * <p>Desi ...
- Sentinel整合Dubbo限流实战(分布式限流)
之前我们了解了 Sentinel 集成 SpringBoot实现限流,也探讨了Sentinel的限流基本原理,那么接下去我们来学习一下Sentinel整合Dubbo及 Nacos 实现动态数据源的限流 ...
- 微服务架构 | 5.2 基于 Sentinel 的服务限流及熔断
目录 前言 1. Sentinel 基础知识 1.1 Sentinel 的特性 1.2 Sentinel 的组成 1.3 Sentinel 控制台上的 9 个功能 1.4 Sentinel 工作原理 ...
- 阿里巴巴开源限流组件Sentinel初探
1 Sentinel主页 https://github.com/alibaba/Sentinel/wiki/主页 1.1 Sentinel介绍 随着微服务的流行,服务和服务之间的稳定性变得越来越重要. ...
- 高可用服务设计之二:Rate limiting 限流与降级
<高可用服务设计之二:Rate limiting 限流与降级> <nginx限制请求之一:(ngx_http_limit_conn_module)模块> <nginx限制 ...
- 基于Redis的限流系统的设计
本文讲述基于Redis的限流系统的设计,主要会谈及限流系统中限流策略这个功能的设计:在实现方面,算法使用的是令牌桶算法来,访问Redis使用lua脚本. 1.概念 In computer netw ...
- Spring Cloud Alibaba | Sentinel:分布式系统的流量防卫兵动态限流规则
Spring Cloud Alibaba | Sentinel:分布式系统的流量防卫兵动态限流规则 前面几篇文章较为详细的介绍了Sentinel的使用姿势,还没看过的小伙伴可以访问以下链接查看: &l ...
- SpringCloudGateWay之限流
一.引言在高并发系统中,经常需要限制系统中的电流化妆.一方面是防止大量的请求使服务器过载,导致服务不可用,另一方面是防止网络攻击.常用的限流方法,如hystrix.应用线程池隔离.超过线程池的负载和g ...
随机推荐
- Linux下多网卡绑定bond及模式介绍
[介绍] 网卡bond一般主要用于网络吞吐量很大,以及对于网络稳定性要求较高的场景. 主要是通过将多个物理网卡绑定到一个逻辑网卡上,实现了本地网卡的冗余,带宽扩容以及负载均衡. Linux下一共有七种 ...
- 面试中的作用域题和THIS 指向的问题
作用域的面试题 1. fn() function fn () { console.log(12) } var as = function () { console.log(45) } 2. var a ...
- 关于C51 keil使用中.c文件的链接心得
这个问题一直烦了我很久,使用C51 keil进行.c文件链接的时候,老是报错诸如下面的信息: *** ERROR L104: MULTIPLE PUBLIC DEFINITIONS SYMBOL: a ...
- leetcode的Hot100系列--461. 汉明距离
求两个数的二进制位不同的位置,最先想到的就是异或操作, 异或:按位运算,相同为0,不同为1. 比如: a = 6 对应的二进制表示为: 0 0 1 1 1 b = 9 对应的二进制表示为: 0 1 ...
- Spring Boot2(十二):手摸手教你搭建Shiro安全框架
一.前言 SpringBoot+Shiro+Mybatis完成的. 之前看了一位小伙伴的Shiro教程,跟着做了,遇到蛮多坑的(´இ皿இ`) 修改整理了一下,成功跑起来了.可以通过postman进行测 ...
- JAVA面试题 启动线程是start()还是run()?为什么?
面试官:请问启动线程是start()还是run()方法,能谈谈吗? 应聘者:start()方法 当用start()开始一个线程后,线程就进入就绪状态,使线程所代表的虚拟处理机处于可运行状态,这意味着它 ...
- Java编程思想:进程控制
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public ...
- ZIP:ZipFile
ZipFile: /* 此类用于从 ZIP 文件读取条目 */ ZipFile(File file) :打开供阅读的 ZIP 文件,由指定的 File 对象给出. ZipFile(File file, ...
- TF项目实战(SSD目标检测)-VOC2007
TF项目实战(SSD目标检测)-VOC2007 训练好的模型和代码会公布在网上: 步骤: 1.代码地址:https://github.com/balancap/SSD-Tensorflow 2.解压s ...
- Java将文本文件压缩为tar.gz
压缩 思路 准备输出流 FileOutputStream BufferedOutputStream TarOutputStream GZIPOutputStream 准备输入流 FileInputSt ...