短信平台技术分享(未完待续)

前言

去年工作需要要写一个短信平台,开发完成后一直没有整理分享,趁着现在还有一些余温,把大概的思路和过程分享出来(不具体贴代码,只发设计模式,开发思路,流程图等信息),中间会用到一些设计模式以及熔断的一些知识。希望可以提供大家一些想法或收获。
先来说一下短信平台开发前后的状况

开发前的状况:

  • 项目中多个地方需要发送短信,并且各自开发,导致一个项目中出现N多个相同的SDK和冗余代码;
  • 维护成本过高,如果出现了更换短信服务商的情况,需要花很多的时间来调整
  • 短信文案不规范,不能有效的控制。阿里的短信是需要优先创建模板及签名才可以,但是有一些短信服务商是不需要的(不举例),可以自己控制短信文案,直接通过接口发送,没办法很好的控制
  • 容易被刷,安全得不到保证,不可能每个自己开发短信的地方都自己做一套防刷的方案
  • 短信日志太乱,不能保证每个都记录了日志,并且按照一定的格式来记录

开发后的状况:

  • 最直观的,方便每个需要短信业务的程序员开发,原先需要一大堆的代码,现在只需要,一个注册的步骤+组合下发送参数+一行的发起请求
  • 统一的日志,每一条日志都可以精确的记录,短信发起人,发送时间,发送模板,发送内容,短信平台回调结果等等,都可以看到
  • 更方便的维护,由原先的多点维护变成现在的只要短信平台单一维护,并且对于调用方可以做到无感
  • 短信设置管理后台界面化,可以进行短信服务商的管理,签名管理,主题(简单可以理解为一个短信模板和短信供应商的集合)管理,熔断配置,流量调控;
  • 不同主题针对不同的短信服务商(多对多),灵活配置
  • 安全中心,统一入口,更方便的做好安全措施(不是必须的,这部分可以通过网关来实现)
  • 多服务商模式配合熔断设置,当一个短信服务商挂掉的时候,切换服务商继续发送,不对用户有任何影响
  • 统一的请求格式和返回格式

设计阶段

核心概念

主题

在当前项目中,主题的概念是,包含了一条短信模板所有信息的一个集合:

  • 短信的标识(需求方注册短信时必填的唯一标识,发短信时通过该标识发送)

  • 主题名称(方便查看)

  • 主题类型(方便筛选,比如说验证码、营销短信、通知短信等)

  • 短信内容(比如说,【您的验证码是{code},您的手机号是{phone}】。如果有变量的话,用固定的格式来包住)

  • 替换变量(对于短信内容有变量的需要填此项【code|phone】)

  • 短信服务商(一个主题下可以有多个短信服务商,可以设置权重,来按需配比。或者一个服务商挂掉的时候,可以及时更换另一个服务商来发送短信)

    • 模板名称(该服务商下该短信模板的名称,方便查看)
    • 服务商标记(需要有单独服务商管理,这里只是需要关联服务商信息)
    • 权重(服务商发送短信的权重,权重越高,优先发送的概率越高)
    • 平台扩展信息(此处比较重要,需要设置该平台中特殊的部分,比如说阿里内部的短信模板信息,如果需要通过阿里的发送短信,需要在此处把阿里的模板id配置上,比如说{“TemplateCode” : “SMS_123456”},会把模板做一个关联,通过策略模式找到阿里发短信的方法,传递短信模板信息及发送的变量值,来发送短信。如果有其他需要设置的地方,都可以在此处配置,在每个服务商发短信的接口中特殊实现)

以上就是主题的大概内容和意思,包含的内容比较多,中间会涉及到短信服务商管理短信服务商签名管理主题类型管理等,下边会有介绍。

其他管理中心

这里只是做简单的介绍,不做详细界面化的介绍,每个人的设计不同,不合理的地方轻喷,欢迎提出不同的建议

短信服务商管理

多服务商的管理,管理每个短信服务商的信息,包括

