首页 > 其他分享 >protobuf和gRPC

protobuf和gRPC

时间:2024-03-18 15:58:09浏览次数:25  
标签:protoc protobuf proto gRPC 生成 -- go grpc

目录

一、grpc介绍

  • grpc 是 google 给出的 rpc 调用方式,它基于 google 的 protobuf 定义方式,提供了一整套数据定义和 rpc 传输的方式

  • 它是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHPC# 支持.

https://github.com/grpc/grpc

[img](http://photo.liuqingzheng.top/20220514-v2-2b4ba23bfa128be75d76aedb2376592a_1440w .jpg)

1.1 RPC 介绍

  • 在介绍 grpc 之前有必要首先介绍一下 rpc。RPC 的英文全名是 Remote Procedure Call(远程过程调用),它实现了远程函数或方法的本地调用。由于不在一个内存空间,不能直接调用,因此需要通过网络来表达调用的语义和传达调用的数据。其基本流程如下图所示。

[img](http://photo.liuqingzheng.top/20220514-v2-df46d9a04856eac010142736eb313013_1440w .jpg)

  • 客户端需要调用某个远程函数,首先需要在 client stub 进行函数语义和数据的网络表达,之后将转义好的数据通过 sockets 经过网络传到服务器端,之后同样经过 sever stub 的解析调用远程服务的函数。之后通过同样的链路将函数的结果返回给客户端

1.2 Protobuf 介绍

  • Protobuf 是 Google 给出的一种通用的数据表示方式,通过 proto 文件定义的数据格式,可以一键式的生成 C++,Python,Java 等各种语言实现

  • protobuf经历了protobuf2和protobuf3,pb3比pb2简化了很多,目前主流的版本是pb3

[img](http://photo.liuqingzheng.top/20220514-v2-604520d912ed2e1df2f574001c36793e_1440w .jpg)

二、下载生成proto工具

# 下载地址
http://github.com/protocolbuffers/protobuf/releases
# 把可执行文件加入到环境变量
vim .bash_profile
# proto
PATH=$PATH:/Users/liuqingzheng/soft/protoc-21.0-rc-1-osx-x86_64/bin:
    
####注意:mac下如果下载的是protoc-21.0-rc-1-osx-x86_64,后面使用google内置的proto会找不到,建议下载 https://github.com/protocolbuffers/protobuf/releases/download/v3.20.1/protoc-3.20.1-osx-aarch_64.zip然后配置环境变量

[image-20220514031404183](http://photo.liuqingzheng.top/20220514-image-20220514031404183 .png)

三、下载go的依赖包

# 设置go proxy 代理,下载速度快  
export GOPROXY=https://goproxy.io,direct
# https://goproxy.io/zh/ ,设置成功可以同过go env查看一下
go env

# 此方式弃用
go get github.com/golang/protobuf/protoc-gen-go
# 使用该方式
go install google.golang.org/protobuf/cmd/[email protected]
go install google.golang.org/grpc/cmd/[email protected]

# 运行成功会在go的bin路径下生成一个可执行文件
/Users/liuqingzheng/go/bin/protoc-gen-go
/Users/liuqingzheng/go/bin/protoc-gen-go-grpc

# mac下的话,把该可执行文件复制到/usr/local/bin/路径()
cp /Users/liuqingzheng/go/bin/protoc-gen-go  /usr/local/bin/protoc-gen-go
cp /Users/liuqingzheng/go/bin/protoc-gen-go-grpc  /usr/local/bin/protoc-gen-go-grpc

# Goland安装高亮Protocol 的插件
# Plugins中搜索 Protocol Buffers

四、快速使用

4.1 编写proto

syntax = "proto3";
// 一定要加go的package路径,否则生成不了,意思是把go文件生成到proto文件夹下
// ;proto 表示生成的go文件,包名为proto
option go_package = "../proto;proto";

// 类似于go的结构体,可以定义属性
message HelloRequest {
  string name = 1; // 1 是编号,不是值
//  int32 age =2
}

4.2 生成go脚本

// 执行命令
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative proto/helloworld.proto

// 执行完生成一个helloworld.pb.go
在这个go文件中,可以找到咱们定义的结构体

[image-20220514041007425](http://photo.liuqingzheng.top/20220514-image-20220514041007425 .png)

4.3 编写main.go

package main

import (
	"encoding/json"
	"fmt"
	"go_test_learn/proto"
)
import pb "github.com/golang/protobuf/proto" // 导入这个模块
func main()  {
	// proto 格式编码解码
	req:=proto.HelloRequest{Name: "lqz"}
	r,_:=pb.Marshal(&req)
	fmt.Println(string(r))

	// 解码
	req2:=proto.HelloRequest{Name: "lqz"}
	pb.Unmarshal(r,&req2)
	fmt.Println(req2)

	// json 格式编码解码
	type  HelloRequest struct{
		Name string
	}
	h:=HelloRequest{Name: "lqz"}
	r1,_:=json.Marshal(&h)
	fmt.Println(string(r1))

	h1:=HelloRequest{}
	json.Unmarshal(r1,&h1)
	fmt.Println(h1)

}

4.4 修改proto,加入更多参数

syntax = "proto3";
// 一定要加go的package路径,否则生成不了,意思是把go文件生成到proto文件夹下
// ;proto 表示生成的go文件,包名为proto
option go_package = "../proto;proto";

// 类似于go的结构体,可以定义属性
message HelloRequest {
  string name = 1; // 1 是编号,不是值
  int32 age = 2;
  repeated string girls = 3;

}

4.5 重新生成

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative proto/helloworld.proto

4.6 修改代码

package main

import (
	"encoding/json"
	"fmt"
	"go_test_learn/proto"
)
import pb "github.com/golang/protobuf/proto" // 导入这个模块

func main() {
	// proto 格式编码解码
	req := proto.HelloRequest{Name: "lqz", Age: 19, Girls: []string{"刘亦菲", "迪丽热巴"}}
	r, _ := pb.Marshal(&req)
	fmt.Println(string(r))

	// 解码
	req2 := proto.HelloRequest{}
	pb.Unmarshal(r, &req2)
	fmt.Println(req2)

	// json 格式编码解码
	type HelloRequest struct {
		Name string
		Age int32
		Girls []string
	}
	h := HelloRequest{Name: "lqz",Age: 19, Girls: []string{"刘亦菲", "迪丽热巴"}}
	r1, _ := json.Marshal(&h)
	fmt.Println(string(r1))

	h1 := HelloRequest{}
	json.Unmarshal(r1, &h1)
	fmt.Println(h1)

}

五、完整的客户端服务端

5.1 proto文件

syntax = "proto3";
option go_package = ".;proto";
// 定义一个服务,gRPC自有的,它需要用grpc插件生成,也就是咱们安装的那个插件
service Hello{
  // 服务内有一个函数叫Hello,接收HelloRequest类型参数,返回HelloResponse类型参数
  rpc Hello(HelloRequest) returns(HelloResponse);
}

// 类似于go的结构体,可以定义属性
message HelloRequest {
  string name = 1; // 1 是编号,不是值
  int32 age = 2;
  repeated string girls = 3;

}
// 定义一个响应的类型
message HelloResponse {
  string reply =1;
}

5.2 生成go脚本

# protoc 可执行文件必须加入环境变量

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative proto/helloworld.proto

5.3 server.go

package main

import (
	"context"
	"fmt"
	"go_test_learn/proto"
	"google.golang.org/grpc"
	"net"
)

// 定义一个结构体,名字随意,只要实现Hello方法即可,相当于proto的service
type HelloServer struct {
	proto.UnimplementedHelloServer
}


// 给结构体绑定Hello方法
// 请求参数必须是两个:context context.Context, request *proto.HelloRequest
// 返回参数必须是两个:*proto.HelloResponse, error
// 自动生成的go文件中结构体中的字段给转成大写字母开头,符合go导出字段规范

func (s *HelloServer) Hello(context context.Context, request *proto.HelloRequest) (*proto.HelloResponse, error) {
	fmt.Println(request.Name)
	return &proto.HelloResponse{Reply:"ok"}, nil
}
func main() {
	// 创建一个grpc对象
	g := grpc.NewServer()
	s := HelloServer{}
	// 把s注册到g对象中
	proto.RegisterHelloServer(g,&s)
	// 监听端口
	lis,error:=net.Listen("tcp","0.0.0.0:50052")
	if error!=nil{
		panic("启动服务异常")
	}
	g.Serve(lis)


}

5.4 client.go

package main

import (
	"context"
	"fmt"
	"go_test_learn/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {

	// 连接grpc服务端,grpc.WithInsecure()跳过对服务器的验证
	//conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithInsecure())
	conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		panic("连接服务异常")
	}
	//defer 关闭
	defer conn.Close()
	//创建一个客户端,传入连接对象
	client := proto.NewHelloClient(conn)
	request := proto.HelloRequest{
		Name: "lqz",
	}
	//调用SayHello方法,传入两个参数
	res, err := client.Hello(context.Background(), &request)
	if err != nil {
		panic("调用方法异常")
	}
	//打印出返回的数据
	fmt.Println(res.Reply)
}

六、注意

6.1 protoc语法

-I 或者 --proto_path:用于指定所编译的源码,就是我们所导入的proto文件,支持多次指定,按照顺序搜索,如果未指定,则使用当前工作目录。

--go_out:同样的也有其他语言的,例如--java_out、--csharp_out,用来指定语言的生成位置,用于生成*.pb.go 文件

--go_opt:paths=source_relative 指定--go_out生成文件是基于相对路径的

--go-grpc_out:用于生成 *_grpc.pb.go 文件

--go-grpc_opt:

paths=source_relative 指定--go_grpc_out生成文件是基于相对路径的

require_unimplemented_servers=false 默认是true,会在server类多生成一个接口

--grpc-gateway_out:是使用到了 protoc-gen-grpc-gateway.exe 插件,用于生成pb.gw.go文件

--grpc-gateway_opt:

logtostderr=true 记录log

paths=source_relative 指定--grpc-gateway_out生成文件是基于相对路径的

generate_unbound_methods=true 如果proto文件没有写api接口信息,也会默认生成

--openapiv2_out:使用到了protoc-gen-openapiv2.exe 插件,用于生成swagger.json 文件

6.2 mustEmbedUnimplemented***方法问题

// 新版protoc-gen-go不支持grpc服务生成,需要通过protoc-gen-go-grpc生成grpc服务接口,但是生成的Server端接口中会出现一个mustEmbedUnimplemented***方法,是为了解决前向兼容问题,如果不解决,就无法传递给RegisterXXXService方法,解决办法有两个:
1. 在grpc server实现结构体中匿名嵌入Unimplemented***Server结构体
  type HelloServer struct {
    proto.UnimplementedHelloServer
  }
2. 使用protoc生成server代码时命令行加上关闭选项protoc --go-grpc_out=require_unimplemented_servers=false

//protoc --go_out=. --go_opt=paths=source_relative proto/helloworld.proto
//protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false --go-grpc_opt=paths=source_relative proto/helloworld.proto

标签:protoc,protobuf,proto,gRPC,生成,--,go,grpc
From: https://www.cnblogs.com/Mcoming/p/18080561

相关文章

  • protobuf进阶
    目录protobuf进阶一、protobuf基本类型和默认值1.1protobuf类型和语言对应关系1.2protobuf默认值3.案例(1)目录结构(2)hello.proto(3)生成go文件(4)client/main.go(5)server/main.go二、optiongo_package的作用三、服务端客户端同步问题3.1顺序导致的错误(1)目录结构(2)server/proto/hello......
  • gRPC的四种通信模式
    目录gRPC的四种通信模式一、RPC(UnaryRPC)二、服务端流RPC三、客户端流RPC四、双向流RPC五、综合案例5.1新建streamdemo.proto5.2执行命令,生成go文件5.3客户端5.4服务端六、总结gRPC的四种通信模式gRPC有四种通信⽅式,分别是:简单RPC(UnaryRPC)、服务端流式RPC(Serverstrea......
  • gRPC进阶
    目录一、grpcmetadata机制1.1proto1.2生成go文件1.3服务端1.4客户端二、grpc拦截器interceptor2.1服务端拦截器grpc.UnaryInterceptor(interceptor)2.2客户端拦截器2.3开源拦截器三、通过metadata+拦截器实现认证3.1自定义(1)服务端(2)客户端(3)proto3.2WithPerRPCCredential......
  • gRPC重试与接口幂等性
    目录一、gRPC超时重试1.1客户端1.2服务端1.3proto二、接口幂等性2.1什么是幂等性2.2什么情况下需要幂等2.3如何保证幂等(1)token机制(2)关键点先删除token,还是后删除token(3)token机制缺点(4)乐观锁机制(5)唯一主键(6)防重表(7)唯一ID(8)唯一ID机制一、gRPC超时重试使用开源的;https://g......
  • 除gRPC之外的另一个选择,IceRPC-支持QUIC
    作者引言自从19年开始接触到RPC,当时完全没有相关概念,接触到的都是http,tcp等,当时公司用的是zeroc出品的ice框架,对应rpc非常强大,跨平台,跨语言。可惜的国内并不是主流,主流是gRPC,万物诸途同归,最终的目地是一样的。主要上看谁简单,方便,好理解。就在去年重新出一个新的RPC框架IceRP......
  • gRPC入门学习之旅(二)
       gRPC入门学习之旅(一)    gRPC是一个高性能、通用的开源远程过程调用(RPC)框架,基于底层HTTP/2协议标准和协议层Protobuf序列化协议开发,支持众多的开发语言,由Google开源。    gRPC也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服......
  • Golang - grpc和http的区别
    gRPC和HTTP都是网络协议,但是它们之间存在一些显著的区别。1、传输协议HTTP使用文本基础的协议,而gRPC使用的是二进制协议,这意味着gRPC数据包更小,传输效率更高。另外,gRPC使用HTTP/2协议,支持多路复用,从而可以更好地处理并发请求。2、性能差异gRPC在性能方面优于HTTP。由于使用了二进......
  • Gossip协议和Grpc协议的区别
    Gossip协议Gossip协议,也称为流言协议,是一种基于成员之间随机通信的分布式系统同步协议。它模仿社会中流言的传播方式,通过节点之间的随机交互传播信息,以此实现数据的最终一致性。Gossip协议特别适合于大规模、分布式的环境中,因为它能够很好地处理节点故障和网络分区问题。特点:......
  • xlua - 增加protobuf库
    lua下常用的2个protobuf库1) GitHub-starwing/lua-protobuf:ALuamoduletoworkwithGoogleprotobuf2) GitHub-cloudwu/pbc:AprotocolbufferslibraryforC这边使用第1个库 a)下载lua-protobuf源码,并解压到xlua的build文件夹根目录 b)CMakeLists.txt中......
  • gRPC-第二代rpc服务
    在如今云原生技术的大环境下,rpc服务作为最重要的互联网技术,蓬勃发展,诞生了许多知名基于rpc协议的框架,其中就有本文的主角gRPC技术。一款高性能、开源的通用rpc框架作者作为一名在JD实习的Cpper,经过一段时间的学习和实践,发现了C++与Java之间的种种不同,这也让我产生了一个想法:既......