Skip to content

Commit

Permalink
🎉 init(项目整体): 单机分布式存储系统实现及其本项目整体规划
Browse files Browse the repository at this point in the history
本次提交实现了单机分布式存储系统的基本实现,并且在一定程度上完善了项目文档,方便展望整体的项目框架和目标

https://github.com/JIeJaitt/goDistributed-Object-storage
  • Loading branch information
JIeJaitt committed Jul 3, 2023
1 parent 29c2875 commit c5a604a
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 5 deletions.
5 changes: 0 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +0,0 @@
/README.md
/go.mod
/log.md
/objects
/server.go
Empty file removed .gitkeep
Empty file.
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# 分布式对象存储 Golang 实现

本项目是胡世杰老师的《分布式对象存储 -- 原理、架构及Go语言实现》的实践学习。书本介绍:本书从云存储的需求出发讲述对象存储的原理,循序渐进地建立起一个分布式对象存储的架构,并且将软件实现出来。全书共8章,分别涉及对象存储简介、可扩展的分布式系统、元数据服务、数据校验和去重、数据冗余和即时修复、断点续传、数据压缩和数据维护等。本书选择用来实现分布式对象存储软件的编程语言是当前流行的Go语言。本书适合从事云存储方面工作的工程师或架构师,也适合想要学习和实现分布式对象存储的读者。

## 云储存概念理解

现在云存储已经是大家司空见惯的一个网络服务了。比如大家用的百度云盘、已经成为实质上的业界标准的亚马逊S3、微软的OneDrive、苹果公司的iCloud 和谷歌的Google Cloud 等。据我了解目前还出现一种流行的同步盘比如坚果云。

## 对象储存与云储存的关系

对象存储是云存储的一部分,它提供了云存储后端的存储服务。云存储是建立在对象存储之上的一个整体的解决方案,除了后端的存储服务之外,它还需要包括各种操作系统和平台上运行的客户端、身份认证、多种管理和监控功能等。胡世杰老师这本书主要集中在对象存储的原理架构和实现上,对云存储其他组件也会有一定的介绍,但不会是本书的主要内容。

## 分布式存储的好处

传统的高端服务器性能强劲、成本高昂,以前只有大公司用来搭建自己的私有存储。互联网生态下的云存储则用数量弥补质量,以大量低成本的普通PC服务器组成网络集群来提供服务。相比传统的高端服务器来说,同样价格下分布式存储提供的服务更好、性价比更高,且新节点的扩展以及坏旧节点的替换更为方便。

## 本书书本缺少的内容

1. 没有实现一个专门的客户端来配合对象存储系统,只是在部分章节中提到一个配套的客户端可以起到的作用。本书使用Linux 下的curl 命令作为我们的客户端进行功能测试,可以帮助我们更好地了解客户端和服务端之间发生的交互行为。但是一个美观UI的专门的客户端对用户来说会更加友好。
2. 没有涉及用户管理,虽然用户管理是云存储系统的一个基本组成部分,但是这部分和其他系统的用户管理没什么区别,一个用户信息数据库就可以满足大多数要求,有兴趣的用户可以自行查阅相关书籍。
3. 没有提到信息安全方面的内容,本书为了方便起见,使用的通信协议都是 HTTP,而事实上一个云存储系统对外一定是使用HTTPS协议,服务端和客户端之间需要建立 SSL 的双向认证。除此之外,用户合法身份的授权和验证等功能通常都会有一个专门的身份认证系统来进行管理,而服务端客户端则可以通过JWT 和身份认证系统打交道。
4. 没有实现对象存储系统的监控。系统监控包括对日志的实时收集和分析,对系统 KPI 的收集和可视化等我们在这里推荐的做法是使用 Logstash 收集和分析系统日志和 KPI,记录在 ElasticSearch 中并用 Kibana 进行可视化。这些功能不涉及Go语言实现,而是通过各种工具的配置来进行。

## 单机版的对象存储系统以及简单的 REST 风格接口

## 扩展和拆分单机原型系统成接口服务和数据服务使得这些服务互相独立地提供功能

我们将这个原型系统进行了扩展,将它分拆成接口服务和数据服务,使得这些服务可以互相独立地提供服务功能,让我们的系统得以自由扩展。

## 解耦合对象的名字和对象的内容

我们又往系统中加入了元数据服务,用于保存描述对象的元数据,包括对象的名字、版本、大小、散列值等。有了元数据服务,我们就可以使得对象的名字和对象的内容解耦合。

## 实现对对象数据的去重

