没有输出的输入是不完整的

0%

【微服务Go-micro第三篇】相关概念解释

在搭建环境的时候其实我就有很多疑问了,怎么有这么多概念?
微服务、RPC, gRPC, protoc等等,它们都是啥?本篇文章来进行比较详细的解释。

微服务

使用一套小服务来开发单个应用的方式,每个服务运行在独立的进程里,一般采用轻量级的通讯机制互联,并且它们可以通过自动化的方式部署。

RPC

  1. 远程过程调用(Remote Procedure Call,RPC)是一个计算机通信协议
  2. 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程
  3. 如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用

gRPC

  1. gRPC由google开发,是一款语言中立、平台中立、开源的远程过程调用系统
  2. gRPC客户端和服务端可以在多种环境中运行和交互,例如用java写一个服务端,可以用go语言写客户端调用

protobuff

  1. gRPC可以实现微服务,将大的项目拆分为多个小且独立的业务模块,也就是服务,各服务间使用高效的protobuf协议进行RPC调用,gRPC默认使用protocol buffer,这是google开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如JSON)
  2. 可以用proto files创建gRPC服务,用message类型来定义方法参数和返回类型

个人理解

结合这里和我们上一篇的文章,我们可以得到以下结论

  1. 我们是使用了proto file(user.proto)来定义对应的方法和返回类型,其中message结构来定义接收参数和返回参数的名称以及类型。 service结构来定义对应的远程调用的方法和接受的对应的参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
syntax = "proto3";

service user{
rpc RegisterUser(RegisterUserReq) returns (RegisterUserRsp){}
}

message RegisterUserReq{
string name = 1;
}

message RegisterUserRsp{
string status = 1;
}
  1. protoc命令会按照proto文件中声明的message的格式构造传输对象。gRPC的服务器端和客户端都遵守protocal buffer协议,在客户端会序列化请求对象,在服务端反序列化请求对象,服务器端处理完成之后,序列化响应对象,客户端再反序列化响应对象。所以共同遵守protocal buff 协议让客户端和服务器端可以方便地进行通信,即使最后客户端和服务器端是用不同的语言来实现的也没有关系。

protobuf语法

简单介绍

  1. 以.proto结尾
  2. Message命名采用驼峰命名方式,字段命名采用小写字母加下划线分隔方式
  3. 结构定义可以包含:message、service、enum
  4. 可以通过required表示这个参数是必须的,optional表示这个参数是可选的。通过default表示这个参数的默认值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
syntax = "proto3"; // 

message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3 [default = 10];
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
optional Corpus corpus = 4 [default = UNIVERSAL];
}

service SearchService {
rpc Search (SearchRequest) returns (SearchResponse) {}
}

更多使用方式可以参见下面的两个链接。

参考链接

  1. [译]Protobuf 语法指南
  2. 1. Protobuf语法

Go-micro

什么是go-micro

go-micro是一个框架,提供了分布式开发所需要的核心需求,包括RPC和基于事件驱动的通信。设计理念是可插拔。

Go-micro特点

  1. 服务发现:自动服务注册和命名解析。服务发现是微服务开发中的核心,当服务A要与服务B协作时,它得知道服务B在哪里。目前默认的服务发现机制是组播multicast DNS (mdns,组播),是一种零配置网络。
  2. 负载均衡:在服务发现的基础上构建客户端的负载均衡。当我们查找一个服务发现它有多个节点时,我们需要一种机制来决定最终路由到哪一个节点。默认使用random hashed load balancing来提供服务的负载均衡。如果出现问题,那么go-micro会尝试使用其他节点。
  3. 消息编码:基于内容类型动态编码消息,客户端和服务器端将会使用基于内容类型的编解码器来对Go语言的类型进行无缝隙的编解码。客户端可以编码并发送各种类型额消息,客户端和服务器端默认能够处理这些消息,默认包含 protobuf and json。
  4. 请求/相应:RPC通信是基于双向信息流的请求和相应。go-micro提供一种同步的抽象。一个发送给服务的请求会被自动的解析、负载均衡、拨号、转成字节流。默认传输是通过gRPC。
  5. 异步信息:发布订阅机制在异步通信和事件驱动架构中非常重要,事件通知在微服务开发中占有重要地位。默认的消息传递系统是http事件消息代理。
  6. 可插拔: Go Micro为每个分布式系统抽象出接口。因此,Go Micro的接口都是可插拔的,允许其在运行时不可知的情况下仍可支持。所以只要实现接口,可以在内部使用任何的技术。更多插件请参考:github.com/micro/go-plugins

go-micro通信流程

  1. Server监听客户端的调用,对Brocker推送过来的信息进行处理。并且Server端需要向Register注册自己的存在或消亡,这样Client才能知道自己的状态
  2. Register服务的注册的发现,Client端从Register中得到Server的信息,然后每次调用都根据算法选择一个的Server进行通信,当然通信是要经过编码/解码,选择传输协议等一系列过程的
  3. 如果有需要通知所有的Server端可以使用Brocker进行信息的推送,Brocker 信息队列进行信息的接收和发布

go-micro接口

go-micro之所以可以高度订制和他的框架结构是分不开的,go-micro由8个关键的interface组成,每一个interface都可以根据自己的需求重新实现,这8个主要的inteface也构成了go-micro的框架结构

go-micro 接口详解

Transort通信接口

