从超市收银故事看互联网技术

从一家超市的收银发展过程中面临的问题以及解决方案来看互联网技术,欢迎感兴趣者继续阅读,:)。

一家小超市,一个收银员,同时还兼着干点其他的事情,例如打扫卫生、摆货;

来买东西的人多起来了,排队很长,顾客受不了,于是加了一个收银台,雇了一个收银员;

忙的时候收银员根本没时间去打扫卫生,超市内有点脏,于是雇了一个专门打扫卫生的;

顾客不断的增加,超市也经过好几次装修,由以前的一层变成了两层,这个时候所做的事情就是不断的加收银台、收银员和打扫卫生的人;

在超市运转的过程中,老板发现一个现象,有些收银台很长的队,有些收银台人不多,了解了下原因是收银台太多了,顾客根本看不到现在各个收银台的状况,对于这个现象,一种简单的方法就是继续加收银台,一方面超市没地方可加收银台了,另一方面作为老板当然不需要雇太多的人,于是开始研究怎么样能让顾客了解到收银台的状况,简单的加了一个摄像头和一个大屏幕,在大屏幕上显示目前收银台的状况,这样后发现基本上排的队的长度是差不多了;

排队长度差不多后,又出现了一个现象,就是有些收银台速度明显比其他的慢,了解了下原因是排在有些收银台的顾客买的东西特别的多,于是又想了一招,就是设立专门的10件以下的通道,这样买东西比较少的顾客就不用排太长的队了,这一招施展后,顾客的满意度明显提升,销售额也好了不少,后来就继续用这招应对了团购状况、vip状况;

在解决了上面的一些烦心事后,关注到了一个存在已久的现象,就是白天收银台很闲,晚上则很忙,从节省成本上来考虑,于是决定实行部分员工只用晚上上班的机制,白天则关闭掉一些收银台,顾客仍然可以通过大屏幕看到哪些收银台是关闭的,避免走到没人的收银台去了,实行这招后,成本大大降低了。

这个生活中的例子以及其解决的方法,其实和互联网网站发展过程中的一些技术是非常类似的,只是在技术层面用其他名词来表达了而已,例如有集群、分工、负载均衡、根据QoS分配资源等。

集群:所有收银员提供的都是收银功能,无论顾客到哪一个去都可完成付款,可以认为所有的收银员就构成了一个集群,都希望能做到顾客增加的时候只用加收银员就行,在现实生活中有场地的限制,而在互联网应用中,能否集群化还受限于应用在水平伸缩上的支撑程度,而集群的规模通常会受限于调度、数据库、机房等;

分工:收银员、打扫卫生的分开,对于互联网而言,这是一项重要而复杂的技术,没有现实生活中这么简单,涉及到的主要有按功能拆分系统以及数据库等,如何拆分以及拆分后如何交互是两个需要面临的挑战,因此会有高性能通信框架、SOA平台、消息中间件、分布式数据层等基础产品的诞生;

负载均衡:让每个收银台排队差不多长,设立小件通道、团购通道、VIP通道等,这些可以认为都是集群带来的负载均衡的问题,从技术层面上来说,实现自然也比生活中复杂很多;

根据QoS分配资源:部分员工仅晚上上班的机制,这个对于生活中来说做到不难,对互联网应用而言,还真是件复杂而且极度具备挑战的事。

参照生活中的例子来说,在面对用户增长的情况下,想出这些招应该不难,不过要掌握以上四点涉及的技术已经相当复杂了,而且互联网中涉及的其他的很多技术还没在这个例子中展现出来,例如缓存、CDN等优化手段;运转状况监测、功能降级、资源劣化、流控等可用性手段,自建机房、硬件组装等成本控制手段,因此构建一个互联网网站确实还是不容易的,技术含量十足,当然,经营一家超市也不简单,如果有同学能继续把涉及的技术补充到这个超市的故事就好了,例如还有超市进货的问题、摆货的问题等,欢迎接龙,:)。

第一次杭州程序员交流会总结

第一次杭州程序员圆桌交流会于上周末在杭城著名的betacafe举行了,本次主题为Java并发编程,参加的人员为14人,分别来自网易、cisco、DBAppSecurity、阿里巴巴b2b、淘宝、支付宝、阿里云,组织者为阿里云的龙浩,twitter id为: @hoorace,交流会上共有7人分享了PPT,其他人员热烈的参与了讨论,:),总结来说,本次的交流会还是很成功的,质量不错,组织形式上后面可以考虑进一步优化,下面进入本次交流会的细节分享,感兴趣的同学可以继续阅读,twitter上的直播实录请搜索#yuanzhuo。

