设为首页 收藏本站
查看: 1488|回复: 0

[经验分享] protobuffer、gRPC、restful gRPC的相互转化

[复制链接]

尚未签到

发表于 2018-9-21 08:16:56 | 显示全部楼层 |阅读模式
  转自:https://studygolang.com/articles/12510

文档


  • grpc中文文档
  • grpc-gateway,restful和grpc转换库
  • protobuf 官网
protobuf
  Google Protocol Buffer(简称 Protobuf)是一种轻便高效的结构化数据存储格式,平台无关、语言无关、可扩展,可用于通讯协议和数据存储等领域。

优点


  • 平台无关,语言无关,可扩展;
  • 提供了友好的动态库,使用简单;
  • 解析速度快,比对应的XML快约20-100倍;
  • 序列化数据非常简洁、紧凑,与XML相比,其序列化之后的数据量约为1/3到1/10。
安装
  参考 golang使用protobuf
  

https://github.com/google/protobuf/releases // 下载并编译、安装  
go get github.com/golang/protobuf/proto   // golang的protobuf库文件
  

  
// 插件
  
go get github.com/golang/protobuf/protoc-gen-go  // 用于根据protobuf生成golang代码,语法 protoc --go_out=. *.proto
  


语法
  book/book.proto
  

syntax="proto3";  
package book;
  

  

// import "xxx/xx.proto"  

  

// 出版社  
message Publisher{
  
required string name = 1
  
}
  
// 书籍信息
  
message Book {
  
required string name = 1;
  
message Author {
  
required string name = 1;
  
required string address = 1;
  
}
  
required Author author = 2;
  

  
enum BookType{
  
SCIENCE = 1 ;
  
LITERATURE = 2;
  
}
  

  
optional BookType type = 3;
  
optional Publisher publisher = 4
  
}
  



  • syntax="proto3":指定protobuf的版本
  • package book:声明一个报名,一般与文件目录名相同
  • import "xxx/xx.proto":导入其他的包,这样你就可以使用其他的包的数据结构
  • required、optional、repeated:表示该字段是否必须填充;required表示必须指定且只能指定一个;当optional表示可选,可指定也可不指定,但不可超过一个不指定值的时候会采用空值,如string类型的字段会用字符串表示;repeated表示可以重复,类似与编程语言中的list
  • message Author:在一个message体内定义一个message结构体
  • enum:是枚举类型结构体
  • 数字:字段的标识符,不可重复
  • 数据类型: int32、int64、uint32、uint64、sint32、sint64、double、float、 string、bool、bytes、enum、message等等
在golang使用
  protobuf采用以上的book.proto文件
  并使用以下命令生成go文件
  

protoc --go_out=. *.proto  

  在代码中使用
  

package main  

  
import (
  
b
"book"  
"github.com/golang/protobuf/proto"
  
)
  

  
func main(){
  
...
  
// 将实例转为proto编码
  
var b = &b.Book{Name:"xxx", Author:b.Author{Name:"yyy"}}
  
protoBook, err := proto.Marshal(b)
  
...
  
// 讲proto编码转化为实例
  
var b2 b.Book
  
err = proto.Unmarshal(protoBook, &b2)
  
...
  
}
  


grpc
  gRPC是由Google主导开发的RPC框架,使用HTTP/2协议并用ProtoBuf作为序列化工具。其客户端提供Objective-C、Java接口,服务器侧则有Java、Golang、C++等接口。使用grpc可以方便的调用其他进程的方法,调用需要传输的数据使用的是proto编码。这对于大型项目来说,可以有效的提高数据的解编码效率和数据传输率。

proto service定义
  一个RPC service就是一个能够通过参数和返回值进行远程调用的method,我们可以简单地将它理解成一个函数。因为gRPC是通过将数据编码成protocal buffer来实现传输的。因此,我们通过protocal buffers interface definitioin language(IDL)来定义service method,同时将参数和返回值也定义成protocal buffer message类型。具体实现如下所示,包含下面代码的文件叫helloworld.proto:
  

syntax = "proto3";  

  
package helloworld;
  

  

// The greeter service definition.  
service Greeter {
  
// Sends a greeting
  
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  
}
  

  
// The request message containing the user's name.
  
message HelloRequest {
  
string name = 1;
  
}
  

  
// The response message containing the greetings
  
