一、生成 kitex pb go

1.1 安装 kitex

go install github.com/cloudwego/kitex/tool/cmd/kitex@latest

当前版本为 v0.15.2, go 版本为 1.25.3

1.2 编写 proto 文件

proto 文件放在 kitex_gen/ 路径下

 1syntax = "proto3";
 2
 3package hello;
 4
 5option go_package = "hello/kitex_gen/hello";
 6
 7message HelloReq {
 8  string Name = 1;
 9}
10
11message HelloResp {
12  string RespBody = 1;
13}
14
15service HelloService {
16  rpc Hello(HelloReq) returns(HelloResp);
17}
  • option go_package = "hello/kitex_gen/hello";: 第一个 hello 代表在 gomod 中的 module 名,后面的 kitex_gen/hello 代表生成 pb go 文件的路径
  • service HelloService: 在 kitex_gen/hello 会再创一个目录,就是该 service 名的小写(本例中为 helloservice)作为一个 api 路径

1.2.1 生成 pb go

  • 基于 .proto 文件生成的命令:kitex -module hello kitex_gen/hello/hello.proto
    • -module hello: gomod 的 module 名
    • kitex_gen/hello/hello.proto: 我执行该命令是在项目根目录下,而这个是 proto 文件存放的路径
    • 可以再带上-service hello参数: service 名字,会生成一些 main 的调用以及 config,sh 脚本之类的(暂时不需要)
  • 原本目录结构如下:
    .
    ├── go.mod
    └── kitex_gen
        └── hello
            └── hello.proto
    
  • 之后生成的目录结构如下:
    .
    ├── go.mod
    └── kitex_gen
        └── hello
            ├── hello.pb.go
            ├── hello.proto
            └── helloservice
                ├── client.go
                ├── helloservice.go
                └── server.go
    
  • 生成代码后记得 go mod tidy

二、编写简单的 Server-Client demo

2.1 server

package main

import (
	"context"
	"fmt"
	"log"
	"net"

	"github.com/cloudwego/kitex/server"
	"github.com/example/kitex_gen/hello"
	"github.com/example/kitex_gen/hello/helloservice"
)

func main() {
	srv := helloservice.NewServer(&helloImpl{},
		server.WithServiceAddr(&net.TCPAddr{
			IP:   net.IPv4zero,
			Port: 9998,
		}),
	)
	if err := srv.Run(); err != nil {
		log.Fatal(err)
	}
}

type helloImpl struct {
}

func (h *helloImpl) Hello(_ context.Context, req *hello.HelloReq) (*hello.HelloResp, error) {
	return &hello.HelloResp{RespBody: fmt.Sprintf("hello %s", req.Name)}, nil
}
  • 这里是一个简单的 server demo,监听在本地的 9998 端口

2.2 client

 1package main
 2
 3import (
 4	"context"
 5	"log"
 6
 7	"github.com/cloudwego/kitex/client"
 8	"github.com/example/kitex_gen/hello"
 9	"github.com/example/kitex_gen/hello/helloservice"
10)
11
12func main() {
13	c, err := helloservice.NewClient("hello", client.WithHostPorts("0.0.0.0:9998"))
14	if err != nil {
15		log.Fatal(err)
16	}
17	resp, err := c.Hello(context.Background(), &hello.HelloReq{Name: "world"})
18	if err != nil {
19		log.Fatal(err)
20	}
21	log.Println(resp.RespBody)
22}
  • helloservice.NewClient("hello", client.WithHostPorts("0.0.0.0:9998")):因为是用的 ip+port 来找到 server 端,因此前面的第一个参数 "hello" 可以随便送,该参数是在服务发现时起到作用的

三、使用 consul 作为服务注册中心的案例

3.1 docker 部署一个简单的 consul

services:
  consul:
    image: consul:1.15.4
    ports:
      - "8500:8500"
    command: agent -dev -client=0.0.0.0

3.2 改造 server

 1package main
 2
 3import (
 4	"context"
 5	"fmt"
 6	"log"
 7	"net"
 8
 9	"github.com/cloudwego/kitex/pkg/registry"
10	"github.com/cloudwego/kitex/server"
11	"github.com/example/kitex_gen/hello"
12	"github.com/example/kitex_gen/hello/helloservice"
13	consul "github.com/kitex-contrib/registry-consul"
14)
15
16func main() {
17	register, err := consul.NewConsulRegister("127.0.0.1:8500")
18	if err != nil {
19		log.Fatal(err)
20	}
21
22	srv := helloservice.NewServer(&helloImpl{},
23		server.WithServiceAddr(&net.TCPAddr{
24			IP:   net.IPv4zero,
25			Port: 9998,
26		}),
27		server.WithRegistry(register),
28		server.WithRegistryInfo(&registry.Info{
29			ServiceName: "hello",
30			Weight:      100,
31		}),
32	)
33	if err := srv.Run(); err != nil {
34		log.Fatal(err)
35	}
36}
37
38type helloImpl struct {
39}
40
41func (h *helloImpl) Hello(_ context.Context, req *hello.HelloReq) (*hello.HelloResp, error) {
42	return &hello.HelloResp{RespBody: fmt.Sprintf("hello %s", req.Name)}, nil
43}
  • consul.NewConsulRegister("127.0.0.1:8500"):初始化 consul 注册客户端
  • server.WithRegistry(register):在新建 srv 时指定注册到该 consul
  • ServiceName: "hello":consul 中 service 名称
  • Weight: 100:权重值,必须要大于 0

3.3 改造 client

 1package main
 2
 3import (
 4	"context"
 5	"log"
 6	"time"
 7
 8	"github.com/cloudwego/kitex/client"
 9	"github.com/example/kitex_gen/hello"
10	"github.com/example/kitex_gen/hello/helloservice"
11	consul "github.com/kitex-contrib/registry-consul"
12)
13
14func main() {
15	resolver, err := consul.NewConsulResolver("127.0.0.1:8500")
16	if err != nil {
17		log.Fatal(err)
18	}
19	c, err := helloservice.NewClient("hello", client.WithResolver(resolver))
20	if err != nil {
21		log.Fatal(err)
22	}
23	resp, err := c.Hello(context.Background(), &hello.HelloReq{Name: "world"})
24	if err != nil {
25		log.Fatal(err)
26	}
27	log.Println(resp.RespBody)
28}
  • consul.NewConsulResolver("127.0.0.1:8500"):初始化 consul 作为服务发现客户端
  • helloservice.NewClient("hello":指定为 consul 中的 service 名称
  • client.WithResolver(resolver):新建 client 时指定作为 resolver

四、做 loadBalance

4.1 改造 server

server 只需要启动多个同 service 名实例(本地环境绑定在不同 port),调整权重值

4.2 改造 client

20	c, err := helloservice.NewClient("hello", client.WithResolver(resolver),
21		client.WithLoadBalancer(loadbalance.NewWeightedRoundRobinBalancer()))
  • client.WithLoadBalancer(loadbalance.NewWeightedRoundRobinBalancer()):带权重的「轮询」负载均衡策略,还有更多的策略可以选择