第一次杭州程序员圆桌交流会于上周末在杭城著名的betacafe举行了,本次主题为Java并发编程,参加的人员为14人,分别来自网易、cisco、DBAppSecurity、阿里巴巴b2b、淘宝、支付宝、阿里云,组织者为阿里云的龙浩,twitter id为: @hoorace,交流会上共有7人分享了PPT,其他人员热烈的参与了讨论,:),总结来说,本次的交流会还是很成功的,质量不错,组织形式上后面可以考虑进一步优化,下面进入本次交流会的细节分享,感兴趣的同学可以继续阅读,twitter上的直播实录请搜索#yuanzhuo。
===================================================================
Topic 1:Java多线程常见陷阱
PPT下载地址:http://www.bluedavy.com/yuanzhuo/20100320/killme2008@taobao.pptx
Topicer: @killme2008
Topic评价:PPT做的很不错,形象的图形,这些陷阱在编写Java多线程程序时需要特别注意,绝对是经验之谈,值得学习,尤其是killme2008 PPT中最后分享到的Sun JDK 1.5里的一些bug。
===================================================================
Topic 2:Java Concurrent Programming
PPT下载地址:http://www.bluedavy.com/yuanzhuo/20100320/joez@cisco.ppt
Topicer: joez
Topic评价:这个Topic分享的更多的是并发编程中需要掌握的一些理论知识,例如线程、原子操作、可见性、wait/notify机制等,和上一Topic一样,都提到了要特别注意InterreputedException的处理,在分享的过程中引发大家讨论的主要是什么是并发,什么是并行,我自己的看法是并发的程序不一定能并行,就此而已,另外一个有意思的点就是交流到了CPU超线程,这个点是有些意思的。
===================================================================
Topic 3:SEDA与Java并行编程点滴
PPT下载地址:http://www.bluedavy.com/yuanzhuo/20100320/tanbamboo@DBAppSecurity.ppt
Topicer:@tanbamboo
Topic评价:SEDA是现在高性能服务器编程中的重要思想,tanbamboo同学分享了一个实际的SEDA的例子,会有助于SEDA思想的理解,在PPT的最后提到了coroutine的一个问题,coroutine只是用一个线程如何发挥多核优势,其实coroutine通常是使用cpu核数线程。
===================================================================
Topic 4:并发编程交流
PPT下载地址:http://www.bluedavy.com/yuanzhuo/20100320/bluedavy@taobao.pptx
Topicer: @bluedavy
Topic评价:自己的PPT不好评价了,分享的主要是Java并发编程中最关键的资源竞争、一致性保证的知识点以及线程相关的知识点,分享过程中我举的一个基于concurrentHashMap、FutureTask改造的例子引发了大家的热烈讨论,cisco同学后来给了一个类似的方案,也是不错的,最后在讲线程部分时也宣扬了下coroutine,并发上来讲最重要的还是尽可能的去除(Non-Blocking)、减少竞争(拆分、lock-free),这个其实不是纯技术方面的,很多时候需要根据场景来分析。
===================================================================
Topic 5:Java并发编程实践
PPT下载地址:http://www.bluedavy.com/yuanzhuo/20100320/hoorace@aliyun.pptx
Topicer: @hoorace
Topic评价:hoorace分享了一个实际的案例,这个还是很受大家关注的,各提意见来进行改造和提升,:),从效果来看,分享案例再讲一些背后的机制是很受大家欢迎的,同时自己也能从中获得一些新的不同角度的改进方法。
===================================================================
Topic 6:线程飞扬
PPT下载地址:http://www.bluedavy.com/yuanzhuo/20100320/lihaibo@netease.ppt
Topicer:杨海波
Topic评价:网易的同学分享了一个数据迁移的案例,这个例子中反应的主要是借助多线程来提升性能的方案,这也是我个人认为实际场景中使用多线程主要是两个目的,一是提升所能支撑的并发量,二是提升性能。
===================================================================
Topic 7:并发程序设计
PPT下载地址:http://www.bluedavy.com/yuanzhuo/20100320/wenshaojin@b2b.ppt
Topicer:温少
Topic评价:温少同学以一个经典的10个老婆的例子来阐述了amdahl、gustafson、sun-ni定律,通俗易懂,太经典了,推荐了这本书:《Synchronization Algorithms and concurrent Programming》; 同时在分享中温少从另一个不同的角度来讲了并发的提升,就是硬件角度,例如现在的GPU,分解了CPU压力;最后分享的基于TimerWheel算法来改进java中头疼的基于Timer做定时触发的问题,很值得学习,如果说评选的话,我会选择温少这个分享作为本次的最佳分享,受益颇丰。