服务商标识(主题会通过这个标识来关联服务商) 服务商名称(方便查看)
扩展参数(需要配置每个服务商特有的信息,比如说account、password、apiurl等,服务商平台需要的信息)
权重(字面意思,设置一个1-100以内的值,表示权重,发短信的时候,会优先调度权重比较高的,调度中心会具体介绍一个简单的计算权重的方法)
熔断设置

  • 累计失败次数(如果请求失败次数累计超过当前设置次数,则会加入熔断)
  • 连续成功次数(加入熔断后,如果间接性请求中,连续请求成功的次数超过当前设置次数,接触熔断)
  • 熔断失败每次增加时间(刚进入熔断和熔断后继续请求失败,增加的请求时间间隔,后边会介绍)
  • 熔断失败最长熔断时间(熔断后连续失败,连续增加请求时间间隔的最大值)

主题类型管理

主要是为了主题的筛选,类型比如说验证码、通知、营销短信等等。会和主题进行关联。

短信服务商签名管理

主要考虑一个服务商下,会有多个签名的可能,在多对多的情况下,需要用到签名管理。
主要关联的主题下的服务商的签名。可以做到每个主题下,可以用自己特有的服务商及签名。

四个中心

安全中心

为了避免被刷,可以设计安全中心的部分,每个请求都需要进行验证,验证的方法有很多,IP白名单,加密验证等。
当然也可以通过网关来实现IP的,其实我是推荐在网关部分来做安全措施,这样更符合微服务的理念,所有请求入口的处理都通过网关来做。

流量中心

量大的时候可以做流量中心,控制流量的最大限额最小限额。比如说精确的控制每个手机号或者每个短信主题发送或者收到的短信数量(比如:每个手机号一天内收到的登录验证码数量,一般服务商会做一个控制,如果我们有需求的话,可以做在自己的平台上,更加精确的控制)。
这部分也可以起到一些防止被刷的功能,比如说,限制手机号的对一些推广短信的接收数量等等。当然这也只能是辅助安全。

调度中心

短信平台的核心部分就是调度中心,做好熔断调度平台。
这里我只介绍我项目中的概念,如果和你理解的不一样,不用纠结,你是对的。

熔断

以下是来自百度的解释

熔断机制
熔断机制(Circuit Breaker),也叫自动停盘机制,是指当股指波幅达到规定的熔断点时,交易所为控制风险采取的暂停交易措施。 [1] 具体来说是对某一合约在达到涨跌停板之前,设置一个熔断价格,使合约买卖报价在一段时间内只能在这一价格范围内交易的机制。

在股市中是为了控制风险采取的暂停交易措施。
在技术里也差不多是这个意思,当一个平台一段时间内失败的次数达到我们预设的失败次数,就进入熔断,在接下来的一段时间内会尝试偶尔发送一个请求,来进行试探,如果这种试探连续多少次全部成功则恢复熔断,如果失败则增加熔断时间。
对应到短信平台上,我们是多服务商模式,每个主题下,会设置多个短信服务商,当一个服务商挂掉,连续几次请求失败,我们会认为该服务商短期内不可用,会进行熔断。熔断情况下,会有定期去请求熔断中的服务器发送短信,如果发送失败,则增加定期请求的熔断时间,加大请求间隔,熔断情况下如果连续几次请求成功,我们会认为该服务商恢复正常,解除熔断。

调度

以下是来自百度的解释

调度中心(dispatching center):指对多种资源进行综合调度的中心。可调度的资源包括自然资源和社会资源,自然资源包括电力、水力、天然气等。社会资源包括人员,车辆,物资等。

字面理解就可以,就是合理调度资源,保证系统正常的运转。
系统中我们主要做了两种方式的调度,熔断和权重。
优先级:熔断 > 权重
我们会先考虑熔断,获得所有不处于熔断期的平台+熔断器可以间断请求的平台,然后把这些平台数据在根据权重来计算一遍,熔断就不多重复说了,最开始讲的配置部分和上边讲的熔断逻辑部分,应该可以知道怎么来做熔断。
权重的计算方法,我做了个PHP的demo来看,尽量加了所有的注释,还是比较简单的,如果大家有更加好的办法,欢迎留言。
我自己私下做了几次测试,输出一下结果,可以很明显的感受到,当下三个平台,a和b获得优先权的次数更多。

