RPC

RPC(Remote Procedure Call: 远程过程调用)是一个计算机通信协议,该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。

gRPC(https://grpc.io/about)

gRPC是一个现代的开源高性能远程过程调用(RPC)框架,可以在任何环境中运行。它可以高效地连接数据中心中和跨数据中心的服务,并支持可插入的负载均衡、跟踪、健康检查和身份验证。它还适用于分布式计算,以连接设备、移动应用程序和浏览器到后端服务。

  1. 编写.proto文件,创建user.proto文件
syntax = "proto3";
option go_package = "./pb_protobuf/white_board;white_board";
package white_board;


message ImCreateBoardRequest {
    int64  id = 1;
    int32  page_count = 2;
    string room_no = 3;
    string doc_id = 4;

    string url_background = 5;
    string page_pos = 6;
    string file_url = 7;

    string uuid = 8;
    string nickname = 9;
}

message ImBoardInfoResponse{
    int64 id = 1;
    int32 page_count = 2;
    string doc_id = 3;
    string create_time = 4;
    string update_time = 5;
}

message ImGetBoardListByRoomNoResponse{
    repeated ImBoardInfoResponse BoardList = 1;
}
message ImGetBoardByIdRequest{
    int64   id = 1;
}
message ImGetBoardByRoomNoRequest{
    string   room_no = 1;
}
message ImBoardResponse{
}

service Board{
    rpc CreateBoard(ImCreateBoardRequest) returns (ImBoardInfoResponse){}
    rpc DeleteBoardById(ImGetBoardByIdRequest) returns (ImBoardResponse){}
    rpc DeleteBoardByRoomNo(ImGetBoardByRoomNoRequest) returns (ImBoardResponse){}
    rpc GetBoardListByRoomNo(ImGetBoardByRoomNoRequest) returns(ImGetBoardListByRoomNoResponse){}
}
syntax = "proto3";

其中第一行指定了我们使用 protocol buffers的版本,这里使用proto3.

option go_package = "path;name";

path:指定生成buffers文件的存放目录,name:给package定义别名,可以与package不一致。

项目目录结构

.
├── README.md
├── app
│   ├── api
│   │   ├── board.go
│   ├── dao
│   │   ├── board.go
│   ├── model
│   │   ├── board.go
│   └── service
│       ├── board.go
├── config
│   ├── config.go
│   ├── config.local.yml
│   ├── config.yml
│   ├── database
│   │   └── database.go
│   └── init.go
├── constant
│   └── constant.go
├── gateway
│   └── gateway.go
├── go.mod
├── go.sum
├── interfaces
│   ├── impls
│   │   ├── api_error.go
│   │   └── rpc.go
│   └── inter.go
├── log
│   └── debug.log
├── main.go
├── middleware
│   ├── auth.go
│   ├── grpc_exception.go
│   ├── recovery.go
│   └── zap_log.go
├── pb_protobuf
│   ├── white_board
│   │   ├── board.pb.go
├── protobuf
│   ├── white_board
│   │   ├── board.proto
├── s.sh
└── util
    ├── grpc.go
    └── util.go

编写shell脚本s.sh

# !bin/sh

proto_files=`find ./protobuf -maxdepth 2 -name "*.proto"`
for proto_file in ${proto_files[*]}
do
    echo  `protoc --proto_path=../ -I/$GOPATH/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.14.6/third_party/googleapis -I./protobuf --go_out=plugins=grpc:. ${proto_file}`
done

执行shell脚本文件./s.sh,在pb_protobuf文件下自动生成white_board文件夹,以及对应的board.pb.go文件。

测试
创建grpc服务端,新建serve.go文件

package main

import (
    "google.golang.org/grpc"
    "im-whiteboard/app/api"
    "im-whiteboard/pb_protobuf/white_board"

    "log"
    "net"
)

func main() {
    // 创建 Tcp 连接
    listener, err := net.Listen("tcp", ":8028")
    if err != nil {
        log.Fatalf("监听失败: %v", err)
    }

    // 创建gRPC服务
    grpcServer := grpc.NewServer()

    // Tester 注册服务实现者
    // 此函数在 board.pb.go 中,自动生成
    white_board.RegisterBoardServer(grpcServer,&api.Board{})

    err = grpcServer.Serve(listener)
    if err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

创建grpc 客户端client.go文件

package main

import (
    "context"
    im_service "im-whiteboard/pb_protobuf/white_board"
    "log"
    "google.golang.org/grpc"
)

// 定义请求地址
const (
    //ADDRESS string = constant.WhiteBoardMedia
    ADDRESS string = "127.0.0.1:8028"
)

// main 方法实现对 gRPC 接口的请求
func main() {
    conn, err := grpc.Dial(ADDRESS, grpc.WithInsecure())
    if err != nil {
        log.Fatalln("Can't connect: " + ADDRESS)
    }
    defer conn.Close()
    
    client := im_service.NewBoardClient(conn)
    res, err := client.GetBoardListByRoomNo(context.Background(),&im_service.ImGetBoardByRoomNoRequest{
        RoomNo:               "c0c6487ab418dabab0ccc9f32c36531b",
    })
    
    if err != nil {
        log.Fatalln("Do Format error:" + err.Error())
    }else{
        log.Println("res",res)
    }

}

然后分别启动 server 和 client端,项目涉及mysql,redis,mongodb这里不展开说明。

image.png

大功告成,此时客户端与服务端以及完成全部通讯。

image.png


杨帆
28 声望3 粉丝