Kitex:
不同的服务使用不同的IDL。
kitex使用thrift协议
示例:
IDL
创建example_shop文件夹,创建idl文件夹存放idl文件。分别创建base.thrift,item.thrift,item.thrift文件
//base.thrift
namespace go example.shop.base
struct BaseResp {
1: string code
2: string msg
}
//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)
}
//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
// 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.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即可
错误:
如果遇到类似如下两种报错:
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
先执行一遍下述命令,再继续操作:
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:
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服务连接,并编写初始化函数:
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
}