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


    上一节编写了一个使用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 全部成功

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

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

    作 者: itdef
    欢迎转帖 请保持文本完整并注明出处
    技术博客 http://www.cnblogs.com/itdef/
    B站算法视频题解
    https://space.bilibili.com/18508846
    qq 151435887
    gitee https://gitee.com/def/
    欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
    如果觉得不错,欢迎点赞,你的鼓励就是我的动力
    阿里打赏 微信打赏
  • 相关阅读:
    flask
    ORACLE EXP不能导出空表的原因分析及解决方法
    安装sqlService需要重启解决方法
    sqlserver2008日志已满解决方法
    Oracle中删除用户下所有对象
    sqlserver生成表行数据insert语句
    对回调函数的理解
    github笔记
    Ubuntu摆脱QQ版本过旧问题!安装 wineinternational版 qq
    Servlet完全教程
  • 原文地址:https://www.cnblogs.com/itdef/p/9605494.html
Copyright © 2020-2023  润新知