2023-03-15
golang
00
请注意,本文编写于 679 天前,最后修改于 679 天前,其中某些信息可能已经过时。

目录

Go微服务
ProtoBuf
message类型:
分配标识号:
保留标识号:
将消息编译成各种语言版本的类库
其他类型
了解RPC
本地过程调用:
远程过程调用:
为什么使用RPC
RPC和HTTP对比
RPC基本构成
Consul
注册中心Consul基本介绍
注册中心Consul关键功能
注册中心Consul两个重要协议

Go微服务

ProtoBuf

​ 在网络通信和通用数据交换等应用场景中经常使用的技术是JSON和XML,在微服务架构中则一般使用另外一个数据交换的协议的工具==ProtoBuf==

​ 全称:protocol buffers,即协议缓冲区,是一种与语言无关、与平台无关的可扩展机制,用于序列化结构化数据

​ 和json/xml最大区别:json/xml基于文本格式,ProtoBuf是二进制格式

​ 首先先去下载protoc

​ 之后将解压后的bin目录放到环境变量中,在命令行执行protoc,若有输出则配置成功

这时候在go中使用,先在goland里写一段protoc代码

protobuf
syntax = "proto3";

package hello;

option go_package = "./;hello";

message Say{
  int64 id = 1;
  string hello = 2;
  repeated string word = 3;
}

在文件的目录下执行

properties
protoc --go_out=. hello.proto

这时候可能会报错

go
'protoc-gen-go' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
--go_out: protoc-gen-go: Plugin failed with status code 1.

这时候是因为缺少了差距,下载即可

go
go get -u google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/protobuf/cmd/protoc-gen-go
go get -u google.golang.org/grpc/cmd/protoc-gen-go-grpc
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc

代码解析:

protobuf
// 定义proto版本号
syntax = "proto3";

// 定义包名,用于proto
package hello;

// 定义go包名,用于生成的.pd.go文件
option go_package = "./;hello";

// 定义消息体
message Say{
  int64 id = 1;
  string hello = 2;
  repeated string word = 3;
}
  • synatx:必写,而且要定义在第一行,不写默认proto2
  • package:定义proto文件的包名
  • option go_package:定义生成的pb.go的包名,通常在proto文件中定义,如果不在proto文件中定义,也可以使用protoc生成代码时指定文件包名
  • message:用于定义消息结构体

message类型:

​ 定义数组类型时,是在字段前面增加repeated关键词来实现,标记当前字段是一个数组

​ message类似go中定义的结构体,在protobuf中指的就是我们要定义的数据结构

分配标识号:

​ ==string query = 1==,其中的1就是分配标识号,在消息定义中,每个字段后面都有一个唯一的数字,这个就是标识号。

​ 标识号的作用:在消息的二进制格式中识别各个字段,一旦开始使用就不能够再改变

​ 注:分配标识号在每个消息内唯一,不同的消息体是可以拥有相同的标识号的。

小技巧:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。

保留标识号:

​ 要为将来有可能添加的、频繁出现的字段预留一些标识号

protobuf
message Reserver {
	// 保留2,5,7到10这些标识号
	reserver 2,5,7 to 10;
}

如果使用了这些保留的标识号,protocol buffer编译器无法编译通过,会输出警告信息。

将消息编译成各种语言版本的类库

​ 编译器命令格式:

protobuf
protoc [OPTION] PROTO_FILES

常用的OPTION选项

go
 --go_out=OUT_DIR            指定代码生成目录,生成 Go 代码
  --cpp_out=OUT_DIR           指定代码生成目录,生成 C++ 代码
  --csharp_out=OUT_DIR        指定代码生成目录,生成 C# 代码
  --java_out=OUT_DIR          指定代码生成目录,生成 java 代码
  --js_out=OUT_DIR            指定代码生成目录,生成 javascript 代码
  --objc_out=OUT_DIR          指定代码生成目录,生成 Objective C 代码
  --php_out=OUT_DIR           指定代码生成目录,生成 php 代码
  --python_out=OUT_DIR        指定代码生成目录,生成 python 代码
  --ruby_out=OUT_DIR          指定代码生成目录,生成 ruby 代码
其他类型
  • 枚举类型

​ Enum:当定义一个消息类型时,如果想为一个字段指定的”预定义值“中的其中一个值,这时候就可以通过枚举实现

protobuf
syntax = "proto3";//指定版本信息,非注释的第一行

enum SexType //枚举消息类型,使用enum关键词定义,一个性别类型的枚举类型
{
    UNKONW = 0; //proto3版本中,首成员必须为0,成员不应有相同的值
    MALE = 1;  //1男
    FEMALE = 2; //2女  0未知
}

