一、生成 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(®istry.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 时指定注册到该 consulServiceName: "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()):带权重的「轮询」负载均衡策略,还有更多的策略可以选择