锁定老帖子 主题:基于总线模式的消息服务
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-07-14
前言一直以来,都对异步事件很感兴趣,比如一个应用在运行一个耗时的过程时,最好的方式是提交这个耗时的过程给一个专门的工作线程,然后立即返回到主线程上,进行其他的任务,而工作线程完成耗时任务后,异步的通知主线程,这个过程本身是很有意思的。传统的事件-监听器模型可以较好的解决这个问题,不过事件和监听器两者的耦合往往略显紧密,所以需要另一种实现,使得这两者的耦合尽量小,那样模块可以比较通用。
总线模式前几天跟同事讨论了下Swing中的消息机制,同事给我讲了下总线模式的消息机制,感觉很有意思,于是周末就自己实现了下。具体的思路是这样的:
这个思路最大的好处是,事件被抽象成消息(Message),具有统一的格式,便于传递。挂在总线上的监听器互相不知道对方的存在,监听器可以指定自己感兴趣的消息类型,消息可以是广播的形式,也可以是点对点的。(后来参看了下JMS,其中有pub/sub的模式(即订阅模式),不过,对于异步消息的传递来说,这个可以不必实现)
消息服务消息服务可以将一大堆分布在不同物理机上的应用整合起来,进行通信,可以将一些小的应用整合为一个大的,可用的应用系统。 用一个例子来说吧: public class Test{ public static void main(String[] args) throws RemoteException{ /* * 创建一个可被通知的对象(监听器), 这个监听器关注这样几个事件 * TIMEOUT, CLOSE, and READY */ Configuration config = new RMIServerConfiguration(null, 0); CommonNotifiableEntry entry1 = new CommonNotifiableEntry(config, "client1", MessageTypes.MESSAGE_TIMEOUT | MessageTypes.MESSAGE_CLOSE | MessageTypes.MESSAGE_READY); /* * 创建另一个监听器, 这个监听器关注这样几个事件 * OPEN, CLOSE, and TIMEOUT. */ CommonNotifiableEntry entry2 = new CommonNotifiableEntry(config, "client2", MessageTypes.MESSAGE_OPEN | MessageTypes.MESSAGE_CLOSE | MessageTypes.MESSAGE_TIMEOUT); // 将监听器挂在BUS上 entry1.register(); entry2.register(); // 创建一个新的消息, MESSAGE_OPEN类型. Message msg = new CommonMessage( entry1.getId(), entry2.getId(), MessageTypes.MESSAGE_OPEN, "busying now"); // 传递给entry2 entry1.post(msg); // 创建一个MESSAGE_CLICKED类型的消息, entry2 // 不关注这个类型的消息,所以此消息不会被传递 Message msgCannotBeReceived = new CommonMessage( entry1.getId(), entry2.getId(), MessageTypes.MESSAGE_CLICKED, "cliked evnet"); entry1.post(msgCannotBeReceived); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } // re use the message object to send another message entry msg.setSource(entry2.getId()); msg.setTarget(entry1.getId()); msg.setType(MessageTypes.MESSAGE_READY); msg.setBody("okay now"); entry2.post(msg); // 卸载这些监听器,当程序退出,或者 // 或者监听器不在关注事件发生的时候 entry1.unregister(); entry2.unregister(); } }
当前,这个系统可以支持远程的消息传递(通过java的RMI机制),不过对于寻址方面还没有做进一步的处理,有时间再来完善吧。
消息服务的实现下面我把消息服务的主要实现部分贴出来分析一下: /** * * @author Abruzzi * */ public class MessageBus extends UnicastRemoteObject implements Bus{ private static MessageBus instance; private List<NotifiableEntry> listeners; private List<Message> messages; private Thread daemonThread = null; public static MessageBus getInstance() throws RemoteException{ if(instance == null){ instance = new MessageBus(); } return instance; } private MessageBus() throws RemoteException{ listeners = new LinkedList<NotifiableEntry>(); messages = new LinkedList<Message>(); Daemon daemon = new Daemon(); daemonThread = new Thread(daemon); daemonThread.setPriority(Thread.NORM_PRIORITY + 3); daemonThread.setDaemon(true); daemonThread.start(); while(!daemonThread.isAlive()); } /** * mount notifiable object to listener list */ public void mount(NotifiableEntry entry) throws RemoteException{ synchronized(listeners){ listeners.add(entry); listeners.notifyAll(); } } /** * unmount the special notifiable object from listener */ public void unmount(NotifiableEntry entry) throws RemoteException{ synchronized(listeners){ listeners.remove(entry); listeners.notifyAll(); } } /** * post a new message into the bus * @param message */ public void post(Message message) throws RemoteException{ synchronized(messages){ messages.add(message); messages.notifyAll(); } } /** * * @author Abruzzi * worker thread, dispatch message to appropriate listener * */ private class Daemon implements Runnable{ private boolean loop = true; public void run(){ while(loop){ if(messages.size() == 0){ synchronized(messages){ try {messages.wait();} catch (InterruptedException e) { e.printStackTrace(); } } } processIncomingMessage(); } } } /** * process the incoming message, remove the first message from * queue, and then check all listeners to see whether should * deliver the message to or not. */ private void processIncomingMessage(){ Message msg; synchronized(messages){ msg = messages.remove(0); } String target = null; int type = 0; int mask = 0; try { target = msg.getTarget(); type = msg.getType(); if(target == MessageTypes.SENDTOALL){ for(NotifiableEntry entry : listeners){ mask = entry.getSense(); if((mask & type) == type){entry.update(msg);} } }else{ for(NotifiableEntry entry : listeners){ mask = entry.getSense(); if(entry.getId().equals(target) && (mask & type) == type){ entry.update(msg); } } } } catch (RemoteException e) { e.printStackTrace(); } } }
消息总线是一个RMI对象,其中mount(), unmout(), post()等方法可以被远程调用。MessageBus维护两个列表,一个消息列表,一个监听器列表。当消息被post到总线上后,post会立即返回,然后工作线程启动,取出消息并将其分发到合适的监听器上。 可能,对同步的处理上考虑不够周全,下来再继续修改。
P.S.我将这个项目托管在google code上了,叫BBMS(Bus Based Message Service),感兴趣的可以去看看:http://code.google.com/p/bbms/。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-07-15
最近也在研究JMS,哈哈。异步的消息机制是很有意思!
|
|
返回顶楼 | |
浏览 4712 次