message HelloReply {
  
string message = 1;
  
}
  

  接着,根据上述定义的service,我们可以利用protocal buffer compiler ,即protoc生成相应的服务器端和客户端的GoLang代码。生成的代码中包含了客户端能够进行RPC的方法以及服务器端需要进行实现的接口。
  假设现在所在的目录是$GOPATH/src/helloworld/helloworld,我们将通过如下命令生成gRPC对应的GoLang代码:
  

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

  此时,将在目录下生成helloworld.pb.go文件

server
  server.go
  

package main  

  

// server.go  

  
import (
  
"log"
  
"net"
  

  
"golang.org/x/net/context"
  
"google.golang.org/grpc"
  
pb "helloworld/helloworld"
  
)
  

  
const (
  
port = ":50051"
  
)
  

  
type server struct {}
  

  
// 当接收到请求的时候回调用该方法
  
// 参数由grpc自己根据请求进行构造
  
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
  
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
  
}
  

  
func main() {
  
lis, err := net.Listen("tcp", port)
  
if err != nil {
  
log.Fatal("failed to listen: %v", err)
  
}
  
s := grpc.NewServer()
  
pb.RegisterGreeterServer(s, &server{})
  
s.Serve(lis)
  
}
  

  其中pb是我们刚才根据proto生成的go文件的包

client
  

package main  

  

//client.go  

  
import (
  
"log"
  
"os"
  

  
"golang.org/x/net/context"
  
"google.golang.org/grpc"
  
pb "helloworld/helloworld"
  
)
  

  
const (
  
address     = "localhost:50051"
  
defaultName = "world"
  
)
  

  
func main() {
  
// 建立一个grpc连接
  
conn, err := grpc.Dial(address, grpc.WithInsecure())
  
if err != nil {
  
log.Fatal("did not connect: %v", err)
  
}
  
defer conn.Close()
  
// 新建一个客户端,方法为:NewXXXClinent(conn),XXX为你在proto定义的服务的名字
  
c := pb.NewGreeterClient(conn)
  

  
name := defaultName
  
if len(os.Args) >1 {
  
name = os.Args[1]
  
}
  
// 调用远程,并得到返回
  
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
  
if err != nil {
  
log.Fatal("could not greet: %v", err)
  
}
  
log.Printf("Greeting: %s", r.Message)
  
}
  


restful转grpc
  使用grpc的优点很多,二进制的数据可以加快传输速度,基于http2的多路复用可以减少服务之间的连接次数,和函数一样的调用方式也有效的提升了开发效率。不过使用grpc也会面临一个问题,我们的微服务对外一定是要提供Restful接口的,如果内部调用使用grpc,在某些情况下要同时提供一个功能的两套API接口,这样就不仅降低了开发效率,也增加了调试的复杂度。于是就想着有没有一个转换机制,让Restful和gprc可以相互转化。
  grpc-gateway应运而生

安装
  首先你得要根据本文之前的步骤安装proto和grpc,然后如下安装一些库
  

go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway  
go
get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger  
go
get -u github.com/golang/protobuf/protoc-gen-go  


用法

定义service的proto文件
  

syntax = "proto3";  
package example;
  

  
import
"google/api/annotations.proto";  

  
message StringMessage {
  

string value = 1;  
}
  

  
service YourService {
  
rpc Echo(StringMessage) returns (StringMessage) {
  
option (google.api.http)
= {  
post:
"/v1/example/echo"  
body:
"*"  
};
  
}
  
}
  

  option 表示处理哪些path的请求以及如何处理请求体(参数),见https://cloud.google.com/serv...
  生成go文件
  

protoc -I/usr/local/include -I. \  

-I$GOPATH/src \  

-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \  

--go_out=plugins=grpc:. \  
path
/to/your_service.proto  

  
protoc
-I/usr/local/include -I. \  

-I$GOPATH/src \  

-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \  

--grpc-gateway_out=logtostderr=true:. \  
path
/to/your_service.proto  

  以上生成的两个文件,第一个是pb.go文件,给grpc server用的;第二个是pb.gw.go文件,给grpc-gateway用的,用于grpc和restful的相互转化

服务器
  

