我曾在一家香港公司做了三年,该公司是为香港的证券公司提供股票交易数据及后台软件。我在这三年时间,基本上都在做两个项目的开发与优化:
最后虽然因为某些原因从这家公司离职,但是这段项目经历非常宝贵,因为在别处是很难有这样的一个机会接触到完整的流程。感谢。
证券公司对软件的要求有三种:
前两点是必须的,大家都在拼第三点。大家都知道,股票市场里时间就是金钱,能比别人早一秒得到交易详情,就能早一点下单,有时候赚亏就在这一刹那。
程序都是由Java开发的,为了能让程序运行得更快,数据延时更小,我花了近一年的时间对它们进行优化,尝试了所有我能想到的办法。最终数据延时在可接受范围内,但并未比其它公司的类似产品有优势。直到离职后,我还经常在想能用什么办法进一步提高其性能,但也没有什么好的办法。
我的一个小弟接手维护我的程序。听说明年香港联交所的数据量会更大,担心现在的程序难以应付,所以与我交流后,我也想再好好想想有什么办法来提高其性能。
前面提到的两个程序之间差异很大,所以这里只谈第一个项目:股票实时数据转发。
简单介绍一下该项目的需求:
从香港联交所拉一条专线到公司的机房,所有股票的实时交易数据,都将通过该专线发送过来,高峰期速度大约为200KB/s。我们有一台root服务器直接连到该专线,接收数据,并转发给多台children servers。其它所有的程序都将连到children server去取数据,这样就可以将压力分散到不同的服务器(但代价是多一次转发过程)。
如上图,可以看到数据的流向。当其它程序取到数据时,这些数据已经经历了两次的转发:第一次是Root转发,第二次是Child转发。
可以看到,如果数据在这两次转发中延时过多,其它程序拿到数据时,便已经晚了,输在了起跑线上。因此,如何减小转发延时是非常关键的。
该项目有以下几个特点:
我使用了以下方式来提高性能:
现在的测试情况:当HKEx速度为550K/s、child数量为1时,root转发的信息有88%左右在0ms内完成。这个数据让我觉得还有再优化的空间。
但这个程序,并不是单纯的接受与转发。比如,它需要将收到的二进制数据根据一定规则分为一个个不同长度的元素,还需要对每个元素进行简单的解析以提取其序号值和类型值进行某些特定的操作。为了能在重启后利用已经取得的数据,还需要把数据保存在数据库中。它还需要处理客户端的登录请求,发送客户端指定序号之后的数据。还需要处理断线等情况。还将记录日志以供后期分析程序运行情况及性能。
所以如何提高性能,但不影响当前的功能,还是一件非常有挑战的工作。
我曾经考虑过一些JMS工具,但试用一些之后,我发现一是难以掌控(对其内部运行原理不太明白),二是想不出自己的设计有什么不妥的地方,最终还是使用了自己的流程。
这两年接触了不少新的信息,所以这两天还是专门花了不少时间来思考,如何能将其性能进一步提高。
一、用Java还是别的语言?
我曾经觉得原因是在Java语言本身,也许换一种语言比如c++或者scala,就能够将性能提高上去。但后来我想起自己以前曾经写过一个简单的socket程序,在两台服务器之间传送数据,可以轻易地达到10MB/s的速度,说明Java本身是有能力利用好带宽的。而这个程序内部逻辑并不复杂,JVM现在的性能也很好,所以原因很可能不在Java身上。如果换成C++或者Scala,很难在短短几个月时间内就掌握并写出一个同样的程序出来,很可能性能还不如现在的程序。
二、使用memcachedb代替程序的转发与同步
这个程序的主要作用,就是把从HKEx收到的数据,同步到多个服务器,减轻单台服务器的压力,让更多的程序使用。我使用的是mina手动收发数据来达到这一目的,能不能使用其它的产品来达到这一目的?
比如memcachedb (http://memcachedb.org/),它是一个分布式的key-value存储系统,基于memcached和berkeleydb。既然它是分布式的,就意味着可以同时部署在多台服务器上,同时向外提供服务。各服务器之间的数据同步,由memcachedb自己负责,其性能会大大提高(它可能会在底层使用如ip multicast这样的技术,发送一次数据同时给多个服务器)。其它的好处是,可以大大简化程序结构,不需要以编程方式进行root与child间的数据同步,postgresql也可以省掉。
memcachedb非常值得尝试,不过需要确定几下几点:
如果各要求都满足,可考虑改用memcachedb完成一个简单的程序,实际测试其性能是否比之前有所提高。
因为该方法比较简单且可行性较大,所以应该优先考虑这一方案。
三、使用Redis代替方案二
Redis是一个内存数据库,它也有主从复制的方案,所以可代替方案二中的memcachedb,看两者谁的性能更高一些。其它要求相同。
四、zeromq
zeromq号称史上最快的消息系统。它有自己的数据传输协议,据说性能很高,远超RabbitMQ, ActiveMQ, MSMQ等,同时它提供了各种语言(包括c/c++/java等)绑定。我们可以使用Java来做,还可以用C进一步提高性能。它是LPGL协议的,意味着我们可以以链接库的形式免费使用。
参考:http://blog.codingnow.com/2011/02/zeromq_message_patterns.html
五、使用jgroups
jgroups可以让我们在Java中进行ip multicast操作。现在的程序,是每个child都与root单独连接,有几个children,root就要发几遍数据。而使用ip mulitcast的话,root只需要发送一遍数据,所有children都能拿到,可以大大提高性能。
不过考虑到某些child可能中途重启,则它需要的数据,就与root多播的数据不同了。如何处理这种情况,需要在程序中好好考虑。因为这种方案着眼于多客户端的传输,对于前面只有一个客户端的测试情况,并没有多大的帮助,所以让这一种方案放在第五位。
六、在solaris下改进现有程序
群里林晴鼓动我们安装使用solaris,说里面有一个dtrace的程序,非常厉害。Java源代码中就有很多dtrace的探针。通过它,我们可以方便且全面地了解程序的各种运行状况,可以轻易地找到程序的瓶颈。说得我很动心,但是考虑到这个比较大的学习成本,将这种方案放在靠后,但也值得一试。
七、使用real-time java
在08年,sun曾提出了一个实时Java的方案,以满足Java在实时软件领域的需求。它有以下几个特点:
可以说这个实时Java中的很多地方都是与传统Java相反的,学习难度比较大,而且还是收费的(只能试用90天)。虽然有很多吸引人的地方,但使用它能否开发出一个超过现有性能的程序出来,我个人还是持怀疑态度。
八、操作系统、硬件等因素
当程序写到一定程度,影响性能的可能会是其它因素,比如操作系统、硬件配置、JVM参数设置等等,这就很难猜到,只能靠一一尝试。所以放在后面考虑。
九、使用其它语言的方案
如果以上所有的方法都不管用,则只能去尝试其它的高性能语言,比如c/c++。网络通常是这些语言的强项,肯定会有相应的解决方案。而且一旦使用这些语言,肯定会用到与操作系统相关的某些底层库,来进一步提高性能。如果走到这一步,那只有再好好研究相应的方法了。
唐升华,加油啊!
这件事,文中提到的方法都不是正解。使用任何一种通用工具的方法都不会比使用编译语言,甚至是原生编译型语言定制的程序来得高效。 曾做过一个与上面这个程序功能基本一致的应用,数据传输率达到理论速率的95%,性能瓶颈仅受限于硬件本身。