我们实现对象数据的校验和去重,使得名字不同但内容相同的对象可以共享同一份存储实体,这样做可以降低对存储空间的要求。

## RS 纠删码减少对象数据的损坏

为了增强数据的可靠性,我们提出了数据冗余的概念并实现了 RS 纠删码。我们在对象数据存取的过程中以流的形式进行编解码,可以在一定程度上修正对象数据的损坏。

## 实现文件的断点续传

为了战胜现实世界不良的网络环境,我们实现了断点续传。客户端在下载对象时自由指定下载数据的偏移量,也可以通过特殊的接口以分批的方式上传对象的数据。

## gzip 数据压缩降低存储空间和带宽要求

我们介绍数据压缩。在大多数情况下,数据压缩都应该在客户端实现。但如果你需要设计一个通过浏览器就可以使用的对象存储系统,且你的大多数对象的数据都适合进行压缩,那么可以参考我们在本章实现的gzip 数据压缩,进一步降低对存储空间和下载带宽的要求。

3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module goDistributed-Object-storage

go 1.19
54 changes: 54 additions & 0 deletions log.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
```bash
➜ goDistributed-Object-storage curl -v 127.0.0.1:12345/object/test
* Trying 127.0.0.1:12345...
* Connected to 127.0.0.1 (127.0.0.1) port 12345 (#0)
> GET /object/test HTTP/1.1
> Host: 127.0.0.1:12345
> User-Agent: curl/8.0.1
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Mon, 03 Jul 2023 13:23:20 GMT
< Content-Length: 19
<
404 page not found
* Connection #0 to host 127.0.0.1 left intact
```
```bash
➜ goDistributed-Object-storage curl -v 192.168.250.144:12345/objects/test -XPUT -d"this is a test object"
* Trying 192.168.250.144:12345...
* Connected to 192.168.250.144 (192.168.250.144) port 12345 (#0)
> PUT /objects/test HTTP/1.1
> Host: 192.168.250.144:12345
> User-Agent: curl/8.0.1
> Accept: */*
> Content-Length: 21
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 200 OK
< Date: Mon, 03 Jul 2023 13:27:14 GMT
< Content-Length: 0
<
* Connection #0 to host 192.168.250.144 left intact
```
```bash
➜ goDistributed-Object-storage curl -v 127.0.0.1:12345/objects/test
* Trying 127.0.0.1:12345...
* Connected to 127.0.0.1 (127.0.0.1) port 12345 (#0)
> GET /objects/test HTTP/1.1
> Host: 127.0.0.1:12345
> User-Agent: curl/8.0.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 03 Jul 2023 13:28:21 GMT
< Content-Length: 21
< Content-Type: text/plain; charset=utf-8
<
* Connection #0 to host 127.0.0.1 left intact
this is a test object%
```
21 changes: 21 additions & 0 deletions objects/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package objects

import (
"io"
"log"
"net/http"
"os"
"strings"
)

func get(w http.ResponseWriter, r *http.Request) {
f, e := os.Open(os.Getenv("STORAGE_ROOT") + "/objects/" +
strings.Split(r.URL.EscapedPath(), "/")[2])
if e != nil {
log.Println(e)
w.WriteHeader(http.StatusNotFound)
return
}
defer f.Close()
io.Copy(w, f)
}
16 changes: 16 additions & 0 deletions objects/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package objects

import "net/http"

func Handler(w http.ResponseWriter, r *http.Request) {
m := r.Method
if m == http.MethodPut {
put(w, r)
return
}
if m == http.MethodGet {
get(w, r)
return
}
w.WriteHeader(http.StatusMethodNotAllowed)
}
21 changes: 21 additions & 0 deletions objects/put.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package objects

import (
"io"
"log"
"net/http"
"os"
"strings"
)

func put(w http.ResponseWriter, r *http.Request) {
f, e := os.Create(os.Getenv("STORAGE_ROOT") + "/objects/" +
strings.Split(r.URL.EscapedPath(), "/")[2])
if e != nil {
log.Println(e)
w.WriteHeader(http.StatusInternalServerError)
return
}
defer f.Close()
io.Copy(f, r.Body)
}
15 changes: 15 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

import (
"goDistributed-Object-storage/objects"
"log"
"net/http"
"os"
)

func main() {
http.HandleFunc("/objects/", objects.Handler)
log.Fatal(http.ListenAndServe(os.Getenv("LISTEN_ADDRESS"), nil))
}


0 comments on commit c5a604a

Please sign in to comment.