Issue Description
bug report
Describe what happened (or what feature you want)
sentinel采用类似于责任链的设计模式,将统计、限流、降级、监控等功能串起来,使每个环节自责更清晰(见DefaultSlotsChainBuilder),这种设计模式对于大多数关于“数量”的统计场景是没问题的,比如QPS、错误量等。但对于线程数限流(即并发限流)这样做是有问题的,详见:https://blog.csdn.net/manzhizhen/article/details/81413014。
在Sentinel中,当前服务对线程数加(请求进来)和减(请求执行完毕)的操作是在StatisticSlot中完成的:
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args) throws Throwable {
// 注意: 其他代码省略
fireEntry(context, resourceWrapper, node, count, args);
**node.increaseThreadNum();**
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
DefaultNode node = (DefaultNode)context.getCurNode();
// 注意: 其他代码省略
**node.decreaseThreadNum();**
}
而线程数的限流操作是在另一个类来做的,例如SystemSlot:
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args) throws Throwable {
**SystemRuleManager.checkSystem(resourceWrapper);**
fireEntry(context, resourceWrapper, node, count, args);
}
其中SystemRuleManager.checkSystem的操作如下:
public static void checkSystem(ResourceWrapper resourceWrapper) throws BlockException {
// 注意: 其他代码省略
// total thread
int currentThread = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.curThreadNum();
**if (currentThread > maxThread) {
throw new SystemBlockException(resourceWrapper.getName(), "thread");
}**
}
将统计和限流分开的这种方式,无法真正做到线程数量(也就是并发度)的精准控制,会有竞态条件产生,比较好的做法是用信号量来实现。
23条答案
按热度按时间shstlldc1#
我同意这个issue。源码中确实会存在这种可能。DefaultProcessorSlotChain中首先会进行例如FlowSlot.entry的检查,然后才会在StatisticSlot.entry进行统计数量。如果不使用同步机制,有可能会同时几个线程同时调用DefaultProcessorSlotChain超过线程上限。
6qqygrtg2#
这里面不做同步的一个重要的原因,是一个性能的平衡. 如果我们在所有的检查中都加锁,会带来一个性能的骤降;所以我们选择了放弃处理这种极端情况. 如果有什么好的建议,也请告知我们。 Regards, 发件人: byamao1 [mailto:notifications@github.com] 发送时间: 2018年8月15日 13:59 收件人: alibaba/Sentinel Sentinel@noreply.github.com 抄送: Subscribed subscribed@noreply.github.com 主题: Re: [alibaba/Sentinel] 关于线程限流问题的讨论 (#59) 我同意这个issue。源码中确实会存在这种可能。DefaultProcessorSlotChain中首先会进行例如FlowSlot.entry的检查,然后才会在StatisticSlot.entry进行统计数量。如果不使用同步机制,有可能会同时几个线程同时调用DefaultProcessorSlotChain超过线程上限。 — You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub <#59 (comment)> , or mute the thread <https://github.com/notifications/unsubscribe-auth/AgQHwEniJJTkmmTnkR5lUO-aWw-cxf86ks5uQ7iTgaJpZM4V74Ec> . <https://github.com/notifications/beacon/AgQHwNZXXD0DTyG0TZUTXLmyvFBR-_IDks5uQ7iTgaJpZM4V74Ec.gif>
vohkndzv3#
我对工程中的FlowThreadDemo进行了修改:
SphU.entry
if(oneSecondPass > 20)
,使得显示的log为超限情境测试结果来看,限流能力与
methodBRunningTime
有正比比关系:methodBRunningTime
越小,能力越小。下步我会结合效率和限流能力,试图找到一个平衡点方案。(有苛刻之处乃本人之误,如需测试代码可联系本人)
c9x0cxw04#
太好了!! 欢迎提一个PR!!! 发件人: byamao1 [mailto:notifications@github.com] 发送时间: 2018年8月15日 15:25 收件人: alibaba/Sentinel Sentinel@noreply.github.com 抄送: 林佳梁(子矜) jialiang.linjl@taobao.com; Comment comment@noreply.github.com 主题: Re: [alibaba/Sentinel] 关于线程限流问题的讨论 (#59) 我对工程中的FlowThreadDemo进行了修改: * 开启10个线程群(1个群25个线程)进行饱和测试 * 加入CyclicBarrier保证线程群内能够同时触发SphU.entry * 在log处改造为if(oneSecondPass > 20) 测试结果来看,限流能力与methodBRunningTime有正比比关系:methodBRunningTime越小,能力越小。 下步我会结合效率和限流能力,试图找到一个平衡点方案。(有苛刻之处乃本人之误,如需测试代码可联系本人) — You are receiving this because you commented. Reply to this email directly, view it on GitHub <#59 (comment)> , or mute the thread <https://github.com/notifications/unsubscribe-auth/AgQHwIREJbPuK8Hm_0fnrHrr3knVg7X7ks5uQ8yvgaJpZM4V74Ec> . <https://github.com/notifications/beacon/AgQHwGt2fII3L3u401OObTU2HoGfM_bOks5uQ8yvgaJpZM4V74Ec.gif>
gj3fmq9x5#
如果有办法将线程限流的判断还有加减1放在一个地方(就可以会用信号量来做了),就可以规避这个问题,而且性能不会太差,但可能会违背了现有的操作链的设计方式,不妨尝试一下
aelbi1ox6#
用令牌的方式来限制资源被访问次数应该是可行的。与信号量差不多的思路(水平有限),只不过是利用原子类实现了令牌的个数限制。代码放在博客,有问题互相交流。
https://blog.csdn.net/byamao1/article/details/81747230
3df52oht7#
@byamao1 你这段代码也是有问题的,令牌桶其实很难实现并发控制的效果:https://blog.csdn.net/manzhizhen/article/details/81413014
t98cgbkg8#
@jialianglinjl@sczyh30@byamao1 我想到一种方案,通过信号量+ThreadLocal来实现,我写完到时候各位看一下
rt4zxlrg9#
好的!!…
------------------------------------------------------------------ 发件人:yizhenqiang notifications@github.com 发送时间:2018年8月20日(星期一) 11:02 收件人:alibaba/Sentinel Sentinel@noreply.github.com 抄 送:林佳梁(子矜) jialiang.linjl@taobao.com; Mention mention@noreply.github.com 主 题:Re: [alibaba/Sentinel] 关于线程限流问题的讨论 (#59) @jialianglinjl@sczyh30@byamao1 我想到一种方案,通过信号量+ThreadLocal来实现,我写完到时候各位看一下 — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
krcsximq10#
好的,方案看起来可待。
kx5bkwkv11#
是否考虑用volatile 去定义线程数。相较于锁,它的性能不差。
mefy6pfw12#
volatile 只能保证可见性,不能保证变量递增递减操作的原子性。
fumotvh313#
感谢指正,我忽略了递增,递减并不是原子性操作,那我只能想到的也就是上面提到的信号量的方式,@byamao1
ibps3vxo14#
如果没有很好的方案,建议给两个策略:
1.严格精确的限流,但是牺牲并发性能
2.不精确的限流,保证并发性能
pw136qt215#
一开始以为做成责任链,可以让用户像netty pipeline一样自定义handler,每个slot都是独立的,现在slotChain里的flowSolt和systemSolt依赖StatisticSlot的统计信息,这个问题,是否考虑让slot维护自己的信号量,而不是依赖Node上的线程数.