在学习实用 Kitex 前首先要粗略了解两个概念: RPC 和 IDL。
RPC
RPC 是 Remote Procedure Call 的缩写,译为“远程过程调用”,是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。--维基百科
相比常用的 HTTP 通信,RPC 基于 TCP 或者 HTTP2 应用层协议,而 HTTP 基于 HTTP 协议(很多浏览器默认采用 HTTP1.1 访问网站);性能上 RPC 可以高效二进制传输,HTTP 大多通过 json,序列化和反序列化更消耗性能;RPC 可以采用自定义的 TCP 协议,HTTP 1.1 会携带很多无用的内容,基于 HTTP 2.0 包装则可以作为 RPC 使用。
HTTP 相对更加通用,RPC 相对性能更好,更多用于服务端内部的服务调用。
IDL
IDL 是 Interface description language 的缩写,译为“接口描述语言”,IDL通过一种独立于编程语言的方式来描述接口,使得在不同平台上运行的对象和用不同语言编写的程序可以相互通信交流。
如果我们要进行 RPC,就需要知道对方的接口是什么,需要传什么参数,同时也需要知道返回值是什么样的。这时候,就需要通过 IDL 来约定双方的协议,好比在调用本地函数的时候,我们要知道函数签名。
Kitex 默认支持的 IDL 有 thrift 和 proto3,对应 thift 和 protobuf 两种序列化协议。以一个简单的 echo server 为例,thrift 的 idl 文件 echo.thrift可写成:
namespace go api
struct Request {
1: string message
}
struct Response {
1: string message
}
service Echo {
Response echo(1: Request req)
}
而用 protobuf 的 echo.proto 则是:
syntax = "proto3";
package pbapi;
// The greeting service definition.
option go_package = "pbapi";
message Request {
string message = 1;
}
message Response {
string message = 1;
}
service Echo {
rpc Echo (Request) returns (Response) {}
}
Kitex
Kitex[kaɪt’eks] 字节跳动内部的 Golang 微服务 RPC 框架,具有高性能、强可扩展的特点,在字节内部已广泛使用。如果对微服务性能有要求,又希望定制扩展融入自己的治理体系,Kitex 会是一个不错的选择。
Kitex 的 RPC 消息协议默认支持 Thrift、Kitex Protobuf、gRPC。Thrift 支持 Buffered 和 Framed 二进制协议;Kitex Protobuf 是 Kitex 自定义的 Protobuf 消息协议,协议格式类似 Thrift;gRPC 是对 gRPC 消息协议的支持,可以与 gRPC 互通。除此之外,使用者也可以扩展自己的消息协议。
Kitex 的使用
首先要安装 Kitex 和 thriftgo (或者 protoc 下载到 $PATH)
# 会一并安装 thiftgo
go install github.com/cloudwego/kitex/tool/cmd/kitex@latest
thrift
将上文的 echo.thrift 创建在一个新目录中,执行:
kitex -module learnkitex -service echoserver ./echo.thrift
生成文件后当前目录如下:
❯ tree
.
├── build.sh
├── echo.thrift
├── go.mod
├── handler.go
├── kitex_gen
│ └── api
│ ├── echo
│ │ ├── client.go
│ │ ├── echo.go
│ │ ├── invoker.go
│ │ └── server.go
│ ├── echo.go
│ ├── k-consts.go
│ └── k-echo.go
├── kitex_info.yaml
├── main.go
└── script
└── bootstrap.sh
5 directories, 14 files
然后我们在 handler.go 里添加处理代码:
package main
import (
"context"
api "learnkitex/kitex_gen/api"
)
// EchoImpl implements the last service interface defined in the IDL.
type EchoImpl struct{}
// Echo implements the EchoImpl interface.
func (s *EchoImpl) Echo(ctx context.Context, req *api.Request) (resp *api.Response, err error) {
// TODO: Your code here...
resp = &api.Response{Message: req.Message}
return
}
现在还缺一个客户端来测试,我们项目根目录下新建文件夹 client,以及其之下的 main.go
package main
import (
"context"
"log"
"time"
"learnkitex/kitex_gen/api"
"learnkitex/kitex_gen/api/echo"
"github.com/cloudwego/kitex/client"
)
func main() {
client, err := echo.NewClient("hello", client.WithHostPorts("0.0.0.0:8888"))
if err != nil {
log.Fatal(err)
}
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)
}
}
然后进行如下步骤便可进行测试:
go mod tidy
# 启动服务端
go run .
# 在另一个终端启动客户端
go run ./client
即可在客户端的终端看到输出:
❯ go run ./client
2023/08/21 23:07:57 Response({Message:my request})
2023/08/21 23:07:58 Response({Message:my request})
2023/08/21 23:07:59 Response({Message:my request})
2023/08/21 23:08:00 Response({Message:my request})
2023/08/21 23:08:01 Response({Message:my request})
2023/08/21 23:08:02 Response({Message:my request})
2023/08/21 23:08:03 Response({Message:my request})
Protobuf
类似地,采用上文的 echo.proto:
kitex -type protobuf -module learnkitex -service echoserver echo.proto
能生成类似的代码,剩下步骤基本一样。