作为第一次这种交流会,质量能保证的这么好,不得不要感谢各位参与者open的分享精神,同时更要感谢的是@hoorace的辛苦组织,从本次交流来看,这种相对交流性质而非培训性质的还是很受欢迎的,因此将继续组办这类交流会,以后的可能的改进方法为:
1、初定为一个月一次,固定场所,每次的主题前期由组织者来指定,开展多了后则可采取投票机制;
2、由于分享的同学可能会很多,为了避免时间过长,后续会采取对需要分享的PPT做一次投票,确保在有限的时间内分享大家最感兴趣的话题,在分享方面最好是实例,然后讲解此次实例过程中涉及的背后的知识点,参与交流会的同学可以随时提供改进的更好建议;
3、录制视频,以便分享给更多的无法来参加的同学,进一步讨论。
下一期主题初定为关于JVM的那点事,对JVM内存管理、编译机制、线程机制等有深入研究和实战经验的同学欢迎参与,可以直接联系我或者是等口碑上的活动报名帖做出来后再行报名。

杭州程序员圆桌交流第一期–并发编程PPT

由阿里云龙浩同学牵头的杭州程序员圆桌交流,第一期主题为并发编程,把自己的经验也分享下,在活动结束后会公开此次交流的资料,具体PPT见文中内容。

由阿里云龙浩同学牵头的杭州程序员圆桌交流,第一期主题为并发编程,把自己的经验也分享下,在活动结束后会公开此次交流的资料,具体PPT如下:

SOA@eBay读后感

在QCon SF 2009的SOA分会场上,eBay的架构师讲了一个SOA @ eBay的PPT,正好和我的工作有很多的交叉点,于是比较认真的看了下这个PPT,感兴趣的同学可以从这里下载:http://qconsf.com/sf2009/file?path=/qcon-sanfran-2009/slides/SastryMalladi_SOAEBayHowIsItAHit.pdf,在这个PPT中可以看到eBay对于SOA的看法以及他们目前的做法,自己也是做这方面工作的,就在这篇blog中介绍下这个PPT以及自己对于SOA的一些看法。

在QCon SF 2009的SOA分会场上,eBay的架构师讲了一个SOA @ eBay的PPT,正好和我的工作有很多的交叉点,于是比较认真的看了下这个PPT,感兴趣的同学可以从这里下载:http://qconsf.com/sf2009/file?path=/qcon-sanfran-2009/slides/SastryMalladi_SOAEBayHowIsItAHit.pdf,在这个PPT中可以看到eBay对于SOA的看法以及他们目前的做法,自己也是做这方面工作的,就在这篇blog中介绍下这个PPT以及自己对于SOA的一些看法。

先来介绍下这个PPT的内容,PPT内容主要分成以下几部分:

1. What is SOA

简单来说,就是一种架构思想,有助于提升系统的重用性。

2. SOA Benefits

提升业务的灵敏性,降低失败需要付出的代价。

3. SOA挑战

  • 由于服务多级调用带来的延时;
  • 调试/跟踪问题困难;
  • 需要高效的请求/会话级别的cache;
  • 更高的安全/监测的要求;
  • 现有应用移植;
  • 部署和回滚;
  • 演变阶段新老技术的共存;
  • QoS和SLA的支持;
  • 集成测试;
  • 高可用和高度可伸缩性;
  • 版本和依赖管理。

4. 应对挑战的措施

  • 轻量、高性能的SOA平台(自己写+开源+商业);
  • 单元测试框架和服务虚拟化;
  • 领域驱动的服务划分方式;
  • 支持REST风格;
  • 服务生命周期管理;
  • 增量的服务部署;
  • 管理工具;
  • 开发人员的培训。

5. eBay的SOA平台

  • 高性能、可扩展(pipeline方式)的轻量级框架;
  • 监测、安全控制、流量限制;
  • 服务注册和服务仓库;
  • ESB;
  • 业务流程引擎;
  • 开发工具和管理工具。

6. SOA治理

  • 设计阶段,接口和类型的review、确认,从而遵循约束;
  • 运行阶段,部署/安全/缓存/监测/可用性策略,并根据运行状况和设计阶段对比,看看是否符合设计;
  • 变更管理,做到依赖或版本的变更不影响现有应用。

