RPC 框架 Kitex 初体验丨青训营笔记

336 阅读5分钟

RPC 框架 Kitex 初体验丨青训营笔记

这是我参与「第五届青训营」笔记创作活动的第 9 天。

一、本堂课重点内容

1. RPC 简介

RPC(Remote Procedure Call)是一种计算机程序设计技术,允许程序调用在远程计算机上的过程。这意味着,如果您有两台计算机,您可以使用RPC在一台计算机上运行的程序调用另一台计算机上的过程。RPC通过网络连接两台计算机,并在其中一台计算机上运行的程序中进行远程过程调用。

RPC技术允许程序访问远程系统上的资源,而不必关心网络的细节。它的实现方式类似于函数调用,但实际上它是远程的,也就是说,它的执行发生在不同的计算机上。这对于分布式系统来说非常有用,因为它允许在不同的计算机之间共享数据和资源。

2. Kitex 概览

Kitex 是字节跳动内部的基于 Golang 语言的高性能分布式微服务框架,一款轻量级高性能的RPC框架。Kitex通过协议层、传输层、连接层、服务层等多个层面的优化,实现了更高的并发性能、更低的延迟和更好的稳定性。

Kitex还提供了一些方便的特性,如上下文传递、中间件、服务发现、负载均衡等等,帮助开发者更方便地构建微服务应用,具有高性能强可扩展的特点,在字节内部已广泛使用。

Kitex 的架构设计如下图所示:

image.png

二、详细知识点介绍

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 --versionthriftgo --version 便可看到具体的版本号。

image.png

定义 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 的消息类型,它包含三个字段:nameagehobbies。其中,nameage 是普通字段,hobbies 是一个 repeated 字段,表示它可以包含多个值。每个字段都有一个唯一的标识符,称为字段标识符,它是一个数字,用来标识字段的序号。

在使用 Protobuf 的 Golang 项目中,需要安装相应的编译器 protoc,通过该编译器生成 Golang 代码,并通过这些代码来序列化和反序列化 Person 消息类型的实例。

Kitex 生成代码

使用kitex -module example -service example echo.thrift命令生成代码。

上述命令中,-module 表示生成的该项目的 go module 名,-service 表明我们要生成一个服务端项目,后面紧跟的 example 为该服务的名字。最后一个参数则为该服务的 IDL 文件。

生成的项目结构如下:

image.png

其中,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.shsh output/bootstrap.shEcho 服务就开始运行啦!

创建 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 拥有非常丰富的扩展生态,以下为一些常用的扩展:

image.png

三、引用参考