1. 前言

REST APIgRPC

2. gRPC与Protobuf简介

gRPC
gRPCjava

微服务架构中,由于每个服务对应的代码库是独立运行的,无法直接调用,彼此间的通信就是个大问题.

protobufprotocol buffers

当然也可以使用其他数据格式如JSON

可以用proto files创建gRPC服务,用message类型来定义方法参数和返回类型

3. 安装

  • 第一步:下载grpc通用编译器
protocprotoc.exe

  • 第二步:把下载的二进制文件路径添加到环境变量中(为了能全局访问protoc)
    • 这里以为mac为例子
# 打开存放环境变量的文件
vim ~/.bash_profile

# 添加如下,后面是路径
alias protoc="/Users/emm/others/protoc-3.12.4-osx-x86_64/bin/protoc"

# 刷新环境变量
source ./.bash_profile
  • 第三步: 安装go专用的protoc的生成器

go get github.com/golang/protobuf/protoc-gen-go

GOPATHprotoc-gen-goprotoc

4. 中间文件演示

4.1 编写中间文件

protoc
// 这个就是protobuf的中间文件

// 指定的当前proto语法的版本,有2和3
syntax = "proto3";

// 指定等会文件生成出来的package
package service;

// 定义request
message ProductRequest{
  int32 prod_id = 1; // 1代表顺序
}

// 定义response
message ProductResponse{
  int32 prod_stock = 1; // 1代表顺序
}

4.2 运行protoc命令编译成go中间文件

.go
protocprotoc-gen-go
# 编译Product.proto之后输出到service文件夹
protoc --go_out=../service Product.proto

如下就在service文件夹自动生成了一个go文件,并且它提示我们不要去修改它

5. 创建gRPC服务端

5.1 新建Product.protoc

这个protoc文件比上面的多出了一个service的定义和里面的一个方法的定义

// 这个就是protobuf的中间文件

// 指定的当前proto语法的版本,有2和3
syntax = "proto3";

// 指定等会文件生成出来的package
package service;

// 定义request model
message ProductRequest{
  int32 prod_id = 1; // 1代表顺序
}

// 定义response model
message ProductResponse{
  int32 prod_stock = 1; // 1代表顺序
}

// 定义服务主体
service ProdService{
  // 定义方法
  rpc GetProductStock(ProductRequest) returns(ProductResponse);
}

5.2 运行protoc命令

注意

  • 这里的protoc命令和之前的命令相比有点不一样
protoc --go_out=plugins=grpc:../service Product.proto
.go

有两个比较需要注意的

  1. RegisterProdServiceServer

后面需要在server中调用这个来注册

  1. ProdServiceServer的接口定义

我们需要继承这个接口,即实现它所有的方法

5.3 实现RegisterProdServiceServer接口

protocProdServiceGetProductStock

这里我们要实现自动生成的go文件中的接口

package service

import "context"

type ProdService struct {
}

func (ps *ProdService) GetProductStock(ctx context.Context, request *ProductRequest) (*ProductResponse, error) {
	return &ProductResponse{ProdStock: request.ProdId}, nil
}

5.4 准备工作完成,创建main函数将服务端跑起来

前面的都是准备工作,这里是真正把服务端跑起来的操作

下面是服务端代码:

package main

import (
	"gomicro-quickstart/grpc_demo/service"
	"google.golang.org/grpc"
	"log"
	"net"
)

func main() {
	// 1. new一个grpc的server
	rpcServer := grpc.NewServer()

	// 2. 将刚刚我们新建的ProdService注册进去
	service.RegisterProdServiceServer(rpcServer, new(service.ProdService))

	// 3. 新建一个listener,以tcp方式监听8082端口
	listener, err := net.Listen("tcp", ":8082")
	if err != nil {
		log.Fatal("服务监听端口失败", err)
	}

	// 4. 运行rpcServer,传入listener
	_ = rpcServer.Serve(listener)
}

排坑

undefined: grpc.SupportPackageIsVersion6undefined: grpc.ClientConnInterface


6. 创建gRPC客户端

grpc_clientgrpc_clientservice

6.1 拷贝Product.pb.go到客户端service文件夹下

6.2 编写client的main函数

package main

import (
	"context"
	"fmt"
	"gomicro-quickstart/grpc_client/service"
	"google.golang.org/grpc"
	"log"
)

func main() {
	// 1. 新建连接,端口是服务端开放的8082端口
	// 并且添加grpc.WithInsecure(),不然没有证书会报错
	conn, err := grpc.Dial(":8082", grpc.WithInsecure())
	if err != nil {
		log.Fatal(err)
	}

	// 退出时关闭链接
	defer conn.Close()

	// 2. 调用Product.pb.go中的NewProdServiceClient方法
	productServiceClient := service.NewProdServiceClient(conn)

	// 3. 直接像调用本地方法一样调用GetProductStock方法
	resp, err := productServiceClient.GetProductStock(context.Background(), &service.ProductRequest{ProdId: 233})
	if err != nil {
		log.Fatal("调用gRPC方法错误: ", err)
	}

	fmt.Println("调用gRPC方法成功,ProdStock = ", resp.ProdStock)
}

6.3 运行并显示结果

  • 先把服务端运行起来
  • 再把客户端运行起来

然后客户端输出正确的结果,第一个go的gRPC调用运行成功