这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天
Kitex的使用
Kitex是字节跳动内部的 Golang 微服务 RPC 框架,具有高性能、强可扩展的特点,在字节内部已广泛使用。
1. 安装代码生成工具
安装kitex代码生成工具:
go install github.com/cloudwego/kitex/tool/cmd/kitex@latest
安装thriftgo
go install github.com/cloudwego/thriftgo@latest
2. 编写IDL
kitex支持thrift IDL语法和ProtoBuf IDL语法。
使用thrift需要安装thriftgo
使用protobuf需要安装protoc
示例为一个echo服务。
namespace go api
struct Reuest {
1: string message
}
struct Response {
1: string message
}
service Echo {
Response echo(1: Response req)
}
3. Kitex生成服务代码
编写IDL后,使用kitex工具生成项目代码。
kitex -module example -service example echo.thrift
-module 表示生成的该项目的go module名,-service表示生成一个服务器项目,后面紧跟的example为该服务的名字。
生成的项目目录结构如下:
.
|-- build.sh
|-- echo.thrift
|-- handler.go
|-- kitex_gen
| `-- api
| |-- echo
| | |-- client.go
| | |-- echo.go
| | |-- invoker.go
| | `-- server.go
| |-- echo.go
| `-- k-echo.go
|-- main.go
`-- script
`-- bootstrap.sh
kitex_gen目录为kitex生成代码,包含了IDL中定义的结构体、服务端、客户端。不需要修改。
main.go和handler.go为创建了一个服务端,main.go为程序入口,在handler.go中编写服务的实际逻辑代码。
运行sh build.sh构建项目,然后sh output/bootstrap.sh运行。
4. 编写服务端
main.go: 启动一个server
package main
import api "github.com/cloudwego/kitex-examples/hello/kitex_gen/api/echo"
func main() {
svr := api.NewServer(new(EchoImpl))
err := svr.Run()
if err != nil {
panic(err)
}
}
Handler.go: 服务逻辑
package main
import (
"context"
"example/kitex_gen/api"
)
// EchoImpl implements the last service interface defined in the IDL.
type EchoImpl struct{}
// Echo implements the EchoImpl interface.
func (s *EchoImpl) Echo(ctx context.Context, req *api.Request) (resp *api.Response, err error) {
return &api.Response{Message: req.Message}, nil
}
5. 编写客户端
创建client
cli, err = echo.NewClient("example", client.WithHostPorts("0.0.0.0:8888"))
if err != nil {
panic(err)
}
发起调用
req := &api.Request{Message: "my request"}
resp, err := cli.Echo(context.Background(), req, callopt.WithRPCTimeout(3*time.Second))
if err != nil {
log.Fatal(err)
}
log.Println(resp)
6. Etcd服务注册与发现
目前kitex的服务注册与发现已经对接了主流的服务注册与对接中心,如ETCD,Nacos等。创建server时,可将server注册到etcd,然后,在创建server时,可通过etcd获取server的地址,无需提供server的地址和端口。
示例
server注册
func main() {
r, err := etcd.NewEtcdRegistry([]string{"127.0.0.1:2379"})
if err != nil {
log.Fatal(err)
}
addr, err := net.ResolveTCPAddr("tcp", ":18888")
if err != nil {
log.Fatal(err)
}
svr := notedemo.NewServer(
new(NoteServiceImpl),
server.WithServiceAddr(addr),
server.WithRegistry(r),
)
err = svr.Run()
if err != nil {
log.Println(err.Error())
}
}
etcd.NewEtcdRegistry([]string{"127.0.0.1:2379"})创建一个etcd注册器,127.0.0.1:2379为etcd服务的运行地址。在创建server时,添加一个参数server.WithRegistry(r)来指定注册器。
client发现server
r, err := etcd.NewEtcdResolver([]string{"127.0.0.1:2379"})
if err != nil {
log.Fatal(err)
}
c, err := noteservice.NewClient(
constants.NoteServiceName,
client.WithResolver(r),
)
if err != nil {
panic(err)
}
etcd.NewEtcdResolver([]string{"127.0.0.1:2379"})创建一个发现器。
创建client时,添加一个参数client.WithResolver(r)来指定发现器。
7. Kitex中间件
中间件Middleware 是扩展 Kitex 框架的一个主要的方法.
- 中间件和套件都只允许在初始化 Server、Client 的时候设置,不允许动态修改。
- Middleware 是按照添加的先后顺序执行的。
两个类型:
Endpoint是一个函数,接收ctx, req, resp, 返回errMiddleware也是一个函数,接收一个Endpoint,返回一个Endpoint
中间件就是一个输入是Endpoint,输出也是Endpoint的函数。
中间件示例
func CommonMiddleware(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req, resp interface{}) (err error) {
ri := rpcinfo.GetRPCInfo(ctx)
// get real request
klog.Infof("real request: %+v\n", req)
klog.Infof("remote service name: %s, remote method: %s\n", ri.To().ServiceName(), ri.To().Method())
if err = next(ctx, req, resp); err != nil {
return err
}
klog.Infof("real response: %+v\n", resp)
return nil
}
}
客户端中件件
有两种方法添加客户端中间件
client.WithMiddleware: 在 Service 熔断和超时中间件之后执行.client.WithInstanceMW: 在服务发现、负载均衡之后执行,如果有实例熔断器,会在实例熔断器后执行。
服务端中间件
server.WithMiddleware