当前位置: 首页 > news >正文

RPC框架之Kitex

前置知识,参考我之前的文章:一文读懂RPC与HTTP的区别-CSDN博客

概念

什么是Kitex?

Kitex是由字节跳动开发并开源的一款高性能微服务框架,专为构建分布式服务和微服务架构设计,提供了一整套构建微服务的工具和库,支持服务注册与发现、负载均衡、故障隔离、链路追踪、服务监控等常见微服务需求。它基于Golang语言,通过优化协议处理、连接池、并发模型等,提供高性能的RPC远程过程调用能力。

环境准备

Go安装参考:Download and install - The Go Programming Language

完成安装后打开终端并输入 go version ,正确输出 Go 版本以及系统架构信息代表安装成功。

$ go version// output
go version go1.19.12 darwin/arm64

安装成功后,你可能需要设置一下国内代理:

go env -w GOPROXY=https://goproxy.cn

代码生成工具

在安装代码生成工具前,确保 GOPATH 环境变量已经被正确地定义(例如 export GOPATH=~/go)并且将$GOPATH/bin添加到 PATH 环境变量之中(例如 export PATH=$GOPATH/bin:$PATH);请勿将 GOPATH 设置为当前用户没有读写权限的目录。

// 检查当前GOPATH设置:
echo $GOPATH
// 如果未设置或需要修改,请执行:
export GOPATH=~/go
// 检查GOPATH/bin是否在PATH中:
echo $PATH | grep "$GOPATH/bin"
// 如果不在PATH中,请添加:
export PATH=$GOPATH/bin:$PATH
// 验证目录权限:
ls -ld $GOPATH

要将GOPATH配置永久生效,执行以下步骤:

// 1.执行以下命令查看当前使用的Shell:
echo $SHELL // output: /bin/bash → 使用~/.bash_profile /bin/zsh → 使用~/.zshrc// 2.编辑对应的配置文件
// 如果是bash:
nano ~/.bash_profile
// 如果是zsh:
nano ~/.zshrc// 3.添加配置内容
export GOPATH=~/go
export PATH=$GOPATH/bin:$PATH// 4.保存并退出
- 在nano编辑器中,按 Ctrl+O 保存
- 按 Enter 确认文件名
- 按 Ctrl+X 退出// 5.使配置立即生效
// bash用户
source ~/.bash_profile
// zsh用户
source ~/.zshrc// 6.执行以下命令验证
echo $GOPATH
echo $PATH | grep "$GOPATH/bin"

Kitex 中使用到的代码生成工具包括 IDL 编译器kitex tool

IDL 编译器

前置知识--IDL介绍IDL 全称是 Interface Definition Language,接口定义语言。如果我们要使用 RPC 进行调用,就需要知道对方的接口是什么,需要传什么参数,同时也需要知道返回值是什么样的,就好比两个人之间交流,需要保证在说的是同一个语言、同一件事。IDL 就是为了解决这样的问题,通过 IDL 来约定双方的协议,就像在写代码的时候需要调用某个函数,我们需要知道签名一样。

对于 RPC 框架,IDL 不仅作为接口描述语言还会根据 IDL 文件生成指定语言的接口定义模块,这样极大简化了开发工作。服务提供方(服务端)需要做的变为 编写 IDL -> 使用代码生成工具生成代码 -> 实现接口;服务调用方(客户端)只需根据服务提供方(服务端)提供的 IDL 生成代码后进行调用。

IDL 编译器能够解析 IDL 并生成对应的序列化和反序列化代码,Kitex 支持 Thrift 和 protobuf 这两种 IDL,这两种 IDL 的解析分别依赖于 thriftgo 与 protoc。

  • thrift 依赖的 thriftgo 在 安装 kitex 工具的时候会安装(最新版本会去掉对 thriftgo 的依赖),无需手动安装
  • protobuf 编译器安装可见 protoc。

kitex tool

kitex 是 Kitex 框架提供的用于生成代码的一个命令行工具。目前,kitex 支持 thrift 和 protobuf 的 IDL,并支持生成一个服务端项目的骨架。kitex 的使用需要依赖于 IDL 编译器确保你已经完成 IDL 编译器的安装。

执行以下命令:

go install github.com/cloudwego/kitex/tool/cmd/kitex@latest

