Go RPC 框架 Kitex 快速入门 | 青训营笔记

2,779 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 9 天

Kitex[kaɪt’eks](读法是 Kite X)字节跳动开源的基于 Go 语言的微服务 RPC 框架,具有高性能、强可扩展的特点。

在 Kitex 之前字节内部的 Golang 框架是 Kite,但 Kite 与 Thrift 深度耦合、生成代码逻辑重, 很难从网络模型或编解码层面改造优化,继续支持新特性势必会造成代码越发臃肿迭代受阻问题,于是字节针对曾经的痛点设计了新的框架 Kitex。虽然 Kitex 是新框架,但已经在线上应用一年多,目前字节内部超过 50% 的 Golang 微服务使用 Kitex。

Kitex 是字节开源的微服务中间集合 CloudWeGo 最早开源的项目之一。目前支持 Thrift 和 Protobuf 两种 IDL,不过目前字节内部用的是 Thrift ,所以对 Thrift 的支持会完善一些,故本文主要基于 Thrift 进行介绍。

(截止目前:2023年1月30日 Kitex 已经可以在 Windows 下编译运行了,但是还不支持代码生成工具。)

架构设计

这个图的结构是指 Kitex 本身的架构设计,不是用 Kitex 开发的项目的架构。和 Hertz 的架构图一样,入门的话也是看看就行。

kitex架构设计.png

不过也同样可以从图上看出 Kitex 的支持的一些特性。

框架特点

  • 高性能

    使用自研的高性能网络库 Netpoll,性能相较 go net 具有显著优势。

  • 扩展性

    提供了较多的扩展接口以及默认扩展实现,使用者也可以根据需要自行定制扩展,具体见下面的框架扩展。

  • 多消息协议

    RPC 消息协议默认支持 ThriftKitex ProtobufgRPC。Thrift 支持 Buffered 和 Framed 二进制协议;Kitex Protobuf 是 Kitex 自定义的 Protobuf 消息协议,协议格式类似 Thrift;gRPC 是对 gRPC 消息协议的支持,可以与 gRPC 互通。除此之外,使用者也可以扩展自己的消息协议。

  • 多传输协议

    传输协议封装消息协议进行 RPC 互通,传输协议可以额外透传元信息,用于服务治理,Kitex 支持的传输协议有 TTHeaderHTTP2。TTHeader 可以和 Thrift、Kitex Protobuf 结合使用;HTTP2 目前主要是结合 gRPC 协议使用,后续也会支持 Thrift。

  • 多种消息类型

    支持 PingPongOneway双向 Streaming。其中 Oneway 目前只对 Thrift 协议支持,双向 Streaming 只对 gRPC 支持,后续可能会支持 Thrift 的双向 Streaming。

  • 服务治理

    支持服务注册/发现、负载均衡、熔断、限流、重试、监控、链路跟踪、日志、诊断等服务治理模块,大部分均已提供默认扩展,使用者可选择集成。

  • 代码生成

    Kitex 内置代码生成工具,可支持生成 ThriftProtobuf 以及脚手架代码。

快速开始

环境搭建

安装好 Go 语言环境并确保 GOPATH 环境变量已经被正确地定义(例如 export GOPATH=~/go)并且将$GOPATH/bin添加到 PATH 环境变量之中(例如 export PATH=$GOPATH/bin:$PATH)。(如果未配置好环境变量可能会找不到命令)

 [root@localhost KitexLearn]# kitex
 bash: kitex: command not found...

命令行安装 kitex:go install github.com/cloudwego/kitex/tool/cmd/kitex@latest

使用kitex --version或者 kitex 查看是否安装成功。

 [root@localhost KitexLearn]# kitex
 No IDL file found.
 [root@localhost KitexLearn]# kitex --version
 v0.4.4

命令行安装 thriftgo:go install github.com/cloudwego/thriftgo@latest

同样使用 thriftgo 或者 thriftgo --version 查看是否安装成功。

 [root@localhost KitexLearn]# thriftgo 
 require exactly 1 argument for the IDL parameter, got: 0
 [root@localhost KitexLearn]# thriftgo --version
 thriftgo 0.2.5

编写IDL

Thriftgo 是 Go 语言实现的 Thrift IDL 解析和代码生成器,支持完善的 Thrift IDL 语法和语义检查,相较 Apache Thrift 官方的 Golang 生成代码,Thriftgo 做了一些问题修复且支持插件机制,用户可根据需求自定义生成代码。

Thrift IDL 语法可参考官方文档:Thrift interface description language

初学者直接看官方文档可能有点抽象,可以看下这篇文章:Thrift & IDL 介绍

 namespace go add
 ​
 struct AddRequest {
     1: i64 a
     2: i64 b
 }
 ​
 struct AddResponse {
     1: i64 sum
 }
 ​
 service Add {
     AddResponse add(1: AddRequest req)
 }

生成 add 服务代码

有了 IDL 以后我们便可以通过 kitex 工具生成项目代码了,执行如下命令:

 $ kitex -module example -service example add.thrift

