背景

目前OneAlert提供短信、邮件、电话、APP四种通知通道,其中前三种的使用量最高(90%以上的用户),因此靠谱的第三方推送提供商至关重要。经过对各种三方推送服务的公司调研,目前锁定了阿里大鱼[2]、容联云[3]、云片[4]、云之讯[5]、SendCloud[6],这5家平台提供商。

首先我们来分析一下接入三方后的通讯模型

推送模型

注:配额这里是指三方服务商的发送限制,比如每小时最多每个电话拨打几次

我们作为一个服务提供商,必然要确保用户的告警可以准确、准时、不漏的投递给用户,这个问题看起来一目了然,没什么难度,其实不然,我们分析下以上通信模型中的几种情况:

s1:发送成功

最好的情况(这里由于无法真正的监控第三方是否真正投递到了用户,不过根据以往的工单经验,丢失的概率很小)

s2:超出配额

换个重发

超出配额,那就换个重发呗?这是下意识的解决思路,我们看看有什么问题:

超出配额

独立状态服务

很直观的发现,重试的次数有可能会很多,这非常影响推送实时性。马上,我们又会想到可以对每次超出配额的情况进行缓存,提炼一个状态服务,如下:

超出配额_推送服务商配额状态服务

问题到此为止,一切美好已经发生… 遗憾的是,该状态服务几乎是不可用的。我们可以进一步思考:

当第一次配额超出的情况发生时,按照上面的设计,该状态服务会缓存下来;

当第二次推送来临时,会首先请求状态服务,拿到配额超出的那个服务商,排除掉它,使用其他服务商发送

当第三次推送来临时,会首先请求状态服务,拿到配额超出的那个服务商,排除掉它,使用其他服务商发送

当第n次推送…

此时问题一目了然,某个三方服务商第一次超出配额后就不再被请求了,这显然不是我们要的。如果我们试图及时、恰当的让某超出配额的状态失效掉,问题是我们如何知道什么时间或者哪一次请求时让其失效呢?

小结

目前看来,针对简单的请求-重试模型是很难解决我们的问题的。

换个思路

上面所有的方案每次发送都是无状态的,如果我们对每个时间段每次发送用的哪个服务商记录下来,结合配额限制,这样是不是可以做到避免处罚超出配额的问题呢?

我们来分析下:

超出配额_记录推送状态

嗯… 此时貌似已经解决了问题。不过从设计来看,NotifySender职责太多了,既要负责发送消息,又得记录发送状态,不符合单一职责原则。如果将来对三方服务商做增加、下线操作,还要动NotifySender,显然是不合理的。

小结

通过有状态的发送,解决了配额问题,但NotifySender职责太多,不利于拓展,需要再次设计。

最后方案

事实上,我们需要一个这样的服务,它能根据三方服务商目前的配额情况,自动的给我们的消息路由到合适的服务商。

实质上,对于每次发送「计数」这件事,其实可以换一个角度思考,我们只要确保在单位时间内,按照配额作为权重将消息分发给服务商即可,这样也能确保不会触发超出配额异常。

由于篇幅关系,这里不对轮询算法进行展开讨论,而且本身算法不是重点,此类算法网上一搜一大堆,笔者也不想赘述,重要的是我们对业务场景进行实质分析。

ok,直接给出结论:加权轮询算法(如果读者有兴趣,可以给我发邮件,我们一起讨论: P)

超出配额_加权轮询方案

对调度服务的补充解释:

我们选择将服务商信息存放在配置中心里,主要考虑了以下几点:

  • 如果服务商将来由于各种原因维护,我们只需要从配置中心去掉这个服务商即可,NotifySender不需要做任何改动
  • 如果服务商将来更改了配额,我们只需要在配置中心重新配置一下这个服务商的配额信息即可,NotifySender也不需要做任何改动

Reference


  1. 1.https://www.onealert.com OneAlert是OneAPM旗下的一站式运维事件管理平台
  2. 2.https://dayu.aliyun.com 阿里大鱼
  3. 3.https://www.yuntongxun.com/ 容联云
  4. 4.https://www.yunpian.com 云片
  5. 5.https://www.ucpaas.com 云之讯
  6. 6.https://sendcloud.sohu.com/ SendCloud

Comments