7. eBay的SOA治理

  • 服务仓库,用于记录服务元信息、生命周期信息以及消费者信息以及进行服务依赖的分析;
  • 服务注册,支持通过UDDI方式,主要用于路由;
  • 基本实现6中SOA治理的设计阶段、运行阶段的要求。

8. 总结

最重要的为:服务层的规划、版本和依赖的管理,目前eBay的SOA化正在进展中。

看完PPT,可以看出,eBay负责这个部分的架构师确实拥有不错的全局观,考虑到的点已经比较全面了,尤其是各种SOA平台落地时会碰到的问题,按照这样的控制让eBay步入SOA化问题应该不大,技术方面而言,eBay的SOA平台应该是基于http方式,实现同步和异步交互,另外就是有专门的开发工具和管理工具,这点是eBay的特色,平台在发展初期就为开发人员的易用做出了充分的考虑,其他技术点看不太出来,希望以后能看到更多eBay SOA平台技术细节的介绍,例如如何实现QoS、部署等,不过SOA,确实是各人有各人的想法,因此做出来都会有些差别,下面就按照这个PPT的结构讲讲自己的一些看法:

1. What is SOA

简单来说,我认为SOA就是要做到系统内或系统间以标准的service方式进行交互。

2. SOA Benefits

  • 标准的交互方式

不管你需要的功能是以什么方式实现的、怎么部署的,都可以用一种统一的方式去发布服务、调用服务,避免出现调这个功能要用webservice、调另外个功能需要用hessian这样的现象。

  • 屏蔽交互细节

功能交互时同步还是异步、通信方式(调用的服务在哪、通信协议、数据交互协议等)是怎么样的,开发人员都可以不用考虑,这将使得开发人员能更加专注于业务功能的实现。

  • 功能重用

一个应用必然会有很多可公用的功能,如果没有一种好的方式提供,搞不成就会折腾成这个系统里有一个类似的业务功能,另外个系统里也有一个,最后要维护起来就痛苦了,这个时候功能的重用就显得非常的重要了。

  • 对互联网应用而言

性能、可用性、伸缩性问题可以更集中精力的去解决,同时也是在对应用进行划分时产生的必须的基础平台,现在外部很多声音是SOA死了吗,但其实N多家互联网公司就是SOA平台的实现以及实施者。

3. SOA挑战

  • 通信方面的性能、可用性以及可伸缩性的挑战,这是互联网应用必须的,没太多可说的;
  • 服务路由,这点其实看要实现到什么程度,简单的话就是个随机或顺序的地址选择,复杂的话就是怎么样才能选择到最佳的服务目标地址,这里面就有很多学问了,这点貌似目前eBay还没重视,不过以后它强调QoS的时候自然要考虑的;
  • 给开发阶段带来的挑战,主要是开发人员习惯的转变,以及调试、查错的复杂,一出问题会很难查问题到底在哪,毕竟有可能一个功能是跳了好几个系统的,这点说起来貌似eBay做的不错,这个在实施SOA平台时会碰到很多麻烦;
  • 多版本共存的挑战,尤其是服务多了以后;
  • 根据QoS有效分配服务资源,这个难度可想而知,之前貌似有看到过IBM的蓝云的demo里貌似能做到;
  • SOA平台自身的可管理和升级,这个问题也会不小,随着SOA平台部署到所有的应用上,可想而知,要升级一次是什么概念;
  • 服务粒度的掌控,这个是最难的,因为没标准,eBay的那个领域驱动划分听起来貌似还行,但没细节,也无法评判;
  • 现有应用移植,这个基本都是共同的头疼点,貌似基本上也没什么好办法,要的就是时间了。

4. 看重的SOA平台的关键点

如果有一个现成的SOA平台摆在我的面前,我想我最关注的是它在以下几点的表现:

  • Service的定义

这不用说,SOA平台必备,但服务的定义至少应包括版本、扩展的服务属性声明等。

  • 支持多种交互方式以及可扩展

这个基本也不用说,至少传输协议上多支持几种、数据交互协议上也多支持几种吧,还有就是交互方式,例如同步、异步等,不用说的就是还是要能扩展的,这样实在不行就自己上了。

  • 高性能、高可用、高度可伸缩

互联网应用,没什么说的,这三点是必须的,因此好歹要有个benchmark、有个网站应用的例子最好了,高度可伸缩嘛,至少SOA平台本身不能成为伸缩路上的拦路者。

  • 服务路由

