摘要

在这篇文章中,主要是跟你介绍一下gRPC这个东西。

然后,我会创建一个简单的练习项目,作为gRPC的Hello World项目。

在这个项目中,只有很简单的一个RPC函数,用于说明gRPC的工作方式。

此外,我也会跟你分享一下我初次接触gRPC所遇到的一些坑,主要是在protocol bufferproto-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-gogoogle.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项目中,我希望实现一个功能,客户端可以发送消息给服务端,服务端收到消息后,返回响应给客户端。

正如前面所说的,在开发serverclient之前,我们需要先定义服务。

因此,在这一节的内容中,我将向你介绍proto文件的编写。

3.1 项目结构

在这之前,先让我们看看整个项目的初始结构。

serverclient我们先不管,在这一节内容中我们先编写`*.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

让我们在看看具体的MessageRequestMessageResponse

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语言的代码,以便serverclient直接使用。

注意,到了这一部分你可能会有一些疑惑。

在网上的一些教程中,有这样的生成方式:

protoc --go_out=plugins=grpc:. helloworld.proto

这种生成方式,使用的就是github版本的protoc-gen-go,而目前这个项目已经由Google接管了。

并且,如果使用这种生成方式的话,并不会生成上图中的xxx_grpc.pb.goxxx.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的更多相关文章

  1. GRPC快速入门

    转载请注明来自ChenJiehua的<GRPC快速入门> GRPC是一个高性能.通用的开源RPC框架,基于HTTP/2协议标准和Protobuf序列化协议开发,支持众多的开发语言. 概述 ...

  2. gRPC快速入门记录

    为什么使用grpc 1.protocl buffer一种高效的序列化结构. 2.支持http 2.0标准化协议. http/2 1.http/2对每个源只需创建一个持久连接,在这一个连接内,可以并行的 ...

  3. 【微服务落地】服务间通信方式: gRPC的入门

    gRPC是什么 官方介绍: https://grpc.io/docs/what-is-grpc/introduction/ "A high-performance, open-source ...

  4. gRPC初探——概念介绍以及如何构建一个简单的gRPC服务

    目录 引言 1. gRPC简介 2. 使用Protocol Buffers进行服务定义 2.1 定义消息 2.2 定义服务接口 3.构建简单的gRPC服务 3.1 编写proto文件,定义消息和接口 ...

  5. 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 ...

  6. Grpc微服务从零入门

    快速入门 安装 JDK 毫无疑问,要想玩Java,就必须得先装Java JDK,目前公司主要使用的是Oracle JDK 8,安装完成后要配置环境才能正常使用,真蠢,不过也就那么一下下,认了吧.配置方 ...

  7. grpc入门(三)

    grpc入门(三) 一.介绍 本文是关于grpc的第三篇博文,是对前两篇博文的具体代码实现,秉着个人一贯的风格,没有太多抒情和总结,直接就上代码. 文章代码参考:https://github.com/ ...

  8. ASP.NET Core gRPC 入门全家桶

    一. 说明 本全家桶现在只包含了入门级别的资料,实战资料更新中. 二.官方文档 gRPC in Asp.Net Core :官方文档 gRPC 官网:点我跳转 三.入门全家桶 正片: ASP.NET ...

  9. gRPC (1):入门及服务端创建和调用原理

    1. RPC 入门 1.1 RPC 框架原理 RPC 框架的目标就是让远程服务调用更加简单.透明,RPC 框架负责屏蔽底层的传输方式(TCP 或者 UDP).序列化方式(XML/Json/ 二进制)和 ...

  10. Go GRPC 入门(一)

    前言 微服务相关 使用 GRPC 通讯的 Golang 微服务入门 举例写一个微服务,接收网址发送请求获取返回结果返回 正文 安装工具 安装 protobuf 这是 proto 文件的编译器 点我下载 ...

随机推荐

  1. kernel 通知链

    原文链接: 深入理解linux网络技术内幕读书笔记(四)--通知链 概述 [注意] 通知链只在内核子系统之间使用. 大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣.为了 ...

  2. Reinforcement Learning, Fast and Slow

    郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! 1 DeepMind, London, UK2 University College London, London, UK3 Prince ...

  3. 可爱的python

    可爱的python 作者:  哲思社区出版社: 电子工业出版社 优点 1. 案列讲解很详细,前几章的内容恰好是我想要了解的,例如利用python os模块读取磁盘的文件,或者获得文字的编码方式.这些内 ...

  4. Java算法——回溯法

    回溯法一种选优搜索法,又称试探法.利用试探性的方法,在包含问题所有解的解空间树中,将可能的结果搜索一遍,从而获得满足条件的解.搜索过程采用深度遍历策略,并随时判定结点是否满足条件要求,满足要求就继续向 ...

  5. 数据中台实战(一):以B2B电商亿订为例,谈谈产品经理视角下的数据埋点

    本文以B2B电商产品“亿订”为实例,与大家一同谈谈数据中台的数据埋点. 笔者所在公司为富力环球商品贸易港,是富力集团旗下汇聚原创设计师品牌及时尚买手/采购商两大社群,通过亿订B2B电商.RFSHOWR ...

  6. 解析WAV音频文件----》生成WAV音频文件头

    前言:请各大网友尊重本人原创知识分享,谨记本人博客:南国以南i WAV音频文件介绍: WAV文件是在PC机平台上很常见的.最经典的多媒体音频文件,最早于1991年8月出现在Windows3.1操作系统 ...

  7. python爬虫-爬取百度图片

    python爬虫-爬取百度图片(转) #!/usr/bin/python# coding=utf-8# 作者 :Y0010026# 创建时间 :2018/12/16 16:16# 文件 :spider ...

  8. go语言之文件操作

    一: 相关的API 1func Create(name string) (file *File, err Error) 根据提供的文件名创建新的文件,返回一个文件对象,默认权限是0666 2 func ...

  9. 用aop去解决事物问题(tx)记录学习之aop1.2

    上一个文章我们了解了什么事aop,以及aop的使用方法,主要是把自己想要加入的通知(advice)加入到我们的方法里, 比如上一章我们说的事把myadvice类中的before方法织入到userser ...

  10. URL的字符编码

    摘要: 在通过URL访问HTTP SERVER的时候,通常会产生trace callback的异常,返回505的错误," VERSION IS NOT SUPPORTED ?" , ...