Kontraktor:Task、Actor调度的另一个选择

更新时间:2014-10-28 09:43:21点击次数:3812次

Java编程中,调度Task、Actor通常采用Executors及ExecutorService。对无状态的任务,通常可以很好的胜任。但对于大量并发的有状态任务,需要使用Actor模型。


Kontraktor是一个Java编写的轻量级高效Actor模型实现。可以直接暴露Actor提供TCP服务、WebService或者WebSockets,从JavaScript客户端调用Actor方法,用JavaScript实现Actor并通过Java调用。


对无状态小任务单元,Executors可以很好的胜任。比如将计算任务分担到多个CPU上。然而,对于运行中的大任务单元Job调度,Executors只能做到次优(sub-optimal)。例如Actor或轻量级进程的消息调度。


许多Actor框架或类似的并发框架使用Executor service批量调度消息。由于Executor service是上下文不敏感的,因此会将单个Actor/Task消息安排多个线程或CPU处理。这会导致访问Actor、Process或Task状态时经常出现缓存未命中(cache miss)的情况。更糟糕的是,因为每个新的“Runnable”会把先前处理的Task缓存冲掉,所以CPU无法维持缓存的稳定。使用忙循环(busy-spin)会带来第二个问题。如果框架使用忙循环读取自己的队列,每个处理线程的CPU负载会升到100%。


借助Kontraktor 2.0,我实现了一种不同的调度机制——使用简单的度量标准测试应用实际需要的CPU资源,再进行水平式扩充。


每个Actor会固定分配到一个Workerthread(“DispatcherThread”)。调度器会定期重新调度Actor,根据信息判断是否需要把它们移动到另一个工作线程。


由于算法过于复杂通常会带来更高的运行时开销,实际调度时采用了一种非常简洁的方式:


如果消费循环连续处理N个消息没有休息(目前设置N=1000),就认定该线程载。


一旦线程标记为“载”,只要SUM_QUEUED_MSG(线程A上运行的Actor)大于SUM_QUEUED_MSG(新创建线程B上的 Actor),信箱(mailbox)中消息多的Actor会移动到新的线程(直到#Threads == ThreadMax)。


如果#Threads == ThreadMax,Actor会根据目前收到的消息和“载”信息重新分配。


问题:


如果处理消息的时间差别很大,对消息队列的统计会产生误导。一种改进是为每个消息根据定期分析设定加权。可以简单地用每个Actor加权乘以队列大小。

对爆发式负载会有延时,延迟结束后所有可用的CPU才能被真正地使用。


JIT真正起效前会有延迟,这会导致错误的分析数据,从而将错误放大(一段时间后能恢复正常,实际情况并没有那么糟糕)。

性能


为了对比自动化调度与Actor线程固定方式的开销,我运行了Computing-Pi测试(可参照前一篇博客)。这些数据并没有展示局部性(locality)带来的影响,只对固定方式与自动化调度进行了比较。


测试1 手动为每个Pi计算Actor分配一个线程,


测试2 一旦监测到实际的负载,总起启动一个worker并且自动进行比例调整。


(注意:示例要求kontraktor2.0-beta-2及更高版本。如果parkNanos选项启用,kontraktor的比例调整会限制在2、3个线程)


该测试运行了8次,每次运行会都会增加thread_max。


测试结果:


Kontraktor Autoscale(通常运行1个线程,然后比例调整到N个线程)


1 threads : 1527

2 threads : 1273

3 threads : 718

4 threads : 630

5 threads : 521

6 threads : 576

7 threads : 619

8 threads : 668

Kontraktor为每个Actor指定固定个数的线程(参见上面源码中被注释的行)


1 threads : 1520

2 threads : 804

3 threads : 571

4 threads : 459

5 threads : 457

6 threads : 534

7 threads : 615

8 threads : 659

结论


运行结果的区别很大程度上可归结与比例调整带来的延迟。对负载明确的情况,预先安排的Actor调度会更有效率。然而,考虑到服务器收到的请求会不断变化,自动化地调度是一种高效的选择。


将Executors/FJ与上面的调度策略进行对比,测试它们各自的缓存(cache)效果是很有意思的。不幸的是,Kontraktor不具备基于ExecutorService的消息分发,也没有针对Akka的调度策略实现。


此外,还需要一个示例Actors管理私有状态才可以观察缓存效果。

  

原文链接: java-is-the-new-c 翻译: ImportNew.com - 唐尤华

译文链接: http://www.importnew.com/13481.html

本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责,本站只提供参考并不构成任何投资及应用建议。本站是一个个人学习交流的平台,网站上部分文章为转载,并不用于任何商业目的,我们已经尽可能的对作者和来源进行了通告,但是能力有限或疏忽,造成漏登,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息