Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discuss: whether the sms API definition needs to be modified; 讨论一下是否需要修改sms API定义 #966

Closed
alilestera opened this issue Jul 7, 2023 · 16 comments

Comments

@alilestera
Copy link
Contributor

Why

当我为sms API开发腾讯云sms的组件时,发现目前pb定义的API不能很好地适配腾讯云sms,但却能很好地适配阿里云sms。说明现在的API定义可能适配性不强。
因此,在2023-07-05的layotto会议讨论后,我们决定重新调研更多云服务厂商的sms api定义,并以调研结果来讨论一下是否需要修改目前的sms API定义。

Result

我目前收集了以下厂商的sms API字段的定义,并添加了一些自己理解到的信息进注释里。由于某些原因,对每个云服务厂商的信息收集得可能不够全,特别是最后三个服务厂商。

  • 阿里云
  • 腾讯云
  • 华为云
  • 七牛云
  • aws
  • google cloud
  • azure

文档地址: https://www.yuque.com/alilestera/record/atx6993nmh40twvg?singleDoc# 《不同云服务厂商的短信服务API定义》

Opinion

目前的国内云服务厂商提供的sms的文档比较清晰、简单,且符合国内多数应用的短信需求; 而其他云服务厂商的sms要么是跟其他类型的消息混用、携带的其他信息比较多,要么是需要的请求参数和实现与其他的差异较大。所以我们目前可以优先考虑根据国内云服务厂商的sms来定义API,其他的云服务厂商的sms仅用作参考。

欢迎大家来参考、讨论

@github-actions
Copy link

github-actions bot commented Jul 7, 2023

Hi @alilestera,
Thanks for opening an issue! 🎉

@alilestera
Copy link
Contributor Author

我来补充回答一下会上提到的七牛云sms的响应体结构比较简单的问题。
我又去看了一下七牛云的sms,这个job_id的意义不大。
他们的使用场景主要是验证码类、通知类、营销类的短信;所以我对此的猜测是,这些都是很简单的使用场景,对错误的容忍度比较高;因此即使提供具体的信息,上游服务也一般会选择无视、丢弃这些信息。

  1. 对于验证码类来说,用户没有收到的话一般会选择再次发送,即使出现短信冗余,也会以最新的验证码短信为准。
  2. 对于通知类、营销类来说,可能并不在乎具体用户有没有收到。

@zhenjunMa
Copy link
Contributor

zhenjunMa commented Jul 16, 2023

目前看下来,对于批量发送短信的场景,大致有两种处理思路:

方案A:
如果保持现状的API不变,则需要汇总不同手机号的发送结果,如全部成功则成功,有任何一个失败则表示失败,并且把失败的发送结果放在meta中返回。

方案B:
Response中通过数组表示各个手机号的发送结果,思路如下

message SendSmsWithTemplateResponse {
  // The unique requestId.
  string request_id = 1;
  
  repeated SendStatus results = 2;

}

message SendStatus {

  // "OK" represents success.
  string code = 1;

  // The error message.
  string message = 2;

  // The send status metadata returned from SMS service.
  map<string, string> metadata = 3;

}

我比较倾向于方案B,无论是单个手机号发送还是批量发送语义更清晰一些。

@kevinten10 有没啥建议?😁

@alilestera
Copy link
Contributor Author

目前看下来,对于批量发送短信的场景,大致有两种处理思路:

方案A: 如果保持现状的API不变,则需要汇总不同手机号的发送结果,如全部成功则成功,有任何一个失败则表示失败,并且把失败的发送结果放在meta中返回。

方案B: Response中通过数组表示各个手机号的发送结果,思路如下

message SendSmsWithTemplateResponse {
  // The unique requestId.
  string request_id = 1;
  
  repeated SendStatus results = 2;

  // The metadata returned from SMS service.
  map<string, string> metadata = 3;
}

message SendStatus {

  // "OK" represents success.
  string code = 2;

  // The error message.
  string message = 3;

}