package main  

  
import (
  

"flag"  
"net/http"
  

  
"github.com/golang/glog"
  
"golang.org/x/net/context"
  
"github.com/grpc-ecosystem/grpc-gateway/runtime"
  
"google.golang.org/grpc"
  

  
gw "path/to/your_service_package"
  
)
  

  
var (
  
echoEndpoint = flag.String("echo_endpoint", "localhost:9090", "endpoint of YourService")
  
)
  

  
func run() error {
  
ctx := context.Background()
  
ctx, cancel := context.WithCancel(ctx)
  
defer cancel()
  

  
mux := runtime.NewServeMux()
  
opts := []grpc.DialOption{grpc.WithInsecure()}
  
err := gw.RegisterYourServiceHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts)
  
if err != nil {
  
return err
  
}
  

  
return http.ListenAndServe(":8080", mux)
  
}
  

  
func main() {
  
flag.Parse()
  
defer glog.Flush()
  

  
if err := run(); err != nil {
  
glog.Fatal(err)
  
}
  
}
  


测试
  

curl -X POST -k http://localhost:8080/v1/example/echo -d '{"name": " world"}  

  
{"message":"Hello  world"}
  

  流程如下:curl用post向gateway发送请求,gateway作为proxy将请求转化一下通过grpc转发给greeter_server,greeter_server通过grpc返回结果,gateway收到结果后,转化成json返回给前端。

Grpc+Grpc Gateway实践
  https://segmentfault.com/a/1190000013339403
  假定我们有一个项目需求,希望用Rpc作为内部API的通讯,同时也想对外提供Restful Api,写两套又太繁琐不符合


  于是我们想到了Grpc以及Grpc Gateway,这就是我们所需要的
DSC0000.png


介绍与环境安装

准备环节
  在正式开始我们的Grpc+Grpc Gateway实践前,我们需要先配置好我们的开发环境


  • Grpc
  • Protoc Plugin
  • Protocol Buffers
  • Grpc-gateway
Grpc

是什么
  Google对Grpc的定义:

A high performance, open-source universal RPC framework
  也就是Grpc是一个高性能、开源的通用RPC框架,具有以下特性:


  • 强大的IDL,使用Protocol Buffers作为数据交换的格式,支持v2、v3(推荐v3)
  • 跨语言、跨平台,也就是Grpc支持多种平台和语言
  • 支持HTTP2,双向传输、多路复用、认证等
安装
  1、官方推荐(需科学上网)
  

go get -u google.golang.org/grpc  

  2、通过github.com
  进入到第一个$GOTPATH目录(因为go get 会默认安装在第一个下)下,新建google.golang.org目录,拉取golang在github上的镜像库:
  

cd /usr/local/go/path/src  

  
mkdir google.golang.org
  

  
cd google.golang.org
/  

  
git clone https:
//github.com/grpc/grpc-go  

  
mv grpc-go/ grpc/
  

  目录结构:
  

google.golang.org/  
└── grpc
  
...
  

  而在grpc下有许多常用的包,例如:


  • metadata:定义了grpc所支持的元数据结构,包中方法可以对MD进行获取和处理
  • credentials:实现了grpc所支持的各种认证凭据,封装了客户端对服务端进行身份验证所需要的所有状态,并做出各种断言
  • codes:定义了grpc使用的标准错误码,可通用
Protoc Plugin

是什么
  编译器插件

安装
  

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

  将Protoc Plugin的可执行文件从$GOPATH中移动到$GOBIN下
  

mv /usr/local/go/path/bin/protoc-gen-go /usr/local/go/bin/  


Protocol Buffers v3

是什么

Protocol buffers are a flexible, efficient, automated mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages. You can even update your data structure without breaking deployed programs that are compiled against the "old" format.
  Protocol Buffers是Google推出的一种数据描述语言,支持多语言、多平台,它是一种二进制的格式,总得来说就是更小、更快、更简单、更灵活,目前分别有v2、v3的版本,我们推荐使用v3


  • proto2 文档地址
  • proto3 文档地址
  建议可以阅读下官方文档的介绍,本系列会在使用时简单介绍所涉及的内容

安装
  

wget https://github.com/google/protobuf/releases/download/v3.5.1/protobuf-all-3.5.1.zip  
unzip protobuf-all-3.5.1.zip
  
cd protobuf-3.5.1/
  
./configure
  
make
  
make install
  

  检查是否安装成功
  

