09.gingrpcnacos
# 00.测试效果
1)拉取模块到本地
go mod tidy
1
2)生成protobuf文件
cd proto
protoc -I=. common/common.proto --go_out=plugins=grpc:. ./hello/helloTest.proto
1
2
2
3)运行server
- 修改
helloService/Configs/configs.yml
端口号为5502
- 修改
cd helloService
go run main.go
1
2
2
4)运行client
cd allClient
go run main.go
1
2
2
5)postman测试负载均衡
http://127.0.0.1:9009/api/v1/helloTest
1
6)负载均衡效果截图
# 01.proto文件夹
cd proto
protoc -I=. common/common.proto --go_out=plugins=grpc:. ./hello/helloTest.proto
1
2
2
# 1.1 proto/common/common.proto
公共 .proto文件
syntax = "proto3"; // 指定proto版本
package common; // 指定包名
// 指定生成位置
option go_package = "./protoGo";
// 通用参数
message CommonRequest {
string CommonRequest_Id = 1;
string CommonRequest_Name = 2;
string CommonRequest_Ip = 3;
string CommonRequest_CreateTime = 4;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 1.2 proto/hello/helloTest.proto
hello服务的 .proto文件
syntax = "proto3"; // 指定proto版本
package hello; // 指定默认包名
// 指定生成位置
option go_package = "./protoGo";
import "common/common.proto";
// 定义Hello服务
service Hello {
// 定义SayHello方法
rpc SayHello(HelloRequest) returns (HelloResponse) {}
}
// HelloRequest 请求结构
message HelloRequest {
string name = 1;
string allParmas = 2;
common.CommonRequest commonRequest = 3;
}
// HelloResponse 响应结构
message HelloResponse {
string message = 1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 02.gRPC服务端
# 2.1 helloService/main.go
package main
import (
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"log"
setting "nacos-grpc-gin/helloService/Configs"
ClientInitial "nacos-grpc-gin/helloService/Initial"
"nacos-grpc-gin/helloService/Provider"
pb "nacos-grpc-gin/proto/protoGo"
"net"
)
func main() {
// 加载配置
setting.Init()
// 将服务注册到 nacos
ClientInitial.RegisterNacos()
// 获取配置信息中 ip:port
Address := setting.Conf.Address + ":" + setting.Conf.Port
log.Println("启动grpc")
listen, err := net.Listen("tcp", Address)
if err != nil {
grpclog.Fatalf("Failed to listen: %v", err)
}
// 实例化grpc Server
s := grpc.NewServer()
// 注册HelloService
pb.RegisterHelloServer(s, HelloProvider.HelloService)
fmt.Println(setting.Conf.Address)
log.Print("监听 " + Address)
s.Serve(listen)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 2.2 viper配置文件
# 2.2.1helloService/Configs/configs.yml
address: "127.0.0.1"
port: 5501
mysql:
host: "127.0.0.1"
port: 3306
user: "root"
password: "1"
dbname: "grpctest"
max_open_conns: 200
max_idle_conns: 50
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 2.2.2 helloService/Configs/configLoad.go
package setting
import (
"fmt"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
var Conf = new(AppConfig)
type AppConfig struct {
Address string `mapstructure:"address"`
Port string `mapstructure:"port"`
*MySQLConfig `mapstructure:"mysql"`
}
type MySQLConfig struct {
Host string `mapstructure:"host"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
DB string `mapstructure:"dbname"`
Port int `mapstructure:"port"`
MaxOpenConns int `mapstructure:"max_open_conns"`
MaxIdleConns int `mapstructure:"max_idle_conns"`
}
func Init() (err error) {
viper.SetConfigFile("./Configs/configs.yml")
err = viper.ReadInConfig() // 读取配置信息
if err != nil {
// 读取配置信息失败
fmt.Printf("viper.ReadInConfig failed, err:%v\n", err)
return
}
// 把读取到的配置信息反序列化到 Conf 变量中
if err := viper.Unmarshal(Conf); err != nil {
fmt.Printf("viper.Unmarshal failed, err:%v\n", err)
}
viper.WatchConfig()
viper.OnConfigChange(func(in fsnotify.Event) {
fmt.Println("配置文件修改了...")
if err := viper.Unmarshal(Conf); err != nil {
fmt.Printf("viper.Unmarshal failed, err:%v\n", err)
}
})
return
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 2.3 将服务注册到 nacos
# 2.3.1 helloService/Initial/nacosregister.go
package ClientInitial
import (
"fmt"
"github.com/nacos-group/nacos-sdk-go/clients"
"github.com/nacos-group/nacos-sdk-go/common/constant"
"github.com/nacos-group/nacos-sdk-go/vo"
setting "nacos-grpc-gin/helloService/Configs"
"strconv"
)
func RegisterNacos() {
// 创建clientConfig
clientConfig := constant.ClientConfig{
//NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", // 如果需要支持多namespace,我们可以场景多个client,它们有不同的NamespaceId。当namespace是public时,此处填空字符串。
TimeoutMs: 5000,
NotLoadCacheAtStart: true,
LogDir: "/tmp/nacos/log",
CacheDir: "/tmp/nacos/cache",
RotateTime: "1h",
MaxAge: 3,
LogLevel: "debug",
}
// 至少一个ServerConfig
serverConfigs := []constant.ServerConfig{
{
IpAddr: "127.0.0.1",
ContextPath: "/nacos",
Port: 8848,
Scheme: "http",
},
}
// 创建服务发现客户端 (推荐)
namingClient, err := clients.NewNamingClient(
vo.NacosClientParam{
ClientConfig: &clientConfig,
ServerConfigs: serverConfigs,
},
)
if err != nil {
fmt.Println("clients.NewNamingClient err,", err)
}
intPort, _ := strconv.Atoi(setting.Conf.Port)
port := uint64(intPort)
fmt.Println(55, setting.Conf.Address, setting.Conf.Port)
success, err := namingClient.RegisterInstance(vo.RegisterInstanceParam{
Ip: setting.Conf.Address,
Port: port,
ServiceName: "demo.go",
Weight: 10,
Enable: true,
Healthy: true,
Ephemeral: true,
Metadata: map[string]string{"idc": "shanghai"},
ClusterName: "cluster-a", // 默认值DEFAULT
GroupName: "group-a", // 默认值DEFAULT_GROUP
})
if !success {
return
} else {
fmt.Println("namingClient.RegisterInstance Success")
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# 2.4 gRPC服务
# 2.4.1 helloService/Provider/helloProvider.go
package HelloProvider
import (
"encoding/json"
"fmt"
"golang.org/x/net/context"
"log"
pb "nacos-grpc-gin/proto/protoGo" // 引入编译生成的包
)
// 定义helloService并实现约定的接口
type helloService struct{}
// HelloService Hello服务
var HelloService = helloService{}
// SayHello 实现Hello服务接口
func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
//log.Printf("服务端接受数据: %v \n", in)
//log.Printf("ctx: %v \n", ctx)
//log.Printf("allParams: %v \n", in.AllParmas)
// 将字节切片映射到指定map上
// key:string类型,value:interface{} 类型能存任何数据类型
var jsonInput map[string]interface{}
err := json.Unmarshal([]byte(in.AllParmas), &jsonInput)
if err != nil {
log.Printf("json转义失败: %v \n", err)
}
// 打印对象结构
log.Printf("jsonInput: %v \n", jsonInput)
log.Printf("es: %v, \n", jsonInput["es"])
resp := new(pb.HelloResponse)
resp.Message = fmt.Sprintf("Hello %s.", in.Name)
return resp, nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 03.gRPC客户端
# 3.1 allClient/main.go
package main
import (
"github.com/gin-gonic/gin"
HelloRoute "nacos-grpc-gin/allClient/helloClient/Route"
)
func main() {
r := gin.New()
// 创建路由组
r = HelloRoute.InitRouter(r)
_ = r.Run(":9009")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 3.2 allClient/Utils/common.go
公共模块
package ClientUtils
import (
"github.com/gin-gonic/gin"
)
// 统一返回体格式
type Res struct {
Code int `json:"code"`
Data interface{} `json:"data"`
Note string `json:"note"`
}
// 对所有分会进行统一封装
func ReturnMsg(ctx *gin.Context, code int, data interface{}, note string) {
ctx.JSON(200, Res{
Code: code,
Data: data,
Note: note,
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 3.3 gRPC客户端
# 3.3.1 allClient/helloClient/Route/helloRoute.go
- 路由
package HelloRoute
import (
"github.com/gin-gonic/gin"
. "nacos-grpc-gin/allClient/helloClient/Controller"
)
func InitRouter(r *gin.Engine) *gin.Engine {
v1 := r.Group("/api/v1")
{
v1.POST("/helloTest", TestClient)
}
return r
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3.3.2 allClient/helloClient/Model/helloModel.go
- gRPC请求参数的结构体
package HelloModel
type Hello struct {
Name string `json:"name"`
Game int `json:"game"`
Kafka string `json:"kafka"`
Redis string `json:"redis"`
Es struct{} `json:"es"`
Sql struct{} `json:"sql"`
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 3.3.3 allClient/helloClient/Controller/helloGin.go
- gin服务的视图函数
package helloClientController
import (
"encoding/json"
"github.com/gin-gonic/gin"
"log"
ClientUtils "nacos-grpc-gin/allClient/Utils"
"nacos-grpc-gin/allClient/helloClient/Model"
pb "nacos-grpc-gin/proto/protoGo"
"net/http"
)
// @Router /api/v1/helloTest [post]
func TestClient(c *gin.Context) {
// Set up a connection to the server.
log.Printf("Test方法 \n")
conn, err := InitGrpc()
if err != nil {
log.Fatalf("服务端链接失败: %v", err)
}
log.Printf("hello服务端链接成功 \n")
client := pb.NewHelloClient(conn)
var input HelloModel.Hello
if err := c.ShouldBindJSON(&input); err != nil {
ClientUtils.ReturnMsg(c, 402, nil, err.Error())
return
}
log.Printf("接口入参: %v \n", input) // 接口入参: {zhangsan 0 {} {}}
name := input.Name
// 联系服务器并打印它的响应
data, _ := json.Marshal(input)
// 接口入参json: {"name":"zhangsan","game":0,"kafka":"","redis":"","es":{},"sql":{}}
log.Printf("接口入参json: %v \n", string(data))
req := &pb.HelloRequest{Name: name, AllParmas: string(data)}
res, err := client.SayHello(c, req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
ClientUtils.ReturnMsg(c, 400, nil, err.Error())
return
} else {
ClientUtils.ReturnMsg(c, 200, res.Message, res.Message)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 3.3.4 allClient/helloClient/Controller/grpcInitial.go
- gRPC服务的负载均衡地址
package helloClientController
import (
"fmt"
"github.com/nacos-group/nacos-sdk-go/clients"
"github.com/nacos-group/nacos-sdk-go/common/constant"
"github.com/nacos-group/nacos-sdk-go/vo"
"google.golang.org/grpc"
"log"
"strconv"
)
func InitGrpc() (*grpc.ClientConn, error) {
addr := getNacosServerIp()
//conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
fmt.Println("addr ---->", addr)
conn, err := grpc.Dial(addr, grpc.WithInsecure())
if err != nil {
log.Fatalf("服务端链接失败: %v", err)
}
//ServiceConnection = conn
log.Printf("hello服务端链接成功 \n")
return conn, err
}
func getNacosServerIp() (string) {
// 创建clientConfig
clientConfig := constant.ClientConfig{
//NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", // 如果需要支持多namespace,我们可以场景多个client,它们有不同的NamespaceId。当namespace是public时,此处填空字符串。
TimeoutMs: 5000,
NotLoadCacheAtStart: true,
LogDir: "/tmp/nacos/log",
CacheDir: "/tmp/nacos/cache",
RotateTime: "1h",
MaxAge: 3,
LogLevel: "debug",
}
// 至少一个ServerConfig
serverConfigs := []constant.ServerConfig{
{
IpAddr: "127.0.0.1",
ContextPath: "/nacos",
Port: 8848,
Scheme: "http",
},
}
// 创建服务发现客户端的另一种方式 (推荐)
namingClient, err := clients.NewNamingClient(
vo.NacosClientParam{
ClientConfig: &clientConfig,
ServerConfigs: serverConfigs,
},
)
if err != nil {
fmt.Println("clients.NewNamingClient error")
}
//// SelectAllInstance可以返回全部实例列表,包括healthy=false,enable=false,weight<=0
//instances, err := namingClient.SelectAllInstances(vo.SelectAllInstancesParam{
// ServiceName: "demo.go",
// GroupName: "group-a", // 默认值DEFAULT_GROUP
// Clusters: []string{"cluster-a"}, // 默认值DEFAULT
//})
// SelectOneHealthyInstance将会按加权随机轮询的负载均衡策略返回一个健康的实例
// 实例必须满足的条件:health=true,enable=true and weight>0
instance, err := namingClient.SelectOneHealthyInstance(vo.SelectOneHealthInstanceParam{
ServiceName: "demo.go",
GroupName: "group-a", // 默认值DEFAULT_GROUP
Clusters: []string{"cluster-a"}, // 默认值DEFAULT
})
addr := instance.Ip + ":" + strconv.Itoa(int(instance.Port))
fmt.Println(addr)
return addr
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
上次更新: 2024/3/13 15:35:10