服务路由,我自己还是很看重的,因为这对于做到按QoS来分配服务资源非常重要,否则就得应用自己折腾了。

  • 强大的SOA治理

由于PPT上eBay把SOA治理单独说了,我也单独说吧,对我而言,强大的SOA治理也是重要的因素,主要是治理的很多基础其实就是平台要先做好的,否则平台没有,自己开发会发现根本没法做,如果治理需要的基础都有了,那难度是不大的。

5. SOA治理

随着服务的增多,治理会变得非常重要,简单说说我对SOA治理的一些需求:

  • 服务依赖关系分析,这个现在已经成治理的基本话题了,所以不必多说;
  • 服务运行状况监测,至少要显示下现在服务的响应时间、请求状况吧;
  • 服务路由调整,这是调整服务资源的重要手法;
  • 服务流量控制,能适当控制下服务能接受的请求的上限,能给不同机器分配不同的流量;
  • 服务请求的全程跟踪,例如可以动态打开track,跟踪其中例如执行失败的某一条的全程,从而迅速判断性能瓶颈、错误根源等,并可同时基于此来做请求的放弃以及响应的丢弃;
  • 服务降级,降级这个貌似现在都成国外几家互联网公司的入门级武器了,所以没得说,咱们也追随下。

貌似现在商业的、开源的都还没有这样的吧,如果什么时候能出现个就好了,好歹也能去膜拜下,好了,我已罗嗦完毕,接下来就是大家的时间了,期待期待。

在Java中使用协程(Coroutine)

本篇blog将讲述coroutine的一些背景知识,以及在Java中如何使用Coroutine,包括一个简单的benchmark对比,希望能借助这篇blog让大家了解到更多在java中使用coroutine的方法,本篇blog的PDF版本可从此下载:http://www.bluedavy.com/open/UseCoroutineInJava.pdf

在讲到具体内容之前,不能不先讲下Coroutine的一些背景知识,来先具体了解下什么是Coroutine。

1.       背景知识

现在的操作系统都是支持多任务的,多任务可通过多进程或多线程的方式去实现,进程和线程的对比就不在这里说了,在多任务的调度上操作系统采取抢占式和协作式两种方式,抢占式是指操作系统给每个任务一定的执行时间片,在到达这个时间片后如任务仍然未释放对CPU的占用,那么操作系统将强制释放,这是目前多数操作系统采取的方式;协作式是指操作系统按照任务的顺序来分配CPU,每个任务执行过程中除非其主动释放,否则将一直占据CPU,这种方式非常值得注意的是一旦有任务占据CPU不放,会导致其他任务”饿死”的现象,因此操作系统确实不太适合采用这种方式。

说完操作系统多任务的调度方式后,来看看通常程序是如何实现支持高并发的,一种就是典型的基于操作系统提供的多进程或多线程机制,每个任务占据一个进程或一个线程,当任务中有IO等待等动作时,则将进程或线程放入待调度队列中,这种方式是目前大多数程序采取的方式,这种方式的坏处在于如想支持高的并发量,就不得不创建很多的进程或线程,而进程和线程都是要消耗不少系统资源的,另外一方面,进程或线程创建太多后,操作系统需要花费很多的时间在进程或线程的切换上,切换动作需要做状态保持和恢复,这也会消耗掉很多的系统资源;另外一种方式则是每个任务不完全占据一个进程或线程,当任务执行过程中需要进行IO等待等动作时,任务则将其所占据的进程或线程释放,以便其他任务使用这个进程或线程,这种方式的好处在于可以减少所需要的原生的进程或线程数,并且由于操作系统不需要做进程或线程的切换,而是自行来实现任务的切换,其成本会较操作系统切换低,这种方式也就是本文的重点,Coroutine方式,又称协程方式,这种方式在目前的大多数语言中都有支持。

各种语言在实现Coroutine方式的支持时,多数都采用了Actor Model来实现,Actor Model简单来说就是每个任务就是一个Actor,Actor之间通过消息传递的方式来进行交互,而不采用共享的方式,Actor可以看做是一个轻量级的进程或线程,通常在一台4G内存的机器上,创建几十万个Actor是毫无问题的,Actor支持Continuations,即对于如下代码:

Actor

act方法

进行一些处理

创建并执行另外一个Actor

通过消息box阻塞获取另一个Actor执行的结果

继续基于这个结果进行一些处理

