Go使用ProtocolBuffers构建高性能的API服务
仓库
- https://github.com/fasgo/protoapi
- https://github.com/fasgo/protogen
- https://github.com/fasgo/protoc-gen-go-http
下面设计思路与实现考虑:
误区
restful api VS protobuf api
- restful api使用http的POST/GET/PUT/DELETE, uri, body实现资源的CRUD操作!
- protobuf api使用grpc, http(post), websocket实现资源的CRUD操作!
二者是完全不同二套API方案! 尝试用protobuf api去实现restful api会觉得很别扭! 真的相当别扭!
但protobuf api的不足就是对文件上传/下载的支持! 而这也是目前最需要解决的问题!
纠正
-
目前的grpc, post, get的方式是没有问题的!
-
不要动态检查,
- 通过+http-form/+http-file明确指定静态代码生成规则也是没有问题的!
swith ContentType { case "" }
如果是application/x-www-form-urlencoded或者multipart/form-data则根据request FormValues()来获取Request的值.
如果是application/json(默认), 则采用json的解析方式!
但这个步骤不应在request时动态解析,否则会浪费性能!
-
通过+http-file的编译指令实际是将字段名作为值赋到字段中! 此后通过
protoapi.FormFile(ctx, field)来获取相关的值! -
通过+http-path指定相应的uri
-
对于Get是用于websocket还是普通的get
- 做法1: 明确添加+http-wbsk. 因为Websocket的接口不会经常打开! 没有哪个APP会常年打开几十个websocket链接连着后台!
-
仍然需要PackagePrefix/PackageSuffix,ServicePrefix/ServiceSuffix,MethodPrefix/MethodSuffix统一定制整套扩展实现!
效果:
syntax = "proto3";
package hello;
// +http-form #按照form表单而非json内容解析Request
message UploadReq {
// +http-file #在form-file中会有一个以"test_file"命名的文件!
string test_file = 1;
}
message UploadRsp {
string status = 1;
}
service Greeter {
// +http-wbsk
// +http-path /demo/upload #默认是 /hello/greeter/upload
rpc Upload(UploadReq) returns (UploadRsp);
}
- 默认每个service.method会暴露grpc/http(POST/GET)二种接口, 可以通过protoapi进行更细设置,就像PackagePrefix/PackageSuffix, ServicePrefix/ServiceSuffix, MethodPrefix/MethodSuffix等, 其实可以更深地抽象为一个HttpPathGenerator接口
type HttpPathGenerator interface{
Generate(package, service, method, httpPath string) string
}
其中会有一个全局的defaultHttpPathGenerator(c *Config) 生成根据prefic/suffix来创建http-path的对象!
-
对于uri或query的参数是否去匹配
- +http-form表示从form表单提取字段值.
- +http-urls表示从path路径提取字段值,包括query!
-
对于全部的build constraints
- 用于message的: +http-form, +http-json
- 用于field的: +http-file
- 用于method的: +http-path, +http-get/+http-get-wbsk
这些都是http标签, 能否把http-前缀去掉:
- +form(form+query), +json
- 解析path,如果带:param等参数,自动加入到参数的解析里面!
-
正常地
- 生成grpc
- 生成http(post), http(get)
如果标记(wbsk)是生成websocket代替掉get, 能否
-
换一种思路:
- 生成grpc
- 生成post
- 生成wbsk
- 生成get
无论是wbsk或者get都不是常用的方式, 指定+get websocket
从可读性来看, 还是带上http-前缀比较明确意思!
汇总
默认行为
- 默认service.method暴露grpc, post接口
- 默认message是application/json解析
- 默认protoapi不开启Grpc或Http接口, 需要配置OpenGrpc(switch int)或OpenHttp(switch int)
扩展行为
-
http-form/http-file, http-json: body的内容是form而不是json, 如果是form则包括query部分(使用http.Request.FormValue(key))来提取值! 不论是form还是json都会解析uri判断是否带有:param,如果有的话, 则会自动添加相应的解析部分! 另外提供protoapi.FormFile(context, field)辅助获取上传的文件!
-
http-path用于指定uri(支持:param), 要在生成的_http.pb.go加上protoapi的相关包引用!
- http-get或http-get-wbsk用于打开Get请求! 默认情况开启http-post足够应付大多数请求场景! 但有时需要支持主页或下载(Get), 或者websocket的情况, 就需要把get开启! 另外提供protoapi.DownFile(context, name, reader). 需要调用者负责关闭文件句柄或者其他! 但是需要注意, 调用protoapi.DownFile()后会关闭http.ResponseWriter,不再往里再写任何操作!
-
上传与下载文件是特殊行为, 使用受特殊限制!