// 模拟平台数据
$platform = [
    [
        'name' => 'a',	// 平台名称
        'weight' => 30,	// 权重
    ],
    [
        'name' => 'b',
        'weight' => 70,
    ],
    [
        'name' => 'c',
        'weight' => 50,
    ]
];

// 测试排序后的数组的值
print_r(getWeight($platform));

// 递归获取根据权重排序后的平台顺序
function getWeight($platform)
{
    
    
    $resultData = [];   // 保存最终排序的数组
    $min = 1; // 初始化权重范围最小值
    $sum = 0;  // 初始化权重范围最大值
    $weightList = [];  // 初始化存放权重数据的数组

    // 如果只剩下一个平台,就不需要在判断,直接获取就可以
    if (count($platform) == 1) {
    
    
        $weightList[] = $platform;
        return $platform;
    }

    foreach($platform as $key => $platformOne) {
    
    
        $sum += $platformOne['weight']; // 获取当前权重范围最大值
        // 把当前平台权重换算成范围写入到权重数组中,
        $weightList[$key] = [
            'name' => $platformOne['name'],
            'min' => $min,  // 该范围的最小值
            'max' => $sum   // 该范围的最大值
        ];

        $min = $sum + 1; // 获得下一个平台的最小值
    }

    // 得到具体的随机数,1到权重总值之间的一个随机数
    $randNum = mt_rand(1, $sum);

    // 循环每一个权重范围的数组
    foreach($weightList as $k => $weightOne) {
    
    
        // 根据随机数及每个平台的权重范围得到当前最优先的,并把该平台从数组中刨除掉
        if (($weightOne['min'] <= $randNum)  && ($weightOne['max'] >= $randNum)) {
    
    
            $weightOne['randNum'] = $randNum;
            $resultData[] = $platform[$k];
            unset($platform[$k]);
            break;
        }
    }

    // 递归调用,把其他的平台重新按权重排序
    $resultData = array_merge($resultData, getWeight($platform));

    // 返回最终排好序的平台数组
    return $resultData;
}

当然调度不只是这样,如果我们只需要拿到优先级最高的平台,我们就不会去获取所有平台的一个优先级排序,我们还需要考虑到,如果优先级最高的平台,当次发送短信失败了要怎么做,难道只是给用户返回一个发送失败吗?这不可能的,那我们多平台的优势就荡然无存,我们还需要在做一步,当优先级最高的发送失败,我们会把当前短信平台,进行熔断判断(熔断只有在这里才会触发。此处可以异步触发),并顺位让下一个平台进行发送,直到发送成功(判断当前平台熔断情况,如果处于熔断,则成功次数+1,如果当前成功次数超过设置的熔断成功次数,解除熔断。此处可以异步处理),或者全部失败。

日志中心

日志中心,就比较简单了,记录所有成功和失败的短信发送记录,并记录相关的平台信息,模板信息,签名信息等。
利用观察者模式或者策略模式来做好回调部分
除了日志,还需要报警系统,比如说,平台发送短信失败、回调失败、触发熔断等的一些异常的时候,我们需要做到监控,最简单的莫过于文件日志+钉钉报警,可以把详细的信息做成文件日志,然后通过钉钉报警的方式来通知我们发生了什么错误,可以尽快的协调修复。

流程图

做开发避免不了的就是流程图,做一个好的流程图可以直观的看到逻辑是否合理、业务或者代码流程是否通顺、是否有明显bug等。

以下是基本的流程图。
点此处跳到流程图原地址List item
向接口或者微服务发起请求->安全中心检测->获取短信主题信息->流量控制->调度中心(包含熔断器)->发送短信->结束
思维导图就不写了,涉及太多的业务部分,不方便摘出来。不过上边那些介绍,基本上也可以把各个组成部分体现出来了。

开发阶段

设计模式的选择

猜你喜欢

转载自blog.csdn.net/qq_15915293/article/details/105715648
今日推荐