关于后端:消息推送接口设计内含源码

3次阅读

共计 3407 个字符,预计需要花费 9 分钟才能阅读完成。

我是 3y,一年 CRUD 教训用十年的 markdown 程序员👨🏻‍💻长年被誉为优质八股文选手

明天要做的就是实现 austin-apiaustin-api-impl模块的局部代码,这块实现了之后模块之间的一整条链路就买通咯


austin 我的项目 外围性能:发送音讯

我的项目呈现意义 :只有公司内有发送音讯的需要,都应该要有相似austin 的我的项目,对各类音讯进行对立发送解决。这有利于对性能的收拢,以及进步业务需要开发的效率

不多 BB,开始明天的正题

01、接口设计

austini-api 模块下定义发送音讯的接口,在 austin-api-impl 下实现具体的逻辑。我的接口实现定义:

public interface SendService {


    /**
     * 单模板单文案发送接口
     * @param sendRequest
     * @return
     */
    SendResponse send(SendRequest sendRequest);


    /**
     * 单模板多文案发送接口
     * @param batchSendRequest
     * @return
     */
    SendResponse batchSend(BatchSendRequest batchSendRequest);

}

对外提供的接口,除了须要提供 Single 接口,最好还提供个 Batch 接口。因为很有可能业务方是须要一次批量执行的(如果只有 Single 接口,那就须要屡次近程调用,这样对业务而言就不太适合了)

我所定义的接口参数如下:

public class SendRequest {

    /**
     * 执行业务类型
     */
    private String code;

    /**
     * 音讯模板 Id
     */
    private Long messageTemplateId;


    /**
     * 音讯相干的参数
     */
    private MessageParam messageParam;
    
}

通过 messageTemplateId 能够去数据库查出整个模板的信息,而 MessageParam 则是业务自行传入的参数(重要的是接收者以及文案的参数信息),而 code 则代表着以后申请要执行什么业务类型的(可基于该 code 扩大,前面会持续聊到)

02、代码实现

从流程能够看到,austin-api接管到申请之后,是把音讯发到 MQ

这样做有什么益处呢?假如某音讯的服务超时,austin-api如果是间接调用下发接口服务,那可能会存在 超时 危险,拖垮整个接口性能。MQ 在这是为了做异步和解耦,并且在肯定水平上抗住业务流量。

对于 绝大多数 发送的音讯而言,业务方也不太关怀是不是能 在接口调用时 就晓得发送后果,并且 某些渠道在发送的时候 也不晓得发送的后果(最初的后果是异步告知的,比方短信和 PUSH 推送)

基于以上的起因,引入 MQ 来承载接口的流量以及做异步,是十分正当的事。

前两天我在博客平台上发了一篇文章《面试官:零碎需要多变时如何设计?》,有网友评论了一把:

面试官:我懂了,回去等告诉吧。…… leader:小王,咱们那个可变零碎的重构打算写的怎么样了?小王:没问题了,首先按找咱们的业务辨别出责任链,而后在每个具体的步骤中部署脚本,下层再减少一个服务编排的接口对立治理…… leader:听起来有点意思,明天的候选人怎么样?小王:别提了,嘴上说 5 年教训有大型零碎设计,连 redis 都没用过。这不是快招聘季了吗,招两个实习生工具人进来给我打打下手就够了。leader:好,把工夫节点和里程碑划分一下,confluence 上立项开干吧。小王:好嘞。

在这次实现中,我也是用了责任链模式,具体残缺的代码大家就去 Gitee 拉就好了。很多同学拉完代码发现看不懂了,大家能够依照上面的图去梳理下责任链的各个角色。如果切实看不懂,倡议翻下我以前写过的责任链文章(曾经投稿过两篇了)

回到代码实现吧,这次我实现的业务是:参数前置查看 -> 参数拼装 -> 发送音讯

呀,都画了这么多图了,先 点个赞,关注一波先咯。

在这几个流程中,可能你下次拉代码的时候,会看到有“后置查看”,或者别的什么的。但不论怎么样,加这种逻辑我再也不必在同一个类上写各种 if else 啦。只有在某个节点处增加一个 Action 就完事了。

