项目地址:
项目目标:
- 基于 Protobuf-V3 语法维护规范, 便于升级和维护
- 更完整的服务支持, 新服务第一时间提供支持
- 更好用的编程接口
在线文档:
接口规范:
配置文件
当前用户的配置文件在 ${HOME}/.qingcloud/config.yaml
, 内容如下:
# QingCloud services configuration
qy_access_key_id: 'ACCESS_KEY_ID'
qy_secret_access_key: 'SECRET_ACCESS_KEY'
host: 'api.qingcloud.com'
port: 443
protocol: 'https'
uri: '/iaas'
connection_retries: 3
json_disable_unknown_fields: false
# Valid log levels are "debug", "info", "warn", "error", and "fatal".
log_level: 'warn'
将 qy_access_key_id
和 qy_secret_access_key
字段替换为 API密钥 中的内容.
其中 json_disable_unknown_fields
是新加的变量, 表示在JSON解码时忽略 proto.Message 遇到未定义成员的错误.
快速入门
以下为 hello.go 的内容:
package main
import (
"fmt"
"log"
"github.com/chai2010/qingcloud-go/config"
pb "github.com/chai2010/qingcloud-go/service"
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
)
func main() {
// 初始化 青云 服务对象
qcService, err := pb.Init(config.MustLoadUserConfig())
if err != nil {
log.Fatal(err)
}
// 返回 NIC 子服务, pek3a 为 北京3区-A
nicService, err := qcService.Nic("pek3a")
if err != nil {
log.Fatal(err)
}
// 列出所有网卡
reply, err := nicService.DescribeNics(nil)
if err != nil {
log.Fatal(err)
}
// 原始返回的json数据
// nicService.LastResponseBody
// JSON 格式打印
fmt.Println(jsonpbEncode(reply))
}
// pb转json, 采用原始名称, 不忽略空值
func jsonpbEncode(m proto.Message) string {
jsonMarshaler := &jsonpb.Marshaler{
OrigName: true,
EnumsAsInts: true,
EmitDefaults: true,
Indent: " ",
}
s, err := jsonMarshaler.MarshalToString(m)
if err != nil {
log.Fatal(err)
}
return s
}
初始化子服务也可以用以下方式:
nicService := pb.NewNicService(config.MustLoadUserConfig(), "pek3a")
运行例子:
go run hello.go
更多例子.
文档指南
使用青云SDK一般是以下步骤:
- 用 config 包构造一个配置对象, 里面含有最重要的 API密钥, 还包含日志级别等信息.
- 基于配置对象调用 service 包的
Init
函数构造一个青云主服务对象qcService
, 其中会根据配置文件设置日志级别. - 假设有一个 UserData 子服务, 那么调用
qcService.UserData("pek3a")
方法将返回子服务对象, 其中参数是区域 - 使用子服务对象就可以调用每个子对象的方法了
我们可以查看子服务对应的接口规范, 在 spec.pb/user_data.proto 文件定义 (青云文档):
service UserDataService {
rpc UploadUserDataAttachment(UploadUserDataAttachmentInput) returns (UploadUserDataAttachmentOutput);
}
message UploadUserDataAttachmentInput {
bytes attachment_content = 2;
string attachment_name = 1;
}
message UploadUserDataAttachmentOutput {
string action = 1;
int32 ret_code = 2;
string message = 3;
string attachment_id = 4;
}
其中service
关键字开头的表示定义一组子服务, 其中rpc
开头的表示子服务中每个具体的方法. 方法的输入参数和返回值分别为UploadUserDataAttachmentInput
和UploadUserDataAttachmentInput
结构体类型, 它们由后面的message
关键字定义.
SDK的代码生成插件 会生成以下的Go语言代码:
type UserDataService struct {
// ...
}
func (p *QingCloudService) UserData(zone string) (*UserDataService, error) {
// ...
}
type UploadUserDataAttachmentInput struct {
// ...
}
type UploadUserDataAttachmentOutput struct {
// ...
}
func (p *UserDataService) UploadUserDataAttachment(
in *UploadUserDataAttachmentInput,
) (
*UploadUserDataAttachmentOutput,
error,
) {
// ...
}
规范文件的语法细节可以参考 spec.pb/README.md, proto3 文件语法可以参考 Protobuf 的官方文档.
与官方文档的兼容性
- 该 SDK 和 官方 SDK 的 API 保持最大的兼容性
- 即使有不兼容的地方, API 也是非常相似的
假设青云的REST规范的文档中有一个名为 job_id
的输入参数, 对应 XXXInput
结构体的成员.
官方文档是根据 json定义的规范, 然后通过一个名为 snips 的工具加自己定义的 模板 生成的代码, XXXInput
输入参数生成的代码可能类似以下结构:
type XXXInput struct {
JobID *string `json:"job_id" name:"job_id" location:"elements"`
}
而我们的SDK采用Protobuf3标准工具生成的代码:
type XXXInput struct {
JobId string `protobuf:"bytes,5,opt,name=job_id,json=jobId" json:"job_id,omitempty"`
}
其中有两个大的差异: 一个是成员名称不同, 分别为 JobID
和 JobId
; 另一个为类型不同, 分别为 *string
和 string
.
snips 采用和 Protobuf-V2 类似的生成规则, 零值是 nil
, 空值是空字符串, 二者是不等价的. 在 Protobuf3 的生成规则中, 默认将零值和空值等价.