又来水文了...
但感觉没什么其他内容可写,就将之前做的一个消息平台稍微做点总结. 简单说说短信模块的实现,做的东西不复杂,权当总结了~.需求
功能需求
功能需求比较直接.
需要提供一些短信和统计功能即可:- 通知短信
- 验证码短信
- 语音短信
- 发送量统计
非功能需求
主要是以下几点
- 稳定的发送保证: 分为两部分,一个是服务本身的稳定性,另一个是短信服务提供商的稳定性. 服务本身稳定是自己需要实现的,可以通过微服务平台已经有的一些工具保证,这里不进行展开.而另一个是需要外部保证的,外部需要保证的东西永远是脆弱的,所以不能仅仅使用一家服务商,需要使用多家.
- 方便扩展: 接着第一点,服务商可能会有更换的需求,需要保证能够方便接入下架服务商.
- 资产保护: 短信相比邮件,微信通知等的一个不同是他花费比较高,一条的计价在3分左右,较长的短信会被拆为多条,如果不加限制进行保护很容易造成大量的资损.
技术选型
有了上面的需求之后就是选用什么技术了.
基础的web服务就使用公司现有的即可. 对于存储,因为涉及到发送信息和报告的记录,量可能会很大(其实上线之后也还好,3个月不到千万记录),统计时可能会按照某些发送内容统计,使用MySQL之类的可能会不太靠谱,选择使用ES. 对于安全,一定需要做手机号每日发送量和发送频率等的限制,这个用redis可以较为轻松解决.最后一个简化版的架构图如下:
实现细节
上面都是这个服务战略上的一些东西,这里说一些战术上的.
接入多家服务商
需要有多家服务商的接入,一家服务商可能同时供应多个短信功能.
同时同一个短信功能需要轮流调用所有提供的服务商,并在一家失败后进行重试. 也需要支持灵活的配置,可以禁用掉一些服务商,也可以方便加入新的. 在实现上先定义了各个发送功能接口,例如:interface NotiSmsSender { SmsSubmitResult sendNoti(NotiRequest request);} interface VerifyCodeSmsSender { SmsSubmitResult sendVerifyCode(VerifyCodeRequest request);}
对应的服务商实现不同的接口,提供不同的服务,每个对应的实现都有一个id字段,用于之后的配置.
现在有了一坨sender,接下来要定义router,用来进行请求的分发.
router内含有sender列表,配置router内的sender就可以实现服务商的下线功能了. 大致流程如下:实际实现中使用了Akka,各个sender是一个actor,router负责监督各个sender的健康情况,此外天然获得了超时时间的能力(底层使用了异步HttpClient,actor内部不会发生阻塞).
发送限制设置
关于手机号和ip的发送数量计数使用reids
的incr
配合时间戳相关的key就可以很简单完成,这里不累述这个.
发送限制的话首先要针对不同功能,因为不同功能的使用量是截然不同的,并且也有隔离的作用,一个功能超限不能影响其他功能.
其次,发送的限制需要分层级,当达到了全局最大限制后,就算没有超过单手机限制也不应该发出去了.
使用的层次是: 功能全局次数(次数) -> 单接入应用限制(次数/白名单/开关) -> 单手机/ip限制(次数/白名单/开关) -> 风控服务(开关) 最后次数要根据统计情况和业务更改做适当调整.性能
这个服务很明显是IO密集的,就没什么计算,主要是调用各种外部服务,等IO返回.
所以对于这些各个用来做请求的线程池就可以配置稍微大一点,实际最主要的就是HTTP的连接池.但连接超时,读取超时也不适合设置过大. 实际测试也没多大问题,平日一天短信请求量也在几w左右,并没有太大的瓶颈.其他的一些点和普通的web服务也大同小异了,没有什么特别的技巧.
其实之前一些文章也是在构建这个服务的时候写的,这段时间没写什么就当划水了,比如: 这个服务构建的时候根据需要用了些之前没有使用的组件,也算额外获得了一些回报吧~