Docker:是一种轻量级的虚拟化方式,Docker在运行应用上跟传统的虚拟机方式相比具有显著优势(简化运维安装复杂环境、占内存少、启动和停止环境快);
FastDFS:一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。
FastDHT:避免大量重复文件导致磁盘资源的浪费,需要对重复文件做去重处理;
Nginx:做storage负载均衡、反向代理、缓存 ;
1.系统:CentOS Linux release 7.6.1810 (Core),命令:cat /etc/redhat-release
2.内核:3.10.0-957.10.1.el7.x86_64,命令:uname -a
注意:Docker 要求 CentOS 系统的内核版本在 3.10以上,通过 uname -r 命令查看当前的内核版本。
3.服务器所需外网。
命令1:
docker run -dit -p 180:80 -p 22122:22122 --name tracker1 --restart=always imlzw/fastdfs-tracker
命令1:(查看tracker容器信息IP)
docker inspect tracker1
如图1:
tracker容器IP为:172.17.0.5
命令2:(进入tracker容器)
docker exec -it tracker1 /bin/bash
命令3:(也可以通过挂载方式,到外部进行修改)
vi /etc/fdfs/client.conf
如图2:
将图中tracker_server=172.17.0.2:22122,修改为tracker_server=172.17.0.5:22122
修改前 | 修改后 |
---|---|
tracker_server=172.17.0.2:22122 |
tracker_server=172.17.0.5:22122 |
命令3:
mkdir /home/imlzw/fastdfs/client
命令4:(退出tracker容器)
exit
命令5:(重启tracker容器)
docker restart tracker1
注意:如果storage多节点,命令可以重复执行,但是容器端口、名称等信息不能一致(单节点搭建可忽略这句话)
比如:docker run -dit -p 281:80 -p 23001:23000 -p11412:11411 --name storage2 --restart=always imlzw/fastdfs-storage-dht
命令1:
docker run -dit -p 280:80 -p 23000:23000 -p11411:11411 --name storage1 --restart=always imlzw/fastdfs-storage-dht
命令2:(查看storage容器信息IP)
docker inspect storage1
如图3(storage1):
storage1容器IP为:172.17.0.6
命令4:(进入storage1)
docker exec -it storage1 /bin/bash
命令5:
vi /etc/fdfs/storage.conf
如图4
将tracker_server=tracker1:22122,修改为172.17.0.5:22122(tracker的ip和端口),去掉tracker_server=tracker2:22122,如果tracker做两个节点集群可以自行添加并修改。
修改前 | 修改后 |
---|---|
tracker_server=tracker1:22122 |
172.17.0.5:22122 |
命令1:
vi /etc/fdht/fdht_client.conf
如图5:
将base_path=/home/yuqing/fastdht,修改为/home/imlzw/fastdfs/fastdht
修改前 | 修改后 |
---|---|
base_path=/home/yuqing/fastdht |
base_path=/home/imlzw/fastdfs/fastdht |
命令2:
vi /etc/fdht/fdht_servers.conf
如图6:
将group0 = 172.17.0.4:11411,改为172.17.0.6:11411(改为storage的ip和端口),去掉group0 = 172.17.0.5:11411,如果storage是多节点集群自行添加。
修改前 | 修改后 |
---|---|
group0 = 172.17.0.4:11411 |
group0 = 172.17.0.6:11411 |
命令3:
exit
命令4:
docker restart storage1
命令1:(提前准备一张图1.jpg 复制到tracker1目录下)
docker cp /usr/local/1.jpg tracker1:/
命令2:(进入tracker容器)
docker exec -it tracker1 /bin/bash
/usr/bin/fdfs_upload_file /etc/fdfs/client.conf /1.jpg
exit
命令5:
docker exec -it storage1 /bin/bash
命令6:
cd /home/imlzw/fastdfs/storage/data/00/00
命令7:
ll
如图9:
说明:FastDFS+FastDHT整合成功!!!
第一次上传返回的结果为:rBEABlyiGIWAV6EYAABB77YyMB4866.jpg,之后每次重复上传的话都是返回一个指向第一次上传的文件的软链接。也就保证了文件只保存了一份。当所有的软链接都被删除的时候,原始文件也会从FastDFS中被删除。
命令1:
vi /etc/fdfs/mod_fastdfs.conf
如图9:
将tracker_server=tracker1:22122,修改为172.17.0.5:22122(tracker的ip和端口),去掉tracker_server=tracker2:22122,如果tracker做两个节点集群可以自行添加并修改。
connect_timeout=10
tracker_server=172.17.0.5:22122
url_have_group_name = true
命令2:(修改nginx配置)
vi /usr/local/nginx/conf/nginx.conf
修改前 | 修改后 |
---|---|
location /M00 { root /home/imlzw/fastdfs/storage/data/;ngx_fastdfs_module;} |
location ~/group([0-9])/M00 { #alias /home/imlzw/fastdfs/storage/data/;ngx_fastdfs_module;} |
命令3:(去掉防盗链,正常是需要的)
vi /etc/fdfs/http.conf
修改前 | 修改后 |
---|---|
http.anti_steal.check_token=true |
http.anti_steal.check_token=false |
命令4:
exit
命令5:
docker restart storage1
本地访问地址:http://192.168.8.21:280/group1/M00/00/00/rBEABlyiS0qACGPmAABB77YyMB4134.jpg
如图10:
以上单节点已搭建成功!!!
步骤跟3.2storage1搭建命令一样,重复执行即可。其中要注意的是:容器名称、端口等信息不能一致。
命令1:
docker inspect storage2
storage2:IP为172.17.0.7,端口为:23001,fdht端口为:11412
storage1:fdht_servers.conf | storage2:fdht_servers.conf |
---|---|
group_count = 1 group0 = 172.17.0.6:11411 group0 = 172.17.0.7:11412 | group_count = 1 group0 = 172.17.0.6:11411 group0 = 172.17.0.7:11412 |
storage1 | storage2 |
---|---|
命令1:
docker exec -it tracker1 /bin/bash
命令2:
vi /usr/local/nginx/conf/nginx.conf
upstream fdfs_group1 {
server 172.17.0.6 weight=1 max_fails=2 fail_timeout=30s;
server 172.17.0.7 weight=1 max_fails=2 fail_timeout=30s;
}
location ~/group([0-9])/M00 {
proxy_pass http://fdfs_group1;
expires 30d;
}
命令3:(nginx重启)
/usr/local/nginx/sbin/nginx -s reload
#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;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /usr/local/nginx/logs/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
#设置缓存
server_names_hash_bucket_size 128;
client_header_buffer_size 32k;
large_client_header_buffers 4 32k;
client_max_body_size 300m;
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 16k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
proxy_temp_file_write_size 128k;
#设置缓存存储路径,存储方式,分别内存大小,磁盘最大空间,缓存期限
proxy_cache_path /fastdfs/cache/nginx/proxy_cache levels=1:2
keys_zone=http-cache:200m max_size=1g inactive=30d;
proxy_temp_path /fastdfs/cache/nginx/proxy_cache/tmp;
#负载均衡配置(权重)
upstream fdfs_group1 {
server 172.17.0.6 weight=1 max_fails=2 fail_timeout=30s;
server 172.17.0.7 weight=1 max_fails=2 fail_timeout=30s;
}
server {
listen 80;
server_name localhost;
access_log /usr/local/nginx/logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
location /group1/M00 {
proxy_next_upstream http_502 http_504 error timeout invalid_header;
proxy_cache http-cache;
proxy_cache_valid 200 304 12h;
proxy_cache_key $uri$is_args$args;
proxy_pass http://fdfs_group1;
expires 30d;
}
#清除缓存的访问权限
location ~/purge(/.*) {
allow 127.0.0.1;
allow 192.168.8.0/24;
deny all;
proxy_cache_purge http-cache $1$is_args$args;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
ngx_cache_purge模块的作用:用于清除指定url的缓存,否则nginx启动报错:
nginx: [emerg] unknown directive “proxy_cache_purge” in /home/data/websrv/nginx/conf/nginx.conf:75
例如:
http://192.168.8.21:180/purge/group1/M00/00/00/rBEABlyjRUKAOykcAABB7x0RIA4797.jpg
命令1:(进入tracker1)
docker exec -it tracker1 /bin/bash
命令2:(创建下载目录)
mkdir /home/install
命令3:
wget http://labs.frickle.com/files/ngx_cache_purge-2.3.tar.gz
命令4:
tar -zxvf ngx_cache_purge-2.3.tar.gz
命令5:
mv ngx_cache_purge-2.3.tar.gz /usr/local/
命令6:
cd /home/imlzw/fastdfs/download/nginx/nginx-1.11.7
命令7:
./configure --prefix=/usr/local/nginx --add-module=/usr/local/ngx_cache_purge-2.3
命令8:(注意这里只要make ,不要make install会覆盖)
make
命令9:
/usr/local/nginx/sbin/./nginx -s stop
命令10:
cd /usr/local/nginx/sbin/
命令11:
cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.back
命令12:
cp /home/imlzw/fastdfs/download/nginx/nginx-1.11.7/objs/nginx /usr/local/nginx/sbin/nginx
命令13:
/usr/local/nginx/sbin/./nginx -s reload
FastDFS内置防盗链采用Token的方式。Token是带时效的,也就是说在设定的时间范围内,比如1分钟,token是有效的。token包含了文件id、时间戳ts和密钥。FastDFS在URL中带上当前时间戳和带时效的token,参数名分别为ts和token。Token的生成和校验都是在服务端,因此不会存在安全问题。形如
http://192.168.8.21:8080/download2?fileUrl=192.168.8.21:180/group1/M00/00/00/rBEABlyjRUKAOykcAABB7x0RIA4797.jpg?token=39427bb27274de88c5fe6cac05c90312&ts=1554519896
http.anti_steal.check_token:是否做token检查,默认值为false,打开true。
http.anti_steal.token_ttl:token TTL,即生成token的有效时长,秒为单位。
http.anti_steal.secret_key:生成token的密钥,尽量设置得长一些,千万不要泄露出去。
http.anti_steal.token_check_fail:token检查失败,返回的文件内容,需指定本地文件名或者页面。
命令1:(进入storage容器)
docker exec -it storage /bin/bash
命令2:
vi /etc/fdfs/http.conf
修改前 | 修改后 |
---|---|
http.anti_steal.check_token=false | http.anti_steal.check_token=true |
注意:这里其他都使用默认值
/**获取防盗链链接
* @return
*/
public String getSecurityUrl(){
//fid为从数据库中读出的值
String fid="group1/M00/00/00/rBEAA1yoPpeAFLLWAACMSn8JadI335.jpg";
String substring = fid.substring(fid.indexOf("/")+1);
System.out.println("name:" + substring);
//unix时间戳 以秒为单位
int ts = (int) (System.currentTimeMillis() / 1000);
//秘钥,在storage下/etc/fdfs/http.conf===》http.anti_steal.secret_key=FastDFS1234567890(默认)
String secret_key = "FastDFS1234567890";
String token=new String();
try {
token= ProtoCommon.getToken(substring, ts, secret_key);
} catch (Exception e) {
e.printStackTrace();
}
StringBuilder sb = new StringBuilder();
sb.append("192.168.8.21:180/");
sb.append(fid);
sb.append("?token=").append(token);
sb.append("&ts=").append(ts);
return "防盗链URL:" + sb;
}
1.采取springboot整合FastDFS客服端
2.采取直接部署CentOS 7
3.所需依赖:https://github.com/leechenxiang/fastdfs-client-java
4.运行项目:java -jar images-0.0.1-SNAPSHOT.jar
5.源码地址:https://github.com/wenmingsen/fastdfs_images
connect_timeout = 2
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 180
http.anti_steal_token = on
http.secret_key = FastDFS1234567890
tracker_server = 172.17.0.5:22122
import java.util.Arrays;
public class FastDFSFile {
private String name;
private byte[] content;
private String ext;
private String md5;
private String author;
//省略getter、setter
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;
}
@Override
public String toString() {
return "FastDFSFile [name=" + name + ", content="
+ Arrays.toString(content) + ", ext=" + ext + ", md5=" + md5
+ ", author=" + author + "]";
}
}
import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.FileInfo;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FastDFSUtil {
private static Logger LOGGER = LoggerFactory.getLogger(FastDFSUtil.class);
private static String classPath = FastDFSUtil.class.getProtectionDomain()
.getCodeSource().getLocation().getPath();
private static String FASTDFS_CLIENT_CONF = "/fastdfs-downloads/fdfs_client.conf";
private static String COMMON_PATH = "http://192.168.8.21/";
private static TrackerClient trackerClient = null;
private static TrackerServer trackerServer = null;
private static StorageClient storageClient = null;
private static StorageServer storageServer = null;
// 默认下载路径
private static String DOWNLOAD_PATH = "/fastdfs-downloads";// "D:/Documents/Downloads";
static{
try {
ClientGlobal.init(FASTDFS_CLIENT_CONF);
trackerClient = new TrackerClient();
trackerServer = trackerClient.getConnection();
storageServer = trackerClient.getStoreStorage(trackerServer);
storageClient = new StorageClient(trackerServer, storageServer);
} catch (Exception e) {
throw new RuntimeException("init exception");
}
}
/**
* 下载
* @param file 图片信息对象
* @return
*/
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;
try {
storageClient = new StorageClient(trackerServer, storageServer);
uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
} catch (Exception e) {
e.printStackTrace();
LOGGER.error("图片上传失败,e:" + e);
throw new RuntimeException("upload exception");
}
LOGGER.info("upload_file time used:" + (System.currentTimeMillis() - startTime) + " ms");
if (uploadResults == 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 imgUrl
* 文件路径
* @return 文件上传后的返回路径
*/
public static String upload(String imgUrl) {
NameValuePair[] metaDataList = null;
return upload(imgUrl, metaDataList);
}
/**
* 上传文件,元信息作为文件属性上传到fastdfs服务器
*
* @param imgUrl
* 文件路径
* @param metaDataList
* 元信息
* @return 文件上传后的返回路径
*/
public static String upload(String imgUrl, NameValuePair[] metaDataList) {
// 取文件扩展名,不要“.”
String fileExtName = imgUrl.substring(imgUrl.lastIndexOf(".") + 1);
return upload(imgUrl, fileExtName, metaDataList);
}
/**
* 上传文件
*
* @param fileUrl
* 上传文件路径
* @param fileExtName
* 文件类型(扩展名),如:jpg,txt
* @param metaDataList
* 元信息
* @return 文件上传后的返回路径
*/
public static String upload(String fileUrl, String fileExtName,
NameValuePair[] metaDataList) {
try {
String fileIds[] = storageClient.upload_file(fileUrl, fileExtName,
metaDataList);
LOGGER.info("图片上传成功,返回路径:" + fileIds[0] + "/" + fileIds[1]);
return COMMON_PATH + fileIds[0] + "/" + fileIds[1];
} catch (Exception e) {
e.printStackTrace();
LOGGER.error("图片上传失败,e:" + e);
throw new RuntimeException("upload exception");
}
}
/**
* 下载文件
*
* @param group
* 组,如:“group1”
* @param file
* 文件路径,不要组名前面部分,如:“M00/00/00/wKgI2luKxXyARIzqAABh2n7xkfg929.jpg”
* @param storePath
* 存储路径,如:“D:/test_fastDFS/新建文件夹/sss”
*/
public static byte[] download(String group, String filePath, String storePath) {
try {
byte[] file_buff;
System.out.println("group:" + group);
System.out.println("file:" + filePath);
file_buff = storageClient.download_file(group, filePath);
/* IOUtils.write(file_buff, new FileOutputStream(new File(storePath
+ "/" + file.substring(file.lastIndexOf("/") + 1))));*/
return file_buff;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("download exception");
}
}
/**
* 文件下载
*
* @param fileAddr
* 文件访问地址,如:
* "http://192.168.8.110/fastdfs/group2/M00/00/00/wKgI2luKZaKACY_jAACQB3kqNKg127.jpg"
*/
public static byte[] download(String fileAddr) {
return download(fileAddr, DOWNLOAD_PATH);
}
/**
* 文件下载
*
* @param file文件访问地址
* ,如:
* "http://192.168.8.110/fastdfs/group2/M00/00/00/wKgI2luKZaKACY_jAACQB3kqNKg127.jpg"
* @param storePath存储路径
* ,如:“D:/test_fastDFS/新建文件夹/sss”
*/
public static byte[] download(String fileAddr, String storePath) {
int start = fileAddr.indexOf("group");
String group = fileAddr.substring(start, start + 6);
String newFile = fileAddr.substring(start + 7);
return download(group, newFile, storePath);
}
/**
* 获取文件信息
*
* @param group
* 组,如:group1
* @param file
* 文件路径,如:M00/00/00/wKgI2VuNfBmANpG6AAB7lbxNbKo940.jpg
* @return
*/
public static FileInfo getFileInfo(String group, String filePath) {
try {
FileInfo fileInfo = storageClient.get_file_info(group, filePath);
LOGGER.info("fileInfo:" + fileInfo);
return fileInfo;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("gets file info exception");
}
}
/**
* 获取文件元信息
*
* @param group
* @param file
* @return
*/
public static NameValuePair[] getFileMataData(String group, String file) {
try {
NameValuePair[] metas = storageClient.get_metadata(group, file);
return metas;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("gets file metadata exception");
}
}
/**
* 删除文件:0:成功;2:文件不存在;其他:文件删除出错
*
* @param file文件访问地址
* ,如:
* "http://192.168.8.110/fastdfs/group2/M00/00/00/wKgI2luKZaKACY_jAACQB3kqNKg127.jpg"
*/
public static int delete(String fileAddr) {
int start = fileAddr.indexOf("group");
String group = fileAddr.substring(start, start + 6);
String newFile = fileAddr.substring(start + 7);
return delete(group, newFile);
}
/**
* 删除文件:0:成功;2:文件不存在;其他:文件删除出错
*
* @param group
* 组,如:group1
* @param file
* 文件路径,如:M00/00/00/wKgI2VuNfBmANpG6AAB7lbxNbKo940.jpg
* @return
*/
public static int delete(String group, String file) {
try {
int num = storageClient.delete_file(group, file);
return num;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("deletes file exception");
}
}
public static String getTrackerUrl(){
return "http://192.168.8.21:180/";
}
}
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.csource.fastdfs.ProtoCommon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
import com.images.util.FastDFSFile;
import com.images.util.FastDFSUtil;
/**
* @Description
* @Author wms
* @Date 2019/4/4
*/
@RestController
public class ImagesTestController {
private static Logger LOGGER = LoggerFactory.getLogger(ImagesTestController.class);
/**
* 测试项目启动
* @return
*/
@RequestMapping("/index")
public String index(){
return "index";
}
/**
* 上传文件
* @param file
*/
@RequestMapping("upload")
public String upload(MultipartFile multipartFile){
String[] fileAbsolutePath={};
InputStream inputStream = null;
try {
String fileName = multipartFile.getOriginalFilename();
String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
byte[] file_buff = null;
inputStream = multipartFile.getInputStream();
if(inputStream!=null){
int len1 = inputStream.available();
file_buff = new byte[len1];
inputStream.read(file_buff);
}
FastDFSFile file = new FastDFSFile(fileName, file_buff, ext);
fileAbsolutePath = FastDFSUtil.upload(file);
} catch (Exception e) {
LOGGER.error("upload file Exception!",e);
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileAbsolutePath==null) {
LOGGER.error("upload file failed,please upload again!");
}
String path=FastDFSUtil.getTrackerUrl()+fileAbsolutePath[0]+ "/"+fileAbsolutePath[1];
System.out.println("path:" + path);
return "path:" + path;
}
/**
* 下载文件
* @param fileUrl
* 文件访问地址,如:
* "http://192.168.8.110/fastdfs/group2/M00/00/00/wKgI2luKZaKACY_jAACQB3kqNKg127.jpg"
* @param response
*/
@RequestMapping("/download")
public void download(@RequestParam("fileUrl")String fileUrl, HttpServletResponse response){
ServletOutputStream outputStream = null;
try {
System.out.println("fileUrl:" + fileUrl);
byte[] bytes = FastDFSUtil.download(fileUrl);
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileUrl.substring(fileUrl.lastIndexOf("/") + 1), "UTF-8"));
response.setCharacterEncoding("UTF-8");
outputStream = response.getOutputStream();
outputStream.write(bytes);
System.out.println("success");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("fail");
}
/**
* 删除
*
* @param fileUrl
* 文件访问地址,如:
* "http://192.168.8.110/fastdfs/group2/M00/00/00/wKgI2luKZaKACY_jAACQB3kqNKg127.jpg"
* @return
*/
@RequestMapping
public String delete(@RequestParam("fileUrl")String fileUrl){
try{
FastDFSUtil.delete(fileUrl);
return "deleteSuccess";
}
catch (Exception e) {
e.printStackTrace();
}
return "deleteFail";
}
}
1.docker镜像是网上找的,tracker用的是imlzw/fastdfs-tracker(整合了nginx),storage是imlzw/fastdfs-storage-dht(整合了nginx和FastDHT)。如果有更好镜像,相对会简单很多。希望大神推荐推荐,某人不才,暂时没研究写FastDFS+FastDHT+Nginx镜像!
2.一台tracker和两台storage,nginx做负载均衡和反向代理 、缓存。