搭建推送中心(一)

在已经过去的2016年,整个后端技术市场上,如果说哪种技术最火,我想微服务应该无人可以出其右吧,可以说火爆大江南北。很多公司的后端服务架构体系都在逐渐从单应用体系转型到微服务体系,我们也不例外的投入这股转型浪潮中。可以说,在可预见的未来几年,微服务的架构体系定将在整个系统架构中成长为苍天大树。

A、简单提提简单的一些技术选型评估

在做微服务中,需要做很多技术的评估,选型等等,好在这些令人头疼的事不需要我的参与(小菜鸟一枚,上不得台面),那么在谈到现在的微服务架构,往往都会把其中的调度协议抓出来大谈特谈一番,被畅聊的比较多的可能是:基于高性能的远程调用rpc协议以及非常轻量级的基于请求-响应模式的http协议。所以可能被提的比较多的几个关键字大概是:dubbo,thrift,restful,以及后边我们最后选定的spring全家桶 spring cloud。在这里不想说去做框架之间的偏激的比较,任何一种技术框架都有其侧重点,以及相对的弱化区,在小子看来,技术框架从来都没有强弱的概念,只要可以解决自身业务痛点的技术,都是好技术。

接下来,说一下为什么我们最后会选择使用目前来说没有很火的spring cloud吧。之所以选择它,第一自然是基于对于spring社区几乎绝对的信任。对于整个Java开发体系来讲,spring社区的影响力之大,便如好莱坞与电影行业之发展一样,所以作为一个javaer,本身对于spring出品的东西,都抱着非常高的期待与信任。第二点是考虑到更为轻量的restful协议,至于说有没有比远程调用协议更优越,小子也不知道(没有使用过rpc协议做过东西,所以没有更多的去了解)。第三点考虑则是基于spring boot微应用理念开发,本身spring cloud就是由spring社区整合旗下的其他框架形成的一个全新的生态,而spring boot微应用的理念简直就是为了微服务架构而生(略偏激)。非常期待,完全相信spring cloud在未来一定可以大放异彩……

那在做评估与选型的时候,是否有其他更多其他的考虑,我暂时也不知道,如果有的话,我后续会再补充进来。这边推荐一个链接,基于DD老师分享的关于dubbo与spring cloud的一些简单对比:http://blog.didispace.com/microservice-framework/

 

B、简体提提搭建系统消息推送模块

推送模块是我接手微服务架构的第一个应用的开发,并且整个系统的设计过程都是由我自己来驱动,自我思考,而老大则是作为建议参考,从旁指导。(菜鸟一枚,羞涩羞涩)所以我才考虑将自己的一些感悟以及思考记录下来,如果有哪一位网友不小心看到,并且对于他提供了一些微不足道的帮助,我想我花的时间与精力来写这一系列文章(后续在持续改进推送模块中,如果有好的改动,好的设计,或者优化,我会持续写,可以当成一个简单的系列)的目的就达到啦。因为开源,分享,可以使我快乐。

一、基础平台搭建

公司现在处于发展阶段,目前对接了几种推送的第三方平台:短信平台,app推送平台,以及微信公众号推送。相当的简陋,因为现在初步转型,后续业务上肯定会持续对接各大平台来做消息推送,所以初步的设计需要做好可扩展的考虑。(不过请注意,切勿过度设计……)

首先,基于我们选择了spring cloud来做平台架构,协议上的选择无疑就是非常轻量,并且极为成熟的基于http的restful协议。所以暂时性的,整个推送中心我们的对于提供两种接口,一种是https的rest请求,一种则是基于消息推送的消息队列。

底层第三方平台的对接,我们采用的是是spring 提出的starter的形式,通过pom文件引入,这种形式对于后续的第三方平台持续追加都是毫无压力的,非常轻松,并且非常轻量;所以第一步的框架构建大概如下图

(图1)

在starter上提供一个service来处理不同的消息,以及即将调用的不同的第三方平台。starter虽然看似简单,实则不容易,特别是我们这种多系统,多平台的对接情况(接入参数的多样性)。

二、水平扩展的消息服务层搭建