我比较倾向于方案B,无论是单个手机号发送还是批量发送语义更清晰一些。

@kevinten10 有没啥建议?😁

方案B的SendStatus还需要一个字段来表明这是哪个手机号的状态。但是问题很明显,这需要云服务的响应体的支持才能填入这个字段。

@kevinten10
Copy link
Member

支持方案B

有一个场景不清楚是否要考虑:短信由于供应商的长度限制,调用sms api的内容如果长度过长,底层是否可能会截成几条,相当于可能调用多次供应商的api?这样1个sms api request可能对应response里多个响应体,是否需要一个id能够串联起来。

@kevinten10
Copy link
Member

再脑洞一下:短信是一种比较昂贵的发送方式,如果能缩减内容长度,那么有可能缩减发送成本。是否在api上允许传入编码类型,然后对内容使用指定编码,以此来达到缩减字符长度的作用呢

@zhenjunMa
Copy link
Contributor

zhenjunMa commented Jul 19, 2023

会议结论:
把现有的SMS API对于response的处理改成批量模式,如下:

message SendSmsWithTemplateResponse {
  // The unique requestId.
  string request_id = 1;
  
  repeated SendStatus results = 2;
}

message SendStatus {

  // "OK" represents success.
  string code = 1;

  // The error message.
  string message = 2;

  // The send status metadata returned from SMS service.
  map<string, string> metadata = 3;

}

对于response中的其他信息(如手机号等)可以按需提取到SendStatus 中的metadata中供用户提取使用。
对于短信拆分/自定义编码等高级特性可以放在下一阶段支持。

@alilestera
Copy link
Contributor Author

alilestera commented Jul 20, 2023

@zhenjunMa 我们目前的做法就是主观地把我们认为有用的云sms响应信息放入metadata中,比如我认为很重要的phone number或者其他用于表示接收目标地址的信息。如果用户对这些“有用的信息”反馈得比较激烈,我们可以再修改加入metadata的信息。

那么我现在的目标就是先确定国内云服务提供商sms的哪些字段信息需要放入metadata中,结果到时候会在这里贴出来。

@alilestera
Copy link
Contributor Author

alilestera commented Jul 20, 2023

我这里还有一个以不变应万变的方法:可以给请求加一个array of string的字段,故且先称为meta_extra,用于表示需要往metadata额外加入的云sms的响应体字段,每个string代表对应的字段名。这个请求字段是可选的,能够把不被我们认为有用的信息加入到metadata里。

大概的实现可以是:
我们对每个云sms实现一系列的option,每个option匹配一个云sms响应字段。只要用户传入的meta_extra中包含此字段就能被匹配到,然后赋值到metadata里。

接着问题是meta_extra中存放的字段名应该是怎样的,遇到复杂的嵌套结构体会出现不同的结构体会有相同的字段名该怎么办。我也有办法:尽管可能会出现这种情况,但不会出现同一层次有相同字段名的情况;所以我们可以根据云sms的响应体层次,用.来分隔,例如SendStatus.PhoneNumber;这样的实现对我们和用户来说都很友好,因为都可以从云服务提供商的文档里得到这些信息。

type OptionHandler func(*Response)

var MetaOption map[string]OptionHandler {
    "xxxxxx.xxxx": aHandler,
}

func aHandler(resp *Response) {
    // ... 
    // implement it
}
for _, field := range MetaExtra {
    if op, ok := MetaOption[field]; ok {
        op(resp)
    }
}

...

不过目前还是先按照讨论好的结果来弄,如果我上述的方法能够得到认同,以后再改也是可以的。

@zhenjunMa
Copy link
Contributor

我这里还有一个以不变应万变的方法:可以给请求加一个array of string的字段,故且先称为meta_extra,用于表示需要往metadata额外加入的云sms的响应体字段,每个string代表对应的字段名。这个请求字段是可选的,能够把不被我们认为有用的信息加入到metadata里。

