1 简介

1.1 RPC

RPC 指远程过程调用(Remote Procedure Call),它的调用包含传输协议和编码(对象序列)协议等,允许运行于一台计算机上的程序调用另一台计算机上的子程序,而开发人员无需额外为这个交互作用编程,就像对本地函数进行调用一样方便。

1.2 gRPC

gRPC 是一个高性能、开源、通用的 RPC 框架,目前提供了 C、Java 和 Go 等语言版本,分别是 gRPC、gRPC-java 和 gRPC-go。其中 C 语言版本支持 C、C++、Node.js、Python、Ruby、Objective-C、PHP 和 C#。gRPC 基于 HTTP/2 标准设计,拥有双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特性。这些特性使得其在移动设备上表现更好,更节省空间。gRPC 的接口描述语言(Interface Description Language,IDL)使用的是 Protobuf,是由 Google 开源的。

在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。

grpc实现Java和golang通信 golang使用grpc_Server

1.3 Protobuf

Protobuf( Protocol Buffers / Protocol Buffer )是一种与语言、平台无关,且可扩展的序列化结构数据描述语言,通常称其为 IDL,常用于通信协议、数据存储等,与 JSON、XML 相比,它更小、更快,因此也更受开发人员的青睐。

下面本文将介绍使用Golang开发一个简单的gRPC应用的基本步骤。

2 环境搭建

2.1 安装 protoc

(1)下载 protoc对应版本的压缩包

下载链接:https://github.com/protocolbuffers/protobuf/releases

找到合适的版本,下载到本地

grpc实现Java和golang通信 golang使用grpc_grpc实现Java和golang通信_02


(2)找到 bin 目录下的 protoc.exe 文件,并将其拷贝至 GOROOT 下面的 bin 目录,完成后,打开 cmd ,运行命令 protoc --version ,验证是否成功。

grpc实现Java和golang通信 golang使用grpc_微服务_03


grpc实现Java和golang通信 golang使用grpc_Server_04


grpc实现Java和golang通信 golang使用grpc_golang_05

成功后,进行下面的步骤。

2.2 安装 gRPC 相关依赖

(1)gRPC包:

go get -u google.golang.org/grpc

grpc实现Java和golang通信 golang使用grpc_Server_06


(2)安装protoc转go的插件:

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

grpc实现Java和golang通信 golang使用grpc_微服务_07


(3)安装 protoc-gen-go:

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

grpc实现Java和golang通信 golang使用grpc_grpc实现Java和golang通信_08


(4)安装以下两项

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

(5)步骤(4)成功后会在 GOPATH 下的 bin 目录生成 protoc-gen-go.exe 与 protoc-gen-go-grpc.exe

grpc实现Java和golang通信 golang使用grpc_Server_09


grpc实现Java和golang通信 golang使用grpc_Server_10


(6)将步骤(5)中的两个 exe 文件复制至 GOROOT 下的 bin 目录

grpc实现Java和golang通信 golang使用grpc_golang_11


(1)在下载的过程中可能会出现超时问题

grpc实现Java和golang通信 golang使用grpc_rpc_12


解决方案:在 Goland 的 Terminal 中运行下面两条命令,成功后再执行上述步骤(1)-(6)

配置国内镜像
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn

(2)关于 GOROOT 以及 GOPATH:
GOROOT:Golang的安装路径
GOPATH:工作目录,Go项目的工作路径,约定三个子目录:

  • src:存放源代码
  • pkg:编译时生成的中间文件
  • bin:编译后生成的可执行文件

3 编写 proto 文件

项目完整代码结构为:

grpc实现Java和golang通信 golang使用grpc_grpc实现Java和golang通信_13

3.1 创建 user.proto 文件

syntax = "proto3";

package user;

//新版本需要的配置
option go_package = "./user";

message UserRequest {
  int32 id = 1;
}

message UserResponse {
  int32 id = 1;
  string name = 2;
}

service UserService {
  rpc getUser(UserRequest) returns (UserResponse) {};
}

(1)syntax:指明协议的版本
(2)package:指明该 .proto 的名称
(3)import:可以在当前 .proto 中引入其它 .proto 文件,gRPC 基本数据类型中不包含时间格式,可以引入 timestamp.proto

3.2 生成 gRPC 文件

(1)在 Terminal 中运行下面的命令

protoc user.proto --go_out=./rpc --go-grpc_out=./rpc --go-grpc_opt=require_unimplemented_servers=false

其中

grpc实现Java和golang通信 golang使用grpc_golang_14


(2)上述步骤(1)命令执行成功后会生成以下两个文件

grpc实现Java和golang通信 golang使用grpc_golang_15

4 编写 client 端以及 server 端代码

4.1 编写 client.go 文件

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"goCode/gRPC/rpc/user"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {
	conn, err := grpc.Dial("127.0.0.1:8889", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		fmt.Println(err)
	}
	defer conn.Close()
	client := user.NewUserServiceClient(conn)
	req := &user.UserRequest{
		Id: 1,
	}
	response, _ := client.GetUser(context.Background(), req)
	resp, err := json.Marshal(response)
	fmt.Printf("%s", resp)
}

4.2 编写 server.go 文件

package main

import (
	"context"
	"fmt"
	"goCode/gRPC/rpc/user"
	"google.golang.org/grpc"
	"net"
)

type UserServiceInterface interface {
	GetUser(ctx context.Context, req *user.UserRequest) (*user.UserResponse, error)
}

type UserServiceStruct struct {
}

func NewUserService() UserServiceInterface {
	return &UserServiceStruct{}
}

func (userService *UserServiceStruct) GetUser(ctx context.Context, req *user.UserRequest) (*user.UserResponse, error) {
	response := &user.UserResponse{
		Id:   req.Id,
		Name: "This is the gRPC test!",
	}
	return response, nil
}

func main() {
	l, err := net.Listen("tcp", "127.0.0.1:8889")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("Server start and listen on 127.0.0.1:8889")
	grpcServer := grpc.NewServer()
	var userService UserServiceInterface
	userService = NewUserService()
	user.RegisterUserServiceServer(grpcServer, userService)
	err = grpcServer.Serve(l)
	if err != nil {
		println(err)
	}

}

5 测试

(1)运行 server.go

grpc实现Java和golang通信 golang使用grpc_grpc实现Java和golang通信_16


(2)运行 client.go

grpc实现Java和golang通信 golang使用grpc_grpc实现Java和golang通信_17


:grpc.WithInsecure is deprecated 问题解决方案:

grpc实现Java和golang通信 golang使用grpc_微服务_18

将上述代码修改为

conn, err := grpc.Dial(“127.0.0.1:8889”, grpc.WithTransportCredentials(insecure.NewCredentials()))

grpc实现Java和golang通信 golang使用grpc_golang_19

6 附录

(1)proto.exe:也叫proto编译器,可以用来生成各种开发语言使用proto协议的代码
(2)protoc-gen-go.exe:生成Golang版本的proto协议代码
(3)protoc-gen-go-grpc.exe:生成Golang版本的gRPC代理代码
(4)proto文件:符合Protocol Buffers语言规范的数据交换协议文件
(5)user.pb.go:包含用于填充、序列化、检索请求和响应消息类型的所有Protocol Buffers代码
(6)user_grpc.pb.go:包含服务端需要继承实现和客户端进行调用的接口定义


7 参考文献

[1] 详解GOROOT和GOPATH [2] go grpc 完整实例 [3] go grpc [4] gRPC 官方文档中文版 [5] gRPC 基础: Go