推送模块就是来做消息推送的,所以离不开业务的格局,所以有必要分析具体的业务。基于业务的设计基本上每个公司都有不同的业务需求,所以结果自然也是天差地别,这里以我们公司的业务作为说明记录,消息推送主要是以系统内部触发消息,以消息队列的形式来触发推送,很多时候都是一些提醒,比如短信提供,公众号提醒;另外就是通过调用推送接口来给指定用户推送内容服务,活动等等;第三点通过接口调用作为其他平台整合,扩展使用。所以我初步把我们的消息类型分类,提取几个拿出来说,如下图:

(图-2)

1、入口设计

仔细考虑,比如一个注册消息触发,需要做短信推送,把注册相关的内容(送代金券之列)推送到用户手中。比如一个收款的消息触发,需要短信提醒交易情况,需要公众号提醒交易额,需要app推送相关流水等等……一条消息进来,有时候仅仅需要单个平台进行推送,有时候却需要多个平台同时去做推送。所以消息处理的一套集合大概是  <消息类型 * 平台>这样一个结果集,如此凌乱,可能后续非常臃肿的结果集,如果请求的入口设计(其他服务调用推送服务必须遵循的协议)不合理,后续根本无法持续维护。

初步设计:提供一个公共的数据模型DTO(数据传输对象 data transfer object),以及一些推送系统内部固定的常量的jar包,目的是为了让我后续的业务不断扩展可以带来极大的方便性,所以我的DTO必须在最开始就尽可能考虑周全(当然,似乎也没有多少好考虑的,哈哈)

public class SenderMessageDTO {
	
	/** 消息平台类型*/
	private String sender;
	
	/** 平台标识*/
	private String 	sys;
	
	/** 消息类型*/
	private String 	msgType;

        // .......
}

有了这个jar包之后,整个推送的入口算是敲定了,其他服务若是需要使用推送服务,那么只需要引入jar包,然后按照我实现约定好的协议来配置它们需要的内容,包括推送方式,推送时间,推送通道等等。

入口敲定之后,我既定的下一层属于消息服务层;

2、简单的消息服务层设计

如图2所画,整个服务层的架构是水平方向扩展的,所以可能我们想到的第一点就是实现接口,来达到统一入口的目的。实现方式如下:

/**
 * 消息处理器
 * @author lennon
 * 2017年3月8日 下午2:45:20
 */
@Service
public class MessageService {
	
	/**
	 * 发送消息:调用消息的处理器
	 * @param senderMessageVO
	 */
	public String send(SenderMessageVO senderMessageVO){
		IMessageServiceType iMessageServiceType = MessageServiceTypeHolder.getMessageServiceType(senderMessageVO.getMsgType());
		if(iMessageServiceType != null) {
			return iMessageServiceType.messageHandler(senderMessageVO);
		}
	}
}

首先定义一个消息服务类,考虑消息种类繁多:注册,登录,充值,下单……所以,在做水平扩展的时候,有两个方案:

        一、以继承体系来维护,即定义好消息基础类(抽象类,或者普通类),目的就是,水平扩展的子类只需要继承这个基础类一次(Java不允许多继承的方式),所以从目前分析的角度来说,这种方式并没有太大问题。

        二、基础类不变,抽取公共接口,让所有消息业务类实现接口。这么做的方式就不存在说多继承的问题,并且水平扩展也毫无障碍。

从上边的代码都可以看出最后选择的是第二种方案,具体实现是:定义消息类型(以枚举类型,或者字符串常量),消息业务类实现了一个接口

/**
 * 消息类型 处理接口
 * @author lennon
 * 2017年3月8日 下午2:46:07
 */
public interface IMessageServiceType {

	public String messageHandler(SenderMessageVO messagevo);
	
}

按道理将,现在的消息体系的调用情况应该如下:

232335_gLh3_1589819.png

http/https 请求进入推送服务中,映射到action层进行处理,action层注入了messageservice对象进行业务处理。messageservice注入holder对象。那么这个holder是用来做什么的?很明显,holder其实是一个HashMap,通过注解与接口实现来扫描到所有消息类型业务处理的单例对象,然后保存在HashMap中。目的就是:通过messageservice就可以以SenderMessageDTO的对象中msgType精确的找到消息类型业务处理对象。引入这种holder的模式,是基于系统后续扩展(百分之百确认)来考虑,只需要实现接口,加入注解,不需要改变messageservice的代码,也不会影响其他的代码(基于开闭原则)。

