欢迎阅读我的开源项目 《迷你微信》服务器 与 《迷你微信》客户端
在一个程序的迭代过程中,复杂度渐渐上升,可能会出现一些跨模块的调用的需求,若是直接得到引用来进行使用,会导致模块间的耦合度越来越高, 消息机制是一种用于解耦这种耦合度高模块的好方法,它的使用非常灵活便利,可以解决一对多(一个发送者,多个接受者)和需求频繁的变更 ,甚至于,在添加新的需求时可以完全的不改动旧的代码。在Java中消息机制可以用观察者模式来实现。
在 【迷你微信】基于MINA、Hibernatye、Spring、Protobuf的即时聊天系统:7.项目介绍之架构 中,我们说道了在发送聊天消息时,可能出现接受者不在线的情况,因为可能出现的各种复杂情况,封装到我们将发送消息这个行为ServerModel_Chatting类中。
不知道大家有没有注意到,微信的聊天消息是可能发送失败的,也许因为网络问题,也许因为对方根本没有开启微信,若是像登陆、注册等请求,服务器向客户端发包失败,想偷懒的话,是可以不作处理的, 不仅因为这种事件发生的几率较少,也是因为这种异常的抛弃不会造成不可接受的后果 ,大不了再点击一次登陆咯。但是,请想象一下,若是您的好友给您发了一条消息,因为您的一时网络问题,服务器不能成功的发送给您,就将其直接抛弃,那么……您可能被您的友人认为是无视了他的消息, 这种后果是不可接受的 。
所以,我们要对发送失败的消息进行存储,并在对方上线后及时的推送过去,这个模块是比较复杂的,而且牵扯到复用(单聊、群聊)问题,还有存储为永久数据问题,所以我们将其独立成单独的一个类来处理。在ServerModel_Chatting类的sendChatting方法中我们首先要从ServerModel中查找接受者是否在线,在线则可以直接推送;而不在线的情况下,则需要暂时存储下来,等待接受者上线。
// 发送一条聊天消息 public void sendChatting(final Chatting chattings) { ClientUser clientUser = serverModel.getClientUserByUserId(chattings.getReceiverUserId()); // 如果不在线,则暂存 if (clientUser == null){ addChatting(chattings); return; } // 发送 } // 暂存一条消息 public void addChatting(Chatting chatting) { // 保存消息 }
可以看到,发送聊天消息这个过程中,首先会判断接受者是否在线,若是不在线会将消息暂存,在接受者上线后将接收到服务器推送的消息。然而,这个模块如何知道接受者上线了呢?我们 通过观察者模式,在用户登陆时通知本模块进行查询是否有为接收消息。
观察者模式中,首先要有被观察者和观察者两个角色(可以有多个观察者,俗称”围观“(⊙o⊙)…),被观察者要继承Observable类,观察者要对被观察者对象调用addObserver 方法。
此处代码皆为删减版,欲查看完整代码,请参考开源项目 《迷你微信》服务器
先说说我们如何在用户登陆时通知消息发送模块进行查验。1.在Server_User的 login方法中,用户登陆成功时,会调用ServerModel的 clientUserLogin 方法,
public void clientUserLogin(ClientUser clientUser, String userId) { // 保存用户信息 clientUser.onLine = true; clientUser.userId = userId; clientUserIdTable.put(userId, clientUser); // 通知变更 setChange(); notifyObservers(new ObserverMessage_Login(clientUser.ioSession, userId)); }
在上面的方法中,我们将自己设置为变更状态,然后通知所有观察者。
2.观察者ServerModel_Chatting添加监听并进行处理
serverModel.addObserver(new Observer() { @Override public void update(Observable o, Object arg) { ObserverMessage om = (ObserverMessage) arg; if (om.type == ObserverMessage.Type.Login) { // 发送登陆者未接收的消息 } } }
在这里,我们对serverModel的实例化对象添加了一个监听,判断是否为监听事件,若是,则发送未接收的消息。
其实在本项目中,使用观察者模式并不是一个好的做法,理由如下:
因为帖主的这个项目中,目前只有这一个点需要用到这样一种功能,所以偷了个懒。一旦需求复杂度提升,我们需要将观察者模式做一些变化,将其做成完整的消息机制,使其具备一下几个特点:
帖主的想法如下:
>的哈希表对象,第一个参数是消息的类名(如MessageA),第二个链表参数是注册监听的所有监听者。