protoc --version  

  如果出现报错
  

protoc: error while loading shared libraries: libprotobuf.so.15: cannot open shared object file: No such fileor directory  

  则执行ldconfig后,再次运行即可成功

为什么要执行ldconfig
  我们通过控制台输出的信息可以知道,Protocol Buffers Libraries的默认安装路径在/usr/local/lib
  

Libraries have been installed in:  

/usr/local/lib  

  
If you ever happen to want to link against installed libraries
  

in a given directory, LIBDIR, you must either use libtool, and  
specify the full pathname of the library, or use the `
-LLIBDIR'  

flag during linking and do at least one of the following:  

- add LIBDIR to the `LD_LIBRARY_PATH' environment variable  
     during execution
  
- add LIBDIR to the `LD_RUN_PATH' environment variable
  
     during linking
  
- use the `-Wl,-rpath -Wl,LIBDIR' linker flag
  
- have your system administrator add LIBDIR to `/etc/ld.so.conf'
  

  
See any operating system documentation about shared libraries for
  
more information, such as the ld(1) and ld.so(8) manual pages.
  

  而我们安装了一个新的动态链接库,ldconfig一般在系统启动时运行,所以现在会找不到这个lib,因此我们要手动执行ldconfig,让动态链接库为系统所共享,它是一个动态链接库管理命令,这就是ldconfig命令的作用

protoc使用
  我们按照惯例执行protoc --help(查看帮助文档),我们抽出几个常用的命令进行讲解
  1、-IPATH, --proto_path=PATH:指定import搜索的目录,可指定多个,如果不指定则默认当前工作目录
  2、--go_out:生成golang源文件

参数
  若要将额外的参数传递给插件,可使用从输出目录中分离出来的逗号分隔的参数列表:
  

protoc --go_out=plugins=grpc,import_path=mypackage:. *.proto  



  • import_prefix=xxx:将指定前缀添加到所有import路径的开头
  • import_path=foo/bar:如果文件没有声明go_package,则用作包。如果它包含斜杠,那么最右边的斜杠将被忽略。
  • plugins=plugin1+plugin2:指定要加载的子插件列表(我们所下载的repo中唯一的插件是grpc)
  • Mfoo/bar.proto=quux/shme: M参数,指定.proto文件编译后的包名(foo/bar.proto编译后为包名为quux/shme)
Grpc支持
  如果proto文件指定了RPC服务,protoc-gen-go可以生成与grpc相兼容的代码,我们仅需要将plugins=grpc参数传递给--go_out,就可以达到这个目的
  

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


Grpc-gateway

是什么

grpc-gateway is a plugin of protoc. It reads gRPC service definition, and generates a reverse-proxy server which translates a RESTful JSON API into gRPC. This server is generated according to custom options in your gRPC definition.
  grpc-gateway是protoc的一个插件。它读取gRPC服务定义,并生成一个反向代理服务器,将RESTful JSON API转换为gRPC。此服务器是根据gRPC定义中的自定义选项生成的。

安装
  

go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway  

  如果出现以下报错,我们分析错误提示可得知是连接超时(大概是被墙了)
  

package google.golang.org/genproto/googleapis/api/annotations: unrecognized import path "google.golang.org/genproto/googleapis/api/annotations" (https fetch: Get https://google.golang.org/genproto/googleapis/api/annotations?go-get=1: dial tcp 216.239.37.1:443: getsockopt: connection timed out)  

  有两种解决方法,
  1、科学上网
  2、通过github.com
  进入到第一个$GOPATH目录的google.golang.org目录下,拉取genproto在github上的go-genproto镜像库:
  

cd /usr/local/go/path/src/google.golang.org  

  
git clone https:
//github.com/google/go-genproto.git  

  
mv go-genproto/ genproto/
  

  在安装完毕后,我们将grpc-gateway的可执行文件从$GOPATH中移动到$GOBIN
  

mv /usr/local/go/path/bin/protoc-gen-grpc-gateway /usr/local/go/bin/  

  到这里我们这节就基本完成了,建议多反复看几遍加深对各个组件的理解!

参考

示例代码


  • grpc-hello-world



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-599143-1-1.html 上篇帖子: 为Go程序创建最小的Docker Image 下篇帖子: Python-IndexError: list index out of range
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表