本文会搭建一个适合低业务访问业务量的高可用的FastDFS集群环境:两个Tracker服务,一个storage group中两个storage服务节点;该方案仅适用于业务访问量较低的环境下。对于大量业务系统的高并发访问,为了保证存储系统正常工作一般的架构思路:安装多个Tracker服务(至少两个,根据业务量调整),搭建多个storage group(至少两个,根据业务量调整),每个storage group中多个storage node(至少两个,做数据的冗余备份,进行容灾机制,而且node必须在不同的机器上)
FastDFS简介 FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。 FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。FastDFS服务端有两个角色:跟踪器(tracker)和存储节点(storage)。跟踪器主要做调度工作,在访问上起负载均衡的作用。存储节点存储文件,完成文件管理的所有功能:就是这样的存储、同步和提供存取接口,FastDFS同时对文件的metadata进行管理。所谓文件的meta data就是文件的相关属性,以键值对(key value)方式表示,如:width=1024,其中的key为width,value为1024。文件metadata是文件属性列表,可以包含多个键值对。跟踪器和存储节点都可以由一台或多台服务器构成。跟踪器和存储节点中的服务器均可以随时增加或下线而不会影响线上服务。其中跟踪器中的所有服务器都是对等的,可以根据服务器的压力情况随时增加或减少。为了支持大容量,存储节点(服务器)采用了分卷(或分组)的组织方式。存储系统由一个或多个卷组成,卷与卷之间的文件是相互独立的,所有卷的文件容量累加就是整个存储系统中的文件容量。一个卷可以由一台或多台存储服务器组成,一个卷下的存储服务器中的文件都是相同的,卷中的多台存储服务器起到了冗余备份和负载均衡的作用。在卷中增加服务器时,同步已有的文件由系统自动完成,同步完成后,系统自动将新增服务器切换到线上提供服务。当存储空间不足或即将耗尽时,可以动态添加卷。只需要增加一台或多台服务器,并将它们配置为一个新的卷,这样就扩大了存储系统的容量。FastDFS中的文件标识分为两个部分:卷名和文件名,二者缺一不可。(简介摘自百度百科)
FastDFS原理介绍 文件上传 FastDFS以客户端库的方式提供基本的文件访问接口如upload、download、append、delete等,Storage 服务会定时的向Tracker服务发送自己的存储信息。当Tracker 服务集群中的Tracker 服务是多个时,各个Tracker服务之间的关系是对等的,因此客户端上传时会任意选择一个Trackre服务。当Tracker服务收到客户端上传文件请求时,会为该文件分配一个可以存储文件的group,当选定了group后就要决定给客户端分配group中的哪一个storage服务。当分配好storage 服务后,客户端向storage发送写文件请求,storage将会为文件分配一个数据存储目录。然后为文件分配一个文件ID标示,然后根据以上的信息生成文件名存储文件。
文件同步 上传文件后,客户端将文件写到group内的一个storage 服务即为上传文件成功,storage服务写完文件后,会由后台线程将文件同步至同group内的其他的storage 服务节点上。 每个storage服务写文件后,会同时写一份binlog,binlog里不包含文件数据,只包含文件名等元信息,这份binlog用于后台同步,storage会记录向group内其他storage同步的进度,以便重启后能接上次的进度继续同步;进度以时间戳的方式进行记录,所以最好能保证集群内的所有server的始终保持同步。最后Storage服务的同步进度会作为元数据的一部分汇报到tracker服务上,tracker服务在选择读storage的时候会以同步进度作为参考指标。
下载文件 当下载文件时,客户端先询问tracker服务下载文件的storage,参数为文件标识(卷名和文件名);然后tracker向客户端返回一台可用的storage;最后客户端直接和storage通讯完成文件下载。
部署环境准备 环境说明 操作系统CentOS7.6 fastdfs版本:6.01 nginx版本:1.16.1 keepalived版本:2.0.19
系统依赖 gcc gcc-c++ perl pcre pcre-devel zlib zlib-devel openssl openssl-devel libnl libnl-devel
软件环境 libevent下载地址:http://libevent.org/ nginx下载地址:http://nginx.org/en/download.html keepalived下载地址:https://www.keepalived.org/software/ fastdfs下载地址:https://github.com/happyfish100/fastdfs/releases libfasttcommon下载地址:https://github.com/happyfish100/libfastcommon/releases fastdfs-nginx-module下载地址:https://github.com/happyfish100/fastdfs-nginx-module/releases
机器及网络环境规划 1 2 3 4 5 6 7 8 9 Fdfs Server VIP: 192.168.100.110 Tracker Server1: 192.168.100.111 Tracker Server2: 192.168.100.112 Storage Group1 Node1: 192.168.100.111 Storage Group1 Node2: 192.168.100.112
防火墙设置 关闭系统防火墙:sudo systemctl stop firewalld && systemctl disable firewalld
Keepalived服务安装配置 下载Keepalived源码包 官网地址:https://www.keepalived.org/ 下载地址:https://www.keepalived.org/software/keepalived-2.0.19.tar.gz
上传并解压Keepalived源码包 tar -zxvf keepalived-2.0.19.tar.gz
编译Keepalived准备 进入解压目录:cd keepalived-2.0.19 执行编译准备:./configure –prefix=/work/keepalived 注意:一定要有gcc和openssl编译相关的依赖
编译安装Keepalived make && make install
安装配置Keepalived keepalived启动时会从/etc/keepalived/中相关的目录下查找keepalived.conf配置文件,因此将keepalived安装录/usr/local/keepalived/etc/keepalived.conf 拷贝到/etc/keepalived/中。 mkdir /etc/keepalived/ cp /work/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf cp /work/keepalived/etc/sysconfig/keepalived /etc/sysconfig/keepalived
设置Keepalived开机启动项 systemctl enable keepalived 然后就能使用systemctl start/stop/status keepalived管理keepalived了
配置Keepalived服务 192.168.100.111机器上配置:vi /etc/keepalived/keepalived.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 vrrp_script check_nginx { interval 3 script "/work/script/check_nginx.sh" } vrrp_instance fdfs_server { state MASTER interface enp0s3 virtual_router_id 110 priority 100 advert_int 3 authentication { auth_type PASS auth_pass password } virtual_ipaddress { 192.168.100.110 } track_script { check_nginx } }
192.168.100.112机器上配置:vi /etc/keepalived/keepalived.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 vrrp_script check_nginx { interval 3 script "/work/script/check_nginx.sh" } vrrp_instance fdfs_server { state BACKUP interface enp0s3 virtual_router_id 110 priority 90 advert_int 3 authentication { auth_type PASS auth_pass password } virtual_ipaddress { 192.168.100.110 } track_script { check_nginx } }
编写nginx服务检测脚本 vi /work/script/check_nginx.sh
1 2 3 4 5 6 7 #!/bin/bash active_status=`netstat -lntp|grep nginx|wc -l` if [ $active_status -gt 0 ]; then exit 0 else exit 1 fi
然后给脚本赋予执行权限:chmod +x /work/script/check_nginx.sh
修改内核参数 vi /etc/sysctl.conf 增加如下内容:
1 2 net.ipv4.ip_nonlocal_bind = 1 #允许忽视VIP的存在 net.ipv4.ip_forward = 1 #允许转发
sysctl –system 使配置生效
安装FastDFS依赖库 安装libevent依赖 解压libevent源码包:tar -zxvf libevent-2.1.11-stable.tar.gz 进入源码目录:cd libevent-2.1.11-stable 编译安装前配置:./configure 编译安装:make && make install 默认安装位置:/usr/local/lib
安装libfasttcommon依赖 解压libfasttcommon源码包:tar -zxvf libfastcommon-1.0.41.tar.gz 进入源码目录:cd libfastcommon-1.0.41 编译安装:./make.sh && ./make.sh install 默认安装位置:/usr/lib64
安装部署Tracker服务和Storage服务 安装fastdfs服务 解压fastdfs源码包:tar -zxvf fastdfs-6.01.tar.gz 进入fastdfs源码包:cd fastdfs-6.01 编译安装:./make.sh && ./make.sh install
fastdfs服务目录信息 安装完成后服务及脚本拷贝到/usr/bin 目录,配置文件拷贝到/etc/fdfs目录,启动脚本拷贝到/etc/init.d/目录
注册开机启动 chkconfig –add fdfs_trackerd chkconfig fdfs_trackerd on chkconfig –add fdfs_storaged chkconfig fdfs_storaged on
数据目录规划 创建fdfs数据主目录:mkdir /work/fdfs 创建tracker数据目录:mkdir /work/fdfs/tracker 创建storage数据目录:mkdir /work/fdfs/storage
配置tracker服务 将/etc/fdfs目录下的tracker.conf.sample改为tracker.conf:mv tracker.conf.sample tracker.conf修改内容如下: 将base_path=/home/yuqing/fastdfs 改为:/work/fdfs/tracker(该目录为上面定义创建) 启动Tracker服务:systemctl start fdfs_trackerd
配置storage服务 将/etc/fdfs目录下的storage.conf.sample改为storage.conf:mv storage.conf.sample storage.conf修改内容如下: base_path=/home/yuqing/fastdfs 改为:base_path=/work/fdfs/storage(该目录为上面定义创建) store_path0=/home/yuqing/fastdfs 改为:store_path0=/work/fdfs/storage(该目录为上面定义创建) tracker_server=192.168.209.121:22122 改为:tracker_server=192.168.100.111:22122和tracker_server=192.168.100.112:22122 将/etc/fdfs/torage_ids.conf.sample 为storage_ids.conf,内容修改为当前group的storage节点信息: 100001 group1 192.168.100.111 100002 group1 192.168.100.112 启动Storage服务:systemctl start fdfs_storaged
Nginx服务安装配置 下载Nginx源码包 官网地址:http://nginx.org/ 下载地址:http://nginx.org/en/download.html
上传并解压Nginx源码包及fastdfs插件包 tar -zxvf nginx-1.16.1.tar.gz tar -zxvf fastdfs-nginx-module-1.21.tar.gz
编译Nginx准备 进入解压目录:cd nginx-1.16.1 拷贝fastdfs插件包到nginx源码目录:mv ../fastdfs-nginx-module-1.21 . 执行编译准备:./configure –prefix=/work/nginx \–with-stream \–add-module=fastdfs-nginx-module-1.21/src 注意:一定要有gcc和openssl编译相关的依赖
编译安装Nginx make && make install
注册到系统服务 vi /usr/lib/systemd/system/nginx.service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [Unit] Description=nginx Documentation=http://nginx.org/en/docs/ After=network.target [Service] Type=forking PIDFile=/work/nginx/logs/nginx.pid ExecStartPre=/work/nginx/sbin/nginx -t -c /work/nginx/conf/nginx.conf ExecStart=/work/nginx/sbin/nginx -c /work/nginx/conf/nginx.conf ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s QUIT $MAINPID PrivateTmp=true [Install] WantedBy=multi-user.target
设置Nginx开机启动项 systemctl enable nginx 然后就能使用systemctl start/stop/status nginx管理nginx了
修改Nginx配置 vi /work/nginx/conf/nginx.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } stream { upstream tracker { server 192.168.100.111:22122 weight=1 max_fails=2 fail_timeout=10s; server 192.168.100.112:22122 weight=1 max_fails=2 fail_timeout=10s; } server { listen 7777; proxy_timeout 5m; proxy_pass tracker; proxy_connect_timeout 10s; } } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; upstream storage { server 192.168.100.111:8888 weight=1 max_fails=2 fail_timeout=10s; server 192.168.100.112:8888 weight=1 max_fails=2 fail_timeout=10s; } server { listen 80; server_name localhost; location /group1 { proxy_pass http://storage; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } server { listen 8888; server_name localhost; location / { alias /work/fdfs/storage/data/; ngx_fastdfs_module; } } }
配置nginx的fdfs插件 将/etc/fdfs下的http.conf.sample和mime.types.sample重命名为:http.conf和mime.types 将fastdfs-nginx-module-1.21/src下的mod_fastdfs.conf拷贝到/etc/fdfs下 修改mod_fastdfs.conf如下: 连接超时时间: connect_timeout=5 Tracker服务地址:tracker_server=192.168.100.111:22122 和tracker_server=192.168.100.112:22122 Storage服务端口:storage\server_port=23000 如果文件ID的uri中包含/group**,则要设置为true:url_have_group_name = true Storage配置的store_path0路径,必须和storage.conf中的一致:store_path0=/work/fdfs/storage 其他详细配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 # connect timeout in seconds # default value is 30s connect_timeout=5 # network recv and send timeout in seconds # default value is 30s network_timeout=10 # the base path to store log files base_path=/work/fdfs/storage # if load FastDFS parameters from tracker server # since V1.12 # default value is false load_fdfs_parameters_from_tracker=true # storage sync file max delay seconds # same as tracker.conf # valid only when load_fdfs_parameters_from_tracker is false # since V1.12 # default value is 86400 seconds (one day) storage_sync_file_max_delay = 86400 # if use storage ID instead of IP address # same as tracker.conf # valid only when load_fdfs_parameters_from_tracker is false # default value is false # since V1.13 use_storage_id = false # specify storage ids filename, can use relative or absolute path # same as tracker.conf # valid only when load_fdfs_parameters_from_tracker is false # since V1.13 storage_ids_filename = storage_ids.conf # FastDFS tracker_server can ocur more than once, and tracker_server format is # "host:port", host can be hostname or ip address # valid only when load_fdfs_parameters_from_tracker is true tracker_server=192.168.100.111:22122 tracker_server=192.168.100.112:22122 # the port of the local storage server # the default value is 23000 storage_server_port=23000 # the group name of the local storage server group_name=group1 # if the url / uri including the group name # set to false when uri like /M00/00/00/xxx # set to true when uri like ${group_name}/M00/00/00/xxx, such as group1/M00/xxx # default value is false url_have_group_name = true # path(disk or mount point) count, default value is 1 # must same as storage.conf store_path_count=1 # store_path#, based 0, if store_path0 not exists, it's value is base_path # the paths must be exist # must same as storage.conf store_path0=/work/fdfs/storage # standard log level as syslog, case insensitive, value list: ### emerg for emergency ### alert ### crit for critical ### error ### warn for warning ### notice ### info ### debug log_level=info # set the log filename, such as /usr/local/apache2/logs/mod_fastdfs.log # empty for output to stderr (apache and nginx error_log file) log_filename= # response mode when the file not exist in the local file system ## proxy: get the content from other storage server, then send to client ## redirect: redirect to the original storage server (HTTP Header is Location) response_mode=proxy # the NIC alias prefix, such as eth in Linux, you can see it by ifconfig -a # multi aliases split by comma. empty value means auto set by OS type # this paramter used to get all ip address of the local host # default values is empty if_alias_prefix= # use "#include" directive to include HTTP config file # NOTE: #include is an include directive, do NOT remove the # before include #include http.conf # if support flv # default value is false # since v1.15 flv_support = true # flv file extension name # default value is flv # since v1.15 flv_extension = flv # set the group count # set to none zero to support multi-group on this storage server # set to 0 for single group only # groups settings section as [group1], [group2], ..., [groupN] # default value is 0 # since v1.14 group_count = 1 # group settings for group #1 # since v1.14 # when support multi-group on this storage server, uncomment following section [group1] group_name=group1 storage_server_port=23000 store_path_count=1 store_path0=/work/fdfs/storage # group settings for group #2 # since v1.14 # when support multi-group, uncomment following section as neccessary #[group2] #group_name=group2 #storage_server_port=23000 #store_path_count=1 #store_path0=/home/yuqing/fastdfs
服务启动及验证 分别启动keepalive、nginx、tracker、storage服务 查看服务是否正常服务: 在任意Storage机器上查看集群状态:fdfs_monitor /etc/fdfs/storage.conf
Java客户端测试 java项目Maven依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 项目地址:https://github.com/tobato/FastDFS_Client 目前客户端主要依赖于SpringBoot,因此必须引入: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> <relativePath/> </parent> FastDFS 依赖包: <dependency> <groupId>com.github.tobato</groupId> <artifactId>fastdfs-client</artifactId> <version>1.26.7</version> </dependency> 将FastDFS引入项目: @Import(FdfsClientConfig.class) 在application.yml当中配置Fdfs相关参数: fdfs: pool: #连接池最大数量 max-total: 200 #每个tracker地址的最大连接数 max-total-per-key: 50 #连接耗尽时等待获取连接的最大毫秒数 max-wait-millis: 5000 so-timeout: 1500 connect-timeout: 600 thumb-image: width: 150 height: 150 tracker-list: - 192.168.100.110:7777 或者 fdfs: pool: #连接池最大数量 max-total: 200 #每个tracker地址的最大连接数 max-total-per-key: 50 #连接耗尽时等待获取连接的最大毫秒数 max-wait-millis: 5000 so-timeout: 1500 connect-timeout: 600 thumb-image: width: 150 height: 150 tracker-list: - 192.168.100.111:22122 - 192.168.100.112:22122 使用接口服务对Fdfs服务端进行操作,主要接口包括: TrackerClient - TrackerServer接口 GenerateStorageClient - 一般文件存储接口 (StorageServer接口) FastFileStorageClient - 为方便项目开发集成的简单接口(StorageServer接口) AppendFileStorageClient - 支持文件续传操作的接口 (StorageServer接口)
代码测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 package com.maxbill;import com.github.tobato.fastdfs.FdfsClientConfig;import com.github.tobato.fastdfs.domain.fdfs.MetaData;import com.github.tobato.fastdfs.domain.fdfs.StorePath;import com.github.tobato.fastdfs.domain.proto.storage.DownloadByteArray;import com.github.tobato.fastdfs.service.FastFileStorageClient;import lombok.extern.log4j.Log4j2;import org.apache.commons.lang3.StringUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Import;import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;import java.io.File;import java.io.FileInputStream;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.*;@Log4j2 @Component @Import(FdfsClientConfig.class) public class FdfsClientUtil { private static final String BASE_URL = "http://192.168.100.110:8888/" ; @Autowired private FastFileStorageClient storageClient; private static FdfsClientUtil fdfsClientUtil; @PostConstruct public void init () { fdfsClientUtil = this ; } public static Map<String, Object> uploadFile (File file, Map<String, String> infoMap) { try { String fileName = file.getName(); String fileType = fileName.substring(fileName.lastIndexOf("\\" ) + 1 ); log.info("[fdfs-upload]-start upload file ... " ); log.info("[fdfs-upload]-request upload file name: {}" , fileName); log.info("[fdfs-upload]-request upload file info: {}" , infoMap); StorePath path = fdfsClientUtil.storageClient.uploadFile(new FileInputStream(file), file.length(), fileType, getMetaData(infoMap)); log.info("[fdfs-upload]-upload success path: {}" , path.getFullPath()); return getResultMap(BASE_URL.concat(path.getFullPath()), null ); } catch (Exception e) { log.error("[fdfs-upload]-upload file exception info: {}" , e.getMessage()); return getResultMap(null , e.getMessage()); } } public static Map<String, Object> downloadFile (String filePath) { try { filePath = filePath.replace(BASE_URL, "" ); StorePath storePath = StorePath.parseFromUrl(filePath); String group = storePath.getGroup(); String path = storePath.getPath(); log.info("[fdfs-download]-start download file ... " ); log.info("[fdfs-download]-request download file group: {}" , group); log.info("[fdfs-download]-request download file path: {}" , path); byte [] data = fdfsClientUtil.storageClient.downloadFile(group, path, new DownloadByteArray()); log.info("[fdfs-download]-request download file success ... " ); return getResultMap(data, null ); } catch (Exception e) { log.error("[fdfs-download]-download file exception info: {}" , e.getMessage()); return getResultMap(null , e.getMessage()); } } public static boolean deleteFile (String filePath) { try { filePath = filePath.replace(BASE_URL, "" ); StorePath storePath = StorePath.parseFromUrl(filePath); String group = storePath.getGroup(); String path = storePath.getPath(); log.info("[fdfs-delete]-start delete file ... " ); log.info("[fdfs-delete]-request delete file group: {}" , group); log.info("[fdfs-delete]-request delete file path: {}" , path); fdfsClientUtil.storageClient.deleteFile(storePath.getGroup(), storePath.getPath()); log.info("[fdfs-delete]-request delete file success ... " ); return true ; } catch (Exception e) { log.error("[fdfs-delete]-delete file exception info: {}" , e.getMessage()); return false ; } } public static Map<String, Object> getFileInfo (String filePath) { try { filePath = filePath.replace(BASE_URL, "" ); StorePath storePath = StorePath.parseFromUrl(filePath); String group = storePath.getGroup(); String path = storePath.getPath(); log.info("[fdfs-meta]-start meta file ... " ); log.info("[fdfs-meta]-request meta file group: {}" , group); log.info("[fdfs-meta]-request meta file path: {}" , path); Map<String, String> infoMap = new HashMap<>(); infoMap.put("createPath" , filePath); Set<MetaData> metaData = fdfsClientUtil.storageClient.getMetadata(storePath.getGroup(), storePath.getPath()); log.info("[fdfs-meta]-request meta file success ... " ); if (null != metaData && !metaData.isEmpty()) { metaData.forEach(meta -> { infoMap.put(meta.getName(), meta.getValue()); }); } return getResultMap(infoMap, null ); } catch (Exception e) { log.error("[fdfs-meta]-meta file exception info: {}" , e.getMessage()); return getResultMap(null , e.getMessage()); } } private static Set<MetaData> getMetaData (Map<String, String> infoMap) { if (null != infoMap && !infoMap.isEmpty()) { Set<MetaData> metaDataSet = new HashSet<>(); for (String key : infoMap.keySet()) { metaDataSet.add(new MetaData(key, infoMap.get(key))); } return metaDataSet; } else { DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" ); Set<MetaData> metaDataSet = new HashSet<>(); metaDataSet.add(new MetaData("createUser" , "MaxBill" )); metaDataSet.add(new MetaData("createDate" , df.format(new Date()))); return metaDataSet; } } private static Map<String, Object> getResultMap (Object data, String info) { Map<String, Object> resultMap = new HashMap<>(); if (StringUtils.isEmpty(info)) { resultMap.put("flag" , true ); resultMap.put("data" , data); resultMap.put("info" , "success" ); } else { resultMap.put("flag" , false ); resultMap.put("info" , info); resultMap.put("data" , null ); } return resultMap; } }