大概的实现可以是: 我们对每个云sms实现一系列的option,每个option匹配一个云sms响应字段。只要用户传入的meta_extra中包含此字段就能被匹配到,然后赋值到metadata里。

接着问题是meta_extra中存放的字段名应该是怎样的,遇到复杂的嵌套结构体会出现不同的结构体会有相同的字段名该怎么办。我也有办法:尽管可能会出现这种情况,但不会出现同一层次有相同字段名的情况;所以我们可以根据云sms的响应体层次,用.来分隔,例如SendStatus.PhoneNumber;这样的实现对我们和用户来说都很友好,因为都可以从云服务提供商的文档里得到这些信息。

type OptionHandler func(*Response)

var MetaOption map[string]OptionHandler {
    "xxxxxx.xxxx": aHandler,
}

func aHandler(resp *Response) {
    // ... 
    // implement it
}
for _, field := range MetaExtra {
    if op, ok := MetaOption[field]; ok {
        op(resp)
    }
}

...

不过目前还是先按照讨论好的结果来弄,如果我上述的方法能够得到认同,以后再改也是可以的。

这种交互方式有点复杂, 我们最终的效果是应该是所有的返回都加入到meta里面,让用户按需读取,不需要加一步用户来指定加入哪些附加信息,现阶段只是因为全部加入到附加信息有些困难,所以才选择性加入

This was referenced Jul 26, 2023
@alilestera
Copy link
Contributor Author

alilestera commented Aug 9, 2023

@zhenjunMa
我有一个疑惑:
对于同一种信息,不同的云服务商的有不同的字段名,比如都是表示手机号,腾讯云的字段名是PhoneNumber,而华为云的是originTo。对于这种情况,在metadata中的key需要保持统一吗,还是要根据不同的云服务商取不同的key?

对于要在metadata里放入的信息,目前我还是认为只有手机号是必要的。

@zhenjunMa
Copy link
Contributor

zhenjunMa commented Aug 9, 2023

  1. phoneNumber
    2. 全平台
  2. customKey
    4. 平台限定

@alilestera
Copy link
Contributor Author

  1. phoneNumber
    2. 全平台
  2. customeKey
    4. 平台限定

由于不同云服务提供商的SMS API差异较大,例如腾讯云和华为云的都有PhoneNumber这个信息,但是阿里云的没有这个信息,七牛云的甚至没有发送状态,所以,对于layotto中SMS API的SendStatus.metadata定义,是不会出现全平台都支持的响应体字段的。

因此,需要告知用户这个metadata会包含哪些信息的话,就要打上注释,说明XX值是被哪几个平台支持的。
那么我的做法/写法如下:

// The status set of SMS.
// Not supported by qiniucloud.
repeated SendStatus results = 2;
// The send status metadata returned from SMS service.
// 1. PhoneNumber, is the phone number SMS send to.
// Supported by tencentcloud and huaweicloud.
map<string, string> metadata = 3;

这种做法/写法是否正确?如果不合理的话,应该怎么做?

@zhenjunMa
Copy link
Contributor

@alilestera 可以,就按照你这种做法搞就行。
本质上我们只是对获取到的信息采用统一的一个key,如果连相关信息都获取不到,也就不用返回这个key给用户了。

@alilestera
Copy link
Contributor Author

@zhenjunMa 好的,那我接下来就先修改sms的proto文件,然后开发腾讯云的sms组件。

proto文件中的注释应该根据目前layotto支持的组件来写,所以我会先在proto文件中这样写注释

// The send status metadata returned from SMS service.
// Include `PhoneNumber`.
// 1. PhoneNumber, is the phone number SMS send to.
// This field supported by tencentcloud.
map<string, string> metadata = 3;

完成proto文件的修改、生成代码和文档后,我会对此提一次PR。

下一步接着开发腾讯云的sms组件,完成后再提一次PR。

这样的流程可以吗?

@zhenjunMa
Copy link
Contributor

@alilestera 好的👍👏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants