后台消息推送框架设计

来源:互联网 发布:2017淘宝设备管理在哪 编辑:程序博客网 时间:2024/05/19 15:40

前言

  最开始自己公司的后台推送系统只能是用户在线时推送,推送消息也不会保存,若用户离线,那么这条推送消息就再也无法获取。更让人头疼的是:推送的内容和推送系统是耦合在一起的,这样往往在改一处代码的同时,会出现意想不到的bug。着就更加坚定了自己要把推送代码重构的决心了。下面就是自己的整个设计过程和期间遇到的问题,写出来和大家分享一下,望大家多多指教。

设计过程

  最开始设计的时候是把数据的存储,修改和消息的推送放到一个类中pushmanager中,各种消息都糅合到一个message中,从后台拉取消息单独提出来pullManager,如图1:
  图1
  
  上面的设计有如下弊端:
  1、Message来表示各种类型的消息,而它又与数据库表直接关系,这样Message要把各种消息类型的不同点都包含进来,这样,添加一个消息类型,就要在数据库中添加字段来表示它与其他类型不同点。不符合开闭原则,维护性极差。
  2、消息存储和推送的耦合性太强了。

  针对上面问题,对其做了进一步的优化(如下图):
  1、用MessageTypeBase这个抽象类来将消息的共同行为和属性抽象出来,各种不同类型的消息继承这个抽象类,然后再针对不同类型的消息添加不同的行为和属性
  2、将消息存储和推送两个分开,messageManager负责消息的增删改查(拉取消息的行为也放入其中),它对外提供服务,PushManager负责消息的推送(包括单推,群推,组推)
  3、MessageType中用静态常量来标识不同类型的消息对应的数字,MessagetypeContent不同消息类型对应的内容模板和标题
  4、在数据库表中添加extendInfo字段(string类型),来存储不同消息的额外信息,具体的消息对其进行具体的处理(参考最后的UML图t_message表,当初自己忘记修改下图的表结构)
  图2
  
  在依照上面设计来实现代码时,自己又发觉上面的那个设计方案仍有很多的弊端:
  1、messageManager不仅负责消息的推送和拉取,还负责和数据库进行交互,功能太过复杂
  2、MessageTypeBase不仅表示消息的类型,还负责推送和拉取消息的组装,最让人无法忍受的是,如果推送的消息格式发生变化,就要改 MessageTypeBase的代码,RealnameMessage等具体的消息类型也会随之发生变化。

  于是,自己又重新对上面的系统进行拆分(如下图):
  1、新增PushMessageType2这个枚举类型来标识消息的推送类型(即单推,群推,组推等)
  2、将messageManager名字改为MessageManagerService
  3、新增MessageDao来专门负责与message数据库来打交道,对应将messageManager中的DBHelper属性改成MessageDao
  4、在PushManager中增加了三个属性,clientCache用来缓存推送时用到的client对象,剩下两个用来标识Android和ios设备,增加了5个方法,一个是获取单例对象(之前的图忘记画了),一个是获取client对象,一个是用于日志打印(方便调试时查看信息),剩下两个是在组推时用到的两个两个方法。
  5、增加MessageManager来负责messageManagerServer与MessageBase之间的交互,用messageType这个属性来标识 MessageBase子类的类型 (比如实名消息,加好友消息等) 。MessageManager中有个map,里面维护着所有的MessageBase子类(比如实名消息,加好友等各种消息类型),messageManagerServer可以根据需要用getMessageBase()取出对应的消息类型,值得注意的是,在用 getMessageBase ()这个方法是,必须先用setMessageType(int messageType,Message message)来设置 MessageBase 子类和消息内容。相对应的将messageManager中的addMessageType(int type,MessageTypeBase message)方法和removeMessageType(int type)这两个方法移到了MessageManager中。增加getMessageMap来获取消息处理后的message消息内容(主要用户拉取),和getPushMessage()来获取推送消息(主要用于推送)。相对应的增加pushManager属性来设置推送消息的内容,这样在MessageManagerServer就可以根据需求灵活地发送消息内容
  这里写图片描述
  
  在编写代码时,自己又发觉上面的设计中的两个弊端:
  1、channelid,tagname,pushType,deployStatus等推送环境参数放在MessageBase中,这样在每次推送时,通过MessageManager取出MessageBase从而获取推送环境参数(上面PushManager中的push方法的参数应该是 MessageManager 类型,自己当初忘记改了,大家可以参考下图的PushManager中的推送方法参数);再者,将这些推送环境参数放在消息类型中,将消息内容和环境参数耦合在一起,不够灵活,也不符合单一责任原则
  2、在MessageManager中,用一个map来维护各种消息类型(在 MessageManager 的构造函数中,会new出各种消息类型的对象,然后放入这个map中),用messageType来标识MessageManager使用的是哪个具体的消息,这样,有以下两点坏处:(1)因为只使用map中的一种具体类型的消息,这样在这个map中,也就只有一个具体的消息有数据,其他各种类型的消息均为空对象,这些空对象与MessageManager中的map绑在一起,也不会被GC回收(因为map中有一个消息类型在使用);(2)若增加一个消息类型,还要改MessageManager的构造函数,违背了开闭原则
  
  依照上述原因,对其进行进一步的优化(如下图):
  1、将推送环境参数从MessageBase中移到MessageManager中,MeeageBase只用来标识消息
  2、在 MessageManager中只维护一个MessageBase,在业务层中将具体的消息类型传入。这样,不管怎么增加或者删除具体的消息类型,也只是在业务中增加或删除而已,MessageManager中的代码不用修改
  这里写图片描述
  至此,整个后台消息推送框架基本设计完成。从最初的设计到最终代码的实现,可谓一波三折,不仅要抽象业务,拆分,解耦,还要考虑异常,错误信息的输出等等各种问题,自己也从中学到了很多,也从中得到了很多的快乐。如大家有什么想法,请多多指教。

1 0
原创粉丝点击