Kitex的使用 | 青训营笔记

426 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 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, 返回err
  • Middleware 也是一个函数,接收一个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
	}
}

客户端中件件

有两种方法添加客户端中间件

  1. client.WithMiddleware: 在 Service 熔断和超时中间件之后执行.
  2. client.WithInstanceMW: 在服务发现、负载均衡之后执行,如果有实例熔断器,会在实例熔断器后执行。

服务端中间件

server.WithMiddleware