golang之gRPC

相关链接:

grpc:

https://grpc.io/docs/languages/go/quickstart/

 

protobuf:

https://protobuf.dev/programming-guides/proto3/

 

protobuf语法:

 

示例:

syntax = "proto3";


// 声明请求参数
message SearchRequest {
  string query = 1;
  int32 page = 2;
  int32 page_size = 3;
}

 

说明:

  1. 声明proto文件使用的语法版本,目前是proto2 和proto3, 如果不声明的话, 他会默认使用protocol2,并且语法声明必须是在非空格的第一行
  2. 声明数据使用message,里面定义参数的类型,名称,索引位置(注意从1开始), 以分号结尾
  3. 索引的取值范围为:1 ~ 536,870,911
  4. 索引的值必须保证唯一性

 

枚举类型:

enum Status {
  STATUS_UNSPECIFIED = 0;
  STATUS_OK = 1;
  STATUS_FAIL = 2;
}

 

通过enum关键字定义枚举类型,

  1. 枚举是一个Int32类型
  2. 第一个枚举类必须从0开始,可以使用XXX_UNSPECIFIED作为占位符
  3. 不推荐出现负数

 

 

 

字段标签:

optional:

 

  • FileOptions —— 文件级别
  • MessageOptions —— 消息级别
  • FieldOptions —— 字段级别
  • ServiceOptions —— service级别
  • MethodOptions —— method级别

 

 

repeated:只有标量,枚举,message类型可以被修饰使用

标识被重复人一次(包括0次),可标识当前修饰类型的变长数组

// repeated
message RepeatedMessage {
    repeated SearchRequest requests = 1;
    repeated Status status = 2;
    repeated int32 number = 3;
}

 

 

 

map:

message MapMessage{
    map<string, string> message = 1;
    map<string, SearchRequest> request = 2;
}

 

 

any:

any类型可以包含一个不需要指定类型的任意的序列化消息。要使用any类型,需要import google/protobuf/any.protoany类型字段的encode/decode交由各语言的运行时各自实现,例如在Go语言中可以这样读写any类型的字段:

 

...
import "google/protobuf/any.proto";
...
message AnyMessage {
    string message = 1;
    google.protobuf.Any details = 2;
}
...

 

 

oneof

Protocol buffer compiler安装

 

【源码】

自定义安装->linux/mac

# 1.卸载老版编译器
apt remove protobuf-compiler
# 2.下载并解压
wget https://github.com/protocolbuffers/protobuf/releases/download/v23.0/protoc-23.0-linux-x86_64.zip
unzip protoc-23.0-linux-x86_64.zip -d protoc-23.0
# 3.设置环境变量
export PATH="$PATH:/usr/local/src/protoc-23.0/bin"

# 4.测试
protoc --version
# libprotoc 23.0

 

window需要到github上下载压缩包到本地并将bin目录下的protoc.exe文件加入环境变量即可

https://github.com/protocolbuffers/protobuf/releases/tag/v23.4

 

 

 

Go 支持

# 1.安装
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

# 2.添加环境变量
export PATH="$PATH:$(go env GOPATH)/bin"

 

 

相关命令:

# 根据.proto文件,生成xxx.pb.go 和 xxx_grpc.pb.go
protoc --go_out=. --go_opt=paths=source_relative \
    --go-grpc_out=. --go-grpc_opt=paths=source_relative \
    helloworld/helloworld.proto

protoc -I=pb \
  --go_out=pb --go_opt=paths=source_relative \
  --go-grpc_out=pb --go-grpc_opt=paths=source_relative \
pb/addsrv.proto

 

 

Demo示例:
1.初始化项目

mkdir user-service
cd user-service

go mod init user-service

mkdir service

2.在Service目录中添加users.proto文件

syntax = "proto3";

package service;

option go_package="user-service/service";

// 请求消息
message UserRequest {
  string email = 1;
  int32  id = 2;
}

// 响应数据
message User {
  string id = 1;
  string first_name = 2;
  string last_name = 3;
  int32 age = 4;
}

message UserResponse {
  User user = 1;
}

service Users {
  rpc GetUser(UserRequest) returns (UserResponse) {}
}

proto同级目录下生成pb.go 和grpc.pb.go文件

protoc --go_out=. --go-opt=paths=source-relative --go-grpc_out=. --go-grpc_opt=paths=source-relative users.proto

 

3.新增服务文件 user-service/server/server.go

package main

import (
    "context"
    "google.golang.org/grpc"
    "log"
    "net"
    "os"

    users "user-service/service"
)

func main() {
    addr := os.Getenv("LISTEN_ADDR")
    if len(addr) == 0 {
        addr = ":5001"
    }

    listen, err := net.Listen("tcp", addr)
    if err != nil {
        log.Fatal(err)
    }

    s := grpc.NewServer()
    registerServices(s)

    log.Fatal(startServer(s, listen))
}

type userService struct {
    users.UnimplementedUsersServer
}

func (s *userService) GetUser(ctx context.Context, in *users.UserRequest) (*users.UserResponse, error) {
    log.Printf("收到数据. 邮件:%s  id:%d", in.Email, in.Id)

    // 返回响应数据
    u := users.User{
        Id:        "user-id",
        FirstName: "first name",
        LastName:  "last name",
        Age:       10,
    }
    return &users.UserResponse{
        User: &u,
    }, nil
}

func registerServices(s *grpc.Server) {
    users.RegisterUsersServer(s, &userService{})
}

func startServer(s *grpc.Server, l net.Listener) error {
    return s.Serve(l)
}

 

4.新增客户端user-service/client/main.go文件

package main

import (
    "context"
    "google.golang.org/grpc"
    "log"
    "os"
    users "user-service/service"
)

func main() {
    if len(os.Args) != 2 {
        log.Fatal("缺少grpc服务地址")
    }

    addr := os.Args[1]
    // 初始化grpc连接
    conn, err := setupGrpcConn(addr)
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()
    // 获取服务客户端实例化
    c := getUserServiceClient(conn)
    // 调用指定方法
    user, err := getUser(c, &users.UserRequest{
        Email: "liaotiam@126.com",
        Id:    110,
    })
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("响应数据: %v", user)
}

func getUser(client users.UsersClient, u *users.UserRequest) (*users.UserResponse, error) {
    return client.GetUser(context.Background(), u)
}

func getUserServiceClient(conn *grpc.ClientConn) users.UsersClient {
    return users.NewUsersClient(conn)
}

func setupGrpcConn(addr string) (*grpc.ClientConn, error) {
    return grpc.DialContext(
        context.Background(),
        addr,
        grpc.WithInsecure(),
        grpc.WithBlock(),
    )
}

分别启动服务端与客户端, 查看是否调用成功

 

posted @ 2023-10-10 16:57  X-Wolf  阅读(50)  评论(0编辑  收藏  举报