gRPC-go 入门(1):Hello World
摘要
在这篇文章中,主要是跟你介绍一下gRPC这个东西。
然后,我会创建一个简单的练习项目,作为gRPC的Hello World项目。
在这个项目中,只有很简单的一个RPC函数,用于说明gRPC的工作方式。
此外,我也会跟你分享一下我初次接触gRPC所遇到的一些坑,主要是在protocol buffer的proto-gen-go插件上面。
1. 简单介绍
在这一节的内容中,我将简单的跟你介绍一下gRPC这个东西。
RPC的全称是Remote Procedure Call,远程过程调用。这是一种协议,是用来屏蔽分布式计算中的各种调用细节,使得你可以像是本地调用一样直接调用一个远程的函数。
而gRPC又是什么呢?用官方的话来说:
A high-performance, open-source universal RPC framework
gRPC是一个高性能的、开源的通用的RPC框架。
在gRPC中,我们称调用方为client,被调用方为server。
跟其他的RPC框架一样,gRPC也是基于”服务定义“的思想。简单的来讲,就是我们通过某种方式来描述一个服务,这种描述方式是语言无关的。在这个”服务定义“的过程中,我们描述了我们提供的服务服务名是什么,有哪些方法可以被调用,这些方法有什么样的入参,有什么样的回参。
也就是说,在定义好了这些服务、这些方法之后,gRPC会屏蔽底层的细节,client只需要直接调用定义好的方法,就能拿到预期的返回结果。对于server端来说,还需要实现我们定义的方法。同样的,gRPC也会帮我们屏蔽底层的细节,我们只需要实现所定义的方法的具体逻辑即可。
你可以发现,在上面的描述过程中,所谓的”服务定义“,就跟定义接口的语义是很接近的。我更愿意理解为这是一种”约定“,双方约定好接口,然后server实现这个接口,client调用这个接口的代理对象。至于其他的细节,交给gRPC。
此外,gRPC还是语言无关的。你可以用C++作为服务端,使用Golang、Java等作为客户端。为了实现这一点,我们在”定义服务“和在编码和解码的过程中,应该是做到语言无关的。
下面放一张官网上面的图:

因此,gRPC使用了Protocol Buffers。
在这里我不会展开来讲Protocol Buffers这个东西,你可以把他当成一个代码生成工具以及序列化工具。这个工具可以把我们定义的方法,转换成特定语言的代码。比如你定义了一种类型的参数,他会帮你转换成Golang中的struct 结构体,你定义的方法,他会帮你转换成func 函数。此外,在发送请求和接受响应的时候,这个工具还会完成对应的编码和解码工作,将你即将发送的数据编码成gRPC能够传输的形式,又或者将即将接收到的数据解码为编程语言能够理解的数据格式。
对gRPC的简单介绍就到这里,下面的内容我们直接开始实践。
2. 环境配置
在这一节中,可能很多内容会不那么的适用。
但是限于篇幅,我没有列举所有的安装方式。如果在安装的过程中你遇到了问题,可以在网上搜索解决,也可以在文章末尾找到我的联系方式,我们一起研究。
2.1 gRPC
go get google.golang.org/grpc
这一步安装的是gRPC的核心库,但是这一步是需要(特别的上网方式)的。所以如果在安装过程中出错了,你可以科学一波,也可以找一找其他的安装方法。
2.2 protocol buffers
在Mac OS中,直接用brew安装。
brew info protobuf
2.3 protoc-gen-go
上一步安装的是protocol编译器。而上文中我们提到了可以生成各种不同语言的代码。因此,除了这个编译器,我们还需要配合各个语言的代码生成工具。
对于Golang来说,称为protoc-gen-go。
不过在这儿有个小小的坑,github.com/golang/protobuf/protoc-gen-go和google.golang.org/protobuf/cmd/protoc-gen-go是不同的。
区别在于前者是旧版本,后者是google接管后的新版本,他们之间的API是不同的,也就是说用于生成的命令,以及生成的文件都是不一样的。
因为目前的gRPC-go源码中的example用的是后者的生成方式,为了与时俱进,本文也采取最新的方式。
你需要安装两个库:
go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
因为这些文件在安装grpc的时候,已经下载下来了,因此使用install命令就可以了,而不需要使用get命令。
然后你看你的$GOPATH路径,应该有标1和2的两个文件:

