RPC 框架 Kitex | 青训营

245 阅读4分钟

Kitex 概述

Kitex 字节跳动内部的 Golang 微服务 RPC 框架,具有高性能、强可扩展的特点,在字节内部已广泛使用。如果对微服务性能有要求,又希望定制扩展融入自己的治理体系,Kitex 会是一个不错的选择。

框架特点:

  • 高性能:使用自研的高性能网络库 Netpoll,性能相较 go net 具有显著优势。
  • 扩展性:提供了较多的扩展接口以及默认扩展实现,使用者也可以根据需要自行定制扩展。
  • 多消息协议:RPC 消息协议默认支持 Thrift、Kitex Protobuf、gRPC。Thrift 支持 Buffered 和 Framed 二进制协议;Kitex Protobuf 是 Kitex 自定义的 Protobuf 消息协议,协议格式类似 Thrift;gRPC 是对 gRPC 消息协议的支持,可以与 gRPC 互通。除此之外,使用者也可以扩展自己的消息协议。
  • 多传输协议:传输协议封装消息协议进行 RPC 互通,传输协议可以额外透传元信息,用于服务治理,Kitex 支持的传输协议有 TTHeader、HTTP2。TTHeader 可以和 Thrift、Kitex Protobuf 结合使用;HTTP2 目前主要是结合 gRPC 协议使用,后续也会支持 Thrift。
  • 多种消息类型:支持 PingPong、Oneway、双向 Streaming。其中 Oneway 目前只对 Thrift 协议支持,双向 Streaming 只对 gRPC 支持
  • 服务治理:支持服务注册/发现、负载均衡、熔断、限流、重试、监控、链路跟踪、日志、诊断等服务治理模块,大部分均已提供默认扩展,使用者可选择集成。
  • 代码生成:Kitex 内置代码生成工具,可支持生成 Thrift、Protobuf 以及脚手架代码。

Kitex 快速开始

基于 IDL 的 KiteX 实践 在 RPC 框架中,我们知道,服务端与客户端通信的前提是远程通信,但这种通信又存在一种关联,那就是通过一套相关的协议(消息、通信、传输等)来规范,但客户端又不用关心底层的技术实现,只要定义好了这种通信方式即可。在 KiteX 中,也提供了一种生成代码的命令行工具:kitex,目前支持 thrift、protobuf 等 IDL,并且支持生成一个服务端项目的骨架。

  1. 创建并进入项目目录:
PS F:\work\Go\golangStudy> mkdir example


    目录: F:\work\Go\golangStudy


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         2023/8/27     15:09                example


PS F:\work\Go\golangStudy> cd example 
  1. 安装 kitex:
PS F:\work\Go\golangStudy> go install github.com/cloudwego/kitex/tool/cmd/kitex\@latest
PS F:\work\Go\golangStudy\example> kitex
No IDL file found.
  1. 编写IDL

创建一个名为echo.proto的 protobuf IDL文件,然后在里面定义我们的服务

syntax = "proto3";

option go_package = "echo"; 

package echo;

message Request {
  string message = 1;
}

message Response {
  string message = 1;
}

service Echo {
  rpc echo (Request) returns (Response);
  rpc sayHello (Request) returns (Response);
}
  1. 生成 echo 服务代码

有了 IDL 以后我们便可以通过 kitex 工具生成项目代码了,执行如下命令:

PS F:\work\Go\golangStudy> kitex -module example -service example echo.proto
go: creating new go.mod: module example

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

  1. 编写 echo 服务逻辑
package main

import (
	"context"
	echo "example/kitex_gen/echo"
)

// 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 *echo.Request) (resp *echo.Response, err error) {
	// TODO: Your code here...
	return &echo.Response{
		Message: req.GetMessage(),
	}, nil
}

// SayHello implements the EchoImpl interface.
func (s *EchoImpl) SayHello(ctx context.Context, req *echo.Request) (resp *echo.Response, err error) {
	// TODO: Your code here...
	return &echo.Response{
		Message: "hello?",
	}, nil
}
  1. 编写 main.go 文件
package main

import (
	"context"
	"example/kitex_gen/echo"
	echo1 "example/kitex_gen/echo/echo"
	"fmt"
	"log"

	"github.com/cloudwego/kitex/client"
)

func main() {

	go func() {
		svr := echo1.NewServer(new(EchoImpl))
		err := svr.Run()

		if err != nil {
			log.Println(err.Error())
		}
	}()

	go func() {
		cli, err := echo1.NewClient("example", client.WithHostPorts("127.0.0.1:8888"))
		if err != nil {
			log.Fatal(err)
		}
		req := &echo.Request{Message: "我的第一个go请求"}
		rsp, err := cli.Echo(context.Background(), req)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(rsp)
	}()

	select {}
}
  1. 编译运行
PS F:\work\Go\golangStudy> go build 
PS F:\work\Go\golangStudy> ./example
2023/08/27 16:43:56.987455 server.go:83: [Info] KITEX: server listen at addr=[::]:8888
2023/08/27 16:43:57.045478 middlewares.go:130: [Warn] KITEX: auto retry retryable error, retry=1 error=remote or network error: get 
connection error: dial tcp 127.0.0.1:8888: i/o timeout
2023/08/27 16:43:57 接收到client请求:我的第一个go请求
message:"我的第一个go请求"