三、水平扩展的消息发送器(基于starter之上的一层调用封装)

在设计完消息服务层之后,需要考虑的是如何发送消息,应该说,如何简单的发送消息,并且从代码设计层面考虑如何毫无压力引入其他平台的设计。在starter引入之后,简单的调用starter来发送消息,其实是非常简单,并且毫无压力的,那么为何还需要做这样的一层设计?举个例子,假如现在有十个公众号,如何调用微信接口来推送消息呢?简单。调用分装好的starter接口,选择好哪个公众号的信息就OK。

闭眼思考,过分简单的东西容易让人怀疑,所以尝试死磕自己吧(罗振宇最常提的一句话,死磕自己)。如果不做分装,这一层的代码放在消息服务层,是不是有十个公众号,就要有十层的if - else来判定公众号?有人说,statert上分装了微信调用的接口,可以在这一层作考虑,把公众号到 信息丢到starter去,然后直接调用。绝对不可以,斩钉截铁的确定的告诉你,不可以!为何?

请思考starter的优势!

starter不是推送服务专属服务,它有很多其他的事要做,所以它只能维护其他平台的接口对接,至于上一层如何调用,坚决不能嵌入starter中,一个臃肿的starter注定不是好的starter。

不谈其他可能有点糟糕的设计,聊聊自己的怎么写(站在现在的小子的经验以及认知的角度,小子认为不能更好了的设计),设计与消息服务的设计相似:

234847_iubf_1589819.png

/**
 * 消息发送接口
 * @ClassName: Sender 
 * @author lennon
 * @date 20170307
 * @since JDK1.8  
 */
@Service
public class Sender {
	
	/**
	 * 发送消息
	 * @param messageInfo
	 */
	public MessageWithObjectVO sendMessage(SenderMessageVO messageInfo){
		ISenderType iSenderType = SenderTypeHolder.getSender(String.valueOf(messageInfo.getSender()));
		if(iSenderType != null) {
			return iSenderType.sendMessage(messageInfo);
		}
		return CommonMessageEnum.OPERATE_ERROR.getMessage();
	}
}

从消息发送器的设计角度来分析,sender通过holder来调用不同的第三方平台对接的上层接口(第三方平台底层是starter封装)因为不想要直接调用starter接口,所以才会有了sender的这个设计与实现。后续添加各种starter的时候,messageservice容易造成各种改动,这是不允许的。以后再添加starter第三方接口,实现senderType然后来调用新的平台,新平台的引入对于消息服务不会产生直接影响。

这里举个例子来分析微信公众号的推送(中间经历了一次改动):

   a、按照公众号来分类形式,通过注解+实现接口的形式可以将十个公众号分为十个类,并且后续假如增加其他公众号推送,依旧是注解+实现接口,根本不需要做很大的代码改动。(看起来的感觉是完美的?)

    b、经老大点醒,这里是属于严重的过度设计,仔细分析公众号之间的调用区别,其实仅仅只是key与srcret的不同而已,如果为此去搞非常多类,那以后如果有一百个公众号,是不是要维护一百个类?想想就打了个机灵。他提供的解决方案是:定义好一个数据结构,来存放所有的key与secret,说到底无非就是

Map<String, Map<String, String>> map;

即便是一百公众号,对于系统而言,不外乎就是往配置文件增加一百个新的key与secret。

四、消息服务层  调用 消息发送器

说完消息服务,说完消息发送器,剩下的就是消息服务来对接消息发送器了。

这里存在比较大的问题是:不同的消息可能需要根据不同的需求来调用不同的消息发送器。

有两种方案:通过为每个具体的消息类型服务定义一个接口来分别实现第三方平台接口,因为不同的业务需要做不同的业务处理。另外就是不要搞得那么复杂不同的平台接口就写不同的函数,按照需要调用,类稍微庞大一点点而已。

……并没有什么好的方案,悲剧。

001618_L92u_1589819.png

整个的大概的设计过程类似酱着……

 

欢迎指教!

 

 

转载于:https://my.oschina.net/u/1589819/blog/861059

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值