文档章节

搭建推送中心(一)

世界和平维护者
 世界和平维护者
发布于 2017/03/17 00:17
字数 3710
阅读 95
收藏 3

在已经过去的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);
	
}

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

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。

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

/**
 * 消息发送接口
 * @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。

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

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

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

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

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

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

 

欢迎指教!

 

 

© 著作权归作者所有

世界和平维护者
粉丝 8
博文 51
码字总数 76601
作品 0
深圳
程序员
私信 提问
京东京麦商家开放平台的消息推送架构演进之路

本文来自京东商城京麦平台组开发工程师曹德然的技术分享,感谢作者。 1、前言 京麦实时消息推送是京东的京麦商家开放平台的核心组成部分。从消息源到消息中心再到触达用户,以及最终根据消息...

JackJiang2011
2018/01/10
0
0
京东架构师这讲述:推送架构的演进之路

前言 京麦消息是京麦商家开放平台的核心组成部分。从消息源到消息中心再到触达用户,以及最终根据消息协议呼起操作页面,京麦消息是一个完整且健康的生态闭环。下面我会详细的介绍下京麦消息...

高级架构师
2018/04/25
73
1
consul 1.0 server cluster集群配置全解密

一、背景故事:上周四听了美的MySQL数据库利用consul实现高可用,还有redis 集群模式,让我对consul产生了浓厚的兴趣,特花了三四天的时间研究consul集群,consul的特点是什么呢? 1、 使用 ...

weiyanwei412
2017/10/24
0
0
Spring + Dubbo + zookeeper (linux) 框架搭建

dubbo简介 节点角色说明: Provider: 暴露服务的服务提供方。 Consumer: 调用远程服务的服务消费方。 Registry: 服务注册与发现的注册中心。 Monitor: 统计服务的调用次调和调用时间的监控中...

王庭
2015/10/27
12.1K
5
云经济团队scrum metting

李磊:联想项目维护,创建vpc,并在vpc中搭建机器环境。 王芳和邱燕芳:基本完成王睿分配的小任务,接下来进行测试。 马俊:研究微服务架构,总结aws subbmit一些架构,并考虑应用到我们的项目...

xiaoxingxing
2015/12/22
3
0

没有更多内容

加载失败,请刷新页面

加载更多

安全组和云防火墙的区别

前言 熟悉云平台的朋友可能都会注意到这样一个事情:无论公有云还是私有云,创建虚拟机的时候都需要选择安全组,来对虚拟机进行安全防护;有的云平台在VPC里,还能选择防火墙,ZStack在3.6版...

ZStack社区版
34分钟前
3
0
教育性app开发的重要性和好处

在这个精通技术的世界中,流行的app主导着无聊的教育系统。当我们将技术和教育结合在一起时,它将带来当代以及强大的学习资源。因此,将教育移动app集成到您的学习过程中,并根据自己的信念把...

a429011717
35分钟前
3
0
IE6/7/8如何兼容CSS3属性

本文转载于:专业的前端网站➩IE6/7/8如何兼容CSS3属性 最近在工作中总是要求IE8兼容CSS3属性,在网上搜了搜主要是引入了一个htc文件(ie-css3.htc或者PIE.htc。个人认为这两个文件的作用差不...

前端老手
50分钟前
4
0
手把手教你ALLEGRO的约束规则的设置教程!

约束规则的设置 分三步, 定义规则(一、基本约束规则设置:1、线间距设置;2、线宽设置;3、设置过孔;4、区域约束规则设置;5、设置阻抗;6、设置走线的长度范围;7、设置等长:7.1、不过电阻的NET 等...

demyar
52分钟前
5
0
完美解决H5滚动滑动穿透方案:不使用系统滚动

网上有很多黑科技解决这个问题,都不是从根本去解决,例如通过js控制弹出时html加上position:fixed; 弹窗关闭后再去掉该样式,总觉得不太对,像是打补丁。 今天终于找到了滚动穿透的原因和完...

未来cc
57分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部