// 定义一个用户消息
message UserInfo
{
    string name = 1; // 姓名字段
    SexType sex = 2; // 性别字段,使用SexType枚举类型
}
  • 消息嵌套

    ​ 在实际开发中,我们需要定义多个proto,消息嵌套则可以帮助我们做到消息的服用

    ​ 例如在go中时常嵌套使用结构体,在protobuf中同样可以如此,在一个消息中嵌套另外一个消息,字段类型可以是另外一个消息类型

​ 而且在protoc文件中,可以import另外一个文件,且可以使用其定义的消息体

  • map类型
    go
    map<key_type,value_type> map_field = N;
    1. key_type可以是任何整数或字符串类型(除浮点类型和字节之外的任何标量类型)
    2. 注意:枚举不是有效的key_type
    3. value_type 可以是除另一个映射之外的任何类型
    4. Map字段不能使用repeated关键字修饰

了解RPC

​ RPC--远程过程调用,对于全称为:Remote Procedure Call,可以简单理解为一个节点请求另外一个节点提供的服务

​ RPC主要依赖于客户端与服务的建立socket链接,而http rest实现通讯的代价比较高,这是RPC的一个优势体现、

本地过程调用:

​ 如果要将本地对象student的age+1,可以实现一个addAge()方法,将student对象传入,对年龄进行更新之后返回即可,本地方法调用的函数体通过函数指针来指定

远程过程调用:

​ 如果addAge()方法在服务端,执行函数的函数体在远程机器上,又该如何调用?

  1. 首先客户端需要告诉服务器,需要调用的函数,这里函数和进程id存在一个映射,客户端远程调用时,需要查一下函数,找到对应的ID,如何执行函数的代码
  2. 客户端需要把本地参数传给远程函数,本地调用的过程中,直接压入栈即可,但是远程调用过程不在同一个内存当中,无法直接传递函数的参数,因此需要客户端将参数转换成字节流,传给服务端,如何服务端将字节流转换成自身能读取的格式,是一个序列号和反序列化的过程。
  3. 数据准备好了以后,如何进行传输?

​ 网络传输层把调用的ID和序列化后的参数传给服务端,然后把计算出来的结果序列号传给客户端,因此TCP层即可完成上述过程,gRPC中采用的是HTTP2协议。

go
// Client端 
//    Student student = Call(ServerAddr, addAge, student)
1. 将这个调用映射为Call ID。
2. 将Call ID,student(params)序列化,以二进制形式打包
3.2中得到的数据包发送给ServerAddr,这需要使用网络传输层
4. 等待服务器返回结果
5. 如果服务器调用成功,那么就将结果反序列化,并赋给student,年龄更新

// Server端
1. 在本地维护一个Call ID到函数指针的映射call_id_map,可以用Map<String, Method> callIdMap
2. 等待客户端请求
3. 得到一个请求后,将其数据包反序列化,得到Call ID
4. 通过在callIdMap中查找,得到相应的函数指针
5. 将student(params)反序列化后,在本地调用addAge()函数,得到结果
6. 将student结果序列化后通过网络返回给Client

image-20230114213801646.png

为什么使用RPC

​ 因为无法在同一个进程内,或者无法在同一个服务器上通过本地调用的方式实现需求,http能满足需求但是不够高效。

​ 使用情况:比如不同系统间通讯,由于计算能力需要横向扩展,需要在多台机器组成的集群上部署应用

RPC和HTTP对比
  1. RPC主要用于公司内部的服务调用,性能消耗低,传输效率高,服务治理方便
  2. HTTP主要用于对外的异构环境,浏览器接口调用,APP接口调用,第三方接口调用等
RPC基本构成
  1. RPC基本构成:服务端、客户端
  2. 服务端基本构成:结构体,请求结构体,响应结构体
  3. 客户端基本构成:请求结构体,响应结构体

Consul

​ 是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置。Consul是分布式的,高可用的,可横向扩展的

注册中心Consul基本介绍

  • Consul是一种服务网格解决方案
  • 提供具有服务发现,配置和分段功能的全功能控制平面
  • Consul附带一个简单的内置代理,可以开箱即用

注册中心Consul关键功能

  • 服务发现

    ​ 客户端可以注册服务,程序可以轻松找到它们所依赖的服务

  • 运行状况检查

    ​ Consul客户端可以提供任意数量的运行状况检查

  • KV存储

    ​ 应用程序可以将Consul的层级键/值存储用于任何目的,包括动态配置,功能标记,协调,领导者选举等

  • 安全服务通信

    ​ Consul可以为服务生成和分发TLS证书,建立相互的TLS连接

  • 多数据中心

    ​ Consul支持多个数据中心

注册中心Consul两个重要协议

  • Gossip Protocol(八卦协议)
  • Raft Protocol(选举协议)

本文作者:Malyue

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!