在支持Continuations的情况下,可以做到消息box阻塞时并不是进程或线程级的阻塞,而只是Actor本身的阻塞,并且在阻塞时可将所占据的进程或线程释放给其他Actor使用,Actor Model实现最典型的就是erLang了。

对于Java应用而言,传统方式下为了支持高并发,由于一个线程只能用于处理一个请求,即使是线程中其实有很多IO中断、锁等待也同样如此,因此通常的做法是通过启动很多的线程来支撑高并发,但当线程过多时,就造成了CPU需要消耗不少的时间在线程的切换上,从而出现瓶颈,按照上面对Coroutine的描述,Coroutine的方式理论上而言能够大幅度的提升Java应用所能支撑的并发量。

2.       在Java中使用Coroutine

Java尚不能从语言层次上支持Coroutine,也许Java 7能够支持,目前已经有了一个测试性质的版本[1],在Sun JDK 7尚未正式发布的情况下如希望在Java中使用Coroutine,Scala或Kilim是可以做的选择,来分别看下。

Scala是现在很火的语言之一,Twitter消息中间件基于Scala编写更是让Scala名声鹊起,除了在语法方面所做出的改进外,其中一个最突出的特色就是Scala Actor,Scala Actor是Scala用于实现Coroutine的方式,先来具体看看Scala在Coroutine支持实现的关键概念。

l  Actor

Scala Actor可以看做是一个轻量级的Java Thread,其使用方式和Java Thread基本也一致,继承Actor,实现act方法,启动时也是调用start方法,但和Java Thread不同的是,Scala Actor可等待外部发送过来的消息,并进行相应的处理。

l  Actor的消息发送机制

发送消息到Actor的方式有异步、Future两种方式,异步即指发送后立即返回,继续后续流程,使用异步发送的方法为:actor ! MessageObject,其中消息对象可以为任何类型,并且Scala还支持一种称为case Object的对象,便于在收到消息时做pattern matching。

Future方式是指阻塞线程等待消息处理的结果,使用Future方式发送的方法为:actor !! MessageObject,在等待结果方面,Scala支持不限时等待,限时等待以及等待多个Future或个别Future完成,使用方法如下:

val ft=actor !! MessageObject // Future方式发送消息

val result=ft() // 不限时等待

val results=awaitAll(500,ft1,ft2,ft3)  // 限时等待多个Future返回值

val results=awaitEither(ft1,ft2) // 等待个别future完成

接收消息方通过reply方法返回Future方式所等待的结果。

l  Actor的消息接收机制

当代码处于Actor的act方法或Actor环境(例如为Actor的act方法调用过来的代码)中时,可通过以下两种方式来接收外部发送给Actor的消息:一为receive方式,二为react方式,代码例子如下:

receive{

case MessageObject(args) => doHandle(args)

}

react{

case MessageObject(args) => doHandle(args)

}

receive和react的差别在于receive需要阻塞当前Java线程,react则仅为阻塞当前Actor,但并不会阻塞Java线程,因此react模式更适合于充分发挥coroutine带来的原生线程数减少的好处,但react模式有个缺点是react不支持返回。

receive和react都有限时接收的方式,方法为:receiveWithin(timeout)、reactWithin(timeout),超时的消息通过case TIMEOUT的方式来接收。

下面来看基于Scala Actor实现并发处理请求的一个简单例子。

class Processor extends Actor{

def act(){

loop{

react{

case command:String => doHandle(command)

}

}

}

def doHandle(command:String){

// 业务逻辑处理

}

}

当需要并发执行此Processor时,在处理时需要的仅为调用以下代码:

val processor=new Processor()

processor.start

processor ! “Hello”

从以上说明来看,要在旧的应用中使用Scala还是会有一些成本,部署运行则非常简单,在Scala IDE Plugin编写了上面的scala代码后,即生成了java class文件,可直接在jvm中运行。

Kilim是由剑桥的两位博士开发的一个用于在Java中使用Coroutine的框架,Kilim基于Java语法,先来看看Kilim中的关键概念。

l  Task

可以认为Task就是Actor,使用方式和Java Thread基本相同,只是继承的为Task,覆盖的为execute方法,启动也是调用task的start方法。

l  Task的消息发送机制

Kilim中通过Mailbox对象来发送消息,Mailbox的基本原则为可以有多个消息发送者,但只能有一个消息接收者,发送的方式有同步发送、异步发送和阻塞线程方式的同步发送三种,同步发送是指保证一定能将消息放入发送队列中,如当前发送队列已满,则等待到可用为止,阻塞的为当前Task;异步发送则是尝试将消息放入发送队列一次,如失败,则返回false,成功则返回true,不会阻塞Task;阻塞线程方式的同步发送是指阻塞当前线程,并保证将消息发送给接收者,三种方式的使用方法如下:

