.net core实践系列之短信服务-架构设计
<h1>前言</h1><p>上篇《.net core实践系列之短信服务-为什么选择.net core(开篇)》简单的介绍了(水了一篇).net core。这次针对短信服务的架构设计和技术栈的简析。</p>
<p>源码地址:https://github.com/SkyChenSky/Sikiro.SMS</p>
<h1>为什么需要架构设计</h1>
<p>有人会问短信服务也要架构设计?不就写个service封装个send方法就得了吗?干嘛还要大动干戈。</p>
<p>如果在单块应用的情况下,以上面的做法是无可厚非的。</p>
<p>然而架构设计解决的是<strong>应用复杂度</strong>,架构设计的大还是小取决于<strong>业务规模,</strong>技术的使用是要落实到<strong>应用场景。</strong></p>
<h1>场景假设</h1>
<p>以我们公司作为例子:</p>
<ul>
<li>已拥有多套系统,运营后台、资金平台、账单平台、APP API等;</li>
<li>需接入多个短信运营商,避免某个出异常后随时切换;</li>
<li>及时发送、定时发送;</li>
</ul>
<p>从上面场景分析出,要由多系统、多平台接入需要单独抽离出来进行服务化,而且随着接入的系统越多,性能将成为瓶颈,因此需要良好的横向拓展能力。定时发送需要调度任务系统进行解决。</p>
<p>因此下面为我设计的架构图</p>
<h1>架构图</h1>
<p><img src="https://images2018.cnblogs.com/blog/488722/201808/488722-20180828153754950-1049432109.png" alt="" width="1007" height="497"></p>
<h1>架构简析</h1>
<h2>SmsApi服务</h2>
<p>以HTTP协议RESTful风格JSON格式提供给其他系统(服务)接入,以swagger作为服务描述提供对外查看。</p>
<p>接口主要功能有:</p>
<ul>
<li>发送短信</li>
<li>查询短信列表</li>
</ul>
<p>发送短信支持批量,接口接受到请求后将数据先持久化到MongoDB。</p>
<p>如果及时发送则立刻发送RabbitMQ,再由Sikiro.SMS.Bus订阅队列进行统一发送;</p>
<p>如果定时发送则等待Sikiro.SMS.Job进行轮循MongoDB,轮询到时的消息则发送到RabbitMQ,再由Sikiro.SMS.Bus订阅队列进行统一发送。</p>
<h2>Sikiro.SMS.Job调度任务服务</h2>
<p>此服务以Quartz.NET框架为基础,通过设计可以随意增加Trigger或者服务,使其多线程或多个进程同时运行,避免数据量大了后成为发送瓶颈。</p>
<p>此服务不直接做短信发送,只是触发器的存在,通过RabbitMQ进行解耦,避免执行过程过长如果停止服务时则中断。</p>
<h2>Sikiro.SMS.Bus队列消费服务</h2>
<p>无论定时、及时短信都由该服务进行发送,如果接入了新的短信运营商,只需要停止该服务进行更新即可。停止了服务消息不会丢失,将暂存在RabbitMQ,因需对RabbitMQ的消息做持久化。</p>
<p>可以在不同的服务器上部署服务,因为订阅同一个队列,良好的横向扩展保证了高可用、高性能</p>
<h2>可伸缩性</h2>
<p>可伸缩性指在不改变系统软硬件设计,仅仅通过新增服务器的情况下,就能提升系统的处理能力。</p>
<p>HTTP API的无状态,在调度任务里的MongoDB原子操作FindOneAndUpdate的使用,多消费者的订阅都是为了可伸缩性。同时通过部署多台服务器也可以提高高性能与高可用。</p>
<h2>MongoDB的选择</h2>
<p>我选择MongoDB主要原因是聚合一致性、无模式。</p>
<p>虽说不需要ACID但不代表没有一致性,而MongoDB体现的聚合一致性,以聚合做操作。</p>
<h3>聚合</h3>
<p>一组具有内聚关系的相关对象的称为集合</p>
<h3>关系型数据库</h3>
<p>则以下面两表通过SmsId关联读取,写入则两表作为一个事务</p>
<p><img src="https://images2018.cnblogs.com/blog/488722/201808/488722-20180828162540297-685958443.png" alt="" width="721" height="166"></p>
<h3>MongoDB</h3>
<p>则以下面聚合方式表示,以聚合取,以聚合写</p>
<p><img src="https://images2018.cnblogs.com/blog/488722/201808/488722-20180828162648066-1145911002.png" alt="" width="440" height="360"></p>
<h3>无模式</h3>
<p>MongoDB一大特点则是无模式,意思是无需预先定义集合结构与字段类型,这体现了良好的拓展性。这是优点也是缺点,假如别的服务对该集合进行操作,在他不知情的情况下随意写入不同类型的值,则会影响已运行的服务。</p>
<p>因此需要将此作为应用服务数据库,也就是服务化,把对集合的操作(读与写)以服务形式提供接口给其他服务使用。</p>
<h1>服务粒度</h1>
<p>有些人会问为什么不把三个运营商Service也拆出来作为独立的API服务?</p>
<p>回顾下现在执行流程,一次短信发送最长的调用链为:请求SmsApi,Sikiro.SMS.Job轮询数据,Sikiro.SMS.Bus消费队列消息并请求短信运营商服务。</p>
<p>架构上的扩展性的本质的确是拆,但是拆得过细将出现三个问题:</p>
<ul>
<li>调用链过长影响性能</li>
<li>调用链过长难以定位问题</li>
<li>增加开发、维护成本</li>
</ul>
<p>假如哪天短信没发送成功,首先看看API日志看看是不是调用成功了,如果没问题那可能JOB出问题了。如果JOB正常跑,难道是队列问题?假如再加多一层,那就定位更加的复杂了。</p>
<p>就如开始所说的如果添加一个短信运营商只需要添加一个Service利用工厂模式,就可以良好的拓展了。而添加一个服务的开发、部署、维护成本无疑是比在组件内扩展的成本高。</p>
<h1>结尾</h1>
<p>该篇描述我的架构设计,下篇会正式对各个服务的实现进行讲解。如果您有更好的建议可以在下方评论反馈给我。</p>
<p>我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=jjdhw6o619ra</p>
</div>
<div id="MySignature" role="contentinfo">
<div style="display: block; background: #406CA4;" id="my_signature">
<p style="padding-right: 10px; padding-bottom: 10px; padding-left: 20px; font-family: 微软雅黑; font-size: 12px; border: #e0e0e0 1px dashed; color: white;">
<br>作 者:<strong><span style="font-size: 12px">
陈珙 </span></strong> <br>
出 处:http://www.cnblogs.com/skychen1218/
<br>
关于作者:专注于微软平台的项目开发。如有问题或建议,请多多赐教!
<br>
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
<br>
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角<strong style="color: red">推荐</strong>一下。您的鼓励是作者坚持原创和持续写作的最大动力!
<br>
</p>
</div><br><br>
来源:https://www.cnblogs.com/skychen1218/p/9547543.html
頁:
[1]