一、认识SeaweedFS
Seaweedfs是一个简单,高扩展性的分布式文件系统,是由Golang开发的分布式存储开源项目,它是用来存储文件的系统,并且与使用的语言无关,任何语言,任何框架都可以以它为文件存储,它的设计原理主要来源于一篇基于 Facebook 的图片存储系统的论文:
https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Beaver.pdf,
感兴趣的可以看看,扩展思维。
它的两个目标分别是:
存储数十亿级的文件
快速响应文件。
seaweedfs选择以键值对(key->file)的实现方式。
seaweedfs的中心节点(center master)并不会管理所有文件的元数据而仅仅管理文件卷(file volmume),文件及其元数据的管理是由volume server实现的。这可以缓解center master的并发压力,并且将文件元数据分配到volume server可以实现更快的文件访问(只需一次磁盘读取操作)。
二、架构原理
下面,我们借助官网和网上的资料,来看看SeaweedFS的整体架构设计原理。前提是小伙伴已经使用过了,可能更好理解。看不懂没问题,看完本文操作成功后,再来看原理和论文就有些感悟了。
通常,分布式文件系统将每个文件拆分为块,中央主服务器保持文件名,到块句柄的块索引以及每个块服务器具体的块。
该架构非常简单。实际数据存储在存储节点的卷上。一个卷服务器可以有多个卷,并且都可以支持基本的读写访问。所有卷由主服务器管理。主服务器包含卷ID到卷服务器映射。这是相当静态的信息,可以轻松缓存。
在每个写入请求上,主服务器还会生成一个file key,这是一个不断增长的64位无符号整数。由于写入请求通常不如读取请求频繁,因此一台主服务器应该能够很好地处理并发
这种设计方案优点固然很多,但是主要的缺点是中央主服务器无法高效地处理许多小文件,并且由于所有读请求都需要通过块主服务器,所以对于许多高并发用户来说可能无法很好地扩展。
主服务器(master server)和卷服务器(volmue server) 。
三、搭建SeaweedFS环境(二进制方式)
(1)、安装GO环境
下载go语言包
sudo wget https://storage.googleapis.com/golang/go1.9.2.linux-amd64.tar.gz
解压到指定目录,并添加环境变量
sudo tar -C /usr/local -xzf go1.9.2.linux-amd64.tar.gz
添加环境变量
sudo vi /etc/profile
#工作目录
export GOPATH=/opt/go
#解压目录
export GOROOT=/usr/local/go
export GOARCH=386
export GOOS=linux
export GOBIN=$GOROOT/bin
export GOTOOLS=$GOROOT/pkg/tool/
export PATH=$PATH:$GOBIN:$GOTOOLS
source /etc/profile
(2)、安装git、mercurial
sudo yum install -y mercurial git
(3)、安装seaweedfs
下载安装seaweedfs地址
如果下载失败,可以手动下载
sudo wget https://github.com/chrislusf/seaweedfs/releases/download/0.96/linux_amd64.tar.gz
sudo tar -zxvf linux_amd64.tar.gz
(4)、配置运行seaweedfs
进入解压目录,以守护进程启动seaweedfs的主服务及集群
(在启动前,先要创建相应的目录,/data/fileData,/data/t_v1,/data/t_v2,/data/t_v3)
sudo nohup ./weed master -mdir=/data/fileData -port=9333 -defaultReplication="001" -ip="150.158.44.198" >>/data/fileData/server_sfs.log &
sudo ./weed volume -dir=/data/t_v1 -max=5 -mserver="150.158.44.198:9333" -port=9080 -ip="150.158.44.198" >>/data/t_v1_sfs.log &
sudo ./weed volume -dir=/data/t_v2 -max=5 -mserver="150.158.44.198:9333" -port=9081 -ip="150.158.44.198" >>/data/t_v2_sfs.log &
sudo ./weed volume -dir=/data/t_v3 -max=5 -mserver="150.158.44.198:9333" -port=9082 -ip="150.158.44.198" >>/data/t_v3_sfs.log &
将上面的ip地址换为你的ip即可,默认可设为localhost。volume多少可以根据自己的情况添加。mdir、dir是指定文件存储路径。
一个 MasterServer 对应三个 VolumeServer ,设置复制模式为 “001” , 也就是在相同 Rack 下复制副本为一份,也就是总共有两份
说明
defaultReplication
000 不备份, 只有一份数据
001 在相同的rackj里备份一份数据
010 在相同数据中心内不同的rack间备份一份数据
100 在不同的数据中心备份一份数据
200 在两个不同的数据中心各复制2次
110 在不同的rack备份一份数据, 在不同的数据中心备份一次
如果数据备份类型是 xyz形式
各自的意义
x 在别的数据中心备份的份数
y 不相同数据中心不同的racks备份的份数
z 在别的服务器相同的rack的备份份数
访问服务器ip地址:9333,可以看到如下界面
四、搭建SeaweedFS环境(docker方式)
准备系统环境
docker
docker-compose
下载seaweedfs源码并安装
1)git clone https://github.com/chrislusf/seaweedfs.git
2)cd seaweedfs/docker
3)docker build -t sunsl/seaweedfs .
4)# 修改docker-compose.yml中的 image 为 sunsl/seaweedfs
5)docker-compose up
整个过程很简单,就不多说了,效果和二进制安装是一样的。
五、seaweedfs在net core中的使用
可以先看看基本使用,很简单。
----查看是否启动成功
curl -X POST http://127.0.0.1:9333/dir/assign
----上传文件
curl -F file=@/test/101-1225-064124-39-20180613013100012.png
http://127.0.0.1:9333/submit
----在浏览器输入地址查看已上传的文件
http://127.0.0.1:9333/1,027bf4fdc5
----删除已上传的文件
curl -X DELETE http://127.0.0.1:9333/3,034537622c
————————————————
接下来我们在BlogCore中进行封装
1、配置文件配置参数
"Seaweed": [
{
"BaseUrl": "http://150.158.44.198:9333",
"DirAssign": "/dir/assign",
"routingKey": "ActUpload"
}
]
2、文件上传
上传文件,支持同名的修改,
就是传同一个fid,可以直接覆盖原来的文件。
[HttpPost]
[AllowAnonymous]
public async Task<MessageModel<string>> UploadPicSeaweed()
{
var data = new MessageModel<string>();
IFormFileCollection files = Request.Form.Files;
if (files == null || !files.Any()) { data.msg = "请选择上传的文件。"; return data; }
FormFile picfile = files[0] as FormFile;
var stream = picfile.OpenReadStream();
var bytes = new byte[stream.Length];
await stream.ReadAsync(bytes, 0, bytes.Length);
var Configuration = (Appsettings.app<SeaweedVo>("Seaweed"))?.FirstOrDefault();
var fileInfo = await GetUploadFileUrlAsync();
var url = $"{fileInfo.PublicUrl}/{fileInfo.Fid}";
var uploadResponse = await UploadFileAsync(url, bytes);
data.response = fileInfo.Fid;
data.msg = "上传成功";
data.success = true;
return data;
}
private async Task<SeaweedFSDirAssignModel> GetUploadFileUrlAsync()
{
using var client = _httpClientFactory.CreateClient();
var Configuration = (Appsettings.app<SeaweedVo>("Seaweed"))?.FirstOrDefault();
var url = $"{Configuration.BaseUrl}{Configuration.DirAssign}";
var response = await client.GetAsync(url);
var body = await response.Content.ReadAsStringAsync();
var json = JsonConvert.DeserializeObject<SeaweedFSDirAssignModel>(body);
return json;
}
private async Task<SeaweedFSUploadResponse> UploadFileAsync(string url, byte[] context)
{
using var client = _httpClientFactory.CreateClient();
using var multipartContext = new MultipartFormDataContent();
multipartContext.Add(
new ByteArrayContent(
context
),
"file"
);
var fileUrl = $"{url}";
var response = await client.PostAsync(fileUrl, multipartContext);
var body = await response.Content.ReadAsStringAsync();
var json = JsonConvert.DeserializeObject<SeaweedFSUploadResponse>(body);
return json;
}
1、获取publicurl与fid
2、根据第一步的url提交文件
3、根据第一步的fid,查看详情
3、文件下载
这里就是上边的预览的封装
[HttpGet]
[AllowAnonymous]
public async Task<FileStreamResult> DownPicSeaweed([FromServices] IWebHostEnvironment environment, string imgname, string type = "")
{
using var httpClient = _httpClientFactory.CreateClient();
var errStream = new MemoryStream(System.Text.Encoding.Default.GetBytes("访问的资源不存在!"));
try
{
var configSeaWeed = (Appsettings.app<SeaweedVo>("Seaweed"))?.FirstOrDefault();
var callback = $"{configSeaWeed.BaseUrl}/{imgname}";
// 支持添加header
//httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
var httpResponse = await httpClient.GetAsync(new Uri(callback));
var filestreamObj = await httpResponse.Content.ReadAsStreamAsync();
var contenttype = httpResponse.Content?.Headers?.ContentType?.ObjToString();
return File(filestreamObj, contenttype);
}
catch (Exception e)
{
Console.WriteLine("DownPicSeaweed:err:" + e.Message + e.StackTrace);
return File(errStream, "application/json");
}
}