ketix基础调用入门演示1(gateway和worker相互流转)

在内部各个服务组件之间相互调用,离不开高性能Rpc。

除了gRpc,bRpc以外,在go的技术栈里还有一个开源好用的组件。

目录

[Kitex 框架介绍](#Kitex 框架介绍)

核心特点与优点

适用场景

支持消息协议(编解码Codec)

传输层协议(Transport)

服务发现种类(Resolver/Registry)

内置调度(负载均衡)算法

自定义调度(自定义负载均衡)

简短小结

建立一个目录,比如叫demo-im

创建协议:im.proto

编译方法:

写一个gateway.go模拟入口网关,从屏幕读取输入,透传给后端:

再一个后端的worker,提供服务,服务里提供方法:SendMessage

执行命令


Kitex 框架介绍

Kitex 是字节跳动 CloudWeGo 开源的Go语言高性能RPC微服务框架,广泛用于构建大规模分布式服务,兼顾高性能、多协议、强扩展能力,内置完整微服务治理能力。

核心特点与优点

  1. 高性能IO :底层基于自研 netpoll 网络库,相比Go原生net,降低goroutine开销,高并发场景下延迟更低、吞吐更高,支持连接多路复用。
  2. 强可扩展:全链路预留扩展接口,编解码、传输、服务发现、负载均衡、中间件、监控均可自定义实现,不与特定基础设施强绑定。
  3. IDL代码生成 :内置kitex命令行工具,读取Thrift/Protobuf IDL,一键生成服务端、客户端桩代码与项目脚手架,减少重复编码工作。
  4. 完备服务治理:原生支持注册发现、负载均衡、熔断、限流、重试、超时、链路追踪、日志、故障诊断,组件按需开启,非强制侵入。
  5. 多协议兼容:一套框架同时支持多种RPC消息、传输协议,可对接gRPC、Thrift生态,也支持自定义私有协议。

适用场景

  1. Go语言后端微服务集群,内部服务之间RPC调用;
  2. 高吞吐、低延迟业务:IM网关、消息服务、网关、数据计算、业务中台;
  3. 需要多协议互通:和Thrift、gRPC服务互相调用;
  4. 云原生环境,支持xDS无代理(Proxyless)模式对接Istio服务网格;
  5. 需要深度定制RPC逻辑:自定义负载、私有编解码、特殊元信息透传。

不适合:浏览器前端直接访问;面向公网对外暴露业务(优先HTTP网关做接入)。

支持消息协议(编解码Codec)

  • Thrift:支持Framed / Buffered二进制,兼容多语言Thrift体系;支持Oneway单向调用。
  • Kitex‑Protobuf :Kitex自研protobuf消息协议,格式风格类似Thrift,配合TTHeader做服务治理,我们之前demo用的就是这套,不走HTTP2/gRPC
  • gRPC‑Protobuf:标准gRPC协议,HTTP2传输,支持双向Streaming流调用,可以和标准gRPC服务互通。
  • 扩展点:可实现 remote.Codec 接口,接入自定义私有编解码。

传输层协议(Transport)

  1. TTHeader / TTHeaderFramed :Kitex私有TCP传输帧,用于透传trace、服务名、集群、环境等治理元数据,搭配Thrift、Kitex‑Protobuf使用,普通内部微服务最常用
  2. HTTP2:主要用于gRPC协议,提供流能力。
  3. PurePayload:裸payload,无治理header,仅透传业务二进制。

交互调用模型:PingPong普通请求‑响应、Oneway单向调用、Streaming双向流。

服务发现种类(Resolver/Registry)

Kitex接口化设计,支持多种注册中心,社区扩展丰富:

  1. Etcd:最常用,适合K8s/自建集群;
  2. Consul
  3. Nacos
  4. 静态Resolver:硬编码节点列表,测试环境、demo开发使用;
  5. HTTP Resolver:通过HTTP接口获取实例列表;
  6. xDS Resolver:对接Istio控制平面,Proxyless无代理服务网格模式;
  7. 自定义:实现discovery.Resolver接口,对接自研注册中心。

区分:Registry 服务端用于把自己服务实例注册到注册中心;Resolver客户端用来拉取下游实例列表。

内置调度(负载均衡)算法

算法 说明
WeightedRoundRobin(默认) 平滑加权轮询,均衡控制inflight并发请求,生产默认推荐
InterleavedWeightedRoundRobin 交错加权轮询,实例数量巨大场景优化
WeightedRandom 加权随机,按实例权重做随机选择
AliasMethod 别名算法,初始化后O(1)选取,高实例数性能更优
ConsistentHash 一致性哈希 相同key固定路由到同一实例,适合本地缓存、状态服务场景

自定义调度(自定义负载均衡)

Kitex完全支持自行编写负载均衡策略:

  1. 实现 loadbalance.LoadBalancer 接口,输入服务发现返回的实例列表,根据自定义规则选出目标endpoint;
  2. Client初始化时通过 client.WithLoadBalancer(yourLB) 注入自定义调度器;
  3. 可以实现机房优先、就近调度、基于实时延迟调度、灰度流量、自定义权重逻辑等。

完整扩展点:除LB之外,还可以自定义Codec、MetaHandler、中间件、Registry、Resolver、Tracer监控上报。

简短小结

Kitex 以netpoll为底层,TTHeaderFramed+Kitex‑Protobuf是内部Go微服务最主流组合;服务发现可对接etcd/nacos/consul/xDS;内置多种负载均衡,并且支持完全自研调度逻辑,适合IM、网关这类高并发分布式服务开发。

那么今天开始一个最基础的入门调用练习,使用大家都熟悉的proto方式,

建立一个目录,比如叫demo-im

复制代码
demo-im/
├── go.mod                    // module demo-im
├── im.proto                  // 放在根目录或 birdproto 目录下
├── birdproto/                // 新建的子目录
│   └── im.proto              // (推荐) proto 文件放在这里
    gen/                      // 生成的代码放在这里
│   └── im/                   // 对应 go_package 中的 im
│       ├── im.pb.go
│       └── im.kitex.go
├── gateway/
│   └── main.go
└── worker/
    └── main.go

创建协议:im.proto

复制代码
syntax = "proto3";

package im;
option go_package = "demo-im/bird-proto/gen/im;im";

message SendMessageReq {
  string content = 1;
}

message SendMessageResp {
  int32 code = 1;
  string message = 2;
}

message PushMessageReq {
  string content = 1;
}

message PushMessageResp {
  int32 code = 1;
  string message = 2;
}

service ImWorkerService {
  rpc SendMessage(SendMessageReq) returns (SendMessageResp);
}

service ImGatewayService {
  rpc PushMessage(PushMessageReq) returns (PushMessageResp);
}

编译方法:

bash 复制代码
//    # 安装kitex工具
//    go install github.com/cloudwego/kitex/tool/cmd/kitex@latest
//    kitex0.16.3
//    # 生成worker服务代码(ImWorkerService)
//    D:\go_work\bin\kitex -type protobuf -service ImWorkerService -gen-path ./ im.proto
//
//    # 生成gateway服务代码(ImGatewayService)
//    D:\go_work\bin\kitex -type protobuf -service ImGatewayService -gen-path ./ im.proto

写一个gateway.go模拟入口网关,从屏幕读取输入,透传给后端:

Go 复制代码
package main

import (
	"bufio"
	"context"
	"demo-im/bird-proto/gen/im"
	"demo-im/bird-proto/gen/im/imgatewayservice"
	"demo-im/bird-proto/gen/im/imworkerservice"
	"fmt"
	"log"
	"net"
	"os"
	"strings"
	"time"

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

// 实现 Gateway 服务的 PushMessage 方法
type GatewayServiceHandler struct{}

func (h *GatewayServiceHandler) PushMessage(ctx context.Context, req *im.PushMessageReq) (*im.PushMessageResp, error) {
	fmt.Printf("\n[Gateway] 收到 Worker 推送: %s\n", req.Content)
	return &im.PushMessageResp{
		Code:    0,
		Message: "Gateway received: " + req.Content,
	}, nil
}

func main() {
	// 1. 启动 Gateway RPC 服务(供 Worker 回调)
	addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:8888")
	svr := imgatewayservice.NewServer(
		new(GatewayServiceHandler),
		server.WithServiceAddr(addr),
	)

	go func() {
		fmt.Println("[Gateway] RPC 服务启动在 127.0.0.1:8888")
		if err := svr.Run(); err != nil {
			log.Fatalf("[Gateway] 服务启动失败: %v", err)
		}
	}()

	// 2. 创建 Worker 服务的客户端
	workerClient := imworkerservice.MustNewClient(
		"ImWorkerService",
		client.WithHostPorts("127.0.0.1:9999"),
	)
	fmt.Println("[Gateway] Worker 客户端已创建")

	// 3. 命令行输入循环
	scanner := bufio.NewScanner(os.Stdin)
	fmt.Println("\n========================================")
	fmt.Println("  IM Demo - Gateway")
	fmt.Println("  输入消息后按回车,Worker 会处理并回调")
	fmt.Println("  输入 'exit' 退出")
	fmt.Println("========================================")

	for {
		fmt.Print("\n📝 请输入消息: ")
		if !scanner.Scan() {
			break
		}
		input := strings.TrimSpace(scanner.Text())
		if input == "exit" {
			fmt.Println("[Gateway] 退出")
			break
		}
		if input == "" {
			continue
		}

		// 4. 调用 Worker 的 SendMessage RPC
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		resp, err := workerClient.SendMessage(ctx, &im.SendMessageReq{
			Content: input,
		})
		cancel()

		if err != nil {
			fmt.Printf("[Gateway] 调用 Worker 失败: %v\n", err)
			continue
		}

		fmt.Printf("[Gateway] Worker 响应: code=%d, msg=%s\n", resp.Code, resp.Message)
	}
}

再一个后端的worker,提供服务,服务里提供方法:SendMessage

Go 复制代码
package main

import (
	"context"
	"demo-im/bird-proto/gen/im"
	"demo-im/bird-proto/gen/im/imgatewayservice"
	"demo-im/bird-proto/gen/im/imworkerservice"
	"fmt"
	"log"
	"net"
	"time"

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

// 实现 Worker 服务的 SendMessage 方法
type WorkerServiceHandler struct {
	gatewayClient imgatewayservice.Client
}

func (h *WorkerServiceHandler) SendMessage(ctx context.Context, req *im.SendMessageReq) (*im.SendMessageResp, error) {
	fmt.Printf("\n[Worker] 收到来自 Gateway 的消息: %s\n", req.Content)

	// 1. 模拟处理(等待1秒)
	time.Sleep(1 * time.Second)
	fmt.Printf("[Worker] 消息处理完成: %s\n", req.Content)

	// 2. 调用 Gateway 的 PushMessage RPC(添加前缀后回调)
	pushContent := "[Worker-Processed] " + req.Content
	fmt.Printf("[Worker] 正在回调 Gateway: %s\n", pushContent)

	pushResp, err := h.gatewayClient.PushMessage(ctx, &im.PushMessageReq{
		Content: pushContent,
	})
	if err != nil {
		fmt.Printf("[Worker] 回调 Gateway 失败: %v\n", err)
		return &im.SendMessageResp{
			Code:    1,
			Message: "回调失败: " + err.Error(),
		}, nil
	}

	fmt.Printf("[Worker] Gateway 回调响应: %s\n", pushResp.Message)

	return &im.SendMessageResp{
		Code:    0,
		Message: "消息已处理并推送",
	}, nil
}

func main() {
	// 1. 创建 Gateway 服务的客户端(用于回调)
	gatewayClient := imgatewayservice.MustNewClient(
		"ImGatewayService",
		client.WithHostPorts("127.0.0.1:8888"),
	)
	fmt.Println("[Worker] Gateway 客户端已创建")

	// 2. 启动 Worker RPC 服务
	addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:9999")
	svr := imworkerservice.NewServer(
		&WorkerServiceHandler{
			gatewayClient: gatewayClient,
		},
		server.WithServiceAddr(addr),
	)

	fmt.Println("[Worker] RPC 服务启动在 127.0.0.1:9999")
	fmt.Println("[Worker] 等待 Gateway 调用...")

	if err := svr.Run(); err != nil {
		log.Fatalf("[Worker] 服务启动失败: %v", err)
	}
}

执行命令

bash 复制代码
go mod init
go mod tidy

# 一个cmd内
go run ./gateway.go

# 一个cmd内
go run ./worker.go

在 Gateway 终端输入任意消息(如 hello),观察两个终端的输出:

Gateway 终端:

text

复制代码
========================================
  IM Demo - Gateway
  输入消息后按回车,Worker 会处理并回调
  输入 'exit' 退出
========================================

📝 请输入消息: hello
[Gateway] 收到 Worker 推送: [Worker-Processed] hello
[Gateway] Worker 响应: code=0, msg=消息已处理并推送

Worker 终端:

text

复制代码
[Worker] 收到来自 Gateway 的消息: hello
[Worker] 消息处理完成: hello
[Worker] 正在回调 Gateway: [Worker-Processed] hello
[Worker] Gateway 回调响应: Gateway received: [Worker-Processed] hello

到此为止,我们已经能用Ketix写RPC调用了,注意这里示范使用了其内置的封装协议比grpc更高效。

未完待续,后文我们测试使用etcd做服务发现。