mailbox.put(messageObject); // 同步发送

mailbox.putnb(messageObject); // 异步发送

mailbox.putb(messageObject); // 阻塞线程方式发送

l  Task的消息接收机制

Kilim中通过Mailbox来接收消息,接收消息的方式有同步接收、异步接收以及阻塞线程方式的同步接收三种,同步接收是指阻塞当前Task,直到接收到消息才返回;异步接收是指立刻返回Mailbox中的消息,有就返回,没有则返回null;阻塞线程方式的同步接收是指阻塞当前线程,直到接收到消息才返回,使用方法如下:

mailbox.get(); // 同步接收,传入long参数表示等待的超时时间,单位为毫秒

mailbox.getnb(); // 异步接收,立刻返回

mailbox.getb(); // 阻塞线程方式接收

下面来看基于Kilim实现并发处理请求的一个简单例子。

public class Processor extends Task{

private String command;

public Processor(String command){

this.command=command;

}

public void execute() throws Pausable,Exception{

// 业务逻辑处理

}

}

在处理时,仅需调用以下代码:

Task processor=new Processor(command);

processor.start();

从以上代码来看,Kilim对于Java人员而言学习门槛更低,但对于需要采用coroutine方式执行的代码在编译完毕后,还需要采用Kilim的kilim.tools.Weaver类来对这些已编译出来的class文件做织入,运行时需要用织入后生成的class文件才行,织入的方法为:java kilim.tools.Weaver –d [织入后生成的class文件存放的目录] [需要织入的类文件所在的目录],目前尚没有Kilim IDE Plugin可用,因此weaver这个过程还是比较的麻烦。

上面对Scala和Kilim做了一个简单的介绍,在实际Java应用中使用Coroutine时,通常会出现以下几种典型的更复杂的使用场景,由于Actor模式本身就是异步的,因此其天然对异步场景支持的就非常好,更多的问题会出现在以下几个同步场景上,分别来看看基于Scala、Kilim如何来实现。

l  Actor同步调用

Actor同步调用是经常会出现的使用场景,主要为Actor发送消息给其他的Actor处理,并等待结果才能继续。

n  Scala

对于这种情况,在Scala 2.7.7中,目前可采取的为以下两种方法:

一种为通过Future方式发送消息来实现:

class Processor(command:String) extends Actor{

def act(){

val actor=new NetSenderActor()

val ft=actor !! command

println(ft())

}

}

class NetSenderActor extends Actor{

def act(){

case command:String => {

reply(“received command:”+command)

}

}

}

第二种为通过receive的方式来实现:

class Processor(command:String) extends Actor{

def act(){

val actor=new NetSenderActor()

actor ! command

var senderResult=””

receive{

case result:String => {

senderResult=result

}

}

println(senderResult)

}

}

class NetSenderActor extends Actor{

def act(){

case command:String => {

sender ! (“received command:”+command)

}

}

}

但这两种方式其实都不好,因为这两种方式都会造成当前Actor的线程阻塞,这也是因为目前Scala版本对continuations尚不支持的原因,Scala 2.8版本将提供continuations的支持,希望到时能有不需要阻塞Actor线程实现上述需求的方法。

还有一种常见的场景是Actor调一段普通的Scala类,然后那个类中进行了一些处理,并调用了其他Actor,此时在该类中如需要等待Actor的返回结果,也可使用上面两种方法。

n  Kilim

在Kilim中要实现Task之间的同步调用非常简单,代码如下:

public class TaskA extends Task{

public void execute() throws Pausable,Exception{

Mailbox<Object> result=new Mailbox<Object>();

Task task=new TaskB(result);

task.start();

Object resultObject=result.get();

System.out.println(resultObject);

}

}

public class TaskB extends Task{

private Mailbox<Object> result;

public TaskB(Mailbox<Object> result){

this.result=result;

}

public void execute() throws Pausable,Exception{

result.put(“result from TaskB”);

}

}

Kilim的Mailbox.get并不会阻塞线程,因此这种方式是完全满足需求的。

l  普通Java代码同步调用Actor

