grpc 基本使用

grpc是由google开发的一款语言中立、平台中立、开源的RPC系统

在grpc中客户端应用可以像调用本地对象一样直接调用另一台不同机器上服务端应用的方法,使得很容易创建分布式应用和服务。与许多RPC系统类似,grpc也是定义一个服务,指定能够被远程调用的方法,在服务端实现该接口,并允许grpc服务器来处理客户端调用。客户端拥有像服务端一样方法的stub。

在这里插入图片描述

grpc允许定义四种服务方法

  • 单项RPC,即客户端发送一个请求给服务端,然后从服务端获取一个应答,就像一次普通的函数调用
  • 服务端流式RPC,客户端发送一个请求给服务端,可获取一个数据流给客户端读取消息
  • 客户端流式RPC,即用客户端提供的一个数据流写入并发送一系列消息给服务端,一旦客户端完成消息写入,就等待服务端读取这些消息并应答
  • 双向流式 RPC,即两边都可以分别通过一个读写数据流来发送一系列消息。这两个数据流操作是相互独立的,所以客户端和服务端能按其希望的任意顺序读写,例如:服务端可以在写应答前等待所有的客户端消息,或者它可以先读一个消息再写一个消息,或者是读写相结合的其他方式。每个数据流里消息的顺序会被保持。

以下以一个简单的获取产品的场景来演示grpc的使用

使用protoc buffer定义服务

protoBuffer主要编写相应的数据结构以及接口方法定义

syntax="proto3";
package pd;

option go_package="/pd";

// 因为rpc方法参数不能为空,所以定义一个空message
message empty {
}

// 产品
message Prod {
	string id = 1;
	string name = 2;
}

// 因为所有参数都只能为message类型,所以对于prodId还是要定义为message
message ProdId {
	int64 val = 1;
}

产品服务定义如下

syntax="proto3";
package pd;

option go_package="/pd";

import "pd/common.proto";

service ProdInfo{
  rpc addProd(Prod) returns (ProdId);
  rpc getProd(ProdId) returns (Prod);
  // stream就是前文介绍的流
  rpc listProds(empty) returns (stream Prod);
  rpc listProdsByIds(stream ProdId) returns (stream Prod);
}

当我们运行protoc命令之后,就会为我们自动生成pb.go文件

# --go-grpc_out 为grpc插件,可以通过
protoc -I ../  --go-grpc_out=../  ../pd/prod.proto
protoc -I ../  --go_out=../  ../pd/common.proto

实现服务

以下是实现过程

import (
	"context"
	"io"
	"learn/grpc-demo/pd"
)

type prodServer struct {
	pd.UnsafeProdInfoServer
	prodMap map[int64]*pd.Prod
	index   int64
}

func (s *prodServer) GetProd(ctx context.Context, id *pd.ProdId) (*pd.Prod, error) {
	return s.prodMap[id.Val], nil
}

func (s *prodServer) ListProds(empty *pd.Empty, server pd.ProdInfo_ListProdsServer) error {
	for _, prod := range s.prodMap {
		if err := server.Send(prod); err != nil {
			return err
		}
	}

	return nil
}

func (s *prodServer) ListProdsByIds(server pd.ProdInfo_ListProdsByIdsServer) error {
	for {
		in, err := server.Recv()
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}
		pid := in.Val
		if prod, ok := s.prodMap[pid]; ok {
			if err = server.Send(prod); err != nil {
				return err
			}
		}
	}
}

func (s *prodServer) AddProd(ctx context.Context, p *pd.Prod) (resp *pd.ProdId, err error) {
	s.prodMap[s.index] = p
	s.index++
	return &pd.ProdId{Val: s.index - 1}, nil
}

值得注意的是出现了一个很奇怪的pd.UnsafeProdInfoServer,在新版protoc-gen-grpc-go编译器中,Server实现必须向前兼容

当然也可以通过以下命令不生成向前兼容service

protoc --go-grpc_out=require_unimplemented_servers=false:.

编写服务端和客户端

服务端监听

	conn,err:=net.Listen("tcp",":8888")
	if err != nil {
		t.Fatalf("fail to listen for %s",err)
	}

	s :=grpc.NewServer()
	pd.RegisterProdInfoServer(s,&prodServer{
		prodMap: make(map[int64]*pd.Prod),
		index: 0,
	})

	err=s.Serve(conn)
	if err != nil {
		t.Fatal("fail to serve")
	}

客户端请求,并可以通过grpc.WithInsecure()取消认证

	conn,err:=grpc.Dial(":8888",grpc.WithInsecure())
	if err != nil {
		t.Fatal("fail to conn server")
	}
	defer conn.Close()

	c:=pd.NewProdInfoClient(conn)
	pid,err:=c.AddProd(context.Background(),&pd.Prod{
		Id:   "x",
		Name: "we",
	})
	if err != nil {
		t.Fatal("add prod failed")
	}

	t.Logf("add prod %d",pid.Val)

Ref

  1. https://doc.oschina.net/grpc?t=58008
  2. https://blog.gopheracademy.com/advent-2017/go-grpc-beyond-basics/
  3. https://grpc.io/docs/guides/auth/
  4. https://shijuvar.medium.com/writing-grpc-interceptors-in-go-bf3e7671fe48
  5. https://stackoverflow.com/questions/65079032/grpc-with-mustembedunimplemented-method
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值