FastDFS檔案系統單機環境搭建並使用
FastDFS檔案系統單機環境搭建
專案中使用,又把原來忘記的東西重新撿起來,整理一份
FastDFS基本介紹
FastDFS 系統三個角色
Tracker Server
Tracker Server: 跟蹤伺服器,主要做排程工作,起到均衡的作用;負責管理所有的 storage server
和 group,每個 storage 在啟動後會連線 Tracker,告知自己所屬 group 等資訊,並保持週期性心跳。
Storage Server
Storage Server:儲存伺服器,主要提供容量和備份服務;以 group 為單位,每個 group 內可以有多臺 storage server,資料互為備份。
Client
Client:客戶端,上傳下載資料的伺服器,也就是我們自己的專案所部署在的伺服器。
fastDFS優點:1.高可靠性:無單點故障 2.高吞吐性:只要 Group 足夠多,資料流量將足夠分散。
搭建fastDFS(阿里伺服器)
安裝 libfastcommon 和 FastDFS
一、安裝 libfastcommon
這裡是通過wget下載:
wget https://github.com/happyfish100/libfastcommon/archive/V1.0.7.tar.gz
解壓 libfastcommon,命令:
tar -zxvf V1.0.7.tar.gz
移動解壓目錄到/usr/local下:
mv libfastcommon-1.0.7 /usr/local/
進入libfastcommon-1.0.7目錄,命令:
cd /usr/local/libfastcommon-1.0.7
編譯
./make.sh
安裝:
./make.sh install
安裝 libfastcommon 成功:
#### 二、安裝 FastDFS ---- 這裡也是通過wget下載: ```java wget https://github.com/happyfish100/fastdfs/archive/V5.05.tar.gz ``` 解壓 FastDFS: ```java tar -zxvf V5.05.tar.gz ``` 移動解壓目錄到/usr/local下: ```java mv fastdfs-5.05/ /usr/local/ ``` 進入fastfds-5.05目錄,命令: ```java cd /usr/local/fastdfs-5.05 ``` 編譯: ```java ./make.sh ``` 安裝: ```java ./make.sh install ``` 安裝成功: ![](https://img2020.cnblogs.com/blog/2026387/202012/2026387-20201209155332130-1517220266.jpg)
配置
配置 Tracker 服務
上述安裝成功後,在/etc/目錄下會有一個fdfs的目錄,進入它。會看到三個.sample字尾的檔案,這是作者給我們的示例檔案,我們需要把其中的tracker.conf.sample檔案改為tracker.conf配置檔案並修改它。參照以下命令:
cd /etc/fdfs/
cp tracker.conf.sample tracker.conf
vim tracker.conf
// 編輯tracker.conf檔案,找到你需要修改的兩個引數就可以了,我這裡不做修改,用預設的
# the base path to store data and log files // 資料和日誌的存放目錄
base_path=/home/yuqing/fastdfs
# HTTP port on this tracker server // http服務埠
http.server_port=8070
修改完成後我們需要建立tracker的工作目錄,不然啟動報錯(上面base_path配置的目錄)
mkdir -p /home/yuqing/fastdfs
啟動tracker服務:
/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf start
檢視監聽:
ps -ef|grep fdfs
#或
netstat -lnpt|grep fdfs
Tracker服務安裝成功,啟動成功:
配置 Storage 服務
現在開始配置 Storage 服務,由於我這是單機器測試,你把 Storage 服務放在多臺伺服器也是可以的,它有 Group(組)的概念,同一組內伺服器互備同步,這裡不再演示。直接開始配置,依然是進入/etc/fdfs的目錄操作,首先進入它。會看到三個.sample字尾的檔案,我們需要把其中的storage.conf.sample檔案改為storage.conf配置檔案並修改它。參照以下命令:
cd /etc/fdfs/
cp storage.conf.sample storage.conf
vim storage.conf
開啟storage.conf檔案後,找到下面引數進行修改:
# the base path to store data and log files // 資料和日誌的存放目錄
base_path=/home/yuqing/fastdfs
# path(disk or mount point) count, default value is 1 // storage在儲存檔案時支援多路徑,預設只設置一個(下面store_path0配置的數量個數)
store_path_count=1
# store_path#, based 0, if store_path0 not exists, it's value is base_path // 配置多個store_path路徑,從0開始,如果store_path0不存在,則base_path必須存在
# the paths must be exist
// 此處可以配置多個路徑,如:store_path0=xx, store_path1=xx,store_path2=xx
// 多個專案的檔案按照資料夾分割,通過/usr/bin/fdfs_upload_file <config_file> <local_filename> [storage_ip:port] [store_path_index] 選擇storage伺服器和store_path角標上傳到相應的專案下
store_path0=/home/yuqing/fastdfs
# tracker_server can ocur more than once, and tracker_server format is // 設定tracker_server換成自己的IP埠
# "host:port", host can be hostname or ip address
tracker_server=192.168.2.121:22122
修改完成後我們需要建立tracker的工作目錄,不然啟動報錯(上面base_path配置的目錄和store_path0配置的目錄)
mkdir -p /home/yuqing/fastdfs
啟動storage服務:
/usr/bin/fdfs_storaged /etc/fdfs/storage.conf start
檢視監聽:
ps -ef|grep fdfs 或 netstat -lnpt|grep fdfs
22122 和 23000埠都在監聽:(/home/yuqing/fastdfs資料夾下看的話,會出現一大堆資料夾)
到這裡,我們安裝配置並啟動了 Tracker 和 Storage 服務,也沒有報錯了。那他倆是不是在通訊呢?我們可以監視一下:
/usr/bin/fdfs_monitor /etc/fdfs/storage.conf
紅線處ACTIVE成功:
配置 fdfs_upload_file 上傳檔案
依然是進入/etc/fdfs的目錄操作,首先進入它。會看到三個.sample字尾的檔案,我們需要把其中的client.conf.sample檔案改為client.conf配置檔案並修改它。參照以下命令:
cd /etc/fdfs/
cp client.conf.sample client.conf
vim client.conf
進行修改:
# the base path to store log files // 日誌的存放目錄
base_path=/home/yuqing/fastdfs
# tracker_server can ocur more than once, and tracker_server format is // tracker_server服務的地址和埠號
# "host:port", host can be hostname or ip address
tracker_server=192.168.2.121:22122
修改完成後我們就可以通過/usr/bin/fdfs_upload_file命令上傳檔案了,/usr/bin/fdfs_upload_file 命令用法:
[root@localhost fdfs]# /usr/bin/fdfs_upload_file
Usage: /usr/bin/fdfs_upload_file <config_file> <local_filename> [storage_ip:port] [store_path_index]
// 命令 配置檔案 上傳的檔案 storage_ip和埠 store_path角標
上傳成功:
附帶 tracker.conf 和 storage.conf 的具體配置註釋
tracker.conf 配置檔案分析:
// 配置tracker.conf這個配置檔案是否生效,因為在啟動fastdfs服務端程序時需要指定配置檔案,所以需要使次配置檔案生效。false是生效,true是遮蔽。
disabled=false
// 程式的監聽地址,如果不設定則監聽所有地址
bind_addr=
// tracker監聽的埠
port=22122
// 連結超時設定
connect_timeout=30
// tracker在通過網路傳送接收資料的超時時間
network_timeout=60
// 資料和日誌的存放地點
base_path=/opt/fdfs
// 服務所支援的最大連結數
max_connections=256
// 工作執行緒數一般為cpu個數
work_threads=4
// 在儲存檔案時選擇group的策略,0:輪訓策略 1:指定某一個組 2:負載均衡,選擇空閒空間最大的group
store_lookup=2
// 如果上面的store_lookup選擇了1,則這裡需要指定一個group
// store_group=group2
// 在group中的哪臺storage做主storage,當一個檔案上傳到主storage後,就由這臺機器同步檔案到group內的其他storage上,0:輪訓策略 1:根據ip地址排序,第一個 2:根據優先順序排序,第一個
store_server=0
// 選擇那個storage作為主下載伺服器,0:輪訓策略 1:主上傳storage作為主下載伺服器
download_server=0
// 選擇檔案上傳到storage中的哪個(目錄/掛載點),storage可以有多個存放檔案的base path 0:輪訓策略 2:負載均衡,選擇空閒空間最大的
store_path=0
// 系統預留空間,當一個group中的任何storage的剩餘空間小於定義的值,整個group就不能上傳檔案了
reserved_storage_space = 4GB
// 日誌資訊級別
log_level=info
// 程序以那個使用者/使用者組執行,不指定預設是當前使用者
run_by_group=
run_by_user=
// 允許那些機器連線tracker預設是所有機器
allow_hosts=*
// 設定日誌資訊重新整理到disk的頻率,預設10s
sync_log_buff_interval = 10
// 檢測storage伺服器的間隔時間,storage定期主動向tracker傳送心跳,如果在指定的時間沒收到訊號,tracker人為storage故障,預設120s
check_active_interval = 120
// 執行緒棧的大小,最小64K
thread_stack_size = 64KB
// storage的ip改變後服務端是否自動調整,storage程序重啟時才自動調整
storage_ip_changed_auto_adjust = true
// storage之間同步檔案的最大延遲,預設1天
storage_sync_file_max_delay = 86400
// 同步一個檔案所花費的最大時間
storage_sync_file_max_time = 300
// 是否用一個trunk檔案儲存多個小檔案
use_trunk_file = false
// 最小的solt大小,應該小於4KB,預設256bytes
slot_min_size = 256
// 最大的solt大小,如果上傳的檔案小於預設值,則上傳檔案被放入trunk檔案中
slot_max_size = 16MB
// trunk檔案的預設大小,應該大於4M
trunk_file_size = 64MB
// http服務是否生效,預設不生效
http.disabled=false
// http服務埠
http.server_port=8080
// 檢測storage上http服務的時間間隔,<=0表示不檢測
http.check_alive_interval=30
// 檢測storage上http服務時所用請求的型別,tcp只檢測是否可以連線,http必須返回200
http.check_alive_type=tcp
// 通過url檢測storage http服務狀態
http.check_alive_uri=/status.html
// if need find content type from file extension name
http.need_find_content_type=true
// 用include包含進http的其他設定
// include http.conf
storage.conf配置檔案
storage.conf配置檔案分析:
// 同tracker.conf
disabled=false
// 這個storage伺服器屬於那個group
group_name=group1
// 同tracker.conf
bind_addr=
// 連線其他伺服器時是否繫結地址,bind_addr配置時本引數才有效
client_bind=true
// 同tracker.conf
port=23000
connect_timeout=30
network_timeout=60
// 主動向tracker傳送心跳檢測的時間間隔
heart_beat_interval=30
// 主動向tracker傳送磁碟使用率的時間間隔
stat_report_interval=60
// 同tracker.conf
base_path=/opt/fdfs
max_connections=256
// 接收/傳送資料的buff大小,必須大於8KB
buff_size = 256KB
// 同tracker.conf
work_threads=4
// 磁碟IO是否讀寫分離
disk_rw_separated = true
// 是否直接讀寫檔案,預設關閉
disk_rw_direct = false
// 混合讀寫時的讀寫執行緒數
disk_reader_threads = 1
disk_writer_threads = 1
// 同步檔案時如果binlog沒有要同步的檔案,則延遲多少毫秒後重新讀取,0表示不延遲
sync_wait_msec=50
// 同步完一個檔案後間隔多少毫秒同步下一個檔案,0表示不休息直接同步
sync_interval=0
// 表示這段時間內同步檔案
sync_start_time=00:00
sync_end_time=23:59
// 同步完多少檔案後寫mark標記
write_mark_file_freq=500
// storage在儲存檔案時支援多路徑,預設只設置一個
store_path_count=1
// 配置多個store_path路徑,從0開始,如果store_path0不存在,則base_path必須存在
store_path0=/opt/fdfs
// store_path1=/opt/fastdfs2
// subdir_count * subdir_count個目錄會在store_path下建立,採用兩級儲存
subdir_count_per_path=256
// 設定tracker_server
tracker_server=x.x.x.x:22122
// 同tracker.conf
log_level=info
run_by_group=
run_by_user=
allow_hosts=*
// 檔案在資料目錄下的存放策略,0:輪訓 1:隨機
file_distribute_path_mode=0
// 當問及是輪訓存放時,一個目錄下可存放的檔案數目
file_distribute_rotate_count=100
// 寫入多少位元組後就開始同步,0表示不同步
fsync_after_written_bytes=0
// 重新整理日誌資訊到disk的間隔
sync_log_buff_interval=10
// 同步storage的狀態資訊到disk的間隔
sync_stat_file_interval=300
// 執行緒棧大小
thread_stack_size=512KB
// 設定檔案上傳伺服器的優先順序,值越小越高
upload_priority=10
// 是否檢測檔案重複存在,1:檢測 0:不檢測
check_file_duplicate=0
// 當check_file_duplicate設定為1時,次值必須設定
key_namespace=FastDFS
// 與FastDHT建立連線的方式 0:短連線 1:長連線
keep_alive=0
// 同tracker.conf
http.disabled=false
http.domain_name=
http.server_port=8888
http.trunk_size=256KB
http.need_find_content_type=true
// include http.conf
nginx簡單整合fastDFS
這裡小編只是簡單的整合,只是為了訪問方便,
配置nginx的配置檔案,nginx.conf檔案:
server {
listen 89;#監聽89埠
server_name localhost;#ip
location /group1/M00 {## 當訪問89埠並且/group1/M00開頭的請求,都有對映到fastDFS的儲存資料區
alias /usr/local/fastdfs-5.05/files_music/data;# 該地址就是檔案儲存的資料資料夾
}
}
FastDFS專案中實現檔案上傳下載
瞭解FastDFS的工作流程
一、pom檔案中加jar包
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
二、配置FastDFS基本資訊:
在專案的resource目錄下面新建fdfs_client.conf配置檔案,配置資訊如下:
connect_timeout = 60
network_timeout = 60
charset = UTF-8
http.tracker_http_port = 8070//搭建時的fastDFS的根據配置
tracker_server = 192.168.200.128:22122
二、工具類
封裝檔案的實體類
public class FastDFSFile {
//檔名字
private String name;
//檔案內容
private byte[] content;
//副檔名
private String ext;
//檔案MD5摘要值
private String md5;
//檔案建立作者
private String author;
public FastDFSFile(String name, byte[] content, String ext, String height,
String width, String author) {
super();
this.name = name;
this.content = content;
this.ext = ext;
this.author = author;
}
public FastDFSFile(String name, byte[] content, String ext) {
super();
this.name = name;
this.content = content;
this.ext = ext;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
}
public String getExt() {
return ext;
}
public void setExt(String ext) {
this.ext = ext;
}
public String getMd5() {
return md5;
}
public void setMd5(String md5) {
this.md5 = md5;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
上傳工具類(上傳、下載和刪除等等操作)
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @author Administrator
* @date 2020/12/9 10:58
**/
public class FastDFSClient {
private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class);
/***
* 初始化載入FastDFS的TrackerServer配置
*/
static {
try {
String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
ClientGlobal.init(filePath);
} catch (Exception e) {
logger.error("FastDFS Client Init Fail!",e);
}
}
/***
* 檔案上傳
* @param file
* @return 1.檔案的組名 2.檔案的路徑資訊
*/
public static String[] upload(FastDFSFile file) {
logger.info("File Name: " + file.getName() + "File Length:" + file.getContent().length);
//獲取檔案的作者
NameValuePair[] meta_list = new NameValuePair[1];
meta_list[0] = new NameValuePair("author", file.getAuthor());
long startTime = System.currentTimeMillis();
//接收返回資料
String[] uploadResults = null;
StorageClient storageClient=null;
try {
//建立StorageClient客戶端物件
storageClient = getTrackerClient();
/***
* 檔案上傳
* 1)檔案位元組陣列
* 2)副檔名
* 3)檔案作者
*/
uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
} catch (Exception e) {
logger.error("Exception when uploadind the file:" + file.getName(), e);
}
if (uploadResults == null && storageClient!=null) {
logger.error("upload file fail, error code:" + storageClient.getErrorCode());
}
logger.info("upload_file time used:" + (System.currentTimeMillis() - startTime) + " ms");
if (uploadResults == null && storageClient!=null) {
logger.error("upload file fail, error code:" + storageClient.getErrorCode());
}
//獲取組名
String groupName = uploadResults[0];
//獲取檔案儲存路徑
String remoteFileName = uploadResults[1];
logger.info("upload file successfully!!!" + "group_name:" + groupName + ", remoteFileName:" + " " + remoteFileName);
return uploadResults;
}
/***
* 獲取檔案資訊
* @param groupName:組名
* @param remoteFileName:檔案儲存完整名
* @return
*/
public static FileInfo getFile(String groupName, String remoteFileName) {
try {
StorageClient storageClient = getTrackerClient();
return storageClient.get_file_info(groupName, remoteFileName);
} catch (Exception e) {
logger.error("Exception: Get File from Fast DFS failed", e);
}
return null;
}
/***
* 檔案下載
* @param groupName
* @param remoteFileName
* @return
*/
public static InputStream downFile(String groupName, String remoteFileName) {
try {
//建立StorageClient
StorageClient storageClient = getTrackerClient();
//下載檔案
byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
InputStream ins = new ByteArrayInputStream(fileByte);
return ins;
} catch (Exception e) {
logger.error("Exception: Get File from Fast DFS failed", e);
}
return null;
}
/***
* 檔案刪除
* @param groupName
* @param remoteFileName
* @throws Exception
*/
public static void deleteFile(String groupName, String remoteFileName)
throws Exception {
//建立StorageClient
StorageClient storageClient = getTrackerClient();
//刪除檔案
int i = storageClient.delete_file(groupName, remoteFileName);
logger.info("delete file successfully!!!" + i);
}
/***
* 獲取Storage組
* @param groupName
* @return
* @throws IOException
*/
public static StorageServer[] getStoreStorages(String groupName)
throws IOException {
//建立TrackerClient
TrackerClient trackerClient = new TrackerClient();
//獲取TrackerServer
TrackerServer trackerServer = trackerClient.getConnection();
//獲取Storage組
return trackerClient.getStoreStorages(trackerServer, groupName);
}
/***
* 獲取Storage資訊,IP和埠
* @param groupName
* @param remoteFileName
* @return
* @throws IOException
*/
public static ServerInfo[] getFetchStorages(String groupName,
String remoteFileName) throws IOException {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName);
}
/***
* 獲取Tracker服務地址
* @return
* @throws IOException
*/
public static String getTrackerUrl() throws IOException {
// return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+"/";
return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":89"+"/";//89為nginx監聽的埠,有nginx代理監聽對映到檔案,進行訪問
}
/***
* 獲取Storage客戶端
* @return
* @throws IOException
*/
private static StorageClient getTrackerClient() throws IOException {
TrackerServer trackerServer = getTrackerServer();
StorageClient storageClient = new StorageClient(trackerServer, null);
return storageClient;
}
/***
* 獲取Tracker
* @return
* @throws IOException
*/
private static TrackerServer getTrackerServer() throws IOException {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
return trackerServer;
}
}
測試前端頁面,提交表上
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="上傳">
</form>
測試後端控制檯,上傳檔案
@Controller
public class UploadController {
private static Logger logger = LoggerFactory.getLogger(UploadController.class);
@PostMapping("/upload")
public String singleFileUpload(@RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {
if (file.isEmpty()) {
redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
return "redirect:uploadStatus";
}
try {
// Get the file and save it somewhere
String path=saveFile(file);
redirectAttributes.addFlashAttribute("message",
"You successfully uploaded '" + file.getOriginalFilename() + "'");
redirectAttributes.addFlashAttribute("path",
"file path url '" + path + "'");
} catch (Exception e) {
logger.error("upload file failed",e);
}
return "上傳成功";
}
/*
* fastdfs上傳到伺服器
*/
public String saveFile(MultipartFile multipartFile) throws IOException {
String[] fileAbsolutePath={};
String fileName=multipartFile.getOriginalFilename();
String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
byte[] file_buff = null;
InputStream inputStream=multipartFile.getInputStream();
if(inputStream!=null){
int len1 = inputStream.available();
file_buff = new byte[len1];
inputStream.read(file_buff);
}
inputStream.close();
FastDFSFile file = new FastDFSFile(fileName, file_buff, ext);
try {
fileAbsolutePath = FastDFSClient.upload(file); //upload to fastdfs
} catch (Exception e) {
logger.error("upload file Exception!",e);
}
if (fileAbsolutePath==null) {
logger.error("upload file failed,please upload again!");
}
String path=FastDFSClient.getTrackerUrl()+fileAbsolutePath[0]+ "/"+fileAbsolutePath[1];
logger.info("檔案上傳成功,地址:"+path);
return path;
}
}
訪問:
訪問成功:
結束語
至此我們的檔案儲存系統就搭建完成啦。