(注:这是第一版实现,前面必定会在根底上增加逻辑或正文的,其实曾经在写了,但我个别是有个小阶段再 push 代码,所以记得 star 下 gitee 不便看最新的代码)

先来说 前置查看 吧,次要就判断模板 ID 是否有传入,音讯参数是否有传入(对参数的惯例查看,如果有问题,间接 break 掉链路,返回通知调用方有问题)

接着来看 参数拼装 ,这块次要就是通过 模板 ID 去查整个模板的内容 ,而后依据 业务入参 拼装出本人的 TaskInfo(工作音讯)。

可能有同学会有疑难❓:为什么不能间接用模板的 POJO 呢?反而须要拼装成 TaskInfo?

其实还是比拟好了解的,模板是作为给用户去配置该音讯的信息,这是最最原始的信息。然而咱们发送的时候是须要做解决的。比方,我要在用户写好的 URL 链接上拼接参数,我要对 占位符 进行替换实在的值,我要在模板的根底上减少业务 ID 进而追踪数据 等等等。

说白了,TaskInfo 是基于模板的,在模板的根底上增加了某些平台性的字段(businessId),解析出用户设置的模板而想要发送的实在内容等等。

在这里,值得要阐明的是 msgContent 该字段的阐明。在模板中,该字段我在数据库正文所下的定义是(这个字段存入数据库肯定是 JSON 格局的):

`msg_content`        varchar(600) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''COMMENT' 音讯内容 占位符用 {$var} 示意 ',

不同的渠道的 JSON 构造还不一样:

  • 短信:{“content”:””,”url”:””}
  • 邮件:{“content”:””,”subTitle”:””}
  • Push:{“content”:””,”subTitle”:””,”phoneImgUrl”:””}
  • 小程序:{“content”:””,”pagePath”:”” …….}

第一反馈,我是想把所有渠道可能用到的字段都定义在 TaskInfo 下。起初感觉这样不太好看,于是我就定义了各种Model(不同的发送渠道领有着本人的内容模型)

于是,我在组装 TaskInfo 的时候利用反射来进行映射,替换占位符则借助的是PropertyPlaceholderHelper

而发送则很简略了,我是间接把 TaskInfo 序列化为 JSON,而后读取的时候再反序列化就好了。

值得注意的是,因为 TaskInfo 用的是 ContentModel 来存储着内容模型,所以咱们在序列化 JSON 的时候须要把 ” 类信息 ” 写进去,不然在反序列的时候是拿不到子类的数据的。

03、总结

对于有源码的我的项目,其实我是不太违心每一步解说我写的代码的。因为我认为我自身写得也没那么简单,也没有炫技的成分在内。

但自从 push 了代码当前,在群里揭示各位跟着做我的项目的小伙伴后,有好几位向我反馈看不太懂,所以这篇我就独自拎进去讲讲。

再回过头看,其实在 austin-api 层接管到申请之后,在发送音讯至 MQ 之前,在这里的操作都是非常简单。其实是能够把 通用业务 做在这(比如说通用去重的性能),但经我思考之后,还是不太适合。

austin-api算是一个接入层,到目前为止它只是通过 id 去数据库读取配置,就没有耗时的操作(这意味着他能承载的并发是极大的)。假如通过 ID 去数据库读取未来存在瓶颈,咱们还能够思考将配置从 Redis 甚至本地内存里取。

这是由业务能够决定的:一个模板的变更往往并不多,即使缓存存在强一致性的问题,但就那点点工夫是齐全可承受的。

Question:为什么发个音讯须要 MQ?

Answer:发送音讯实际上是调用各个服务提供的 API,假如某音讯的服务超时,austin-api如果是间接调用服务,那存在 超时 危险,拖垮整个接口性能。MQ 在这是为了做异步和解耦,并且在肯定水平上抗住业务流量。

Question:能简略说下接入层做了什么事吗?

Answer

关注我的微信公众号【Java3y】除了技术我还会聊点日常,有些话只能轻轻说~ 【对线面试官 + 从零编写 Java 我的项目】继续高强度更新中!求 star!!原创不易!!求三连!!

Gitee 链接:https://gitee.com/austin

GitHub 链接:https://github.com/austin

正文完
 0