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 成功:
配置
配置 Tracker 服务
上述安装成功后,在/etc/目录下会有一个fdfs的目录,进入它。会看到三个.sample后缀的文件,这是作者给我们的示例文件,我们需要把其中的tracker.conf.sample文件改为tracker.conf配置文件并修改它。参照以下命令:
cd /etc/fdfs/cp tracker.conf.sample tracker.confvim 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服务安装成功,启动成功:
pat(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[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.confvim 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 addresstracker_server=192.168.2.121:22122
修改完成后我们就可以通过/usr/bin/fdfs_upload_file命令上传文件了,/usr/bin/fdfs_upload_file 命令用法:
[root@localhost fdfs]# /usr/bin/fdfs_upload_fileUsage: /usr/bin/fdfs_upload_file <config_file> <local_filename> [storage_ip:port] [store_path_index]// 命令 配置文件 上传的文件 storage_ip和端口 store_path角标
上传成功:
// 配置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:负载均衡,选择空闲空间最大的groupstore_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的频率,默认10ssync_log_buff_interval = 10// 检测storage服务器的间隔时间,storage定期主动向tracker发送心跳,如果在指定的时间没收到信号,tracker人为storage故障,默认120scheck_active_interval = 120// 线程栈的大小,最小64Kthread_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,默认256bytesslot_min_size = 256// 最大的solt大小,如果上传的文件小于默认值,则上传文件被放入trunk文件中slot_max_size = 16MB// trunk文件的默认大小,应该大于4Mtrunk_file_size = 64MB// http服务是否生效,默认不生效http.disabled=false// http服务端口http.server_port=8080// 检测storage上http服务的时间间隔,<=0表示不检测http.check_alive_interval=30// 检测storage上http服务时所用请求的类型,tcp只检测是否可以连接,http必须返回200http.check_alive_type=tcp// 通过url检测storage http服务状态http.check_alive_uri=/status.html// if need find content type from file extension namehttp.need_find_content_type=true// 用include包含进http的其他设置// include http.conf
## storage.conf配置文件```javastorage.conf配置文件分析:// 同tracker.confdisabled=false// 这个storage服务器属于那个groupgroup_name=group1// 同tracker.confbind_addr=// 连接其他服务器时是否绑定地址,bind_addr配置时本参数才有效client_bind=true// 同tracker.confport=23000connect_timeout=30network_timeout=60// 主动向tracker发送心跳检测的时间间隔heart_beat_interval=30// 主动向tracker发送磁盘使用率的时间间隔stat_report_interval=60// 同tracker.confbase_path=/opt/fdfsmax_connections=256// 接收/发送数据的buff大小,必须大于8KBbuff_size = 256KB// 同tracker.confwork_threads=4// 磁盘IO是否读写分离disk_rw_separated = true// 是否直接读写文件,默认关闭disk_rw_direct = false// 混合读写时的读写线程数disk_reader_threads = 1disk_writer_threads = 1// 同步文件时如果binlog没有要同步的文件,则延迟多少毫秒后重新读取,0表示不延迟sync_wait_msec=50// 同步完一个文件后间隔多少毫秒同步下一个文件,0表示不休息直接同步sync_interval=0// 表示这段时间内同步文件sync_start_time=00:00sync_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_servertracker_server=x.x.x.x:22122// 同tracker.conflog_level=inforun_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.confhttp.disabled=falsehttp.domain_name=http.server_port=8888http.trunk_size=256KBhttp.need_find_content_type=true// include http.conf
nginx简单整合fastDFS
这里小编只是简单的整合,只是为了访问方便,
配置nginx的配置文件,nginx.conf文件:
server {listen 89;#监听89端口server_name localhost;#iplocation /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 = 60network_timeout = 60charset = UTF-8http.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 {//创建StorageClientStorageClient 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 {//创建StorageClientStorageClient 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 {//创建TrackerClientTrackerClient trackerClient = new TrackerClient();//获取TrackerServerTrackerServer 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>
测试后端控制台,上传文件
@Controllerpublic 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 somewhereString 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;}}
访问:
访问成功:
结束语
至此我们的文件存储系统就搭建完成啦。
