CometPipe数据发送一点改动
Author:放翁(文初)
场景:
前提:长连接推送(每个请求会话时间保持较久)。在事件驱动模式下,多线程可能同时完成任务并通过Http长连接下发数据,对于Response需要有一定的并发保护。
第一版
增加一个lock,获得以后才可以使用Response。
每个线程的处理流程:get lock à use response àrelease lock。
在同一个通道的事件大量并发产生的时候,由于use response比较“重”,使得大量线程生命周期加大(顺序的获取锁),上下文切换频繁,系统load较高。
第二版(现在的版本)
去掉lock,为每一个请求会话增加一个队列和后台线程。
每个线程的处理流程:add message to queue。
后台线程block to wait message and deliver。
第一版的问题不存在了,但在没有消息下行的时候,大量后台线程block wait,对于内存来说还是有些浪费。
复杂却未必有好效果的版本
从上面来看,需要做的就是防止将发送数据放入竞争事务(也就是一个时期只有一个线程负责对队列数据的获取和下发),需要复用线程为多个请求处理下行任务。
大致解释一下流程:
1. 每个线程在放入队列的时候需要先获得读写锁的读锁,然后将数据放入队列。具体获得读锁的目的最后谈。
2. 线程判断是否已经有后台支持线程来处理消息下发,如果没有尝试的去操作needworkerflag的原子布尔对象。
3. 线程如果成功的将needworkerflag原子布尔对象由true改成了false状态,那么表示他获得了取得线程事件的令牌,就向线程池发起执行下发消息的任务。
4. 线程池收到任务后,分配一个线程循环的去获取数据并下发,直到队列瞬时为空。
5. 获取写锁,重新再检查队列是否有数据,如果有下行,然后修改needworkerflag为true,最后释放写锁。(用读写锁就是为了在后台线程退出时保证队列中的被加入的数据完全被执行,而没有并发导致遗留数据在队列但没有任何线程处理的情况,带来的坏处就是在这个写锁临界区里面会有写出动作会阻塞外部在那个时候放数据的过程)
设计很复杂,能够带来的比第二个设计好的点就是可能在消息并发较低的时候充分利用资源,但坏处还是很多的,包括线程切换,线程退出时的短时阻塞,线程池容量大小的考虑。
大家如果有更好的设计和实现的方式可以一起讨论并给出设计和实现的细节说明。