由于已有的应用是普通的Java代码,经常会出现这样的场景,就是希望实现在这些Java代码中同步的调用Actor,并等待Actor的返回结果,但由于Scala和Kilim都强调首先必须是在Actor或Task的环境下才行,因此此场景更佳的方式应为Scala Actor(Kilim Task) à Java Code à Scala Actor(Kilim Task),这种场景在对已有的应用中会是最常出现的,来看看在Scala和Kilim中如何应对这样的需求。

n  Scala

目前Scala中如希望在Java Code中调用Scala Actor,并等待其返回结果,暂时还没办法,做法只能改为从Java Code中去调一个Scala的Object,然后在这个Object中调用Actor,并借助上面提到的receive或future的方法来获取返回值,最后将这个返回值返回Java Code。

n  Kilim

目前Kilim中如希望实现上面的需求,其实非常简单,只需要在Java Code的方法上加上Throw Pausable,然后通过mailbox.get来等待Kilim Task返回的结果即可,在Kilim中只要调用栈上的每个方法都有Throw Pausable,就可在这些方法上做等待返回这类的同步操作。

从上面这两个最常见的需求来看,无疑Kilim更符合需求,但要注意的是对于Kilim而言,如果出现Task call  nonpausable method call pausable method这样的状况时,pausable method中如果想执行阻塞当前Task的操作,是无法做到的,只能改造成Task (在mailbox上做等待,并传递mailbox给后续步骤)  call nonpausable method (传递mailbox) call  pausable method (将逻辑转为放入一个Task中,并将返回值放入传递过来的mailbox),这种状况在面对spring aop、反射调用等现象时就会出现了,目前kilim 0.6的版本尚未提供更透明的使用方法,不过据kilim作者提供的一个试用版本,其中已经有了对于反射调用的透明化的支持,暂时在目前只能采用上述方法,迁移成本相对较大,也许以后的kilim版本会考虑这样的场景,提供相应的方法来降低迁移的成本。

3.       性能、所能支撑的并发量对比

在对Scala、Kilim有了这些了解后,来具体看看采用Scala、Kilim后与传统Java方式在性能、所能支撑的并发量上的对比。

l  测试模型

采用一个比较简单的模型进行测试,具体为有4个线程,这4个线程分别接收到了一定数量的请求,每个请求需要交给另外一个线程去执行,这个线程所做的动作为循环10次获取另外一个线程的执行结果,此执行线程所做的动作为循环1000次拼接一个字符串,然后返回。

l  实现代码

由于目前Scala版本对Continuation支持不够好,但上面的场景中又有此类需求,所以导致Scala版本的代码写的比较麻烦一些。

实现代码以及可运行的环境请从此处下载:

http://www.bluedavy.com/open/benchmark.zip

l  结果对比

测试机器为一台4核的linux机器。

TPS的对比结果如下:

tps

Load的对比结果如下:

load

从上面的测试结果来看,在这个benchmark的场景中,基于Kilim和Scala实现的Coroutine版本在随着请求数增长的情况下load的增长幅度都比纯粹的Java版本低很多,Kilim版本表现尤其突出,在TPS方面,由于目前Scala版本对Continuation支持的不好,因此在这个测试场景中有点吃亏,表现反而最差,经过上面的测试可以看到,基于Coroutine版本可以以同样的load或更低的load来支撑更高的TPS。

到此为止,基本上对Java中使用Coroutine的相关知识做了一个介绍,总结而言,采用Coroutine方式可以很好的绕开需要启动太多线程来支撑高并发出现的瓶颈,提高Java应用所能支撑的并发量,但在开发模式上也会带来变化,并且需要特别注意不能造成线程被阻塞的现象,从开发易用和透明迁移现有Java应用两个角度而言目前Coroutine方式还有很多不足,但相信随着越来越多的人在Java中使用Coroutine,其易用性必然是能够得到提升的。

4.       参考资料

  1. http://en.wikipedia.org/wiki/Computer_multitasking
  2. http://en.wikipedia.org/wiki/Coroutine
  3. http://en.wikipedia.org/wiki/Actor_model
  4. http://en.wikipedia.org/wiki/Continuation
  5. http://lamp.epfl.ch/~phaller/doc/haller07coord.pdf
  6. http://www.scala-lang.org/sites/default/files/odersky/jmlc06.pdf
  7. http://www.malhar.net/sriram/kilim/kilim_ecoop08.pdf
  8. http://lamp.epfl.ch/~phaller/doc/ScalaActors.pdf

[1] http://weblogs.java.net/blog/forax/archive/2009/11/19/holy-crap-jvm-has-coroutinecontinuationfiber-etc