上一节编写了一个使用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 }
定位消息
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 全部成功
下一节 解决这样一个问题 如果我们多次重复上传同一个对象 那么这个对象可能在所有服务器上都有存储 甚至可能是不一样的数据(存储是随机选择数据节点服务器)
我们添加版本控制来解决这个问题 记录版本等数据的重要信息 也就是元数据