乐趣区

关于go:怎么给自己的服务写个SDK

为什么须要一个 SDK?

  1. 频繁遇到签名报错问题,排查起来费时费力;
  2. 对接人程度参差不齐,遇到天选之人,让人头大;
    比方,有老哥不晓得 query 参数须要本义,有人甚至不晓得要把 http 响应 body 读取并解析
  3. 简化环境对齐步骤,升高对本服务部署形式、域名抉择等常识的了解老本
  4. 打消客户端、服务器因为实现语言不同而带来的技术细节问题

实现一个什么指标?

暗藏申请签名和申请解决的复杂性,让对接的人闭嘴,让本人省心省力。

最终将业务性能封装成一个本地办法调用,传入参数对象,失去后果对象。

DoYourWork(request) response

一个 SDK 中有什么?

  1. Http Client

应用http.Client,golang 规范库外面有个全局的变量DefaultClient,倡议不要间接应用,因为很可能会被整个我的项目共用,如果须要对此 Client 做配置,则可能存在抵触。

倡议自定义一个 Client 示例,或者克隆一个

httpClient := &http.Client{Transport: http.DefaultTransport.(*http.Transport).Clone(), // 克隆 DefaultTransport}

规范库中对于 DefaultTransport 的设置如下:

var DefaultTransport RoundTripper = &Transport{
    Proxy: ProxyFromEnvironment,
    DialContext: defaultTransportDialContext(&net.Dialer{
        Timeout:   30 * time.Second,
        KeepAlive: 30 * time.Second,
    }),
    ForceAttemptHTTP2:     true,
    MaxIdleConns:          100,
    IdleConnTimeout:       90 * time.Second,
    TLSHandshakeTimeout:   10 * time.Second,
    ExpectContinueTimeout: 1 * time.Second,
}

一些能够设置的选项:

  • 申请的连贯建设超时
  • 申请的读写超时: 对于 golang 而言,能够用申请的 context 管制
  • HTTP 申请的 KeepAlive 超时
  • 闲暇连贯超时
  1. Signer

每个业务的签名算法可能不太一样,然而基本上都是对 http 申请签名,能够做一个相似上面的签名办法:

AWS: public void sign(SignableRequest<?> request, AWSCredentials credentials);

腾讯:func signRequest(request tchttp.Request, credential CredentialIface, method string) (err error)

  1. Logger

通常,咱们能够把 SDK 的所有报错信息都返回给调用方。

然而,如果实现的 SDK 比较复杂,或者心愿有调试信息帮忙排查问题,能够定义一个 Logger 接口,让调用方去实现,而后在 SDK 外面应用 Logger 的办法来打印一些重要日志。

// 比方腾讯云 SDK 中定义了一个 Logger 接口
type Logger interface {Printf(format string, args ...interface{})
}

一个策略是,如果接入方不提供,就不打印相干日志;

另一方面,SDK 中能够筹备一个内置的日志打印形式。

然而,自行设计日志打印时关注两个问题:

  • 打印到哪里,是规范输入、规范谬误还是某个文件,最好还是让接入方传入一个Writer
  • 并发打印问题,通常应该在写入时加把锁
mu.Lock()
defer mu.Unlock()
w.Write(logs)
  1. 包装申请、响应、谬误

申请、响应中,尽可能减少用于追踪的 trace_id,比方减少 request_id

按照具体业务,精细化谬误分类。

  1. 申请重试 Retry

思考几个问题:

  1. 确定业务场景是否能够重试,重试会不会给业务带来副作用;
  2. 确定哪些谬误类型是能够重试的;

    1. 网络超时
    2. IO 谬误
    3. 限流谬误
    4. 网关谬误:5xx 谬误,比方 AWS 将 500,502,503,504 作为可重试场景
  3. 由谁来重试更好,是接入方自行重试,还是内置重试策略。

最大重试次数:

对于个别的 HTTP 申请而言,重试次数不应该设置很大,AWS 默认设置为 2;

举个 AWS SDK 中的例子,退却策略:

  • 固定延时退却(Fixed Delay Backoff)
  • 指数退却(Exponential Backoff)
  • 齐全随机退却策略(Full Jitter Backoff)
  • 等距离随机退却策略(Equal Jitter Backoff)
  1. 断路器 Breaker

首先确定业务场景是否反对,须要你的服务有备选计划。个别断路器能够依据最近一段时间内申请的错误率,来判断某个连贯地址以后是否可用,如果不可用,则切换备用地址,或者做降级解决。

// 一个腾讯云的例子
type breakerSetting struct {
    // backupEndpoint
    // the default is "ap-guangzhou.tencentcloudapi.com"
    backupEndpoint string
    // max fail nums
    // the default is 5
    maxFailNum int
    // max fail percentage
    // the default is 75/100
    maxFailPercentage int
    // windowInterval decides when to reset counter if the state is StateClosed
    // the default is 5minutes
    windowInterval time.Duration
    // timeout decides when to turn StateOpen to StateHalfOpen
    // the default is 60s
    timeout time.Duration
    // maxRequests decides when to turn StateHalfOpen to StateClosed
    maxRequests int
}

代码格局上的一些思考

  1. 仓库组织

更具体的介绍见 golang 官网博客

每个仓库对应一个服务的 sdk

每个仓库对应多个服务的 sdk

  • Module path: example.com/mymodules/module1
  • Version tag: module1/v1.2.3
  • Package path imported by a user: example.com/mymodules/module1/package1
  • Module path as given in a user’s require directive: example.com/mymodules/module1` module1/v1.2.3`
  1. 版本更新和兼容性

更具体的介绍见 golang 官网博客

对于兼容性,援用 golang 官网答复:

If an old package and a new package have the same import path, the new package must be backwards compatible with the old package.

如果两个包导入门路雷同,则肯定要放弃向后兼容。

如果呈现了不兼容,则须要更改导入门路,比方 xxx/v2,减少了 v2 后缀。

呈现 v2 版本之后,怎么组织和保护代码呢?

  • 应用 v2 文件夹
  • 应用分支
  1. 正文、文档和测试用例

更具体的介绍见 golang 官网博客

通常想要疾速理解一个包对外裸露的接口、对象、常量等,咱们能够应用 go doc 命令。

该命令会将文档中的具备 Export 属性的内容依照标准模式显示进去,所以在写咱们本人的 sdk 时,咱们也要留神一下 go doc 的输入模式。

退出移动版