分布式对象存储笔记2

137 阅读5分钟

分布式对象存储笔记 | 青训营笔记

这是我参与「第四届青训营 」笔记创作活动的的第11天。

第二章 可扩展的分布式系统

2.1 什么是分布式系统

一个分布式系统要求各节点分布在网络上,并通过消息传递来合作完成一个共同目标。

分布式系统的三大关键特征是:节点之间并发工作,没有全局锁以及某个节点上发生的错误不影响其他节点。

分布式的好处在于可扩展性,只需要加入新的节点就可以自由扩展集群的性能。

第一章提到的单机版对象存储原型,它的接口和数据存储是被紧密耦合在一起的。这样的系统不是分布式的,也无法扩展。

因此本章要做的就是解开这个耦合,让接口和数据存储成为互相独立的服务节点,两者互相合作提供对象存储服务。一旦接口和数据存储分离,我们就可以往集群中随意添加新的接口服务节点或数据服务节点。

2.2 接口和数据存储分离的架构

image.png 接口服务和数据服务之间的接口有两种,第一种接口实现对象的存取。和上一个版本一样,对象的存取使用REST 接口。也就是说数据服务本身也提供REST接口,此时,接口服务节点作为HTTP客户端向数据服务请求对象。

第二种接口通过RabbitMQ消息队列进行通信。关于RabbitMQ消息队列的详细介 绍请参见其官网。在我们的架构中对 RabbitMQ的使用分为两种模式,一种模式是向某个exchange进行一对多的消息群发,另一种模式则是向某个消息队列进行一对一的消息单发。

每一个数据服务节点都需要向所有的接口服务节点通知自身的存在,为此,我们创建了一个名为apiServers的exchange,每一台数据服务节点都会持续向这个exchange发送心跳消息。所有的接口服务节点在启动以后都会创建一个消息队列来绑定这个exchange,任何发往这个exchange 的消息都会被转发给绑定它的所有消息队列,也就是说每一个接口服务节点都会接收到任意一台数据服务节点的心跳消息。

另外,接口服务需要在收到对象GET请求时定位该对象被保存在哪个数据服务节点上,所以我们还需要创建一个名为dataServers的exchange。所有的数据服务节点绑定这个exchange并接收来自接口服务的定位消息。拥有该对象的数据服务节点则使用消息单发通知该接口服务节点。

之所以必须使用REST和消息队列这两种不同类型的接口是为了满足不同的需求:对象存取的特点是数据量有可能很大,不适合将一个巨大的对象通过消息队列传输。而REST接口虽然能够处理大数据量传输,但是对于群发却显得力不从心。

需要说明的是,虽然我们需要两种不同类型的接口,但不一定要选用REST和RabbitMQ。我们可以直接用TCP协议来传输对象;消息队列也有很多种,如ActiveMQ或ZeroMQ,甚至我们可以用UDP协议实现自己的消息群发。选择有很多,在这里我们使用REST 和RabbitMQ是因为它们都已经具备了成熟的Go语言库,实现起来较为容易。而且 Ubuntu上自带RabbitMQ安装包,安装和调试都非常方便。

2.2.1 REST接口

对于数据服务层,它的REST接口和上一个版本完全相同,也就是对象的PUT和GET方法。

对于接口服务层,除了PUT和GET方法之外,我们还另外提供了一个用于定位的locate接口,用来帮助验证架构。

GET /locate/<object_name>

客户端通过GET方法发起对象定位请求,接口服务节点收到该请求后会向数据服务层群发一个定位消息,然后等待数据服务节点的反馈。如果有数据服务节点发回确认消息,则返回该数据服务节点的地址;如果超过一定时间没有任何反馈,则返回HTTP错误代码404 NOT FOUND。

2.2.2 RabbitMQ消息设计

image.png apiServers和 dataServers这两个exchange需要在RabbitMQ服务器上预先创建。每个接口服务节点在启动后都会创建自己的消息队列并绑定至 apiServers exchange。每个数据服务节点在启动后每隔5s就会发送一条消息给apiServers exchange,消息的正文就是该数据服务节点的 HTTP监听地址。接口服务节点在收到该消息后就会记录这个地址。

定位的时候流程如图: image.png 每个数据服务节点在启动时都必须创建自己的消息队列并绑定至 dataServersexchange。当接口服务需要定位时,会创建一个临时消息队列,然后发送一条消息给dataServers exchange,消息的正文是需要定位的对象,返回地址则是该临时队列的名字。定位成功的数据服务节点需要将反馈消息发送给这个临时队列,反馈消息的正文是该数据服务节点自身的监听地址。临时消息队列会在一定时间后关闭。如果在关闭前没有收到任何反馈则该对象定位失败,接口服务节点就会知道该对象不存在于数据服务层。

2.2.3 PUT和GET流程

image.png image.png

GO语言实现(略)