昨天在看Cache Client代码的时候,发现在从资源池中获取SocketIO部分代码在高并发情况下效率不高,因此考虑通过一些变通的方式来提高效率,下面说的内容仅仅是当前自己琢磨出来可以部分提高效率的方法,希望看了这篇文章的同学能够有更好的方式或者算法来提高效率。
情景:
Cache Client 的SocketIO资源池是一个两级的Map,具体定义为:ConcurrentMap<String, ConcurrentMap<SockIO, Integer>>。第一级Map以Host作为Key,第二级Map以SockIO本身作为Key,三种SockIO状态(可用,占用,废弃)作为value。之所以采用一个Pool来存储三种状态主要是考虑到在高并发下,多个池之间保持原子性的复杂。
每一次获取可用的SocketIO的操作需要经历:1.遍历Host所在的Map。2.逐个比较状态。3.原子方法获取可用SocketIO。(并发问题所要求的,具体代码可以下载:http://memcache-client-forjava.googlecode.com/files/alisoft-xplatform-asf-cache-2.5.1-src.jar )。
在修改过去的版本里面,首先遍历的过程是一个固定顺序的过程(keyset),这样会导致在高并发的情况下,越来越多的资源申请命中率会下降,因为压力总是落在keyset靠前的那些SockIO上(重复比较)。需要考虑通过什么手段可以提高在高并发下的申请命中率。
思考:
1. 资源申请的越早,被释放的可能性越高,因此是否可以考虑采用更新SockIO最后申请时间来作为后续申请的初步依据。(本身复杂度带来的耗时可能会超过命中率降低带来的损耗)
2. 采用随机数的方式来确定keyset的起始游标,也就不是每次都从keyset第一位开始(可以把keyset看作一个首尾相接的数组)。
3. 在每次资源回收的时候纪录下该资源为可用(当前为每一个Host就记录一个可能可用的资源,简单化操作),作为申请的首选尝试。(尝试不成功在去遍历)。
当前实现了2,3组合,发现效果明显,在500个并发下,每个线程200次操作(一系列动作),压力测试结果如下:
Cache test consume(cache测试总共耗时),average boundle consume(每个线程总耗时),average per request(每个线程每次操作总耗时)
没有作任何改动以前的测试结果:
cache test consume: 11507741, average boundle consume: 57538, average per request :115
采用了2策略以后的测试结果:
cache test consume: 10270512, average boundle consume: 51352, average per request :102
采用了2,3策略以后的测试结果:
cache test consume: 9140660, average boundle consume: 45703, average per request :91