在ThreadPoolExecutors中确定Netty任务的优先级

时间:2011-11-15 17:05:41

标签: netty

我有一个在netty中实现的服务器,它处理用户请求,与中间件通信,并发送响应。与中间件的往返相比,I / O预计可以忽略不计,因此为了最小化阻塞,我在OrderedMemoryAwareThreadPoolExecutor之上的管道中有一个ExecutionHandler。到目前为止没问题。

我正在调查服务器在高负载下的行为。根据我们对协议的过去经验,我们倾向于被意外的DOS攻击所淹没 - 通常,用户的脚本陷入无限循环或类似情况。理想情况下,一旦通过某个使用阈值,我们就可以对其频道进行去优先排序,这样其他用户的服务就不会受到影响。

我实现了一个简单的ThreadPoolExecutor,它使用PriorityBlockingQueue并根据从我们自己的Session类中提取的数据设置优先级(附加到ChannelHandler中的上下文)。到目前为止,没有问题。

当试图利用netty的内置ThreadPoolExecutors的排序和内存意识时,遇到了困难。理想情况下,MyThreadPoolExecutor可以只扩展OrderedMemoryAwareThreadPoolExecutor并连接优先级队列。唉,这是不可能的,原因有二:私人和最终。更详细:

a)ThreadPoolExecutor.workQueue可以在其构造函数中设置,但是MemoryAwareThreadPoolExecutor将其硬编码为LinkedTransferQueue,并且不会将其暴露给它的子OrderedMemoryAwareThreadPoolExecutor(即MyThreadPoolExecutor无权设置它)。如果有必要,可以通过一些基于反射的私人场调整来克服这个问题。

b)我希望能够覆盖MyThreadPoolExecutor.doUnorderedExecute(),这样我就可以插入优先级处理并构造必要的对象,但它被声明为final。调用它的代码不需要更改。

结果是要保留所有漂亮的netty功能,但是使用优先级队列,我将不得不复制'n'paste OrderedMemoryAwareThreadPoolExecutor和MemoryAwareThreadPoolExecutor,调整每行的几行,然后从那里扩展。这并没有让我感觉良好的编码习惯!即使考虑到它也引发了警钟。

现在提出几个问题:

1)我解决了错误的问题吗?为了达到我想达到的目的,我是否完全错误地咆哮了?

2)如果没有,有没有比上面讨论的更好的方法呢?

3)上述方法为服务器总负载始终处于容量的去优先级任务带来了饥饿风险。我准备为“顽皮”的用户容忍这一点,但是一旦他们恢复到正常状态,他们现有的任务仍然会挨饿,并且为了保持排序,必须在他们后面添加任何新的,更高优先级的任务。你有什么建议可以解决这个问题吗? (业务不允许禁止用户。)

4)这是半问题,半反馈。 OrderedMemoryAwareThreadPoolExecutor的netty文档有一个方便的线程图X& Y - 可能这些是ThreadPoolExecutor中汇集的线程,而不是I / O工作线程?值得一提的是这一点。此外,当不使用ExecutionHandler时,每个通道都绑定到一个I / O工作线程 - 在ExecutionHandler后面仍然是这种情况吗?即,任务被添加到ExecutionHandler的顺序是否保证与它们​​到达Channel的顺序相同?如果是这种情况,那么我无法看到MemoryAwareThreadPoolExecutor的文档中的Thread X如何在事件1之前处理事件2 - 我接受这里不同的线程可以按任何顺序完成工作,但我看不出工作如何不按顺序分配给同一个线程(它从workQueue弹出)。 ExecutionHandler中的文档暗示了这一点,但会从更多细节中受益。

非常感谢阅读,并且非常感谢任何帮助。

2 个答案:

答案 0 :(得分:3)

1)不,你的想法很好。它只是OrderedMemoryAwareThreadPoolExecutor缺少这样的功能。你会file an issue吗?

2)我只是分叉OrderedMemoryAwareThreadPoolExecutor,简化它,并添加优先级队列。这样,您就可以更好地控制如何处理队列项(事件)。

3)您可以拥有两个队列 - 一个用于高优先级项目,另一个用于低优先级项目,而不是使用优先级队列。线程可以先处理高优先级队列,但是你可以控制循环,使它不会在那里停留太长时间。

4)是的,它们是ThreadPoolExecutor的主题。如果不清楚,我们应该更新我们的文档。请随意提出问题或通过分叉直接做出贡献。

答案 1 :(得分:0)

您的“闹铃”解决方案看起来很熟悉 - 只需从小型游泳池开始。

您似乎说您需要一个缓慢服务器的解决方案。 您可能想要查看服务器在负载下减速的原因。 - 线程争用(竞争)问题。 - 初始池大小 - GC配置