RPC 框架 Kitex 初体验丨青训营笔记
这是我参与「第五届青训营」笔记创作活动的第 9 天。
一、本堂课重点内容
1. RPC 简介
RPC(Remote Procedure Call)是一种计算机程序设计技术,允许程序调用在远程计算机上的过程。这意味着,如果您有两台计算机,您可以使用RPC在一台计算机上运行的程序调用另一台计算机上的过程。RPC通过网络连接两台计算机,并在其中一台计算机上运行的程序中进行远程过程调用。
RPC技术允许程序访问远程系统上的资源,而不必关心网络的细节。它的实现方式类似于函数调用,但实际上它是远程的,也就是说,它的执行发生在不同的计算机上。这对于分布式系统来说非常有用,因为它允许在不同的计算机之间共享数据和资源。
2. Kitex 概览
Kitex 是字节跳动内部的基于 Golang 语言的高性能分布式微服务框架,一款轻量级高性能的RPC框架。Kitex通过协议层、传输层、连接层、服务层等多个层面的优化,实现了更高的并发性能、更低的延迟和更好的稳定性。
Kitex还提供了一些方便的特性,如上下文传递、中间件、服务发现、负载均衡等等,帮助开发者更方便地构建微服务应用,具有高性能、强可扩展的特点,在字节内部已广泛使用。
Kitex 的架构设计如下图所示:
二、详细知识点介绍
1. Kitex 快速开始
Kitex 目前对 Windows 的支持尚不完善,如果本地开发环境是 Windows 建议使用虚拟机 WSL2,安装教程见我的前篇文章。
安装代码生成工具
安装 kitex:go install github.com/cloudwego/kitex/tool/cmd/kitex@latest
安装 thriftgo:go install github.com/cloudwego/thriftgo@latest
安装成功后,执行 kitex --version 和 thriftgo --version 便可看到具体的版本号。
定义 IDL
IDL(Interface Definition Language,接口定义语言)是一种用于描述软件组件接口的语言。它可以用来描述组件之间的接口、方法、参数和返回值类型等信息,以便不同编程语言和平台之间进行交互和通信。
如果我们要进行 RPC,就需要知道对方的接口是什么,需要传什么参数,同时也需要知道返回值是什么样的。这时候,就需要通过 IDL 来约定。
当使用 Protobuf 的 IDL 生成代码时,通常需要定义一个 .proto 文件,举一个简单的例子:
syntax = "proto3";
package example;
message Person {
string name = 1;
int32 age = 2;
repeated string hobbies = 3;
}
在这个例子中,我们定义了一个名为 Person 的消息类型,它包含三个字段:name、age 和 hobbies。其中,name 和 age 是普通字段,hobbies 是一个 repeated 字段,表示它可以包含多个值。每个字段都有一个唯一的标识符,称为字段标识符,它是一个数字,用来标识字段的序号。
在使用 Protobuf 的 Golang 项目中,需要安装相应的编译器 protoc,通过该编译器生成 Golang 代码,并通过这些代码来序列化和反序列化 Person 消息类型的实例。
Kitex 生成代码
使用kitex -module example -service example echo.thrift命令生成代码。
上述命令中,-module 表示生成的该项目的 go module 名,-service 表明我们要生成一个服务端项目,后面紧跟的 example 为该服务的名字。最后一个参数则为该服务的 IDL 文件。
生成的项目结构如下:
其中,build.sh 是构建脚本,kitex_gen 是 IDL 内容相关的生成代码,主要是基础的 Server/Client 代码,main.go 是程序人口,handler.go 用户在该文件里实现 IDL service 定义的方法。
Kitex 基本使用
编写 echo 服务逻辑
服务默认监听 8888 端口
package main
import (
"context"
"example/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) {
return &api.Response{Message: req.Message}, nil
}
这里的 Echo 函数就对应了我们之前在 IDL 中定义的 echo 方法。
在执行 sh build.sh 和 sh output/bootstrap.sh 后Echo 服务就开始运行啦!
创建 Client
import "example/kitex_gen/api/echo"
import "github.com/cloudwego/kitex/client"
...
c, err := echo.NewClient("example", client.WithHostPorts("0.0.0.0:8888"))
if err != nil {
log.Fatal(err)
}
上述代码中,echo.NewClient 用于创建 client,其第一个参数为调用的 服务名,第二个参数为 options,用于传入参数, 此处的 client.WithHostPorts 用于指定服务端的地址。
发起请求
import "example/kitex_gen/api"
...
req := &api.Request{Message: "my request"}
resp, err := c.Echo(context.Background(), req, callopt.WithRPCTimeout(3*time.Second))
if err != nil {
log.Fatal(err)
}
log.Println(resp)
上述代码中,我们首先创建了一个请求 req , 然后通过 c.Echo 发起了调用。
运行上述代码,可以看到如下输出:
2023/02/15 16:51:35 Response({Message:my request})
此时便成功编写了一个 Kitex 的服务端和客户端,并完成了一次调用!
2. Kitex 服务注册与发现
Kitex 框架提供服务注册与发现的扩展,目前已经支持与业界主流注册中心对接,如 ETCD、Nacos等。
其使用方式以 DNS Resolver 为例:
import (
...
dns "github.com/kitex-contrib/resolver-dns"
"github.com/cloudwego/kitex/client"
...
)
func main() {
...
client, err := echo.NewClient("echo", client.WithResolver(dns.NewDNSResolver()))
if err != nil {
log.Fatal(err)
}
...
}
其他多种服务注册与发现方式的使用方式与上述步骤类似,只是需要更换相应的客户端和函数即可。
3. Kitex 生态
Kitex 拥有非常丰富的扩展生态,以下为一些常用的扩展: