三个月写了个短信平台,开源出来!
1 初心
大家好,我是勇哥。花了三个月的时间,我手写了个短信平台服务 platform-sms
,今天开源出来 Beta 版本。
写这个开源项目的初心其实很简单:"帮助初中级研发工程师入门架构设计,提升他们的技术认知"。
2018年,作为架构师,我参与一个短信平台的重构。发送短信的场景包括还款业务、CRM、促销业务等。
不同的技术团队都是使用客户端模式发送短信,但并不统一,大概分为四种 :
- 使用阿里云提供的短信 SDK 发送短信 。
- 根据亿美提供的样例直接发送短信 。
- 使用绿城提供的短信 SDK 发送短信。
- 架构团队短信 SDK ,类似于
SMS4J
的设计方式,支持亿美、绿城短信发送 。
客户端的模式在多团队协作场景中,缺点还是很明显:
-
维护成本
假如运营不再使用某一个短信渠道,那么很多团队将会收到影响,不得不配合重新修改配置,重新上线,耗费的时间成本很高。
-
无法支持高级功能
客户端实现某些功能比较麻烦,比如:客户端因为偶发情况(网络原因)通过三方渠道发送短信超时,此时需要将短信发送到备份渠道,从而确保短信发送的成功率。
因此,多团队协作的场景中,短信服务的模式应该是服务端模式。
我参考了腾讯云的短信服务的设计思路 :
- 模仿腾讯云的 SDK 设计,提供简单易用的发送短信方法 (单发,群发,营销单发,营销群发,模板单发,模板群发) ;
- 设计短信服务 API 端,接收发短信请求,发送短信信息到消息队列;
- worker 服务消费消息,按照负载均衡的算法,调用不同渠道商的短信接口;
- 控制台可以查看短信发送记录,配置渠道商信息、模版信息等。
短信平台研发完成之后,满足了当时的业务需求,因为短信的管理也归于统一,提升了业务接入短信服务的效率,所以各个技术团队也比较认可。
随着经验的累积,我见过了不少公司的短信服务,核心问题不外乎两点:
-
短信服务与业务服务边界问题。
为了满足业务服务需求,在短信平台中添加过多的业务功能,导致短信服务功能臃肿,也不经意的加入了隐藏的风险 。
-
切换三方渠道非常不方便。
当运营端需要从三方短信渠道 A 切换到 B 时,因为代码不够抽象,增加三方渠道代码时维护成本较高。
基于这些原因,我想写一个迷你版的短信服务,它应该包含如下的功能:
-
简单的短信 SDK 支持按照模版发送短信 。
业务服务对于短信是从哪一个三方短信渠道发送出来的并不在乎,只需要确保发送短信的成功率即可。
因此,SDK 提供的核心接口是:按照模版编号发送短信。
阿里云、腾讯云、华为云提供的都是按照模版发送短信的接口,为了统一管理模版,我们也只提供按照模版发送短信的接口。
短信平台需要提供业务服务的
appKey
和appSecret
, SDK 与服务端之间通过固定协议交互。 -
短信平台支持模版的管理 。
阿里云、腾讯云、华为云都提供签名、模版管理的接口,因此从产品设计层面,理论上,我们可以通过短信平台管理所有的签名和模版。
短信平台当前提供了手工绑定的短信模版的功能,也就是我们需要先在阿里云或者腾讯云先申请签名和模版,然后绑定到我们在平台创建的模版。
-
适配器模式维护三方短信渠道。
参考了开源项目
canal
的适配器模块,将三方短信渠道的 API 独立成模块单独维护,这样可以大大提升代码的可维护性。
2 架构
项目的设计应该设计得简单,因为它的目标首先是让初中级工程师快速入门架构设计。
所以,我将短信平台设计成单体应用的模式,架构图如下:
短信平台分为两个部分,这两部分可以独立部署,也可以将前端文件放置在后端中,生成单部署包。
1、前端:admin-ui
控制台模块是 vue 项目,管理员登录之后可以进行应用管理、渠道管理、短信管理、模版管理。
2、后端:admin-web
后端模块按照功能依次分为五个模块:请求控制层、业务服务层、命令处理器、三方渠道适配器插件、数据库访问层。
3 演示
3.1 环境准备
1、创建数据库以及相关表
创建数据库tech_platform
,执行 doc/sql
目录下的 tech_platform.sql
。
执行后效果如下:
2、修改部署包配置
从 Release 下载 platform-sms-admin.tar.gz
,解压缩后,进入 conf
目录 。
编辑 application.yml
文件:
进入 bin 目录,启动服务:
bin/startup.sh
3.2 操作流程
1、登录页面
服务启动后,访问地址:http://localhost:8089
。
用户名和密码存储在
conf
目录的application.yml
,默认用户名密码分别是:admin/admin1984 。
2、新建应用
应用信息包含应用名称、应用 appKey
, 应用秘钥
,备注
。其中,应用 key 和 密钥在使用客户端 SDK 时需要配置 。
3、新建三方短信渠道
注意:因为腾讯云的 SDK 请求 中需要携带 APPID ,所以 Beta 版中将 appId 存储在 附件属性中。
4、创建模版 在模版管理
模块,点击新建模版
按钮。
新建模版时,签名名称必须和渠道申请的签名必须一致。
下图展示了笔者的腾讯云申请的签名,笔者创建的模版必须和腾讯云账号的签名保持一致。
创建完模版之后,需要绑定渠道,我们需要在三方渠道先创建短信模版,然后提交绑定。
- 三方渠道先创建短信模版
如上图,笔者创建了编号为 1955325 的短信模版,因为我们需要在绑定界面绑定该渠道的模版,理论上在短信平台创建的模版可以绑定多个渠道。
- 绑定渠道
绑定完成之后,可以在模版管理页面查看模版列表 。
3.3 发送短信
发送短信可以参考 DEMO 模块:
1、添加依赖
<dependency>
<groupId>com.courage</groupId>
<artifactId>platform-sms-client</artifactId>
<version>${parent.version}</version>
</dependency>
2、客户端配置
首先在 application.yml
中配置如下:
sms:
smsServerUrl: http://localhost:8089
appKey: qQjEiFzn80v8VM4h
appSecret: 9c465ece754bd26a9be77f3d0e2606bd
然后编写配置类:
@Configuration
public class SmsConfiguration {
@Value("${sms.smsServerUrl}")
private String smsServerUrl;
@Value("${sms.appKey}")
private String appKey;
@Value("${sms.appSecret}")
private String appSecret;
@Bean
public SmsSenderClient createClient() {
SmsConfig smsConfig = new SmsConfig();
smsConfig.setAppKey(appKey);
smsConfig.setSmsServerUrl(smsServerUrl);
smsConfig.setAppSecret(appSecret);
SmsSenderClient smsSenderClient = new SmsSenderClient(smsConfig);
return smsSenderClient;
}
}
3、单发短信
@Autowired
private SmsSenderClient smsSenderClient;
@GetMapping("/test")
public String test() {
// 手机号
String mobile = "15011319235";
// 短信平台模版编号
String templateId = "555829270636703745";
// 模版参数
Map<String, String> param = new HashMap<String, String>();
param.put("code", "1234");
param.put("time", "10");
SmsSenderResult senderResult = smsSenderClient.sendSmsByTemplateId(mobile, templateId, param);
System.out.println("senderResult:" + JSON.toJSONString(senderResult));
return "hello , first short message !";
}
调用接口之后,用户就会收到如下的短信:
4 开源
代码库地址:
勇哥想把这个项目做为架构入门的教学项目,您可以从中学到 :
- 设计一个精简的客户端 SDK 。
- 理解 SPI 机制以及适配器模式。
- 配置合理的线程模型。
如果我的文章对你有所帮助,还请帮忙点赞、在看、转发一下,你的支持会激励我输出更高质量的文章,非常感谢!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
详解CCE服务:一站式告警配置和云原生日志视图
本文分享自华为云社区《新一代云原生可观测平台之CCE服务日志和告警篇》,作者:云容器大未来。 告警和日志是运维人员快速定位问题、恢复异常的主要手段。运维人员日常的工作模式往往是先接收告警信息,再根据告警信息初步判断异常的范围和影响,通过相关组件的日志定位出故障原因,进行系统恢复。因此,如何给运维人员提供简单易用的告警和日志管理平台是各个云原生平台高度关注的问题。 相较传统系统,云原生场景下应用数量非常巨大,监控指标、事件、日志等运维数据更是海量的。同时,告警配置需要联通多个系统,如告警通知人的配置涉及消息通知系统、指标阈值告警规则涉及监控系统、日志关键字告警涉及日志管理系统等。这就导致云原生场景告警的配置复杂度相当高,且涉及跳转到不同系统,流程存在断点。 同样,云原生场景下日志文件庞杂繁复。日志有容器标准输出日志、容器内日志、节点日志等多种类型;且日志可能分布在不同的主机上,位置不固定,从而导致日志查找困难。因此,如何帮助运维人员快速精确地查找到故障时间点的完整日志链路并清晰的呈现是日志服务所面临的关键挑战。 图1日志和告警中的挑战 针对于上述云原生场景下告警和日志的问题,华为云CCE...
-
下一篇
领域驱动设计总结——如何构造领域模型
领域驱动设计总结——如何构造领域模型 本文为领域驱动设计系列总结的第三篇,主要对领域驱动设计概念做个介绍,本系列领域驱动设计总结主要是在Eric Evans 所编写的《领域驱动设计》 一书的基础上进行归纳和总结。本文主要介绍在领域驱动设计中如何构造领域模型。 了解了如何创建和运用模型之后,我们再来探讨下如何构造一个领域模型。这就需要我们对领域进行分离,了解领域对象的分类及生命周期的管理。 一 分离领域 与领域有关的代码分散在大量的其他代码之中,那么查看和分析领域代码就会变得异常困难。也难以进行领域驱动设计。所以我们首先应该对领域进行分层。 我们需要给复杂的应用程序划分层次。在每一层内分别进行设计,使其具有内聚性并且只依赖于它的下层。采用标准的架构模式,只与上层进行松散的耦合。将所有与领域模型相关的代码放在一个层中,并把它与用户界面层、应用层以及基础设施层的代码分开。领域对象应该将重点放在如何表达领域模型上,而不需要考虑自己的显示和存储问题,也无需管理应用任务等内容。这使得模型的含义足够丰富,结构足够清晰,可以捕捉到基本的业务知识,并有效地使用这些知识。 目前软件大都采用LAYERED ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8编译安装MySQL8.0.19
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2全家桶,快速入门学习开发网站教程
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- MySQL8.0.19开启GTID主从同步CentOS8