==背景==
項目是私有化部署,有一些圖片及文件需要存儲,數量不多,單文件大小也不是很大,
目前只能放在linux下的絕對路徑下面,有必要研究一下文件系統。
想找一個簡單(不需要依賴其他組件,安裝容易)易用(使用起來簡單)的分布式文件系統的技術,
朋友介紹了seaweedfs這個結束,過來研究一下,並記錄一下筆記。
==環境==
試驗了兩個環境:
環境1:Linux CentOS7 (個人虛擬機)
環境2:阿里雲ECS服務器 Centos8
Seaweedfs版本:2.13
==參考博客==
在整個驗證之前,我看了兩個比較好的博客。
https://www.cnblogs.com/townsend/p/11315348.html
http://www.diyhi.com/seaweedfs.html
==簡單部署==
1、下載
官網:https://github.com/chrislusf/seaweedfs/releases
我的操作系統選擇的是linux centos8,選擇了下載linux_amd64.tar.gz
2、上傳並解壓縮
tar -zxvf linux_amd64.tar.gz
可以先查看一下weed命令的支持參數
命令:weed -h
指令 說明 benchmark 測試seaweedfs的文件讀寫性能 backup 將volume備份到本地 compact 壓縮volume文件 filer.copy 將一個或多個文件復制到filer目錄下 fix 發生崩潰時修復索引文件 filer.replicate 將文件的修改復制到另一個目標 server 啟動master服務、volume服務、filer和s3服務 master 啟動master服務 filer 啟動filer服務,指向一個或者多個master服務 s3 啟動s3服務,前提是啟動filer服務 upload 上傳一個或多個文件 download 根據文件id下載文件 scaffold 生成基本的配置文件 shell 執行可交互的管理指令 version 打印seaweedfs的版本 volume 啟動volume服務 export 列出或者輸出volume中的文件 mount 將filer掛載成一個目錄 webdav 啟動webdav服務,前提是啟動filer服務
3、啟動master
命令:./weed master
nohup方式啟動:nohup /home/radmin/soft/weed master whiteList=127.0.0.1,192.168.29.100 > /home/radmin/soft/weed.log &
4、創建並添加存儲路徑
創建兩個路徑:
/home/radmin/data/seaweedfs/volume1
/home/radmin/data/seaweedfs/volume2
添加存儲節點目錄。命令:
nohup /home/radmin/soft/weed volume -dir="/home/radmin/data/seaweedfs/volume1" -max=1000 -mserver="vm1:9333" -port=10001 whiteList=127.0.0.1,192.168.20.100 > /home/radmin/soft/volume1.log &
nohup /home/radmin/soft/weed volume -dir="/home/radmin/data/seaweedfs/volume2" -max=1000 -mserver="vm1:9333" -port=10002 whiteList=127.0.0.1,192.168.20.100 > /home/radmin/soft/volume2.log &
5、通過瀏覽器打開頁面
地址:http://192.168.29.100:9333/
==文件存儲==
1、獲取一個文件id以及volume服務的url
curl http://192.168.29.100:9333/dir/assign {"fid":"3,01c226517c","url":"192.168.29.100:10001","publicUrl":"192.168.29.100:10001","count":1}
PS:關於文件ID
如上述的:3,01c226517c。可以通過/dir/assign
來獲取。
文件id分為3個部分。
第一部分:逗號左邊的數字3表示volume id。是一個32位無符號整型
第二部分:逗號右邊的01
表示file key。是一個64為無符號整型
第三部分:剩下c226517c表示file cookie。是一個32位無符號整型,用來防止文件url被猜到。
2、根據url和fid來上傳文件
curl -F file=@/home/radmin/soft/nice.jpg 192.168.29.100:10001/3,01c226517c {"name":"nice.jpg","size":224273,"eTag":"96a0c7bbbceafae209d518bb6b192051"}
==文件獲取==
1、根據volume id查詢訪問volume的url
curl "http://192.168.29.100:9333/dir/lookup?volumeId=3" {"volumeId":"3","locations":[{"url":"192.168.29.100:10001","publicUrl":"192.168.29.100:10001"}]}
2、通過url訪問文件
http://192.168.29.100:10001/3,01c226517c
怎么樣,看到個美女心情還是不錯的吧。
==filer服務==
上面的一系列操作之后,文件是可以看到了,不過貌似還的通過文件id什么的搞,對於喜歡“路徑及文件名”的人來說,實在是不爽。
filer服務就是為了解決這個問題而存在的。filer是一個在seaweedfs之上的服務,它保存路徑與文件id的映射關系,最終還是使用文件id來訪問文件。
1、filer配置文件樣例
可以先運行以下命令來查看filer配置文件的樣例。
命令:/home/radmin/soft/weed scaffold -config=filer
filer配置包括recursive_delete(是否遞歸刪除),以及存儲方式的選擇。
filer服務支持多種數據庫來保存路徑與文件id的映射關系,包括:leveldb2、mysql、postgres、cassandra、redis、redis_cluster、etcd、tikv。
2、創建filer配置文件
我這里在weed同目錄下創建了配置文件,並嘗試使用mysql來保存映射關系。
文件名:filer.toml
路徑:/home/radmin/soft
關鍵配置項:
另外,需要根據提示,在mysql中創建數據庫及表。
數據庫名字:seaweedfs
建表語句:
CREATE TABLE
IF
NOT EXISTS filemeta (
dirhash BIGINT COMMENT 'first 64 bits of MD5 hash value of directory field', NAME VARCHAR ( 1000 ) COMMENT 'directory or file name', DIRECTORY TEXT COMMENT 'full path to parent directory', meta LONGBLOB, PRIMARY KEY ( dirhash, NAME ) ) DEFAULT CHARSET = utf8
3、啟動filer服務
命令:/home/radmin/soft/weed filer -master="192.168.29.100:9333" -ip=192.168.29.100
后台啟動:nohup /home/radmin/soft/weed filer -master="192.168.29.100:9333" -ip=192.168.29.100 > /home/radmin/soft/filer.log &
4、嘗試通過路徑來管理文件
上傳:curl -F file=@/home/radmin/soft/nice2.jpg http://192.168.29.100:8888/beautygirl
注意:beautygirl是一個文件的名字,而不是文件夾的名字。
查看:curl "http://192.168.29.100:8888/beautygirl"
也可以通過瀏覽器查看:http://192.168.29.100:8888/beautygirl
還可以加上快讀來指定圖片大小:http://192.168.29.100:8888/beautygirl?width=500
刪除文件
命令:curl -X DELETE "http://192.168.29.100:8888/beautygirl"
遞歸刪除路徑下所有的文件以及目錄
命令:curl -X DELETE http://192.168.29.100:8888/path/to/dir?recursive=true
遞歸刪除所有的文件以及目錄,忽略遞歸錯誤
命令:curl -X DELETE http://192.168.29.100:8888/path/to/dir?recursive=true&ignoreRecursiveError=true
==目錄掛載==
seaweedfs可以方便地掛載到本地,可以像普通文件一樣操作其中的文件。
命令:/home/radmin/weed mount -filer=192.168.29.100:8888 -dir=/home/radmin/data/seaweedfs/mount(Linux本地的路徑)
【小插曲】
上面的命令執行的時候一直報錯,
解決辦法:
我一個兄弟說是缺少fuse的包,所以上網上下載了fuse的依賴
當然,如果服務器能聯網,直接yum install fuse就可以了,我的虛擬機可能有點問題,沒有下載下來,於是手動下載了fuse的rpm包
https://rpmfind.net/linux/rpm2html/search.php?query=%2Fusr%2Fbin%2Ffusermount
安裝之后,重新執行mount命令
發現,數據果然mount下來了。
感謝建新同學。
==副本策略==
seaweedfs的副本機制是volume層面,而不是文件層面的。這意味着不同的節點存在相同的volume,其中的文件也是相同的。
volume啟動時可以通過-dataCenter和-rack指定數據中心和機架。
seaweedfs的副本機制就是通過在不同的數據中心和機架創建相同的volume來實現的。
在master啟動時可以指定副本的策略:
命令:/home/radmin/soft/weed master -defaultReplication=001
副本策略是xyz形式的數字,其含義如下:
- x 在其他數據中心的副本數量
- y 在相同數據中心,其他機架的副本數量
- z 在相同機架,不同服務器的副本數量
x,y,z分別可以是0,1,2,因此有9種副本類型組合。每種副本類型都創建了x+y+z+1個文件。
==安全==
(未驗證)
==線上使用==
【節點數】
3個(阿里雲ECS服務器)
【路徑規划】
【部署命令】
1-1、啟動節點1的master
nohup /home/radmin/seaweedfs-2.13/weed master -ip=rexel-ids001 -port=9333 -mdir=/home/data/seaweedfs/master -defaultReplication=001 > /home/radmin/seaweedfs-2.13/master.log &
1-2、啟動節點1的volume
nohup /home/radmin/seaweedfs-2.13/weed volume -ip=rexel-ids001 -port=10001 -dir=/home/data/seaweedfs/volume1 -max=10 -mserver=rexel-ids001:9333 -dataCenter=dc1 -rack=rack1> /home/radmin/seaweedfs-2.13/volume1.log &
nohup /home/radmin/seaweedfs-2.13/weed volume -ip=rexel-ids001 -port=10002 -dir=/home/data/seaweedfs/volume2 -max=10 -mserver=rexel-ids001:9333 -dataCenter=dc1 -rack=rack1> /home/radmin/seaweedfs-2.13/volume2.log &
1-3、啟動節點1的filer
nohup /home/radmin/seaweedfs-2.13/weed filer -ip=rexel-ids001 -port=8888 -port.readonly=7777 -master=rexel-ids001:9333 > /home/radmin/seaweedfs-2.13/filer.log &
2-1、啟動節點2的volume
nohup /home/radmin/seaweedfs-2.13/weed volume -ip=rexel-ids002 -port=10001 -dir=/home/data/seaweedfs/volume1 -max=10 -mserver=rexel-ids001:9333 -dataCenter=dc1 -rack=rack1> /home/radmin/seaweedfs-2.13/volume1.log &
nohup /home/radmin/seaweedfs-2.13/weed volume -ip=rexel-ids002 -port=10002 -dir=/home/data/seaweedfs/volume2 -max=10 -mserver=rexel-ids001:9333 -dataCenter=dc1 -rack=rack1> /home/radmin/seaweedfs-2.13/volume2.log &
2-2、啟動節點2的filer
nohup /home/radmin/seaweedfs-2.13/weed filer -ip=rexel-ids002 -port=8888 -port.readonly=7777 -master=rexel-ids001:9333 > /home/radmin/seaweedfs-2.13/filer.log &
3-1、啟動節點3的volume
nohup /home/radmin/seaweedfs-2.13/weed volume -ip=rexel-ids003 -port=10001 -dir=/home/data/seaweedfs/volume1 -max=10 -mserver=rexel-ids001:9333 -dataCenter=dc1 -rack=rack1> /home/radmin/seaweedfs-2.13/volume1.log &
nohup /home/radmin/seaweedfs-2.13/weed volume -ip=rexel-ids003 -port=10002 -dir=/home/data/seaweedfs/volume2 -max=10 -mserver=rexel-ids001:9333 -dataCenter=dc1 -rack=rack1> /home/radmin/seaweedfs-2.13/volume2.log &
3-2、啟動節點3的filer
nohup /home/radmin/seaweedfs-2.13/weed filer -ip=rexel-ids003 -port=8888 -port.readonly=7777 -master=rexel-ids001:9333 > /home/radmin/seaweedfs-2.13/filer.log &
【路徑規范】
規則:http://rexel-ids001:9433/{project}/{type}/{file}
樣例:http://rexel-ids001:9433/ids/pict/nice1.jpg
【filer配置】
配置文件存放路徑:/etc/seaweedfs/filer.toml
==Java客戶端調用==
寫了一個簡單的Spring Boot的java調用樣例,供參考。
1、配置文件
在spring boot的resource配置文件中,增加seaweedfs的上傳地址
seaweedfs:
url: http://rexel-ids001:8888/ids/online
2、增加pom依賴
根據實際驗證結果,需要增加兩個依賴。其中tika是用來解析MediaType的工具類。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> <version>2.3.5.RELEASE</version> </dependency> <dependency> <groupId>org.apache.tika</groupId> <artifactId>tika-core</artifactId> <version>1.25</version> </dependency>
3、Spring Boot代碼
代碼結構如下圖所示:
文件:SeaweedfsInfo.java
package com.rexel.core.compnent.seaweedfs.vo; import org.springframework.context.annotation.Configuration; import org.springframework.beans.factory.annotation.Value; @Configuration public class SeaweedfsInfo { /** * Seaweedfs連接地址 * */ @Value("${seaweedfs.url}") public String url; }
文件:CompSeaweedfsController.ajva
package com.rexel.core.compnent.seaweedfs.controller; import com.rexel.core.compnent.seaweedfs.service.ICompSeaweedfsService; import com.rexel.core.framework.web.domain.AjaxResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; /** * Seaweedfs Controller * * @author admin * @date 2020-12-9 */ @RestController @RequestMapping("/rexel/seaweedfs") public class CompSeaweedfsController { @Autowired ICompSeaweedfsService seaweedfsService; @PostMapping("/upload") public AjaxResult upload(@RequestParam("file") MultipartFile file) { return seaweedfsService.upload(file); } }
文件:ICompSeaweedfsService.java
package com.rexel.core.compnent.seaweedfs.service; import com.rexel.core.framework.web.domain.AjaxResult; import org.springframework.web.multipart.MultipartFile; /** * Seaweedfs Service接口 * * @author admin * @date 2020-12-9 */ public interface ICompSeaweedfsService { /** * 上傳文件到Seaweedfs服務器 * * @param multipartFile 需要上傳的文件 * @return 結果 */ AjaxResult upload(MultipartFile multipartFile); }
文件:CompSeaweedfsServiceImpl.java
package com.rexel.core.compnent.seaweedfs.service.impl; import com.rexel.core.common.utils.UuidUtils; import com.rexel.core.compnent.seaweedfs.service.ICompSeaweedfsService; import com.rexel.core.compnent.seaweedfs.vo.SeaweedfsInfo; import com.rexel.core.framework.web.domain.AjaxResult; import java.io.File; import java.io.IOException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.tika.Tika; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; /** * Seaweedfs Service業務層處理 * * @author admin * @date 2020-12-9 */ @Service @Slf4j public class CompSeaweedfsServiceImpl implements ICompSeaweedfsService { @Autowired private SeaweedfsInfo seaweedfsInfo; /** * 文件上傳 * * @param multipartFile 需要上傳的文件 * @return 結果 */ @Override public AjaxResult upload(MultipartFile multipartFile) { // 獲取上傳文件名 String filename = multipartFile.getOriginalFilename(); if (StringUtils.isBlank(filename)) { return AjaxResult.error("上傳文件為空。"); } // 獲取文件擴展名 String fileExtensionName = filename.substring(filename.lastIndexOf(".")); // 生成文件唯一識別碼 String fileNewName = UuidUtils.get16Uuid() + fileExtensionName; // 文件目標位置 File file = new File(fileNewName); // 將文件內容輸入到file中 try { FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), file); } catch (IOException ioe) { return AjaxResult.error("上傳文件錯誤。"); } // 獲取上傳地址 String url = seaweedfsInfo.url + "/" + fileNewName; // 執行文件上傳 String result = doWebClientPost(file, url); log.info(result); return AjaxResult.success("操作成功", url); } /** * 執行文件上傳 * * @param file File實例 * @param url 上傳地址 * @return 結果 */ private String doWebClientPost(File file, String url) { MediaType mediaType = getMediaType(file); HttpHeaders headers = new HttpHeaders(); headers.setContentType(mediaType); HttpEntity<FileSystemResource> entity = new HttpEntity<>(new FileSystemResource(file), headers); Mono<String> response = WebClient.create().post().uri(url).contentType(mediaType) .body(BodyInserters.fromMultipartData("filename", entity)).retrieve() .bodyToMono(String.class); return response.block(); } /** * 獲取MediaType * * @param file File實例 * @return MediaType */ private MediaType getMediaType(File file) { try { Tika tika = new Tika(); return MediaType.valueOf(tika.detect(file)); } catch (IOException e) { e.printStackTrace(); } return MediaType.MULTIPART_FORM_DATA; } }
4、通過postman驗證
發送請求
查看文件服務器結果
==遺留問題==
1、Master是單點的,存在單點故障的問題。
2、未配置systemctl守護進程。
3、程序中未對文件類型及文件大小限制。
--END--