| Roger Chen's profileLearningBlogLists | Help |
|
|
4/30/2006 Newbie到新公司已经一个礼拜了,很忙,也很充实。连Blog都没有时间上来写了:)
我现在跟进的这个项目需要Unix下的C++编程技能,在这方面我纯粹是新手,任务的deadline也很紧,所以我只有不停的学习才能跟上进度。比较有意思的是这个项目在网络方面的代码比较多,而上个月我已经“顺便”看完了《Unix网络编程》这本书,刚好派上了用场。
最近在当当上又买了近300块钱的书,五一除了陪来上海玩的同学,剩下的时间就是抓紧时间K书了。
似乎有很长一段时间没有过这种紧张的感觉了,呵呵,还蛮享受的:) 4/20/2006 Cindy 3.0b1 released修改记录:
终于发布beta1了,API结构已经稳定下来了,原来使用cindy 3.0 alpha版本的用户请尽快升级到beta版,以后的版本发布只会更新bug或增加功能,但是不会影响到API结构。
User Guide也同步升级,谢谢wei_cheng提出的很多好问题! Msn Space被封好几天断断续续的访问不到,原来以为是Microsoft自己在做维护工作,直到昨天才发现直接访问不了时,通过tor就可以访问到,毫无疑义,肯定是被GFW给盯上了。
DNS返回spaces.msn.com的IP是65.54.153.254,无法访问;不过65.54.153.250也是微软的服务器,没有被封,因此只要把spaces.msn.com指向到这里,访问就没有问题了。
Windows的用户可以编辑windows\system32\drivers\etc\hosts文件,在下面加入:
就能正常访问了。 4/10/2006 拿到HP的Offer了收到邮件的那一瞬间,居然没有我想象的那么激动,不过倒是好好的舒了一口气,悬在心里的一块石头终于落地了:)
我很清楚自己目前的强处和弱点所在:强处是技术上能力还不错,并且有很强的学习精神;弱点是外语相对较差以及缺乏大公司经验。我希望能够在下一家公司中继续加强我的强项并且有机会修正这些弱点,理想的情况是世界500强的IT企业。当然,工作内容也要是我喜欢的,为了钱而工作那是一点乐趣都没有的。虽然有很明确的目标,不过还是花了我半年的时间来等待合适的职位出现,不过总算功夫不负有心人啊:)
非常感谢Sam给了我这个机会!也希望庄表伟和陈超在新的工作中一切顺利! 4/8/2006 Unix网络编程这本书是今年我阅读过的技术书籍中影响我最大的一本:)
我买的是第三版,前两版我没有看过,无法评价。这一版是Bill Fenner和Andrew M.Rudoff执笔,对上一版进行全面修订,加入了IPv6的一些信息和SCTP协议,删除了X/Open传输接口的内容,加入的这些内容很具有实用性。虽然这一版作者的名气比不上前两版大名鼎鼎的W.Richard Stevens,但是我的阅读体验非常好(这既有Stevens的功劳,和译者的努力也是分不开的),内容非常匹配我当前的水平,所以阅读起来非常流畅。不夸张的说,通过阅读这本书,我才迈入了网络编程的大门。
我认为这本书最有价值的地方不在于其描述的套接口编程函数(虽然这些对我这个Unix网络编程的新手来说也是很有价值的),而在于哪些分散在各个章节中对网络编程底层实现细节的注释。你了解各种I/O模型的异同么?知道在在调用某些套接口函数的背后它替你完成了什么工作么?知道SO_LINGER怎么样影响close的行为么?UDP的外出接口是怎么确定的?为什么有时connected udp会返回ICMP异常给客户程序,而普通udp则不会?IPv4和IPv6的服务端和客户端怎么协同工作?
当我看到第八章对connected udp和普通udp的性能比较时,禁不住拍案叫绝(书217页):
目前我只读到第14章,但是我已经准备好在读完后再精读一遍。如果实在要说这本书有什么缺陷的话,那就是它的份量。你知道,长时间捧着一本这么重的书,实在是一件体力活:) 4/4/2006 CVS和Issue Tracker迁移到javaeye4/3/2006 Sourceforge的稳定性还真有待提高因为以前版本都是我一个人开发,所以代码都是使用本地的CVS,一来稳定,二来速度快。现在庄表伟加入后,我想还是把代码check到sourceforge的CVS上,这样协同比较方便。
可是自从我把Cindy 3.0a5版本的代码check到sourceforge上后,连续4天我都没有办法再连接上sourceforge的CVS服务(匿名用户的CVS服务一直可以,但是开发者的CVS一直没法连接上) :(发现没有CVS真是寸步难行。修改的地方越多担心就越大,无法提交,也无法恢复成CVS上的当前版本,慢慢的就不敢下手改代码了。
实在没办法,等了它四天都没有修复,只能恢复成使用本地CVS了。将已经确认的改动提交进去后,就松了一口气,开始放心大胆的改代码了:) 4/1/2006 Selector.select(timeout) throws NullPointerException occasionally (win)今天本想对Cindy进行一些效率上的优化,尽量减少对Selector.select的调用以及传递给Selector的句柄数量,没想到意外又触发了nio的一个Bug:在Windows平台上,Selector.select调用可能抛出空指针异常。
堆栈类似于:
我机器上有四个Java版本,在Sun JDK 1.4.2_05、Sun JDK 1.5.0_02、JRockit 1.5.0_04上都不会出现问题,只有在Sun JDK 1.5.0_06上面能重现Bug。反编译sun.nio.ch.WindowsSelectorImpl,可以看到其FdMap.remove中忘记判断空了(红色代码部分):
private MapEntry remove(SelectionKeyImpl selectionkeyimpl)
{ Integer integer = new Integer(selectionkeyimpl.channel.getFDVal()); MapEntry mapentry = (MapEntry)get(integer); if(mapentry.ski.channel == selectionkeyimpl.channel) return (MapEntry)remove(integer); else return null; } 到Sun的Bug database中一查,早已有人提交了该Bug,这个Bug目前只在mustang b75版本中得到了解决。
我看了看Sun以前版本的实现,虽然不会出现该Bug,但是恐怕会有其他的问题。Sun可能是想修改代码来解决原来的一些Bug,没想到引入了一个新Bug:(虽然Sun bug database中已经有了解决方案,但是绝大部分的用户应该都不会自己去修改Sun的源码然后打包的。
目前发布的这个Cindy版本不会受到这个Bug的影响。鉴于Sun目前还没有将该Bug的修正放到Java 5.0的升级包中,所以我也暂时取消对Cindy效率上的一些优化(由于这些优化才触发了NIO的Bug),等到它升级包发布出来后再另行考虑。 3/27/2006 Cindy 3.0a5 released修改记录:
比较重要的几点改进:
比如如下代码:
第一行代码使得Future完成事件被触发,第二行代码使得SessionFilterChain上的sessionStarted事件被触发。看上去没有任何问题,是吗? 可是,如果加入这么一个FutureListener:
在使用DirectDispatcher的情况下,事件的触发顺序就变成了: Start future completed(上面的第一行代码) --> session close --> session closed --> session started(上面的第二行代码),出现了明显的错误。 所以这次Cindy 3.0a5中把所有这种顺序派发的事件全部改在Dispatcher内完成,比如上面的代码就改成了: // keep dispatch order public void run() { 可以参考DirectDispatcher中的代码,就能了解为什么这么做就能保持事件分发顺序。 3/25/2006 目标最近常被问到的一个问题是:做高效率的网络应用都是用C/C++,为什么要用Java?我也一直在思考这个问题,这牵涉到Cindy的定位,以及将来的发展方向。
如果存在一个这样的假设:C/C++和Java在网络处理方面拥有接近的运行效率,你会选择用什么语言开发网络应用?我的答案会是Java。为什么?因为对于大部分普通程序员而言(某些C/C++高手除外),在业务逻辑的开发效率上,Java要超出C/C++许多,这也正是Java流行起来的原因。
但是遗憾的是,这个假设目前还不存在:( JSR 203提议在Java中加入对操作系统本身异步IO的支持,但没有人推动;IBM推出过aio4j,不开源,而且在2004年就停止了更新。所以目前在你所看到的现实中,这类应用仍旧是C/C++的天下。
虽然假设并不存在,但需求是切切实实存在的:大家对Java网络方面的效率有着更高的期望。比如Tomcat,在它的5.5版本中有基于ARP的JNI实现;比如Resin以及其他很多商业WEB服务器,都有JNI的实现。但是遗憾的是,这些JNI并不是一个独立的网络框架,所以虽然这些人都在自己发明轮子,但是其他人很难用上他们发明好的轮子。
既然有需求,而且需求无法得到满足,那么这里就有机会。
做为一个比较资深的Java程序员,做为一个Java网络框架的开发者,我所关心的是:能不能让这个假设成为现实?或者能不能缩小它们的差距,给Java这边加些砝码?这就是Cindy的长远目标。目前虽然我的能力离实现这个目标还有差距,不过只要目标清晰,一直往前走,总是能实现的。
希望能有志同道合者和我一起来实现这个目标:) 3/22/2006 LDAP的一些用途没使用LDAP时没感觉到它的好处,现在才觉得以前那真是生活在原始社会中……
你们是如何管理你们的服务器呢? 3/14/2006 Cindy 3.0a5计划加入Qos控制本来打算下一个版本就b1了,不过这两天发现一个非常值得改进的地方:发送的Qos控制,而且该改动会影响一些现有的API,如send(Object, int)/flush(Object,int)等等,所以还是下一个版本还会是alpha版本。
在a4的实现中,Qos控制是由一个PriorityQueue来完成的,根据不同的优先级权重来调整发送顺序。但是该实现只是一种特定的Qos控制,我的计划是把Qos控制这部分分离出来,可以由用户来进行设置。 比如Concurrent包中有Delayed接口和DelayQueue实现,DelayQueue能够让添加进去的Delayed对象在指定时间后才能被取出,这种Qos控制可以用于控制包发送间隔时间。对于类似WEB、FTP这类应用,发送速度是越快越好,Qos要求可能不明显;但对于流媒体这类应用,并不是发送速度越快越好,只要保证流媒体能够流畅观看即可,发送速度过快反而可能导致其他人无法流畅观看,这种Qos控制就非常有用。
但是Concurrent包的Queue/BlockingQueue接口并不能直接用于Cindy的Qos控制,因为它们只提供了阻塞和轮询两种处理方式,这种开销对Cindy来说是不可接受的,所以需要实现队列的回调方法。另外可配置性的Qos控制怎么与现有的发送API、SessionFilter、Packet接口等结合起来,仍在考虑之中,希望能想出一个比较可行的解决方法来。 3/11/2006 Cindy 3.0a4 效率测试硬件环境:
软件环境:
测试工具:
测试结果
Echo web server:
File web server (small file 100 bytes):
File web server (small file 100 bytes, keep alive):
<P
File web server (large file 947111 bytes):
总结 先解释一下为什么Cindy 3.0a4的测试会有两种:一种是基于SocketSessionAcceptor的,一种是基于ServerSocketChannelSession的。在开发机器上测试时,这两种类型的测试结果有较大的差距,基于Acceptor的测试实际上同时有两个线程在运行,一个线程不停的accept,然后另一个核心线程做相应的处理;而基于Session的测试实际上只有一个线程在运行,就是核心线程既负责accept,也负责处理。在赛扬2.4的机器上,第一种的测试成绩大约为650#/sec,而第二种的测试成绩大约能到900#/sec。鉴于这两者在开发机器上的性能差距,所以在服务器上也做了这两种测试。 另外,由于这种测试绝大部分时间都是消耗在网络I/O上面,业务逻辑的处理用时非常少,所以只有在使用DirectDisptcher时才拥有最好的响应时间,多开线程会使响应时间大幅下降。也正是这个原因,前两种类型在开发机上的测试结果有如此大的差距。不过由于这次测试机器的CPU是XEON,双核,所以这两种类型的测试结果差距不明显。
在Echo web server的测试中,MINA 0.9.2在完成一个请求后就不再相应后续的请求了,可能是代码中存在的一些Bug所导致,所以这次测试对MINA来说其实稍微有些不公平,我会等它在后续的版本修正了这个问题后再进行一次测试;本来在开发机上QuickServer的测试结果和Cindy的测试结果非常接近,在blocking模式下,性能甚至稍微超出Cindy 3.0a4,没想到放到服务器上测试时会有这么大的差距;Cindy 2.4.4依旧傻快傻快,呵呵,不过它的模型没有Cindy 3.0这么容易扩充,而且Cindy 3.0在这种场景下和它的差距也是非常小的,所以还是推荐尽早升级到Cindy 3.0。 基于小文件的测试,Resin和Tomat遥遥领先,阻塞I/O+线程池的效率充分发挥了出来。并且这种压力测试速度又快,一个连接非常短的时间就能被处理完,正好是阻塞I/O+线程池的优势所在。在concurrent较小的情况下,这种优势更加明显,在concurrent逐渐增大后,阻塞I/O和非阻塞I/O处理效率上就越来越接近了。在这个测试中Jetty的结果比较令人失望。 Keep alive的连接测试中,基于Session的测试耗时太长被中途放弃,怀疑是由于单线程运行+Keep alive造成了始终在处理第一个连接所造成,这个还有待证实;Tomcat的表现更是突出,而在这个场景中本来应该是非阻塞I/O的强项所在。不过最大的concurrent也只到1000,在真实的环境中,如果concurrent持续增大,cindy的优势应该会更加突出才对。 大文件的测试类似于Keep alive的测试,这种场景应该也是非阻塞I/O的优势场景。在该场景中,cindy总算体现了非阻塞I/O的优势,而tomcat的表现就有点失望了,失败数也太多了一点……我研究过Tomcat的源代码,没采用JNI的话它是基于阻塞I/O的实现,只能通过SO_TIMEOUt来中断对数据的读取和写入,所以它在运行时会通过计算服务器的负载来设置相应的SO_TIMEOUT,负载小于33%情况下采用默认的SO_TIMEOUT,33%到66%之间SO_TIMEOUT减半,66%到90%之间,SO_TIMEOUT变为默认的1/3,超过90%则急速降为默认的5%。我怀疑在大文件的测试当中,由于服务器线程负载太大,所以导致超时时间变为默认的5%,从而造成了这么多的失败数。
总体来讲,在基于NIO的异步I/O框架中,我认为Cindy的效率应该算是很不错的了;阻塞I/O+线程池的做法在并发量少、连接时间短的应用中比NIO有着更高的效率,但是随着并发量的上升,两者的差距会逐渐缩小(再上升会不会反超?未经证实……)。但Java的网络层效率还是和C/C++的实现还是有着明显差距,高性能网络应用的首选还是C/C++。 Cindy 3.0a4 released修改记录:
等待一段时间后准备进入beta阶段。
Update: 发布后才发现http server的ant脚本有些问题。如果各位要测试http server,要么把ant脚本修改成fork一个新的java进程,要么命令行指定ant -Dparam=-acceptor http-server来运行。 非常抱歉! 3/7/2006 又发现NIO的一个小Bug按照NIO的规范来说,对于非阻塞Channel,如果当前write方法无法发送任何字节,则应该返回0,而不是抛出异常。 以前我就发现了它的一个Bug:在Java 1.4的虚拟机上,SocketChannel的write(ByteBuffer[])的JNI操作实现中没有检测某个返回值,抛出了java.io.IOException: A non-blocking socket operation could not be completed immediately异常。而这种情况下本应该返回0的。好在这个Bug不影响大局,一般只要绕过对该方法的调用即可,况且这个Bug已经在Java 5.0中得到了解决。 不过今天又发现了它的一个Bug:SocketChannel的write(ByteBuffer)的JNI操作,在Window的实现上没有检测WSAENOBUFS的返回值,抛出了java.io.IOException: An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full异常。而这种情况下也本应该返回0的。 应该很少会有应用会触发这个异常,我之所以发现了这个异常也是运气:) 在Cindy简单的HttpServer示例中,图个方便简单的使用了内存映射文件,而且不管文件大小都是全部映射(我是示例Cindy的用法,千万不要在生产系统中这么做)。晚上测试一个200多M文件的下载,我的内存肯定是足够的,所以内存映射没有出现任何问题,但是一次性通过write方法发送200多M的Direct ByteBuffer,呵呵,这个现象就能重现出来了:)
实际应用中应该不会有人象我这么玩的,呵呵,所以这个Bug影响倒也不大。对于应用来说,一方面是不要一次性发送这么大的数据,另一方面是增大Socket的发送缓冲区大小。对于Cindy而言,要对应用屏蔽这个Bug,即使应用发送这么大的数据也不要触发这个异常,倒是有一个简单的方法:把传递给write方法的ByteBuffer控制在一定的字节数内。 即使应用要Cindy发送一个200M的ByteBuffer,也可以控制成每次调用write方法时,只传给它一个1M大的ByteBuffer,分200次发送即可。这样就可以简单的对应用屏蔽这个Bug了。 3/6/2006 Cindy 3.0a4 configurationGlobal configuration
Buffer configuration
Dispatcher configuration
Session configuration
Acceptor configuration
For example, to enable jmx, use direct buffer, set dispatcher keep alive time to 10s and enable acceptor reuse address, you can specify configuration in system environment :
or you can put cindy.properties on your classpath:
3/2/2006 Cindy 3.0a3 user guide releasedUser Guide中的示例都是非常简单的示例,起个入门的作用。有了这个引子,再看看cindy源代码example目录下的示例,应该很容易就能上手。
希望各位多提意见,这样才好根据你们的意见来做改进:)
a4的主要目标是提高Cindy的易用性,比如通过运行期参数配置的Dispatcher线程池大小等等。如无意外的话应该两个礼拜后会release出来。
BTW:我这边好像无法访问sourceforge各个项目的主页,即xxx.sourceforge.net。难道又被封了?
Update: 晚上将原示例中简单的Echo Http Server扩充成为一个相对完整的基于文件的Http Server,然后又测试了一下效率,效率上居然也没有太多的降低。原来Requests Per Second差不多是4200#/sec,现在Requests Per Second差不多是3600#/sec。看来等a4版发布后,可以做一个比较完整的评测了,不仅可以和MINA等比较,还可以和现有的一些HttpServer进行比较(当然,它们的实现要完整的多,对它们本身的效率会有些影响,这点要考虑进去) 2/27/2006 Cindy 3.0a3 released下载地址: http://sourceforge.net/projects/cindy 修改记录:
最大的改进就是加入了SSLFilter以及反向分发xxxSend/Sent事件。 SocketSessionAcceptor中所增加的属性也是在实现网络服务器时经常要用到的,至于SocketSession、DatagramSession中经常要设置的属性由于数量太多,还是通过暴露出来的Socket和DatagramSocket来设置比较方便一点。
SSLFilter还有一个小缺陷,由于Session没有提供beforeClose的一个事件出来,因此在Session关闭前不能发送SSL的close message。这样服务器端可能会抛出一个异常: javax.net.ssl.SSLException: Inbound closed before receiving peer's close_notify: possible truncation attack? 这种实现不是太完美,不过完美的实现只能通过继承已有的SocketSession实现来解决,在close前发送close message,但这就缺乏扩展性。目前权衡下来暂时采用Filter的设计,如果应用真的在意这个close message,那么可以通过组合SSLFilter和SocketSession实现来加以解决。 SessionFilter顺序在实现Cindy 3.0a3 SSLFilter的过程中,发现一个有意思的问题:不同类型的SessionFilter对于过滤事件的顺序有着不同的要求。
假设有这么一个场景,通过SSL连接接收和发送数据,并统计所有的网络流量。
对于接收而言,SessionFilter的顺序应该是:
这样统计到的是真实的接收流量,应用拿到的也是解码后的数据。
对于发送而言,SessionFilter的顺序则应该是:
可以看到,对于发送和接收而言,这两个Filter的顺序是相反的。如果这个场景再复杂一些,数据发送前需要先进行压缩,接收到数据后再解压缩,那么从发送到接收的整个流程是(红色是发送,蓝色是接收):
可以看到在这些场景中,Filter在处理发送和接收事件上顺序是相反的。而在以前Cindy的设计里面,对于所有事件的分发都是按照相同的顺序,这可能造成困惑。
目前我的想法是,针对应用层的Filter,将所有的send/sent事件分发按照Filter添加的顺序反向分发,其他的事件则按照添加顺序分发。但这种做法缺乏灵活性,因为这暗示了所有Filter需要的就是这种顺序(虽然目前除了系统级的Filter外,我还没想到什么样的Filter处理发送和接收事件的顺序需要保持一致)。另一种做法可以把所有事件都拆分开,指定每个事件的处理顺序,但这种做法言应该会过于繁琐。
目前Cindy内系统级Filter对发送和接收事件分发的顺序需要保持一致。如果在上面这个流程图中加入系统级Filter,那么整个顺序是这样的(红色代表系统级Filter)
虽然这种全部反向分发接收事件的做法还有待商榷,但可以肯定的是,该做法比原来所有顺序一致的做法要好,因为反向分发至少覆盖了大部分的应用类型。
如果你来设计,你会怎么做呢? |
|
|