Go使用ProtocolBuffers构建高性能的API服务

仓库

下面设计思路与实现考虑:

误区

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,不再往里再写任何操作!
  • 上传与下载文件是特殊行为, 使用受特殊限制!

posted @ 2020-11-24 18:22  zolo®  阅读(347)  评论(0编辑  收藏  举报