Go:GRPC

时间:Aug. 17, 2021 分类:

目录:

安装依赖

mac安装依赖

$ brew install protobuf
$ protoc --version
libprotoc 3.17.2

安装需要的包

go get github.com/golang/protobuf/proto 
go get github.com/golang/protobuf/protoc-gen-go
go get google.golang.org/grpc

如果安装不了grpc

cd $GOPATH/src
git clone https://github.com/grpc/grpc-go ./google.golang.org/grpc
git clone https://github.com/golang/net.git ./golang.org/x/net
git clone https://github.com/google/go-genproto.git ./google.golang.org/genproto
git clone https://github.com/golang/text.git ./golang.org/x/text
go install google.golang.org/grpc

创建proto文件

proto文件是微服务交互的基本

创建一个简单的demo:spider.proto,格式可以参考proto格式

syntax = "proto3";  // 协议为proto3

package spider;  // 包名

// 发送请求
message SendAddress {
    // 发送的参数字段
    // 参数类型 参数名 标识号(不可重复)
    string address = 1;  // 要请求的地址
    string method = 2;  // 请求方式
}

// 返回响应
message GetResponse {
    // 接收的参数字段
    // 参数类型 参数名 标识号
    int32 httpCode = 1;  // http状态码
    string response = 2;  // 返回体
}

// 定义服务,可定义多个服务,每个服务可多个接口
service GoSpider {
    // rpc请求 请求的函数 (发送请求参数) returns (返回响应的参数)
    rpc GetAddressResponse (SendAddress) returns (GetResponse);
}

生成.bp.go文件

protoc --go_out=plugins=grpc:./ ./spider.proto

生成的spider.pb.go文件为server和client通信的协议,业务代码不在这里

如果两端都是go,就是使用相同的pb.go模板文件即可,其他语言可以用对应语言生成对应的pd

server端

package main

import (
    "context"
    "google.golang.org/grpc"
    "google.golang.org/grpc/reflection"
    "io/ioutil"
    "net"
    "net/http"
    "server/spider"
)

type server struct{}

const (
    // Address 监听地址
    Address string = "localhost:8080"
    // Method 通信方法
    Method string = "tcp"
)

// 接收client端的请求,函数名需保持一致
// ctx参数必传
// 参数二为自定义的参数,需从pb文件导入,因此pb文件必须可导入,文件放哪里随意
// 返回值同参数二,为pb文件的返回结构体指针
func (s *server) GetAddressResponse(ctx context.Context, a *spider.SendAddress) (*spider.GetResponse, error) {
    // 逻辑写在这里
    switch a.Method {
    case "get", "Get", "GET":
        // 演示微服务用,故只写get示例
        status, body, err := get(a.Address)
        if err != nil {
            return nil, err
        }
        res := spider.GetResponse{
            HttpCode: int32(status),
            Response: body,
        }
        return &res, nil
    }
    return nil, nil
}

func get(address string) (s int, r string, err error) {
    // get请求
    resp, err := http.Get(address)
    if err != nil {
        return
    }
    defer resp.Body.Close()
    s = resp.StatusCode
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return
    }
    r = string(body)
    return
}

func main() {
    // 监听本地端口
    listener, err := net.Listen(Method, Address)
    if err != nil {
        return
    }
    s := grpc.NewServer()                       // 创建GRPC
    spider.RegisterGoSpiderServer(s, &server{}) // 在GRPC服务端注册服务

    reflection.Register(s) // 在GRPC服务器注册服务器反射服务
    // Serve方法接收监听的端口,每到一个连接创建一个ServerTransport和server的grroutine
    // 这个goroutine读取GRPC请求,调用已注册的处理程序进行响应
    err = s.Serve(listener)
    if err != nil {
        return
    }
}

client端代码

package main

import (
    "client/spider"
    "context"
    "google.golang.org/grpc"
)

import "fmt"

const (
    // Address server端地址
    Address string = "localhost:8080"
)

func main() {
    // 连接服务器
    conn, err := grpc.Dial(Address, grpc.WithInsecure())
    if err != nil {
        fmt.Println(err)
        return
    }
    defer conn.Close()

    // 连接GRPC
    c := spider.NewGoSpiderClient(conn)
    // 创建要发送的结构体
    req := spider.SendAddress{
        Address: "http://www.baidu.com",
        Method:  "get",
    }
    // 调用server的注册方法
    r, err := c.GetAddressResponse(context.Background(), &req)
    if err != nil {
        fmt.Println(err)
        return
    }
    // 打印返回值
    fmt.Println(r)
}