Sikiro.SMS实现优化
上面介绍了队列定时任务基本原理,然而我们需要自己的项目进行修改优化。
API消息发布
EasyNetQ是一款非常良好使用性的RabbitMQ.Client封装。对队列定时任务他也已经提供了相应的方法FuturePublish给我们使用。
然而他的FuturePublish由有三种调度方式:
- DeadLetterExchangeAndMessageTtlScheduler
- DelayedExchangeScheduler
- ExternalScheduler
DelayedExchangeScheduler是需要EasyNetQ项目提供的调度程序,本质上也是轮询
ExternalScheduler是通过使用MQ的插件。
DeadLetterExchangeAndMessageTtlScheduler才是我们之前通过DEMO实现的方式,在EasyNetQ组件上通过下面代码进行启用。
services.RegisterEasyNetQ(_infrastructureConfig.Infrastructure.RabbitMQ, a => { a.EnableDeadLetterExchangeAndMessageTtlScheduler(); });
下面代码是Sikiro.SMS.Api的优化改造:
/// <summary> /// 添加短信记录 /// </summary> /// <param name="model"></param> /// <returns></returns> [HttpPost] public ActionResult Post([FromBody] List<PostModel> model) { _smsService.Page(model.MapTo<List<PostModel>, List<AddSmsModel>>()); ImmediatelyPublish(); TimingPublish(); return Ok(); } /// <summary> /// 及时发送 /// </summary> private void ImmediatelyPublish() { _smsService.SmsList.Where(a => a.TimeSendDateTime == null).ToList().MapTo<List<SmsModel>, List<SmsQueueModel>>() .ForEach( item => { _bus.Publish(item, SmsQueueModelKey.Topic); }); } /// <summary> /// 定时发送 /// </summary> private void TimingPublish() { _smsService.SmsList.Where(a => a.TimeSendDateTime != null).ToList() .ForEach( item => { _bus.FuturePublish(item.TimeSendDateTime.Value.ToUniversalTime(), item.MapTo<SmsModel, SmsQueueModel>(), SmsQueueModelKey.Topic); }); }
重发机制
重发一般是请求服务超时的情况下使用。而导致这种原因的主要几点是网络波动、服务压力过大。因为前面任意一种原因都无法在短时间恢复,因此对于简单的重试 类似while(i<3)ReSend() 是没有什么意义的。
因此我们需要借助队列定时任务+发送次数*延迟时间来完成有效的非频繁的重发。
public void Start() { Console.WriteLine("I started"); _bus.Subscribe<SmsQueueModel>("", msg => { try { _smsService.Send(msg.MapTo<SmsQueueModel, SmsModel>()); } catch (WebException e) { e.WriteToFile(); ReSend(); } catch (Exception e) { e.WriteToFile(); } }, a => { a.WithTopic(SmsQueueModelKey.Topic); }); } private void ReSend() { var model = _smsService.Sms.MapTo<SmsModel, SmsQueueModel>(); model.SendCount++; _bus.FuturePublish(TimeSpan.FromSeconds(30 * model.SendCount), model, SmsQueueModelKey.Topic); }
SMS日志集合维度
SMS日志作为非必要业务的运维型监控数据,在需要的时候随时可以对此进行删除或者归档处理。因此以时间(年月)作为集合维度,可以很好的对日志数据进行管理。
mongoProxy.Add(MongoKey.SmsDataBase, MongoKey.SmsCollection + "_" + DateTime.Now.ToString("yyyyMM"), model);