安装成功后,执行 kitex --version 可以看到具体版本号的输出:

kitex --version
// output 示例
v0.14.1

基础示例

获取示例代码

使用 git 克隆代码仓库:

git clone https://github.com/cloudwego/kitex-examples.git

运行

进入示例仓库的 hello 目录:

cd kitex-examples/hello

运行服务端代码:

go run .

// 输出类似日志代表运行成功

2025/08/04 22:41:52.272147 server.go:79: [Info] KITEX: server listen at addr=[::]:8888

另启一个终端运行客户端代码:

go run ./client

// 每隔一秒输出类似日志代表运行成功
2025/08/04 22:51:42 Response({Message:my request})
2025/08/04 22:51:43 Response({Message:my request})
2025/08/04 22:51:44 Response({Message:my request})
2025/08/04 22:51:45 Response({Message:my request})

至此,我们成功通过 Kitex 发起了 RPC 调用。

新增方法

打开 hello 目录下的 hello.thrift 文件,你会看到如下内容:

namespace go apistruct Request {1: string message
}struct Response {1: string message
}service Hello {Response echo(1: Request req)
}

接下来为新的方法定义一个新的请求和响应,AddRequest 和 AddResponse ,并在 service hello 中添加 add 方法:

namespace go apistruct Request {1: string message
}struct Response {1: string message
}struct AddRequest{1:i64 first2:i64 second
}struct AddResponse{1:i64 sum
}service Hello {Response echo(1: Request req)AddResponse add(1:AddRequest req)
}

生成新代码

运行如下命令后,kitex 工具根据 hello.thrift 内容自动更新代码文件:

kitex -module "github.com/cloudwego/kitex-examples" -service a.b.c hello.thrift

执行完上述命令后,kitex 工具将更新下述文件

  1. 更新 ./handler.go,在里面增加一个 Add 方法的基本实现
  2. 更新 ./kitex_gen,里面有框架运行所必须的代码文件

补全服务端逻辑

上述步骤完成后,./handler.go 中会自动补全一个 Add 方法的基本实现,类似如下代码:

