最新要闻
- 热讯:华硕发布新款Vivobook Go 14/15 OLED笔记本:高配酷睿i3-N305
- 天天观速讯丨真我GT Neo5 1TB存储网友用掉了500多G:直呼“真香”
- 全球视讯!Win12及DX13要来了?微软预告DirectX全新功能
- 通信能力是5G的10倍!全球17家运营商发布6G白皮书:预计2030商用
- 熬夜伤不起!警惕睡眠质量受电子设备影响
- 个人交340单位交680退休能拿多少?主要看个人缴纳的部分
- 焦点速讯:匹克态极速浪跑鞋99元抄底:门店299元
- 天天热文:女子公园租电动车3个多小时扣466元 运营方:上海价格都是这
- 焦点滚动:别羡慕了!经常不分场合秒睡可能是种病:得看医生
- 全球观速讯丨宁德时代麒麟电池已量产 同体积比特斯拉电池电量高13%
- 世界新消息丨省的都是钱 长安汽车推“百亿补贴”:不到11万可买CS75 PLUS
- 世界速递!三主摄时代来了!OPPO Find X6 Pro大漠银月图赏
- 全球看点:49dB降噪行业第一!OPPO Enco Free3图赏
- 1949元 ROG魔方幻“月曜白”路由器今晚开售:三频万兆速率
- 今热点:微软Bing上线在线绘图功能:文字生成图片 仅支持英文
- 天天快资讯:安全性远超燃油车!特斯拉发布调查报告:是美国平均水平7.4倍
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
每日消息!微服务与rpc/grpc
微服务(micro services)
微服务概述
微服务简介
微(micro)狭义来讲就是体积小,著名的"2 pizza 团队"很好的诠释了这一解释(2 pizza 团队最早是亚马逊 CEO Bezos提出来的,意思是说单个服务的设计,所有参与人从设计、开发、测试、运维所有人加起来 只需要2个披萨就够了 )。
服务(service)区别于系统,服务一个或者一组相对较小且独立的功能单元,是用户可以感知最小功能集
从广义上来讲,微服务是一种分布式系统解决方案,推动细粒度服务的使用,这些服务协同工作
(相关资料图)
微服务架构
微服务架构风格是将单个应用程序作为一组小型服务开发的方法,每个服务程序都在自己的进程中运行,并与轻量级机制(通常是HTTP资源API)进行通信。这些服务是围绕业务功能构建的。可以通过全自动部署机器独立部署。这些服务器可以用不同的编程语言编写,使用不同的数据存储技术,并尽量不用集中式方式进行管理
微服务架构是将复杂的系统使用组件化的方式进行拆分,并使用轻量级通讯方式进行整合的一种设计方法。
微服务是通过这种架构设计方法拆分出来的一个独立的组件化的小应用。
微服务架构定义的精髓,就是“分而治之,合而用之”。
将复杂的系统进行拆分的方法,就是“分而治之”。分而治之,可以让复杂的事情变的简单
使用轻量级通讯等方式进行整合的设计,就是“合而用之”的方法,合而用之可以让微小的力量变动强大
服务拆分原则:高内聚低耦合
微服务和单体架构
单体架构的问题和缺陷
复杂性逐渐变高:代码越多复杂性越高,越难解决遇到的问题
技术债务逐渐上升:人员流动越大所留下的坑越多,也就是所谓的技术债务越来越多
耦合度太高,维护成本大
持续交付周期长
技术选型成本高:单块架构倾向于采用统一的技术平台或方案来解决所有问题,如果后续想引入新的技术或框架,成本和风险都很大
可扩展性差:随着功能的增加,垂直扩展的成本将会越来越大;而对于水平扩展而言,因为所有代码都运行在同一个进程,没办法做到针对应用程序的部分功能做独立的扩展
微服务架构的解决方案
单一职责
微服务架构中的每个服务,都是具有业务逻辑的,符合高内聚、低耦合原则以及单一职责原则的单元,不同的服务通过“管道”的方式灵活组合,从而构建出庞大的系统。
轻量级通信
服务之间通过轻量级的通信机制实现互通互联,而所谓的轻量级,通常指语言无关、平台无关的交互方式。
对于轻量级通信的格式而言,例如 XML 和 JSON,它们是语言无关、平台无关的
对于通信的协议而言,通常基于 HTTP,能让服务间的通信变得标准化、无状态化
使用轻量级通信机制,可以让团队选择更适合的语言、工具或者平台来开发服务本身。
独立性
每个服务在应用交付过程中,独立地开发、测试和部署
在单体式架构中:所有功能都在同一个代码库,功能的开发不具有独立性;当不同小组完成多个功能后,需要经过集成和回归测试,测试过程也不具有独立性;当测试完成后,应用被构建成一个包,如果某个功能存在 bug,将导致整个部署失败或者回滚
在微服务架构中:每个服务都是独立的业务单元,与其他服务高度解耦,只需要改变当前服务本身,就可以完成独立的开发、测试和部署
进程隔离
单块架构中,整个系统运行在同一个进程中,当应用进行部署时,必须停掉当前正在运行的应用,部署完成后再重启进程,无法做到独立部署。
在微服务架构中,应用程序由多个服务组成,每个服务都是高度自治的独立业务实体,可以运行在独立的进程中,不同的服务能非常容易地部署到不同的主机上。
微服务架构的缺陷
运维要求高,难度大
分布式的复杂性,导致bug不好调试
接口成本高:一旦微服务的接口发生大的变动,那么所有依赖它的微服务都要做相应的调整,由于微服务可能非常多,那么调整接口造成的成本会很高。
重复劳动:对于单体架构来讲,如果某段业务被多个模块所共同使用,便可以抽象成一个工具类,被所有模块直接调用,但是微服务却无法这样做,因为这个微服务的工具类是不能被其它微服务所直接调用的,从而我们便不得不在每个微服务上都建这么一个工具类,从而导致代码的重复。
业务不好分离
单体架构和微服务架构的对比
新功能开发 | 所需要时间 | 开发和实现的难易度 |
---|---|---|
传统单体架构 | 分布式微服务化架构 | |
部署 | 不经常而且容易部署 | 经常发布,部署复杂 |
隔离性 | 故障影响范围大 | 故障影响范围小 |
架构设计 | 初期技术选型难度大 | 设计逻辑难度大 |
系统性能 | 相对时间快,吞吐量小 | 相对时间慢,吞吐量大 |
系统运维 | 运维难度简单 | 运维难度复杂 |
新人上手 | 学习曲线大(应用逻辑) | 学习曲线大(架构逻辑) |
技术 | 技术单一而且封闭 | 技术多样而且容易开发 |
测试和差错 | 简单 | 复杂(每个服务都要进行单独测试,还需要集群测试) |
系统扩展性 | 扩展性差 | 扩展性好 |
系统管理 | 重点在于开发成本 | 重点在于服务治理和调度 |
RPC协议
RPC概述
RPC(Remote Procedure Call Protocol),是远程过程调用的缩写,通俗的说就是调用远处的一个函数
使用微服务化的一个好处就是,不限定服务的提供方使用什么技术选型,能够实现公司跨团队的技术解耦,如下图:
这样的话,如果没有统一的服务框架(RPC框架),各个团队的服务提供方就需要各自实现一套序列化、反序列化、网络框架、连接池、收发线程、超时处理、状态机等“业务之外”的重复技术劳动,造成整体的低效。所以,统一RPC框架把上述“业务之外”的技术劳动统一处理,是服务化首要解决的问题。
go语言中使用RPC
Go语言的RPC规则:方法只能有两个可序列化的参数,其中第二个参数是指针类型,并且返回一个error类型,同时必须是公开的方法。
golang 中的类型比如:channel(通道)、complex(复数类型)、func(函数)均不能进行 序列化
Server端
func main(){ // rpc注册服务, 注册rpc服务,维护一个hash表,key值是服务名称,value值是服务的地址 rpc.RegisterName("HelloService", new(HelloService)) // 设置服务监听 listener,err := net.Listen("tcp","127.0.0.1:8888") if err != nil { panic(err) } // 接受传输的数据 conn,err := listener.Accept() if err != nil { panic(err) } // rpc调用,并返回执行后的数据 // 1.read,获取服务名称和方法名,获取请求数据 // 2.调用对应服务里面的方法,获取传出数据 // 3.write,把数据返回给client rpc.ServeConn(conn)}
rpc.Register
函数调用会将对象类型中所有满足RPC规则的对象方法注册为RPC函数,所有注册的方法会放在“HelloService”服务空间之下然后建立一个唯一的TCP链接,并且通过
rpc.ServeConn
函数在该TCP链接上为对方提供RPC服务
Client端
func main(){ //用rpc连接 client,err := rpc.Dial("tcp","127.0.0.1:8888") if err != nil { panic(err) } var reply string //调用服务中的函数 err = client.Call("HelloService.Hello","world",&reply) // 这里的HelloServer就是Server注册的服务名,然后Hello就是这个服务下的Hello方法 if err != nil { panic(err) } fmt.Println("收到的数据为,",reply)}
首选是通过
rpc.Dial
拨号RPC服务,然后通过client.Call
调用具体的RPC方法在调用
client.Call
时,第一个参数是用点号链接的RPC服务名字和方法名字,第二和第三个参数分别定义RPC方法的两个参数。
跨语言的RPC
RPC协议封装
未封装的代码服务名都是写死的,不够灵活,且容易写错,所以这里对RPC的服务端和客户端再次进行一次封装,来屏蔽掉服务名。
服务端封装
package mainimport ( "fmt" "net" "net/rpc" "net/rpc/jsonrpc")var ServiceName = "login"// 定义一个接口,作用是限定我们自己定义的RegisterRPC函数的参数类型,以及它要实现的方法type RPCDesign interface { Say(string, *string) error}// 定义要注册到RPC的类和方法,这里Persion实现的了Say方法,所以它可以作为RegisterRPC的参数type Person struct{}func (p *Person) Say(in string, out *string) error { *out = in + "hahahahahaha" return nil}// 把RPC的注册方法再封装一层func RegisterRPC(srv RPCDesign) error { return rpc.RegisterName(ServiceName, srv)}func main() { // 设置监听 Listener, err := net.Listen("tcp", "127.0.0.1:9999") if err != nil { fmt.Println("listen failed, err: ", err) return } defer Listener.Close() fmt.Println("Start Listening...") for { // 开始接受连接 conn, err := Listener.Accept() if err != nil { fmt.Println("connection err: ", err) return } defer conn.Close() fmt.Println(conn.RemoteAddr().String() + "connect success.") // 注册RPC err = RegisterRPC(new(Person)) if err != nil { fmt.Println("Regist RPC failed, err: ", err) return } // 把rpc服务和套接字绑定 rpc.ServeConn(conn) // 跨语言的版本,使用json编码 //rpc.ServeCodec(jsonrpc.NewServerCodec(conn)) }}
客户端封装
package mainimport ( "fmt" "net/rpc")// rpc.Dial是*rpc.Client的方法,所以这里吧*rpc.client封装起来type RPCClient struct { client *rpc.Client}// 创建RPCClient的构造方法func NewRPCClient(addr string) *RPCClient { conn, err := rpc.Dial("tcp", addr) if err != nil { fmt.Println("connect failed, err: ", err) return new(RPCClient) } return &RPCClient{client: conn}}// 为RPCClient添加一个CallFunc来对Server端调用func (r *RPCClient) CallFunc(req string, rest *string) (err error) { err = r.client.Call("login.Say", req, rest) if err != nil { fmt.Println("call failed, err: ", err) return } return}func main() { client := NewRPCClient("127.0.0.1:9999") var res string client.CallFunc("hgzerowzh", &res) fmt.Println("receive value: ", res)}
protobuf
protobuf概述
protobuf简介
Protobuf是Protocol Buffers的简称,它是Google公司开发的一种数据描述语言,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化 。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。
protobuf是类似与json一样的数据描述语言(数据格式)
protobuf非常适合于RPC数据交换格式
注意:protobuf
本身并不是和gRPC
绑定的。它也可以被用于非RPC场景,如存储等
protobuf的优劣势
1)优势:
序列化后体积相比Json和XML很小,适合网络传输
序列化反序列化速度很快,快于Json的处理速度
消息格式升级和兼容性还不错
支持跨平台多语言
2)劣势:
应用不够广(相比xml和json)
二进制格式导致可读性差
缺乏自描述
protoc安装(windows)
protoc就是protobuf的编译器,它把proto文件编译成不同的语言
下载安装protoc编译器(protoc)
下载protobuf:https://github.com/protocolbuffers/protobuf/releases/download/v3.20.1/protoc-3.20.1-win64.zip
解压后,将目录中的 bin 目录的路径添加到系统环境变量,然后打开cmd输入protoc
查看输出信息,此时则安装成功
安装protocbuf的go插件(protoc-gen-go)
由于protobuf并没直接支持go语言需要我们手动安装相关插件
protocol buffer编译器需要一个插件来根据提供的proto文件生成 Go 代码,Go1.16+要使用下面的命令安装插件:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest // 目前最新版是v1.3.0
安装grpc(grpc)
go get -u -v google.golang.org/grpc@latest // 目前最新版是v1.53.0
安装grpc的go插件(protoc-gen-go-grpc)
说明:在google.golang.org/protobuf
中,protoc-gen-go
纯粹用来生成pb序列化相关的文件,不再承载gRPC代码生成功能,所以如果要生成grpc相关的代码需要安装grpc-go相关的插件:protoc-gen-go-grpc
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest // 目前最新版是v1.3.0
protobuf语法
protobuf语法概述
类型:类型不仅可以是标量类型(
int
、string
等),也可以是复合类型(enum
等),也可以是其他message
字段名:字段名比较推荐的是使用下划线/分隔名称
字段编号:一个message内每一个字段编号都必须唯一的,在编码后其实传递的是这个编号而不是字段名
字段规则:消息字段可以是以下字段之一
singular
:格式正确的消息可以有零个或一个字段(但不能超过一个)。使用 proto3 语法时,如果未为给定字段指定其他字段规则,则这是默认字段规则optional
:与singular
相同,不过可以检查该值是否明确设置repeated
:在格式正确的消息中,此字段类型可以重复零次或多次。系统会保留重复值的顺序map
:这是一个成对的键值对字段
保留字段:为了避免再次使用到已移除的字段可以设定保留字段。如果任何未来用户尝试使用这些字段标识符,编译器就会报错
简单语法
proto文件基本语法
syntax = "proto3"; // 指定版本信息,不指定会报错package pb; // 后期生成go文件的包名// message为关键字,作用为定义一种消息类型message Person{ string name = 1; // 名字 int32 age = 2 ; // 年龄}enum test{ int32 age = 0;}
protobuf消息的定义(或者称为描述)通常都写在一个以 .proto 结尾的文件中:
第一行指定正在使用
proto3
语法:如果不这样做,协议缓冲区编译器将假定正在使用proto2(这也必须是文件的第一个非空的非注释行)第二行package指明当前是pb包(生成go文件之后和Go的包名保持一致)
message关键字定义一个Person消息体,类似于go语言中的结构体,是包含一系列类型数据的集合。
许多标准的简单数据类型都可以作为字段类型,包括
bool
,int32
,float
,double
,和string
也可以使用其他message类型作为字段类型。
在message中有一个字符串类型的value成员,该成员编码时用1代替名字。在json中是通过成员的名字来绑定对应的数据,但是Protobuf编码却是通过成员的唯一编号来绑定对应的数据,因此Protobuf编码后数据的体积会比较小,能够快速传输,缺点是不利于阅读。
message常见的数据类型与go中类型对比
.proto类型 | Go类型 | 介绍 |
---|---|---|
double | float64 | 64位浮点数 |
float | float32 | 32位浮点数 |
int32 | int32 | 使用可变长度编码。编码负数效率低下——如果你的字段可能有负值,请改用sint32。 |
int64 | int64 | 使用可变长度编码。编码负数效率低下——如果你的字段可能有负值,请改用sint64。 |
uint32 | uint32 | 使用可变长度编码。 |
uint64 | uint64 | 使用可变长度编码。 |
sint32 | int32 | 使用可变长度编码。符号整型值。这些比常规int32s编码负数更有效。 |
sint64 | int64 | 使用可变长度编码。符号整型值。这些比常规int64s编码负数更有效。 |
fixed32 | uint32 | 总是四字节。如果值通常大于228,则比uint 32更有效 |
fixed64 | uint64 | 总是八字节。如果值通常大于256,则比uint64更有效 |
sfixed32 | int32 | 总是四字节。 |
sfixed64 | int64 | 总是八字节。 |
bool | bool | 布尔类型 |
string | string | 字符串必须始终包含UTF - 8编码或7位ASCII文本 |
bytes | []byte | 可以包含任意字节序列 |
protobuff语法进阶
message嵌套
messsage除了能放简单数据类型外,还能存放另外的message类型:
syntax = "proto3"; // 指定版本信息,不指定会报错package pb; // 后期生成go文件的包名// message为关键字,作用为定义一种消息类型message Person{ string name = 1; // 名字 int32 age = 2 ; // 年龄 // 定义一个message message PhoneNumber { string number = 1; int64 type = 2; } PhoneNumber phone = 3;}
message成员编号,可以不从1开始,但是不能重复,不能使用19000 - 19999
repeated关键字
repeadted关键字类似与go中的切片,编译之后对应的也是go的切片,用法如下:
syntax = "proto3"; // 指定版本信息,不指定会报错package pb; // 后期生成go文件的包名// message为关键字,作用为定义一种消息类型message Person{ string name = 1; // 名字 int32 age = 2 ; // 年龄 // 定义一个message message PhoneNumber { string number = 1; int64 type = 2; } repeated PhoneNumber phone = 3;}
默认值
解析数据时,如果编码的消息不包含特定的单数元素,则解析对象对象中的相应字段将设置为该字段的默认值
不同类型的默认值不同,具体如下:
对于字符串,默认值为空字符串
对于字节,默认值为空字节
对于bools,默认值为false
对于数字类型,默认值为零
对于枚举,默认值是第一个定义的枚举值,该值必须为0。
repeated字段默认值是空列表
message字段的默认值为空对象
enum关键字
在定义消息类型时,可能会希望其中一个字段有一个预定义的值列表
比如说,电话号码字段有个类型,这个类型可以是,home,work,mobile
我们可以通过enum在消息定义中添加每个可能值的常量来非常简单的执行此操作。示例如下:
syntax = "proto3"; // 指定版本信息,不指定会报错package pb; // 后期生成go文件的包名// message为关键字,作用为定义一种消息类型message Person{ string name = 1; // 名字 int32 age = 2 ; // 年龄 // 定义一个message message PhoneNumber { string number = 1; PhoneType type = 2; } repeated PhoneNumber phone = 3;}// enum为关键字,作用为定义一种枚举类型enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2;}
如上,enum的第一个常量映射为0,每个枚举定义必须包含一个映射到零的常量作为其第一个元素。这是因为:
必须有一个零值,以便我们可以使用0作为数字默认值。
零值必须是第一个元素,以便与proto2语义兼容,其中第一个枚举值始终是默认值。
enum还可以为不同的枚举常量指定相同的值来定义别名。如果想要使用这个功能必须将allow_alias
选项设置为true,负责编译器将报错。示例如下:
syntax = "proto3"; // 指定版本信息,不指定会报错package pb; // 后期生成go文件的包名// message为关键字,作用为定义一种消息类型message Person{ string name = 1; // 名字 int32 age = 2 ; // 年龄 // 定义一个message message PhoneNumber { string number = 1; PhoneType type = 2; } repeated PhoneNumber phone = 3;}// enum为关键字,作用为定义一种枚举类型enum PhoneType { // 如果不设置将报错 option allow_alias = true; MOBILE = 0; HOME = 1; WORK = 2; Personal = 2;}
oneof关键字
如果有一个包含许多字段的消息,并且最多只能同时设置其中的一个字段,则可以使用oneof功能,示例如下:
message Person{ string name = 1; // 名字 int32 age = 2 ; // 年龄 //定义一个message message PhoneNumber { string number = 1; PhoneType type = 2; } repeated PhoneNumber phone = 3; oneof data{ string school = 5; int32 score = 6; }}
定义RPC服务
如果需要将message与RPC一起使用,则可以在.proto
文件中定义RPC服务接口,protobuf编译器将根据你选择的语言生成RPC接口代码。示例如下:
//定义RPC服务service HelloService { rpc Hello (Person)returns (Person);}
注意:默认protobuf编译期间,不编译服务,如果要想让其编译,需要使用gRPC
protobuf编译
编译器调用
protobuf 编译是通过编译器 protoc 进行的,通过这个编译器,我们可以把 .proto 文件生成 go,Java,Python,C++, Ruby或者C# 代码
可以使用以下命令来通过 .proto 文件生成go代码(以及grpc代码)
// 将当前目录中的所有 .proto文件进行编译生成go代码protoc --go_out=./ --go_opt=paths=source_relative *.proto
protobuf 编译器会把 .proto 文件编译成 .pd.go 文件
--go_out 参数
作用:指定go代码生成的基本路径
protocol buffer编译器会将生成的Go代码输出到命令行参数
go_out
指定的位置go_out
标志的参数是你希望编译器编写 Go 输出的目录编译器会为每个
.proto
文件输入创建一个源文件输出文件的名称是通过将
.proto
扩展名替换为.pb.go
而创建的
--go_opt 参数
protoc-gen-go
提供了--go_opt
参数来为其指定参数,可以设置多个:
paths=import
:生成的文件会按go_package
路径来生成,当然是在--go_out
目录例如,
go_out/$go_package/pb_filename.pb.go
如果未指定路径标志,这就是默认输出模式
paths=source_relative
:输出文件与输入文件放在相同的目录中例如,一个
protos/buzz.proto
输入文件会产生一个位于protos/buzz.pb.go
的输出文件。
module=$PREFIX
:输出文件放在以 Go 包的导入路径命名的目录中,但是从输出文件名中删除了指定的目录前缀。例如,输入文件
pros/buzz.proto
,其导入路径为example.com/project/protos/fizz
并指定example.com/project
为module
前缀,结果会产生一个名为pros/fizz/buzz.pb.go
的输出文件。在module路径之外生成任何 Go 包都会导致错误,此模式对于将生成的文件直接输出到 Go 模块非常有用。
--proto_path 参数
--proto_path=IMPORT_PATH
IMPORT_PATH是 .proto 文件所在的路径,如果忽略则默认当前目录。
如果有多个目录则可以多次调用--proto_path,它们将会顺序的被访问并执行导入。
使用示例:
protoc --proto_path=src --go_out=out --go_opt=paths=source_relative foo.proto bar/baz.proto// 编译器将从 `src` 目录中读取输入文件 `foo.proto` 和 `bar/baz.proto`,并将输出文件 `foo.pb.go` 和 `bar/baz.pb.go` 写入 `out` 目录。如果需要,编译器会自动创建嵌套的输出子目录,但不会创建输出目录本身
使用grpc的go插件
安装proto-gen-go-grpc
在google.golang.org/protobuf
中,protoc-gen-go
纯粹用来生成pb序列化相关的文件,不再承载gRPC代码生成功能。生成gRPC相关代码需要安装grpc-go相关的插件protoc-gen-go-grpc
// 安装protoc-gen-go-grpcgo install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest // 目前最新版是v1.3.0
生成grpc的go代码:
// 主要是--go_grpc_out参数会生成go代码protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative *.proto
--go-grpc_out 参数
作用:指定grpc go代码生成的基本路径
命令会产生的go文件:
protoc-gen-go
:包含所有类型的序列化和反序列化的go代码protoc-gen-go-grpc
:包含service中的用来给client调用的接口定义以及service中的用来给服务端实现的接口定义
--go-grpc_opt 参数
和protoc-gen-go
类似,protoc-gen-go-grpc
提供 --go-grpc_opt
来指定参数,并可以设置多个
github.com/golang/protobuf
和 google.golang.org/protobuf
github.com/golang/protobuf
github.com/golang/protobuf
github.com/golang/protobuf
现在已经废弃它可以同时生成pb和gRPC相关代码的
用法:
// 它在--go_out加了plugin关键字,paths参数有两个选项,分别是 import 和 source_relative--go_out=plugins=grpc,paths=import:. *.proto
google.golang.org/protobuf
它
github.com/golang/protobuf
的升级版本,v1.4.0
之后github.com/golang/protobuf
仅是google.golang.org/protobuf
的包装它纯粹用来生成pb序列化相关的文件,不再承载gRPC代码生成功能,生成gRPC相关代码需要安装grpc-go相关的插件
protoc-gen-go-grpc
用法:
// 它额外添加了参数--go-grpc_out以调用protoc-gen-go-grpc插件生成grpc代码protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative *.proto
GRPC协议
grpc概述
gRPC
是谷歌开源的一款高性能、支持多种开发语言的服务框架
序列化协议。gRPC
使用protobuf
,首先使用protobuf定义服务,然后使用这个文件来生成客户端和服务端的代码。因为pb是跨语言的,因此即使服务端和客户端语言并不一致也是可以互相序列化和反序列化的
网络传输层。gRPC使用http2.0
协议,http2.0
相比于HTTP 1.x
,大幅度的提升了 web 性能
grpc的安装
由于通常都是配合 gRPC 来使用 protobuf ,所以我们也需要基于.proto
文件生成Go代码的同时生成 gRPC 代码。
生成 gRPC 代码需要先安装grpc
以及 protoc-gen-go-grpc
插件。
// 安装grpcgo get -u -v google.golang.org/grpc@latest // 目前最新版是v1.53.0// 安装protoc-gen-go-grpc插件go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest // 目前最新版是目前最新版是v1.3.0
grpc的使用
检查对应依赖的插件
PS E:\go_learning\gogogo\20-rpc-grpc\02-grpc\pb> protoc --versionlibprotoc 3.19.4PS E:\go_learning\gogogo\20-rpc-grpc\02-grpc\pb> protoc-gen-go-grpc --versionprotoc-gen-go-grpc 1.3.0PS E:\go_learning\gogogo\20-rpc-grpc\02-grpc\pb> protoc-gen-go --versionprotoc-gen-go.exe v1.30.0PS E:\go_learning\gogogo\20-rpc-grpc\02-grpc\pb> go list -m google.golang.org/grpcgoogle.golang.org/grpc v1.53.0
生成grpc的go代码
编译
站在Protobuf的角度来看,GRPC只不过是一个针对service接口生成代码的生成器。
1)定义 .proto 文件
syntax = "proto3";package pb;option go_package = "./grpc";message Teacher { int32 age = 1; string name = 2;}// 定义RPC服务service SayName { rpc SayHello (Teacher) returns (Teacher);}
2)对 .proto 文件进行编译
进入到刚刚定义的 .proto 文件的同级目录后,执行以下命令进行编译:
protoc --go_out=./ --go-grpc_out=./ *.proto
对生成文件进行分析
在以 _grpc.pb.go
结尾的文件中,GRPC插件会为服务端和客户端生成不同的接口:
// 客户端接口type SayNameClient interface { SayHello(ctx context.Context, in *Teacher, opts ...grpc.CallOption) (*Teacher, error)}// 服务端接口type SayNameServer interface { SayHello(context.Context, *Teacher) (*Teacher, error) mustEmbedUnimplementedSayNameServer()}
后续可以根据这两个接口重新实现SayName服务:
// 定义一个类(结构体)type Children struct { pb.UnimplementedSayNameServer // 注意这里需要继承这个类}// 按接口(SayNameServer接口)绑定类方法func (c *Children) SayHello(ctx context.Context, t *pb.Teacher) (*pb.Teacher, error) { t.Name += " hahahahahaha" return t, nil}
编写grpc服务端和客户端
grpc服务端
package mainimport ( "context" "fmt" pb "gogogo/20-rpc-grpc/02-grpc/pb/grpc" "google.golang.org/grpc" "net")// 定义一个类(结构体)type Children struct { pb.UnimplementedSayNameServer}// 按接口(SayNameServer接口)绑定类方法func (c *Children) SayHello(ctx context.Context, t *pb.Teacher) (*pb.Teacher, error) { t.Name += " hahahahahaha" return t, nil}func main() { // 1.初始化一个grpc对象 grpcServer := grpc.NewServer() // 2.注册服务 pb.RegisterSayNameServer(grpcServer, new(Children)) // 3.设置监听, 指定IP、Port listener, err := net.Listen("tcp", "127.0.0.1:9999") if err != nil { fmt.Println("listen failed, err: ", err) return } fmt.Println("Start Listening...") defer listener.Close() // 4.启动服务 err = grpcServer.Serve(listener) if err != nil { fmt.Println("Err: ", err) return }}
grpc客户端
package mainimport ( "context" "fmt" pb "gogogo/20-rpc-grpc/02-grpc/pb/grpc" "google.golang.org/grpc")func main() { // 1. 连接grpc服务 grpcConn, err := grpc.Dial("127.0.0.1:9999", grpc.WithInsecure()) // 注意加这个grpc.WithInsecure() if err != nil { fmt.Println("connect grpc service failed, err: ", err) return } defer grpcConn.Close() // 2.初始化grpc客户端 grpcClient := pb.NewSayNameClient(grpcConn) // 创建并初始化Teacher对象 var teacher pb.Teacher teacher.Name = "hgzero" teacher.Age = 18 // 3.调用远程服务 t, err := grpcClient.SayHello(context.TODO(), &teacher) if err != nil { fmt.Println("Err: ", err) return } fmt.Println("Value: ", t)}
关键词:
-
全球观热点:第132篇:npm第一次使用自己的包(package-lock.json、package.json文件作用说明)
好家伙,1 新建一个文件夹,命名为test2 下载包npmipanghu-planebattle空白的文件夹中多了两个文件packa...
来源: 每日消息!微服务与rpc/grpc
全球观热点:第132篇:npm第一次使用自己的包(package-lock.json、package.json文件作用说明)
精彩看点:【Visual Leak Detector】在 QT 中使用 VLD(方式一)
热讯:华硕发布新款Vivobook Go 14/15 OLED笔记本:高配酷睿i3-N305
天天观速讯丨真我GT Neo5 1TB存储网友用掉了500多G:直呼“真香”
全球视讯!Win12及DX13要来了?微软预告DirectX全新功能
通信能力是5G的10倍!全球17家运营商发布6G白皮书:预计2030商用
熬夜伤不起!警惕睡眠质量受电子设备影响
记录--前端加载超大图片(100M以上)实现秒开解决方案
关于“堆”题的总体思路
焦点速讯:地表最强AI,GPT-4专治各种不服
个人交340单位交680退休能拿多少?主要看个人缴纳的部分
焦点速讯:匹克态极速浪跑鞋99元抄底:门店299元
天天热文:女子公园租电动车3个多小时扣466元 运营方:上海价格都是这
焦点滚动:别羡慕了!经常不分场合秒睡可能是种病:得看医生
全球观速讯丨宁德时代麒麟电池已量产 同体积比特斯拉电池电量高13%
世界新消息丨省的都是钱 长安汽车推“百亿补贴”:不到11万可买CS75 PLUS
【Visual Leak Detector】安装 VLD
世界速递!三主摄时代来了!OPPO Find X6 Pro大漠银月图赏
全球看点:49dB降噪行业第一!OPPO Enco Free3图赏
1949元 ROG魔方幻“月曜白”路由器今晚开售:三频万兆速率
今热点:微软Bing上线在线绘图功能:文字生成图片 仅支持英文
天天快资讯:安全性远超燃油车!特斯拉发布调查报告:是美国平均水平7.4倍
环球今头条!DVWA-XSS(Reflected)
环球观焦点:关于基于AWS-Cli的方式对EC2及AMI资源批量添加或者删除tag的方法
每日观点:为什么Tomcat架构要这么设计?这篇文章告诉你答案!
全球微动态丨记录监控摄像头的接入过程及web端播放
全球快资讯丨narak靶机
当前快播:新农股份: 2022年度业绩快报
环球观天下!OPPO Find X6 Pro搭载三星E6屏:亮度高达2500nit、支持Pro XDR显示
焦点滚动:用到安卓17没问题!OPPO宣布Find X系列将支持4次大版本更新
449元-6999元!OPPO Find X6发布会四大新品一图看懂
全球速递!报告:2022年中国每四辆新车就有一辆电动车 比亚迪无敌
全球焦点!为什么不建议用redis做消息队列
世界今亮点!【数位DP】计数问题
前端设计模式——状态模式
每日讯息!关于 Vue 列表渲染 key 绑定 index 的性能问题
每日观察!黄金时间·千海金:避险情绪推升金价 但本周的美联储议息会议依然关键
环球观点:杜绝虚标!哈趣投影率先启用中国CVIA亮度标准:成单片式LCD领头羊
OPPO Find N2系列赢麻了!连续三个月折叠屏销量第一
天天速看:自动洗烘拖布 石头自清洁扫拖机器人P10图赏
道奇纯燃油谢幕之作!挑战者SRT恶魔170发布:V8机械增压马力超千匹
简单讲透Mac环境下多版本python的环境变量设置,仅对小白生效
视点!"error LNK2019: 无法解析的外部符号"原因分析
今头条!IDEA Rebuild项目错误:Information:java: java.lang.AssertionError: Value of x -1
对斗破苍穹进行python文本分析
实时焦点:VsCode 常用好用插件/配置+开发Vue 必装的插件
环球简讯:爵士力克国王将湖人挤出附加赛区 迷失盐湖城小萨准三双数据难掩低迷状态
入睡妙招!研究表明穿袜子睡觉更助眠
全球热消息:AMD Zen4霸气!移动版12核心解锁130W 直追170W桌面12核心
"周杰伦演唱会门票"登顶微博热搜 14万张秒售罄
dnf机械牛和悲鸣图在哪里?DNF机械牛和悲鸣的门票分别是什么?
雨过天晴一键还原怎么用?怎么删除雨过天晴一键还原?
OA对话框打不开是怎么回事?OA对话框怎么变成普通对话框?
今日最新!脚本编写的一个通用框架
天天速讯:编写高质量c#代码的20个建议
面试常考:C#用两个线程交替打印1-100的五种方法
全球新资讯:Paramiko_Linux
【全球独家】跟着字节AB工具DataTester,5步开启一个实验
英雄联盟自动关闭是什么意思?英雄联盟自动关闭怎么解决?
冒险岛的时空裂缝是什么?冒险岛怎么提升面板?
全球热推荐:今天春分白昼长了!全国春日地图出炉 看看春天到哪了
天天热议:汽车界“海底捞服务”!蔚来2023无忧服务发布:11600元/年
世界聚焦:重庆不再实行旧车置换:直接给予新车补贴 总计达3000万
世界报道:跨界做智能手表 比亚迪回应:消息属实 4月上新
对标《原神》!二次元开放世界游戏《鸣潮》开启测试招募
热点!如果设备不支持vulkan,就用swiftshader,否则就加载系统的vulkan的正确姿势(让程序能够智能的在vulkan-1.dll和libvk_s
【全球快播报】springboot使用easyExcel导出Excel表格以及LocalDateTime时间类型转换问题
《前端serverless 面向全栈的无服务器架构实战》读书笔记
每日视点!海关总署:2月下旬以来我国出口用箱量持续增长
国产纯电跑车前途K50美国秽土转生:换了名称、LOGO还没变
全球最新:40万级领先行业两代 赵长江:腾势N7月销量将轻松破万 抢夺BBA用户
【全球速看料】沙县小吃旗舰店包间最低消费300元 网友:吃的完吗?
全球头条:英国小镇被巨型老鼠入侵:像猫一样大 悬崖都要被挖塌了
OpenGL 图像 lookup 色彩调整
天天时讯:剑指 Offer 07. 重建二叉树(java解题)
为什么Redis不直接使用C语言的字符串?看完直接吊打面试官!
天天观天下!广东人睡觉时间全国最晚:“打工人”平均睡眠时长7.5小时
一根USB线就能偷走韩系车!现代、起亚已开始免费送车主方向盘锁
今日关注:再不发力就晚了!新一代奥迪Q5效果图曝光:内外大变革
当前时讯:沙尘暴黄色预警:北方超10省将迎来大范围沙尘天气
环球热点评!昔日巨头彻底退场!爱普生宣布所有相机明年终止官方服务
环球关注:论文解读TCPN
西部证券:3月20日融资买入1459.71万元,融资融券余额12.72亿元
环球速讯:中国罐头在海外多国热销:成为香饽饽
天天讯息:大反转!南京大学团队推翻美室温超导技术 复刻实验没发现超导现象
天天速看:又一致命真菌爆发:已遍布美国一半以上的州
天天微动态丨OPPO Find X6系列外观公布:拼接设计、后摄巨大
比亚迪汉唐冠军版发布会高能金句感受下 合资燃油车瑟瑟发抖
MAUI Blazor 加载本地图片的解决方案
每日热点:朴素系统优化思维的实践
焦点热文:债券通北向通2月成交规模环比增超三成 政金债跃升为最活跃券种
今日热讯:LCD荣光犹在!iQOO Z7开启预售:1599元起
全球观察:漫威后期制作总裁离职
当前通讯!2022年度个税汇算今起不用预约:多退少补你能退多少
移除雷达传感器后 特斯拉车祸数量上升:车主反映莫名刹车故障
快消息!读C#代码整洁之道笔记02_类、对象和数据结构及编写整洁函数
鲁抗医药:3月20日融资买入477.87万元,融资融券余额2.49亿元
全球速读:今年以来险企“补血”超340亿元 数百亿元补充资本“在路上”
世界观点:国际金融市场早知道:3月21日