至此,所有的准备工作已经完成。
3. proto文件创建
在开始开发之前,先说说我们的目标。
在这个grpc-practice项目中,我希望实现一个功能,客户端可以发送消息给服务端,服务端收到消息后,返回响应给客户端。
正如前面所说的,在开发server与client之前,我们需要先定义服务。
因此,在这一节的内容中,我将向你介绍proto文件的编写。
3.1 项目结构
在这之前,先让我们看看整个项目的初始结构。

server和client我们先不管,在这一节内容中我们先编写`*.proto'文件。
在proto文件夹中创建message.proto文件。
在文件的第一行,我们写上:
syntax = "proto3";
这是在说明我们使用的是proto3语法。
然后我们应该写上:
option go_package = ".;message";
这部分的内容是关于最后生成的go文件是处在哪个目录哪个包中,.代表在当前目录生成,message代表了生成的go文件的包名是message。
然后我们需要定义一个服务,在这个服务中需要有一个方法,这个方法可以接受客户端的参数,再返回服务端的响应。
那么我们可以这么写:
service MessageSender {
rpc Send(MessageRequest) returns (MessageResponse) {}
}
其实很容易可以看出,我们定义了一个service,称为MessageSender,这个服务中有一个rpc方法,名为Send。这个方法会发送一个MessageRequest,然后返回一个MessageResponse。
让我们在看看具体的MessageRequest和MessageResponse:
message MessageResponse {
string responseSomething = 1;
}
message MessageRequest {
string saySomething = 1;
}
message关键字,其实你可以理解为Golang中的结构体。这里比较特别的是变量后面的“赋值”。注意,这里并不是赋值,而是在定义这个变量在这个message中的位置。更具体的内容我应该会在源码分析部分讲到。
在编写完上面的内容后,在/grpc-practice/src/helloworld/proto目录下执行如下命令:
protoc --go_out=. message.proto
protoc --go-grpc_out=. message.proto
这两条命令会生成如下的两个文件:

在这两个文件中,包含了我们定义方法的go语言实现,也包含了我们定义的请求与相应的go语言实现。
简单来讲,就是protoc-gen-go已经把你定义的语言无关的message.proto转换为了go语言的代码,以便server和client直接使用。
注意,到了这一部分你可能会有一些疑惑。
在网上的一些教程中,有这样的生成方式:
protoc --go_out=plugins=grpc:. helloworld.proto
这种生成方式,使用的就是github版本的protoc-gen-go,而目前这个项目已经由Google接管了。
并且,如果使用这种生成方式的话,并不会生成上图中的xxx_grpc.pb.go与xxx.pb.go两个文件,只会生成xxx.pb.go这种文件。
此外,你也可能遇到这种错误:
protoc-gen-go-grpc: program not found or is not executable
Please specify a program using absolute path or make sure the program is available in your PATH system variable
--go-grpc_out: protoc-gen-go-grpc: Plugin failed with status code 1.
这是因为你没有安装protoc-gen-go-grpc这个插件,这个问题在本文中应该不会出现。
你还可能会遇到这种问题:
--go_out: protoc-gen-go: plugins are not supported; use 'protoc --go-grpc_out=...' to generate gRPC
这是因为你安装的是更新版本的protoc-gen-go,但是你却用了旧版本的生成命令。
但是这两种方法都是可以完成目标的,只不过api不太一样。本文是基于Google版本的protoc-gen-go进行示范。
至于其他更详细的资料,你可以在这里看到:https://github.com/protocolbuffers/protobuf-go/releases/tag/v1.20.0#v1.20-generated-code
4. 服务端
4.1 注册
我们在server目录下面创建一个server.go文件。
在main函数中加入如下的代码:
srv := grpc.NewServer()
message.RegisterMessageSenderService(srv, &message.MessageSenderService{})
很容易可以看出,我们在这一部分创建了一个Server,然后注册了我们的Service。
在注册函数的第二个参数中,我们传进去了一个MessageSenderService实例。
来看看这个实例有什么样的结构:
type MessageSenderService struct {
Send func(context.Context, *MessageRequest) (*MessageResponse, error)
}
可以看出,这个实例里面有一个方法,这个方法就是我们定义的send方法。也就是说,这一部分是需要我们在Server端实现这个send方法的。
因此我们创建这么一个方法:
func handleSendMessage(ctx context.Context, req *message.MessageRequest) (*message.MessageResponse, error) {
log.Println("receive message:", req.GetSaySomething())
resp := &message.MessageResponse{}
resp.ResponseSomething = "roger that!"
return resp, nil
}
注意,“实现定义的方法”,并不是说我们需要创建一个同名的方法,而是说我们需要创建一个有相同函数签名的方法。也就是说,需要有相同的入参,出参。
然后我们将这个方法写进注册函数中,变成了这样:
message.RegisterMessageSenderService(srv, &message.MessageSenderService{
Send: handleSendMessage,
})
至此,我们已经成功的在server端实现了我们声明的方法了。
4.2 监听
其实这个过程跟golang的web服务器是很像的,也是创建Handler,然后对端口进行监听。
那么到了这一步也一样。
listener, err := net.Listen("tcp", ":12345")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
err = srv.Serve(listener)
if err != nil {
log.Fatalf("failed to serve: %v", err)
}
监听12345端口的TCP连接,然后启动服务器。
至此,服务端开发完毕。
5. 客户端
在客户端中,我们应该先与server端建立连接,然后才能够调用各种方法。
conn, err := grpc.Dial("127.0.0.1:12345", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
以上代码,就是跟本地的12345端口建立连接。
然后,按照定义,我们调用server端的方法,应该要像调用本地方法一样方便。
那么,我们这么做:
client := message.NewMessageSenderClient(conn)
resp, err := client.Send(context.Background(), &message.MessageRequest{SaySomething: "hello world!"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
很容易可以理解,我们在本地创建了一个client,然后直接调用我们之前定义好的Send方法,就可以实现我们需要的逻辑了。
简单的来讲,我们在*.proto文件中定义了方法,然后在server端实现定义的rpc方法的具体逻辑,在client端调用这个方法。
对于其他的部分,由proto buffer负责对Golang中存储的数据结构与rpc传输中的数据进行转换,grpc负责封装所有的逻辑。
server端和client端都跑起来,你会看到这样的画面:


至此,成功Hello了个World。
写在最后
首先,谢谢你能看到这里!
在这篇文章中,主要是跟你介绍一下hello world的写法,以及在say hello的过程中可能遇到的一些坑。
我认为最大的坑是在于protoc-gen-go这个插件这里,因为两种语法让我迷惑了很久。
如果在这期间,你还有一些问题没有解决,欢迎留言,或者直接公众号找到我,我们一起研究。
如果在文章中有哪些错误,还请不吝指教,谢谢!
最后,再次感谢你能看到这里!
按照惯例,甩个公众号在这,不管有没有问题,都欢迎来找我玩~

gRPC-go 入门(1):Hello World的更多相关文章
- GRPC快速入门
转载请注明来自ChenJiehua的<GRPC快速入门> GRPC是一个高性能.通用的开源RPC框架,基于HTTP/2协议标准和Protobuf序列化协议开发,支持众多的开发语言. 概述 ...
- gRPC快速入门记录
为什么使用grpc 1.protocl buffer一种高效的序列化结构. 2.支持http 2.0标准化协议. http/2 1.http/2对每个源只需创建一个持久连接,在这一个连接内,可以并行的 ...
- 【微服务落地】服务间通信方式: gRPC的入门
gRPC是什么 官方介绍: https://grpc.io/docs/what-is-grpc/introduction/ "A high-performance, open-source ...
- gRPC初探——概念介绍以及如何构建一个简单的gRPC服务
目录 引言 1. gRPC简介 2. 使用Protocol Buffers进行服务定义 2.1 定义消息 2.2 定义服务接口 3.构建简单的gRPC服务 3.1 编写proto文件,定义消息和接口 ...
- ASP.NET Core 3.0 gRPC 拦截器
目录 ASP.NET Core 3.0 使用gRPC ASP.NET Core 3.0 gRPC 双向流 ASP.NET Core 3.0 gRPC 拦截器 一. 前言 前面两篇文章给大家介绍了使用g ...
- Grpc微服务从零入门
快速入门 安装 JDK 毫无疑问,要想玩Java,就必须得先装Java JDK,目前公司主要使用的是Oracle JDK 8,安装完成后要配置环境才能正常使用,真蠢,不过也就那么一下下,认了吧.配置方 ...
- grpc入门(三)
grpc入门(三) 一.介绍 本文是关于grpc的第三篇博文,是对前两篇博文的具体代码实现,秉着个人一贯的风格,没有太多抒情和总结,直接就上代码. 文章代码参考:https://github.com/ ...
- ASP.NET Core gRPC 入门全家桶
一. 说明 本全家桶现在只包含了入门级别的资料,实战资料更新中. 二.官方文档 gRPC in Asp.Net Core :官方文档 gRPC 官网:点我跳转 三.入门全家桶 正片: ASP.NET ...
- gRPC (1):入门及服务端创建和调用原理
1. RPC 入门 1.1 RPC 框架原理 RPC 框架的目标就是让远程服务调用更加简单.透明,RPC 框架负责屏蔽底层的传输方式(TCP 或者 UDP).序列化方式(XML/Json/ 二进制)和 ...
- Go GRPC 入门(一)
前言 微服务相关 使用 GRPC 通讯的 Golang 微服务入门 举例写一个微服务,接收网址发送请求获取返回结果返回 正文 安装工具 安装 protobuf 这是 proto 文件的编译器 点我下载 ...
随机推荐
- kernel 通知链
原文链接: 深入理解linux网络技术内幕读书笔记(四)--通知链 概述 [注意] 通知链只在内核子系统之间使用. 大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣.为了 ...
- Reinforcement Learning, Fast and Slow
郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! 1 DeepMind, London, UK2 University College London, London, UK3 Prince ...
- 可爱的python
可爱的python 作者: 哲思社区出版社: 电子工业出版社 优点 1. 案列讲解很详细,前几章的内容恰好是我想要了解的,例如利用python os模块读取磁盘的文件,或者获得文字的编码方式.这些内 ...
- Java算法——回溯法
回溯法一种选优搜索法,又称试探法.利用试探性的方法,在包含问题所有解的解空间树中,将可能的结果搜索一遍,从而获得满足条件的解.搜索过程采用深度遍历策略,并随时判定结点是否满足条件要求,满足要求就继续向 ...
- 数据中台实战(一):以B2B电商亿订为例,谈谈产品经理视角下的数据埋点
本文以B2B电商产品“亿订”为实例,与大家一同谈谈数据中台的数据埋点. 笔者所在公司为富力环球商品贸易港,是富力集团旗下汇聚原创设计师品牌及时尚买手/采购商两大社群,通过亿订B2B电商.RFSHOWR ...
- 解析WAV音频文件----》生成WAV音频文件头
前言:请各大网友尊重本人原创知识分享,谨记本人博客:南国以南i WAV音频文件介绍: WAV文件是在PC机平台上很常见的.最经典的多媒体音频文件,最早于1991年8月出现在Windows3.1操作系统 ...
- python爬虫-爬取百度图片
python爬虫-爬取百度图片(转) #!/usr/bin/python# coding=utf-8# 作者 :Y0010026# 创建时间 :2018/12/16 16:16# 文件 :spider ...
- go语言之文件操作
一: 相关的API 1func Create(name string) (file *File, err Error) 根据提供的文件名创建新的文件,返回一个文件对象,默认权限是0666 2 func ...
- 用aop去解决事物问题(tx)记录学习之aop1.2
上一个文章我们了解了什么事aop,以及aop的使用方法,主要是把自己想要加入的通知(advice)加入到我们的方法里, 比如上一章我们说的事把myadvice类中的before方法织入到userser ...
- URL的字符编码
摘要: 在通过URL访问HTTP SERVER的时候,通常会产生trace callback的异常,返回505的错误," VERSION IS NOT SUPPORTED ?" , ...