// Add implements the HelloImpl interface.
func (s *HelloImpl) Add(ctx context.Context, req *api.AddRequest) (resp *api.AddResponse, err error) {// TODO: Your code here...return

这个方法对应我们在 hello.thrift 中新增的 Add 方法,我们要做的就是增加我们想要的业务逻辑代码。例如返回请求参数相加后的结果:

// Add implements the HelloImpl interface.
func (s *HelloImpl) Add(ctx context.Context, req *api.AddRequest) (resp *api.AddResponse, err error) {// TODO: Your code here...resp = &api.AddResponse{Sum: req.First + req.Second}return
}

新增客户端调用

服务端已经有了 Add 方法的处理,现在让我们在客户端增加对 Add 方法的调用:

./client/main.go 中你会看到类似如下的 for 循环:

for {req := &api.Request{Message: "my request"}resp, err := client.Echo(context.Background(), req)if err != nil {log.Fatal(err)}log.Println(resp)time.Sleep(time.Second)
}

现在让我们在里面增加 Add 方法的调用:

for {req := &api.Request{Message: "my request"}resp, err := client.Echo(context.Background(), req)if err != nil {log.Fatal(err)}log.Println(resp)time.Sleep(time.Second)addReq := &api.AddRequest{First: 512, Second: 512}addResp, err := client.Add(context.Background(), addReq)if err != nil {log.Fatal(err)}log.Println(addResp)time.Sleep(time.Second)
}

重新运行

重新运行服务端和客户端,出现类似输出代表运行成功:

// 服务端
2025/08/05 10:03:53.327377 server.go:79: [Info] KITEX: server listen at addr=[::]:8888// 客户端
2025/08/05 10:07:45 Response({Message:my request})
2025/08/05 10:07:46 AddResponse({Sum:1024})
2025/08/05 10:07:47 Response({Message:my request})
2025/08/05 10:07:48 AddResponse({Sum:1024})
2025/08/05 10:07:49 Response({Message:my request})
2025/08/05 10:07:50 AddResponse({Sum:1024})

进阶示例

接下来,我们将会模拟一个简单的电商场景,包括商品服务库存服务API 服务,商品服务调用库存服务查询库存,API 服务调用商品服务查询商品信息,对前端或用户暴露 HTTP 接口供查询商品信息。

创建项目目录

创建一个目录用于存放后续代码,进入该目录后初始化 Go module:

mkdir example_shop
cd example_shop
go mod init example_shop

编写 IDL

按照开发流程,我们首先需要编写 IDL,这里以 thrift IDL 为例子。

创建 idl 目录用于存放项目 idl 文件:

mkdir idlcd idl

一般不同的服务都会使用不同的 IDL,所以我们这里创建 item.thrift stock.thrift 分别定义商品服务与库存服务的接口,同时创建 base.thrift 定义公共数据结构。

base.thrift

namespace go example.shop.basestruct BaseResp {1: string code2: string msg
}

item.thrift

namespace go example.shop.iteminclude "base.thrift"struct Item {1: i64 id2: string title3: string description4: i64 stock
}struct GetItemReq {1: required i64 id
}struct GetItemResp {1: Item item255: base.BaseResp baseResp
}service ItemService{GetItemResp GetItem(1: GetItemReq req)
}

stock.thrift

namespace go example.shop.stockinclude "base.thrift"struct GetItemStockReq {1: required i64 item_id
}struct GetItemStockResp {1: i64 stock255: base.BaseResp BaseResp
}service StockService {GetItemStockResp GetItemStock(1:GetItemStockReq req)
}

代码生成

有了 IDL 以后我们便可以通过 kitex 工具生成项目代码了,我们在先回到项目的根目录即 example_shop。因为我们有两个 IDL 定义了服务,所以执行两次 kitex 命令:

kitex -module example_shop idl/item.thriftkitex -module example_shop idl/stock.thrift

生成的代码分两部分,一部分是结构体的编解码序列化代码,由 IDL 编译器生成;另一部分由 kitex 工具在前者产物上叠加,生成用于创建和发起 RPC 调用的桩代码。它们默认都在 kitex_gen 目录下。

上面生成的代码并不能直接运行,需要自己完成 NewClient 和 NewServer 的构建。kitex 命令行工具提供了 -service 参数能直接生成带有脚手架的代码,接下来让我们为商品服务和库存服务分别生成脚手架。

首先为两个 RPC 服务分别单独创建目录:

mkdir -p rpc/item rpc/stock

再分别进入各自的目录中,执行如下命令生成代码:

// item 目录下执行
kitex -module example_shop -service example.shop.item -use example_shop/kitex_gen ../../idl/item.thrift

// stock 目录下执行
kitex -module example_shop -service example.shop.stock -use example_shop/kitex_gen ../../idl/stock.thrift

参数解释: 

kitex 默认会将代码生成到执行命令的目录下,kitex 的命令中:

  • -module 参数表明项目 go mod 中的 module name,在本例中为 example_shop
  • -service 参数表明我们要生成脚手架代码,后面紧跟的 example.shop.item 或 example.shop.stock 为该服务的名字。
  • -use 参数表示让 kitex 不生成 kitex_gen 目录,而使用该选项给出的 import path。在本例中因为第一次已经生成 kitex_gen 目录了,后面都可以复用。
  • 最后一个参数则为该服务的 IDL 文件

生成代码后的项目结构如下:

.
├── go.mod // go module 文件
├── idl // 示例 idl 存放的目录
│   ├── base.thrift
│   ├── item.thrift
│   └── stock.thrift
├── kitex_gen
│   └── example
│       └── shop
│           ├── base
│           │   ├── base.go // 根据 IDL 生成的编解码文件,由 IDL 编译器生成
│           │   ├── k-base.go // kitex 专用的一些扩展内容
│           │   └── k-consts.go
│           ├── item
│           │   ├── item.go // 根据 IDL 生成的编解码文件,由 IDL 编译器生成
│           │   ├── itemservice // kitex 封装代码主要在这里
│           │   │   ├── client.go
│           │   │   ├── itemservice.go
│           │   │   └── server.go
│           │   ├── k-consts.go
│           │   └── k-item.go // kitex 专用的一些扩展内容
│           └── stock
│               ├── k-consts.go
│               ├── k-stock.go // kitex 专用的一些扩展内容
│               ├── stock.go // 根据 IDL 生成的编解码文件,由 IDL 编译器生成
│               └── stockservice // kitex 封装代码主要在这里
│                   ├── client.go
│                   ├── server.go
│                   └── stockservice.go
└── rpc├── item│   ├── build.sh // 用来编译的脚本,一般情况下不需要更改│   ├── handler.go // 服务端的业务逻辑都放在这里,这也是我们需要更改和编写的文件│   ├── kitex_info.yaml│   ├── main.go│   └── script│       └── bootstrap.sh└── stock├── build.sh // 用来编译的脚本,一般情况下不需要更改├── handler.go // 服务端的业务逻辑都放在这里,这也是我们需要更改和编写的文件├── kitex_info.yaml├── main.go // 服务启动函数,一般在这里做一些资源初始化的工作,可以更改└── script└── bootstrap.sh

拉取依赖

使用 go mod tidy 命令拉取项目依赖

若想要升级 kitex 版本,执行 go get -v github.com/cloudwego/kitex@latest 即可。

编写商品服务逻辑

我们需要编写的服务端逻辑都在 handler.go 这个文件中,目前我们有两个服务,对应了两个 handler.go,他们的结构都是类似的,我们先看看商品服务的服务端逻辑 rpc/item/handler.go

package mainimport ("context"item "example_shop/kitex_gen/example/shop/item"
)// ItemServiceImpl implements the last service interface defined in the IDL.
type ItemServiceImpl struct{}// GetItem implements the ItemServiceImpl interface.
func (s *ItemServiceImpl) GetItem(ctx context.Context, req *item.GetItemReq) (resp *item.GetItemResp, err error) {// TODO: Your code here...return
}

这里的 GetItem 函数就对应了我们之前在 item.thrift IDL 中定义的 GetItem 方法:

现在让我们修改一下服务端逻辑,本项目仅仅演示使用方法,重点不在于业务逻辑,故简单处理后返回:

package mainimport ("context"item "example_shop/kitex_gen/example/shop/item"
)// ItemServiceImpl implements the last service interface defined in the IDL.
type ItemServiceImpl struct{}// GetItem implements the ItemServiceImpl interface.
func (s *ItemServiceImpl) GetItem(ctx context.Context, req *item.GetItemReq) (resp *item.GetItemResp, err error) {resp = item.NewGetItemResp()resp.Item = item.NewItem()resp.Item.Id = req.GetId()resp.Item.Title = "Kitex"resp.Item.Description = "Kitex is an excellent framework!"return
}

除了 handler.go 外,我们还需关心 main.go 文件,可以看看 main.go 中做了什么事情:

package mainimport (item "example_shop/kitex_gen/example/shop/item/itemservice""log"
)func main() {svr := item.NewServer(new(ItemServiceImpl))err := svr.Run()if err != nil {log.Println(err.Error())}
}

main.go 中的代码很简单,即使用 kitex 生成的代码创建一个 server 服务端,并调用其 Run 方法开始运行。通常使用 main.go 进行一些项目初始化,如加载配置等。

运行商品服务

至此,我们便可以开始运行商品服务了,kitex 也为我们生成了编译脚本,即 build.sh

#!/usr/bin/env bash
RUN_NAME="example.shop.item"mkdir -p output/bin
cp script/* output/
chmod +x output/bootstrap.shif [ "$IS_SYSTEM_TEST_ENV" != "1" ]; thengo build -o output/bin/${RUN_NAME}
elsego test -c -covermode=set -o output/bin/${RUN_NAME} -coverpkg=./...
fi

build.sh 主要做了以下事情:

  1. 定义了一个变量 RUN_NAME,用于指定生成的可执行文件的名称,值为我们在 IDL 中指定的 namespace。本例中为 example.shop.item
  2. 创建 output 目录,此后的编译出的二进制文件放在 output/bin 下。同时将 script 目录下的项目启动脚本复制进去
  3. 根据环境变量 IS_SYSTEM_TEST_ENV 的值判断生成普通可执行文件或测试可执行文件。值为 1 则代表使用 go test -c 生成测试文件,否则正常使用 go build 命令编译。

直接执行 sh build.sh 即可编译项目。

编译成功后,生成 output 目录:

output
├── bin // 存放二进制可执行文件
│   └── example.shop.item
└── bootstrap.sh // 运行文件的脚本

执行 sh output/bootstrap.sh 即可启动编译后的二进制文件。

输出类似以下命令,代表运行成功:

2025/08/05 23:21:36.689760 server.go:79: [Info] KITEX: server listen at addr=[::]:8888

在上面的日志输出中,addr=[::]:8888 代表我们的服务运行在本地的 8888 端口,此参数可以在创建 server 时传入 option 配置来修改,更多服务端配置见 Server Option。

运行 API 服务

有了商品服务后,接下来就让我们编写 API 服务用于调用刚刚运行起来的商品服务,并对外暴露 HTTP 接口。

首先,同样的,我们回到项目根目录先创建一个目录用于存放我们的代码:

mkdir api && cd api

然后创建一个 main.go 文件,就可以开始编写代码了。

以下是 main.go 的整体代码,包含创建 client、调用商品服务、暴露 HTTP 接口:

前置知识:此处使用 Hertz (基于 Golang 的微服务 HTTP 框架),更多参考:概览 | CloudWeGo

package mainimport ("context""log""time""example_shop/kitex_gen/example/shop/item""example_shop/kitex_gen/example/shop/item/itemservice""github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/app/server""github.com/cloudwego/kitex/client""github.com/cloudwego/kitex/client/callopt"
)var (cli itemservice.Client
)func main() {c, err := itemservice.NewClient("example.shop.item", client.WithHostPorts("0.0.0.0:8888"))if err != nil {log.Fatal(err)}cli = chz := server.New(server.WithHostPorts("localhost:8889"))hz.GET("/api/item", Handler)if err := hz.Run(); err != nil {log.Fatal(err)}
}func Handler(ctx context.Context, c *app.RequestContext) {req := item.NewGetItemReq()req.Id = 1024resp, err := cli.GetItem(context.Background(), req, callopt.WithRPCTimeout(3*time.Second))if err != nil {log.Fatal(err)}c.String(200, resp.String())
}

执行 go run . 命令启动 API 服务,监听 8889 端口,请求 localhost:8889/api/item 即可发起 RPC 调用商品服务提供的 GetItem 接口,并获取到响应结果。

如果执行 go run . 时出现下面的问题,是由于项目中缺少 Hertz 框架的依赖包:

执行下面的两个命令即可:

go get github.com/cloudwego/hertz/pkg/app
go get github.com/cloudwego/hertz/pkg/app/server

更多参考:进阶教程 | CloudWeGo

http://www.dtcms.com/a/318358.html

相关文章:

  • 云手机和云真机之间存在的不同之处有什么?
  • [Oracle] LPAD()和RPAD()函数
  • Python实现电商商品数据可视化分析系统开发实践
  • 一、Istio基础学习
  • 自定义报表调研
  • 居家养老场景下摔倒识别准确率提升 29%:陌讯动态姿态建模算法实战解析
  • JuiceFS存储
  • C++实现线程池(5)计划线程池
  • Redis知识学习
  • 深度解析:AI如何重塑供应链?从被动响应到预测性防御的三大核心实践
  • (Python)待办事项升级网页版(html)(Python项目)
  • 未解决|TransmittableThreadLocal 怎么用| 阿里线程池工具避免手动在传递MDC traceId
  • 数字取证和网络安全:了解两者的交叉点和重要性
  • 《爬虫实战指南:轻松获取店铺详情,开启数据挖掘之旅》
  • 【网络基础】计算机网络发展背景及传输数据过程介绍
  • cad c#二次开发 图层封装 获取当前层
  • 《第十一篇》深入解析 `embedding.py`:基于 SiliconFlow API 的文本向量化服务
  • 《算法导论》笔记——归并排序及循环不变式证明
  • [AI 生成] 大数据数仓面试题
  • 无人机共轴双桨动力测试-如何确认桨叶最优间距(效率/噪音/重量/尺寸)
  • 无人机航拍数据集|第3期 无人机军事目标目标检测YOLO数据集3556张yolov11/yolov8/yolov5可训练
  • 2025年高防IP隐身术:四层架构拆解源站IP“消失之谜”
  • 笔试——Day30
  • 吴声 2025 年度演讲:“场景革命十年”的多面审视,理念重复之嫌!
  • 笔记html模板
  • OpenHarmony源码解析之init进程
  • 题解:CF1453D Checkpoints
  • 看不见的伪造痕迹:AI时代的鉴伪攻防战
  • 微信小程序多媒体功能实现
  • n8n循环处理完全指南