Kitex使用示例

293 阅读4分钟

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

上面生成的代码并不能直接运行,需要自己完成 NewClientNewServer 的构建。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.itemexample.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
}
​