ExecuteLimitFilter

ExecuteLimitFilter ,在服务提供者,通过 <dubbo:service /> 的 "executes" 统一配置项开启:

表示每服务的每方法最大可并行执行请求数。

ExecuteLimitFilter是通过信号量来实现的对服务端的并发数的控制。

ExecuteLimitFilter执行流程:

  1. 首先会去获得服务提供者每服务每方法最大可并行执行请求数
  2. 如果每服务每方法最大可并行执行请求数大于零,那么就基于基于服务 URL + 方法维度获取一个RpcStatus实例
  3. 通过RpcStatus实例获取一个信号量,若果获取的这个信号量调用tryAcquire返回false,则抛出异常
  4. 如果没有抛异常,那么久调用RpcStatus静态方法beginCount,给这个 URL + 方法维度开始计数
  5. 调用服务
  6. 调用结束后计数调用RpcStatus静态方法endCount,计数结束
  7. 释放信号量

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 过滤器,用于服务提供者中,提供限流的功能。

配置方式:

  1. 通过 <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>
  1. 通过 <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是如何控制并发数和限流的?的更多相关文章

  1. WCF之并发,吞吐量和限流

    并发 Single重入模式.对于每一个服务实例,同一时刻只能处理一个请求,其他对该实例的请求被排队. PerCall,每一线程会分配一个新的服务实例上.不会有并发性问题.不影响吞吐量. PerSess ...

  2. spring控制并发数的工具类ConcurrencyThrottleSupport和ConcurrencyThrottleInterceptor

    官方文档: /** * Support class for throttling concurrent access to a specific resource. * * <p>Desi ...

  3. Sentinel整合Dubbo限流实战(分布式限流)

    之前我们了解了 Sentinel 集成 SpringBoot实现限流,也探讨了Sentinel的限流基本原理,那么接下去我们来学习一下Sentinel整合Dubbo及 Nacos 实现动态数据源的限流 ...

  4. 微服务架构 | 5.2 基于 Sentinel 的服务限流及熔断

    目录 前言 1. Sentinel 基础知识 1.1 Sentinel 的特性 1.2 Sentinel 的组成 1.3 Sentinel 控制台上的 9 个功能 1.4 Sentinel 工作原理 ...

  5. 阿里巴巴开源限流组件Sentinel初探

    1 Sentinel主页 https://github.com/alibaba/Sentinel/wiki/主页 1.1 Sentinel介绍 随着微服务的流行,服务和服务之间的稳定性变得越来越重要. ...

  6. 高可用服务设计之二:Rate limiting 限流与降级

    <高可用服务设计之二:Rate limiting 限流与降级> <nginx限制请求之一:(ngx_http_limit_conn_module)模块> <nginx限制 ...

  7. 基于Redis的限流系统的设计

    本文讲述基于Redis的限流系统的设计,主要会谈及限流系统中限流策略这个功能的设计:在实现方面,算法使用的是令牌桶算法来,访问Redis使用lua脚本.   1.概念 In computer netw ...

  8. Spring Cloud Alibaba | Sentinel:分布式系统的流量防卫兵动态限流规则

    Spring Cloud Alibaba | Sentinel:分布式系统的流量防卫兵动态限流规则 前面几篇文章较为详细的介绍了Sentinel的使用姿势,还没看过的小伙伴可以访问以下链接查看: &l ...

  9. SpringCloudGateWay之限流

    一.引言在高并发系统中,经常需要限制系统中的电流化妆.一方面是防止大量的请求使服务器过载,导致服务不可用,另一方面是防止网络攻击.常用的限流方法,如hystrix.应用线程池隔离.超过线程池的负载和g ...

随机推荐

  1. 四种途径提升RabbitMQ传输数据的可靠性

    前言 RabbitMQ虽然有对队列及消息等的一些持久化设置,但其实光光只是这一个是不能够保障数据的可靠性的,下面我们提出这样的质疑: (1)RabbitMQ生产者是不知道自己发布的消息是否已经正确达到 ...

  2. 搭建本地pip源

    搭建本地的pip源 开发环境部署机器的时候, 每次从网上下载pip包会很慢, 将需要的包和相关依赖下载到本地, 搭建一个本地源服务器. 基本都是安装多个包, 推荐使用文件的方式, 文件内容格式, 可以 ...

  3. 快速掌握mongoDB(一)——mongoDB安装部署和常用shell命令

    1.mongoDB简介 mongoDB 是由C++语言编写的,是一种分布式的面向文档存储的开源nosql数据库.nosql是Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统 ...

  4. Mybatis辅助神器-MyBatis Log Plugin

    1. 问题描述 Java操作数据库的两台流行天王-mybatis和hibernate,mytabis和hibernate的区别不想废话了,以前用hibernate,最近几年一直用的mybatis,目前 ...

  5. android布局几点随想

    1. 正式布局界面时,先在纸上画出整个布局,并考虑用什么布局比较适合: 2. 布局界面先做出框架,并用不同的背景颜色标记出来,确保大的布局框架式正确的: 3. 接着在每个大的布局框架内布局小的布局: ...

  6. python中变量在内存中的存储与地址关系解析、浅度/深度copy、值传递、引用传递

    ---恢复内容开始--- 1.变量.地址 变量的实现方式有:引用语义.值语义 python语言中变量的实现方式就是引用语义,在变量里面保存的是值(对象)的引用(值所在处内存空间的地址).采用这种方式, ...

  7. Python入门基础(8)

    上一篇介绍了一些python中函数的基本用法,那么,现在就来谈一谈python中的一些内置函数吧 python中的内置函数 1.map()函数 map()函数接受两个参数值,一个是函数,一个是Iter ...

  8. MFC在一个工程中启动其他工程的exe文件

    说明:有的时候把两个工程合并,但是偷懒不想在工程中添加代码,所以想到了这个办法,仅限偷懒哈哈哈哈 方法:新建一个主程序,在主程序的界面中添加按钮,在按钮的程序代码中添加以下语句: void CMain ...

  9. 个人永久性免费-Excel催化剂功能第25波-小白适用的文本处理功能

    翻看各大插件,都不约而同地出现系列文本处理的功能,自己在使用Excel过程中,在临时性的需求时,也会用上这几种文本处理,但仅适用于小范围的使用,使用这些功能不是数据处理的正确的之道,数据处理的核心需求 ...

  10. spring全局异常抓取validation校验信息

    @ControllerAdvicepublic class GlobalExceptionHandler { @ExceptionHandler({Exception.class}) @Respons ...