通信相关接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type Socket interface {
Recv(*Message) error
Send(*Message) error
Close() error
}

type Client interface {
Socket
}

type Listener interface {
Addr() string
Close() error
Accept(func(Socket)) error
}

type Transport interface {
Dial(addr string, opts ...DialOption) (Client, error)
Listen(addr string, opts ...ListenOption) (Listener, error)
String() string
}

Codec编码接口

编解码,底层也是protobuf

1
2
3
4
5
6
7
type Codec interface {
ReadHeader(*Message, MessageType) error
ReadBody(interface{}) error
Write(*Message, interface{}) error
Close() error
String() string
}

1.1.3. Registry注册接口
服务注册发现的实现:etcd、consul、mdns、kube-DNS、zk

1
2
3
4
5
6
7
8
9
type Registry interface {
Register(*Service, ...RegisterOption) error
Deregister(*Service) error
GetService(string) ([]*Service, error)
ListServices() ([]*Service, error)
Watch(...WatchOption) (Watcher, error)
String() string
Options() Options
}

Selector负载均衡

根据不同算法请求主机列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type Selector interface {
Init(opts ...Option) error
Options() Options
// Select returns a function which should return the next node
Select(service string, opts ...SelectOption) (Next, error)
// Mark sets the success/error against a node
Mark(service string, node *registry.Node, err error)
// Reset returns state back to zero for a service
Reset(service string)
// Close renders the selector unusable
Close() error
// Name of the selector
String() string
}

Broker发布订阅接口

pull push watch

1
2
3
4
5
6
7
8
9
10
type Broker interface {
Options() Options
Address() string
Connect() error
Disconnect() error
Init(...Option) error
Publish(string, *Message, ...PublishOption) error
Subscribe(string, Handler, ...SubscribeOption) (Subscriber, error)
String() string
}

Client客户端接口

1
2
3
4
5
6
7
8
9
10
type Client interface {
Init(...Option) error
Options() Options
NewMessage(topic string, msg interface{}, opts ...MessageOption) Message
NewRequest(service, method string, req interface{}, reqOpts ...RequestOption) Request
Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error
Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error)
Publish(ctx context.Context, msg Message, opts ...PublishOption) error
String() string
}

Server服务端接口

1
2
3
4
5
6
7
8
9
10
11
12
13
type Server interface {
Options() Options
Init(...Option) error
Handle(Handler) error
NewHandler(interface{}, ...HandlerOption) Handler
NewSubscriber(string, interface{}, ...SubscriberOption) Subscriber
Subscribe(Subscriber) error
Register() error
Deregister() error
Start() error
Stop() error
String() string
}

Serveice接口

1
2
3
4
5
6
7
8
type Service interface {
Init(...Option)
Options() Options
Client() client.Client
Server() server.Server
Run() error
String() string
}

Go Micro接口详解

总结

看了上面那么多概念,我们可以稍微做一下梳理和总结。

  1. 我们对业务需求进行梳理划分,分解一个一个的单一职责的任务,我们分别通过一个微服务来实现对应的任务。
  2. go-micro是一个可以用来实现微服务的框架,设计理念是可插拔,什么意思呢,就是组件可以随时替换,有多种实现方式。go-micro的整理设计逻辑如下图所示。
  3. 微服务在实现过程中是有一定的技术要求的。
  • 服务注册与发现:用户想调用一个服务,必须首先找到这个服务,所以对于一个服务而言,就必须在开启时要进行注册,go-micro让Registry来负责这个任务,而Registry有多种实现方式,可以参见下表,而我们搭建环境的时候就用的etcd
  • 要进行负载均衡。用户在找同一个服务的时候可能会有多个节点实现这个服务,所以需要根据相应的负载均衡机制进行节点选择。这部分就由Selector来负责。
  • 要进行消息编解码,不同的service在调用的时候是需要传递数据的,数据在编码和解码上要有统一的规则,从而进行序列化和反序列化。编解码支持json和protobuff。这部分的编解码工作由Codec来负责。我们在用go-micro编写微服务时,我们会按照protobuf的语法来编写proto file,然后通过protoc,protoc-gen-go,protoc-gen-mico来生成对应的消息对象以及相应服务的API.
  • 要进行通信:这部分由transort来负责。
  • Broker主要对异步信息进行处理

4.这几部分具体有哪些实现呢?可以看go-plugins这个库中的插件的集合。

Directory Description
Broker PubSub messaging; NATS, NSQ, RabbitMQ, Kafka
Client RPC Clients; gRPC, HTTP
Codec Message Encoding; BSON, Mercury
Micro Micro Toolkit Plugins
Registry Service Discovery; Etcd, Gossip, NATS
Selector Load balancing; Label, Cache, Static
Server RPC Servers; gRPC, HTTP
Transport Bidirectional Streaming; NATS, RabbitMQ
Wrapper Middleware; Circuit Breakers, Rate Limiting, Tracing, Monitoring
  1. 对我们而言如何使用go-micro呢?
  • 根据服务需求来写好proto file
  • 根据proto file 生成对应的传输对象以及服务端和客户端的api
  • 编写服务器端和客户端代码
  • 执行服务器端和客户端代码

注意:在执行的时候可以指定最下面一层的各个组件,具体指定方式可以通过命令行指定或者设置环境变量或者直接在文件中写死等方式,更加详细的内容可以查看Go-Plugins-README