Grpc初尝试

1,123 阅读1分钟

step1: 安装必要工具

go get -u google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/protobuf/cmd/protoc-gen-go

go get -u google.golang.org/grpc/cmd/protoc-gen-go-grpc
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc

step2: 在product目录下编辑ProductInfo.proto文件:

syntax ="proto3";

package product;

option go_package=".;product";

message Product {
	string id =1;
	string name=2;
	string description=3;
};

message ProductId {
	string value =1;
};
service ProductInfo {
	rpc addProduct (Product) returns (ProductId);
	rpc getProduct (ProductId) returns (Product);
};

step3: 在product目录下(也就是ProductInfo.proto文件相同目录)执行如下命令:

# 生成message的定义,message => struct
protoc --go_out=./ --go_opt=paths=source_relative  ProductInfo.proto

# 生成service的定义
protoc --go-grpc_out=./  --go-grpc_opt=paths=source_relative  ProductInfo.proto

step4: grpc server代码

type server struct {
	productMap map[string]*product.Product
	product.UnimplementedProductInfoServer
}

func (s *server) AddProduct(ctx context.Context, req *product.Product) (resp *product.ProductId, err error) {
	resp = &product.ProductId{}
	out, err := uuid.NewV4()
	if err != nil {
		return resp, status.Errorf(codes.Internal, "err while generate uuid", err)
	}
	req.Id = out.String()
	if s.productMap == nil {
		s.productMap = make(map[string]*product.Product)
	}
	s.productMap[req.Id] = req
	resp.Value = req.Id
	return
}

func (s *server) GetProduct(ctx context.Context, req *product.ProductId) (resp *product.Product, err error) {
	if s.productMap == nil {
		s.productMap = make(map[string]*product.Product)
	}
	resp = s.productMap[req.Value]
	return
}

server main.go代码:

package main

import (
   "context"
   "github.com/gofrs/uuid"
   "google.golang.org/grpc/codes"
   "google.golang.org/grpc/status"
   "google.golang.org/grpc"
   "grpc-demo/product"
   "log"
   "net"
)
const port = ":9527"

func main(){
	listener, err := net.Listen("tcp", port)
	if err != nil {
		log.Println("net listen err ", err)
		return
	}
	s := grpc.NewServer()
	product.RegisterProductInfoServer(s, &server{})
	log.Println("start gRPC liston on port" + port)
	if err := s.Serve(listener); err != nil {
		log.Println("failed to serve...", err)
		return
	}
}

step5: grpc client代码

package main

import (
	"context"
	"generalTest/test/grpcTest/product"
	"log"

	"google.golang.org/grpc"
)

const address = "127.0.0.1:9527"

func AddProduct(ctx context.Context, client product.ProductInfoClient) (id string) {
	aMac := &product.Product{Name: "MacBookPro 2021", Description: "From Apple Inc."}
	productId, err := client.AddProduct(ctx, aMac)
	if err != nil {
		log.Println("add product fail.", err)
		return
	}
	log.Println("add product success,id=", productId.Value)
	return productId.Value
}

func GetProduct(ctx context.Context, client product.ProductInfoClient, id string) {
	p, err := client.GetProduct(ctx, &product.ProductId{Value: id})
	if err != nil {
		log.Println("get product,err.", err)
		return
	}
	log.Printf("get product success: %+v\n", p)
}

func main(){
	conn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		log.Println("dial connect fail.", err)
		return
	}
	client := product.NewProductInfoClient(conn)
	ctx := context.Background()

	id := AddProduct(ctx, client)
	GetProduct(ctx, client, id)
}

可能遇到的错误:

  • 错误一: protoc-gen-go-grpc: unable to determine Go import path for "ProductInfo.proto" 原因: ProductInfo.proto文件中需要定义option go_package=".;product"
  • 错误二: --go_out: protoc-gen-go: plugins are not supported; use 'protoc --go-grpc_out=...' to generate gRPC 原因: 新版本protoc已经不支持--go_out=plugins=grpc:。只能使用protoc --go-grpc_out=./

参考问题:

protoc-gen-go-grpc: program not found or is not executable
最简单的 gRPC 教程— 1 初识 gRPC