go-tip
截止到本文发布时,Go-Kit
在github上的star数为22.2k,超过了我们已经一起看过的Go-Micro
与Kratos
。
Go-Kit
不同于前两者,它更像是一种Go语言的工具集,而不是一种统一化的框架。
官网 - https://gokit.io/
Github - https://github.com/go-kit/kit
Go kit is a collection of Go (golang) packages (libraries) that help you build robust, reliable, maintainable microservices. 官方的定义是一种功能集合collection。
官方将Go-Kit
核心分成了三层,分别为:
The transport domain is bound to concrete transports like HTTP or gRPC.
通信协议相关的传输层。
这一层具有很多通信协议相关的功能。比如在HTTP
协议中,根据返回数据的具体格式,在HTTP
头中设置Content-Type
。这部分的功能具有很强的重复性,如果设计良好,往往不需要太多的coding。
An endpoint is like an action/handler on a controller; it’s where safety and antifragile logic lives.
RPC函数的控制层入口。
从一个请求来说,通过Transport
上一定的路由关系后,就会落到具体的Endpoint
层。一个具体的Endpoint
有两个关键的、类型确定的参数:请求与响应。
定义中还讲到了安全与反脆弱性,可以理解为在Endpoint
层要处理panic等异常信息,不要让Endpoint
与Service
层的问题影响到整个服务的稳定性。
Services are where all of the business logic is implemented.
业务逻辑的实现层。
整个框架的对Service
的定义很宽松,给业务开发很大的空间。如何组织,可以参考其余框架的实践。
Go-Kit
的详细信息并不多,我们就从一个官方的示例入手,来更好地了解Go-Kit
,链接如下:
https://github.com/go-kit/examples/blob/master/stringsvc1/main.go
// 两个功能接口
type StringService interface {
Uppercase(string) (string, error)
Count(string) int
}
func main() {
svc := stringService{}
// Uppercase方法的定义,包括request和response的编解码
uppercaseHandler := httptransport.NewServer(
makeUppercaseEndpoint(svc),
decodeUppercaseRequest,
encodeResponse,
)
// Count方法的定义,包括request和response的编解码
countHandler := httptransport.NewServer(
makeCountEndpoint(svc),
decodeCountRequest,
encodeResponse,
)
// 将上面两个endpoint注册上去
http.Handle("/uppercase", uppercaseHandler)
http.Handle("/count", countHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
我们以其中一个Endpoint
为例:
func makeCountEndpoint(svc StringService) endpoint.Endpoint {
return func(_ context.Context, request interface{}) (interface{}, error) {
// 类型判断
req := request.(countRequest)
v := svc.Count(req.S)
return countResponse{v}, nil
}
}
type stringService struct{}
func (stringService) Count(s string) int {
return len(s)
}
从整体的调用链路来说,Go-Kit
的使用方式还是比较简单的。我依旧尝试着从中挑两个改进点和大家聊聊:
编解码方式往往可以被包含在协议中。
以HTTP
为例,可以根据Content-Type去解析数据,而不是在编码中自行定义。这点是Go-Kit
微服务框架为了兼容各种编解码方式,而引入的额外工作量,我个人反倒是建议可以在这块做一些强限制,提高编写代码的便利性。
定义如:func(_ context.Context, request interface{}) (interface{}, error)
。
对框架来说可以做到统一,但对使用者来说很容易带来不好的体验,比如说:
interface{}
返回,但如果我们要提取内部某个字段做埋点或监控,又需要做一次转换。我们不妨以Kratos
中的protobuf提供的gRPC-Gateway
方案进行对比,其实Transport
+Endpoint
层完全可以通过protoc等代码生成工具实现。但是,Go-Kit
为了兼容各类RPC框架,无法在这一块利用代码生成等技术继续提效,而只能通过人工组合。
这一方面体现了集合类框架的价值 - 兼容性强,但也带来了一个最大的弊端 - 不如体系化的框架那么方便。
官网有一段关于依赖注入的内容,这块的思想与Kratos
框架提供的wire
有异曲同工之妙。但在细节的实现上会有一些差异,我们会在后面聊到这块~