上述命令中,-module 表示生成的该项目的 go module 名,-service 表明我们要生成一个服务端项目,后面紧跟的 example 为该服务的名字。最后一个参数则为该服务的 IDL 文件。

生成后的项目结构如下:

 .
 |-- build.sh
 |-- add.thrift
 |-- handler.go
 |-- kitex_gen
 |   `-- api
 |       |-- add
 |       |   |-- add.go
 |       |   |-- client.go
 |       |   |-- invoker.go
 |       |   `-- server.go
 |       |-- add.go
 |       |-- k-add.go
 |       `-- k-consts.go
 |-- main.go
 `-- script
     |-- bootstrap.sh
     `-- settings.py

上面这个图是通过 tree 命令生成的,Windows貌似自带有,Linux则需要额外安装。

CentOS使用 yum -y install tree 进行安装。

 [root@localhost KitexLearn]# yum -y install tree
 (安装信息...)
 Installed:
   tree.x86_64 0:1.6.0-10.el7
 Complete!
 [root@localhost KitexLearn]# tree
 (就上面那个图)
 22 directories, 40 files

编写 add 服务逻辑

我们需要编写的服务端逻辑都在 handler.go 这个文件中,现在这个文件应该如下所示:

 package main
 ​
 import (
     api "KitexLearn/kitex_gen/api"
     "context"
     "log"
 )
 ​
 // EchoImpl implements the last service interface defined in the IDL.
 type AddImpl struct{}
 ​
 // Add implements the AddImpl interface.
 func (s *AddImpl) Add(ctx context.Context, req *api.AddRequest) (resp *api.AddResponse, err error) {
     // TODO: Your code here...
     return
 }

这里的 Add 函数就对应了我们之前在 IDL 中定义的 add 方法。

现在让我们修改一下服务端逻辑,让 Add 服务名副其实。

修改 Add 函数为下述代码:

 func (s *EchoImpl) Add(ctx context.Context, req *api.Request) (resp *api.Response, err error) {
     log.Printf("两个数是%d,%d", req.A, req.B)
     return &api.AddResponse{Sum: req.A + req.B}, nil
 }

编译运行

kitex 工具已经帮我们生成好了编译和运行所需的脚本:

编译:

 $ sh build.sh

执行上述命令后,会生成一个 output 目录,里面含有我们的编译产物。(编译时间可能有点久,看着不动的话耐心等待即可)

生成好的 output 文件夹结构

 [root@localhost output]# tree
 .
 ├── bin
 │   └── example
 ├── bootstrap.sh
 └── log
     ├── app
     └── rpc

运行:

 [root@localhost KitexLearn]# sh output/bootstrap.sh 
 2023/01/30 22:44:01.154991 server.go:81: [Info] KITEX: server listen at addr=[::]:8888

执行上述命令后,add 服务就开始运行啦!

编写 rpc 客户端

创建一个 add 文件夹,在该文件夹下新建一个 main.go 文件

 package main
 ​
 import (
     "KitexLearn/kitex_gen/api"
     "KitexLearn/kitex_gen/api/add"
 ​
     "context"
     "log"
     "time"
 ​
     "github.com/cloudwego/kitex/client"
     "github.com/cloudwego/kitex/client/callopt"
 )
 ​
 func main() {
     // 创建客户端
     c, err := add.NewClient("example", client.WithHostPorts("0.0.0.0:8888"))
     if err != nil {
         log.Fatal(err)
     }
 ​
     // 发起调用
     req := &api.AddRequest{A: 1, B: 2}
     resp, err := c.Add(context.Background(), req, callopt.WithRPCTimeout(3*time.Second))
     if err != nil {
         log.Fatal(err)
     }
     log.Println(resp)
 }

上述代码中,add.NewClient 用于创建 client,其第一个参数为调用的 服务名,第二个参数为 options,用于传入参数, 此处的 client.WithHostPorts 用于指定服务端的地址,更多参数可参考基本特性一节。

然后创建了一个请求结构体 , 通过 c.Add 发起了调用。

其第一个参数为 context.Context,通过通常用其传递信息或者控制本次调用的一些行为,你可以在后续章节中找到如何使用它。

其第二个参数为本次调用的请求。

其第三个参数为本次调用的 options ,Kitex 提供了一种 callopt 机制,顾名思义——调用参数 ,有别于创建 client 时传入的参数,这里传入的参数仅对此次生效。 此处的 callopt.WithRPCTimeout 用于指定此次调用的超时(通常不需要指定,此处仅作演示之用)同样的,可以在基本特性一节中找到更多的参数。

发起调用

新建一个终端,切换到add目录下执行Go代码即可。

 [root@localhost KitexLearn]# cd rpc/add/
 [root@localhost add]# go run .
 2023/01/30 22:49:15 AddResponse({Sum:3})

在微服务里,一般在 handler 里调用 rpc 包(RPC客户端)下定义的方法来调用实际业务(RPC服务端)的代码。