Kitex:
不同的服务使用不同的IDL。
kitex使用thrift协议
示例:
IDL
创建example_shop文件夹,创建idl文件夹存放idl文件。分别创建base.thrift,item.thrift,item.thrift文件
go
代码解读
复制代码
//base.thrift namespace go example.shop.base struct BaseResp { 1: string code 2: string msg }
go
代码解读
复制代码
//item.thrift namespace go example.shop.item include "base.thrift" struct Item { 1: i64 id 2: string title 3: string description 4: i64 stock } struct GetItemReq { 1: required i64 id } struct GetItemResp { 1: Item item 255: base.BaseResp baseResp } service ItemService{ GetItemResp GetItem(1: GetItemReq req) }
perl
代码解读
复制代码
//stock.thrift namespace go example.shop.stock include "base.thrift" struct GetItemStockReq{ 1:required i64 item_id } struct GetItemStockResp{ 1:i64 stock 255:base.BaseResp BaseResp } service StockService{ GetItemStockResp GetItemStock(1:GetItemStockReq req) }
使用命令生成代码:
生成的代码分两部分,一部分是结构体的编解码序列化代码,由 IDL 编译器生成;另一部分由 kitex 工具在前者产物上叠加,生成用于创建和发起 RPC 调用的桩代码。它们默认都在 kitex_gen
目录下
kitex -module example_shop idl/item.thrift
生成文件:
同理:D:\example_shop> kitex -module example_shop .\idl\stock.thrift
上面生成的代码并不能直接运行,需要自己完成 NewClient
和 NewServer
的构建。kitex 命令行工具提供了 -service
参数能直接生成带有脚手架的代码,接下来让我们为商品服务和库存服务分别生成脚手架。
生成脚手架
首先为两个rpc服务创建文件夹:
mkdir -p rpc/item rpc/stock
arduino
代码解读
复制代码
// 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
-module
参数表明生成代码的go mod
中的module name
,在本例中为example_shop
-service
参数表明我们要生成脚手架代码,后面紧跟的example.shop.item
或example.shop.stock
为该服务的名字。-use
参数表示让 kitex 不生成kitex_gen
目录,而使用该选项给出的import path
。在本例中因为第一次已经生成kitex_gen
目录了,后面都可以复用。- 最后一个参数则为该服务的 IDL 文件
生成文件:
生成项目结构如下:
go
代码解读
复制代码
. ├── go.mod // go module 文件 ├── go.sum ├── 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 │ │ │ ├── invoker.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 │ ├── invoker.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即可
错误:
如果遇到类似如下两种报错:
go
代码解读
复制代码
github.com/apache/thrift/lib/go/thrift: ambiguous import: found package github.com/apache/thrift/lib/go/thrift in multiple modules github.com/cloudwego/kitex@v0.X.X/pkg/utils/thrift.go: not enough arguments in call to t.tProt.WriteMessageBegin
先执行一遍下述命令,再继续操作:
ini
代码解读
复制代码
go mod edit -droprequire=github.com/apache/thrift/lib/go/thrift //go mod edit -droprequire="github.com/apache/thrift/lib/go/thrift" for Windows go mod edit -replace=github.com/apache/thrift=github.com/apache/thrift@v0.13.0 //go mod edit -replace="github.com/apache/thrift=github.com/apache/thrift@v0.13.0" for Windows
这是因为 thrift 官方在 0.14 版本对 thrift 接口做了 breaking change,导致生成代码不兼容。
若想要升级 kitex 版本,执行 go get -v github.com/cloudwego/kitex@latest
即可。
修改业务逻辑
后续修改rpc目录下对应服务的handler中的业务逻辑即可
接着编写api,调用rpc服务,再对外暴露http接口。此处用hertz:
go
代码解读
复制代码
package main import ( "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() { //kitex_gen中有封装好的客户端,直接调用即可。 example.shop.item为服务名 c, err := itemservice.NewClient("example.shop.item", client.WithHostPorts("0.0.0.0:8888")) if err != nil { log.Fatal(err) } cli = c hz := server.New(server.WithHostPorts("localhost:8889"))//起一个web服务 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 = 1024 resp, err := cli.GetItem(context.Background(), req, callopt.WithRPCTimeout(3*time.Second))//调用rpc服务 if err != nil { log.Fatal(err) } c.String(200, resp.String()) }
后续库存服务见D:/example_shop/rpc/stock
注意handler和main中注释内容。
用rpc调用另一个rpc
补充商品服务(即在rpc/item服务中调用stock服务):
在rpc/item/handler中的item服务接口添加stock服务连接,并编写初始化函数:
go
代码解读
复制代码
package main import ( "context" "log" item "example_shop/kitex_gen/example/shop/item" "example_shop/kitex_gen/example/shop/stock" "example_shop/kitex_gen/example/shop/stock/stockservice" "github.com/cloudwego/kitex/client" ) // ItemServiceImpl implements the last service interface defined in the IDL. type ItemServiceImpl struct{ stockCli stockservice.Client } //初始化函数 func NewStockClient(addr string) (stockservice.Client, error) { return stockservice.NewClient("example.shop.stock", client.WithHostPorts(addr)) } // 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!" //添加其他业务逻辑 stockReq := stock.NewGetItemStockReq() stockReq.ItemId = req.GetId() stockResp, err := s.stockCli.GetItemStock(context.Background(), stockReq) //此处调用stock服务 if err != nil { log.Println(err) stockResp.Stock = 0 } resp.Item.Stock = stockResp.GetStock() return }