分布式对象存储 读书笔记(二) 解耦

上一节编写了一个使用REST服务进行对象存取得单机程序

本节接续对其进行扩展,为了满足加入新的节点就可以自由扩展服务器集群的需求,我们需要将单机版的接口与数据存储进行解耦.

让接口与数据存储成为互相独立的服务节点,两者互相合作提供对象存储服务。这样节点就可以按照需要添加,并且分布在不同的服务器上。

 

1 整体框架

架构图

如图 接口层服务提供对外的REST接口,数据服务层则提供数据的存储功能.

接口服务和数据服务之间有两种接口:1 REST服务接口 2 消息队里,采用RabbitMQ(也可以采用其他消息队列甚至是其他方式)

每种接口有格子的特点和用处 REST适合大数量的数据传输 消息队列则可以满足1:1 1:n n:1 n:n的消息传输

 

2 消息设计

心跳消息

dataServer每隔5秒 通过消息队列想所有 apiServer发送心跳包

apiServer接收心跳包 并且每隔10秒移除未及时更新心跳包的dataServer

 1 dataServer hearbeat
 2 func StartHeartbeat() {
 3     q := rabbitmq.New(os.Getenv("RABBITMQ_SERVER"))
 4     defer q.Close()
 5     for {
 6         q.Publish("apiServers", os.Getenv("LISTEN_ADDRESS"))
 7         time.Sleep(5 * time.Second)
 8     }
 9 }
10 
11 apiServer hearbeat
12 func ListenHeartbeat() {
13     q := rabbitmq.New(os.Getenv("RABBITMQ_SERVER"))
14     defer q.Close()
15     q.Bind("apiServers")
16     c := q.Consume()
17     go removeExpiredDataServer()
18     for msg := range c {
19         dataServer, e := strconv.Unquote(string(msg.Body))
20         if e != nil {
21             panic(e)
22         }
23         mutex.Lock()
24         dataServers[dataServer] = time.Now()
25         mutex.Unlock()
26     }
27 }
28 
29 func removeExpiredDataServer() {
30     for {
31         time.Sleep(5 * time.Second)
32         mutex.Lock()
33         for s, t := range dataServers {
34             if t.Add(10 * time.Second).Before(time.Now()) {
35                 delete(dataServers, s)
36             }
37         }
38         mutex.Unlock()
39     }
40 }
View Code

 

定位消息

apiServer locate
func Locate(name string) string {
    q := rabbitmq.New(os.Getenv("RABBITMQ_SERVER"))
    q.Publish("dataServers", name)
    c := q.Consume()
        //广播消息  等待1秒内的回复信息
    go func() {
        time.Sleep(time.Second)
        q.Close()
    }()
    msg := <-c
    s, _ := strconv.Unquote(string(msg.Body))
    return s
}    

dataServer locate
func Locate(name string) bool {
    _, err := os.Stat(name)
    return !os.IsNotExist(err)
}

func StartLocate() {
    q := rabbitmq.New(os.Getenv("RABBITMQ_SERVER"))
    defer q.Close()
    q.Bind("dataServers")
    c := q.Consume()
        //绑定消息队列 等待询问定位信息 然后在本地查找 若找到则返回监听地址
    for msg := range c {
        object, e := strconv.Unquote(string(msg.Body))
        if e != nil {
            panic(e)
        }
        if Locate(os.Getenv("STORAGE_ROOT") + "/objects/" + object) {
            q.Send(msg.ReplyTo, os.Getenv("LISTEN_ADDRESS"))
        }
    }
}

 put get操作与上节类似

代码地址 https://github.com/stuarthu/go-implement-your-object-storage/tree/master/chapter2

 

3 操作验证

因为测试机器有限 需要在一台机器上开启6个apiServer 2个dataServer 和 开启RabbitMQ

所以我们需要在同一台机器上绑定多个地址

执行命令如下:

安装配置rabbitmq 这里就不再介绍了

然后创建各个apiServer需要的存储文件夹 开启6个apiServer 2个dataServer 对应模拟不用的ip地址

 

我们上传一个test2对象

 

下面进行测试

我们上传一个叫做test2的对象 再locate定位他在哪个数据服务节点 然后换另一个数据服务节点查询 看看是否成功

OK 全部成功

下一节 解决这样一个问题 如果我们多次重复上传同一个对象 那么这个对象可能在所有服务器上都有存储 甚至可能是不一样的数据(存储是随机选择数据节点服务器)

我们添加版本控制来解决这个问题 记录版本等数据的重要信息 也就是元数据

 

posted on 2018-09-07 16:23  itdef  阅读(516)  评论(0编辑  收藏  举报

导航