林小应
有一封信要寄出去,你不会傻到自己送过去吧,写上地址、投进街边邮筒就可以了,自己继续干其他事情。
KFC把送外卖的事情都交给了宅急送。如果自己干,一天还能卖几个鸡翅啊。
……
有些事情,不是非常重要,也不影响后续工作,但又比较耗时的,可以交给专门的人或机构去完成,自己腾出手来继续干其他事。可以很大的提高工作效率。
计算机程序也是这样,比如要将一些日志送到远程日志主机,或者要将配置数据存到Memcached(简称MC),如果由主线程自己完成,会浪费很多时间在等待返回上。可以起一批线程来负责传送日志、存配置数据等。主线程需要传送日志或者存配置数据到MC时,只需要将动作包装成一个动作对象,丢进公共的容器、就像“邮筒”。一批线程会不断的扫描“邮筒”,只要发现有新的动作对象,就从中取出执行掉。
将一些不是非常重要,不影响后续工作的动作,包装成动作对象,交给缓冲区完成,就像new一个对象一样简单。
思路:新建一个队列,存储动作对象,再新建一批工作线程来执行对象。客户端有需要执行的动作,只需要封装成动作对象,丢进队列就可以了。
一个缓冲区由N个缓冲队列组成
一个缓冲队列由一个队列、m工作线程、一个监控线程组成
队列:用来保存待执行的动作对象
工作线程:不断扫描队列,发现有新的动作对象加进来,就取出执行掉
监控线程:监控工作线程的状态,如果发现有线程死掉,就重新启动该工作线程,如果重启十次仍不成功,就重现一个工作线程替换原来的。还负责监控队列执行的情况,如果最近t毫秒(我们配的是100ms)推送进来的动作对象没有执行完,则暂停该缓冲区,后续推送进来的动作对象不入队列,直接执行动作。
以上变量 n、m、t都是可配置的
抽象的数据模型:
缓冲区模型:
上面是一个5*5的缓冲区,就是有5个缓冲队列,每个队列有5个守护线程在不断扫描,一旦发现新的动作对象,就取出执行。
动作对象模型:
/** * @author linxy 2012-11-30 * */ public abstract class AsynItem { public final long birthTime = System.currentTimeMillis(); public abstract boolean launch(); }
缓冲队列中存放的必须是该类型的动作对象
动作对象类型为AsynItem,只有一个方法launch,所有推送进来的对象只要实现launch方法就可以了,工作线程拿到动作对象以后,会执行launch方法。
利用缓冲区保存日志的例子:
AsynItem item = new AsynItem() { publicboolean launch() { client.sendMsg(msg); returntrue; } }; AsynLaunchUtil.asynLaunch (item);
第一句是new一个动作对象,要执行动作是client.sendMsg(msg);第二句AsynLaunchUtil.asynLaunch (item);是将对象丢进缓冲区。
在我们的系统中,缓冲区在收集日志、保存配置数据到memcached等方面很大的提高了性能,运行稳定。而且通用性很好,调用简单——就像上面只需要两行代码。
QQ:346420558