Ubuntu Docker 安装 FastDFS 及使用
一、安装
- 创建安装目录
mkdir /usr/local/docker/fastdfs/environment
cd /usr/local/docker/fastdfs/environment
- 创建 Dockerfile
FROM ubuntu:xenial
MAINTAINER topsale@vip.qq.com
# 更新数据源
WORKDIR /etc/apt
RUN echo 'deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse' > sources.list
RUN echo 'deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse' >> sources.list
RUN echo 'deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse' >> sources.list
RUN echo 'deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse' >> sources.list
RUN apt-get update
# 安装依赖
RUN apt-get install make gcc libpcre3-dev zlib1g-dev --assume-yes
# 复制工具包
ADD fastdfs-5.11.tar.gz /usr/local/src
ADD fastdfs-nginx-module_v1.16.tar.gz /usr/local/src
ADD libfastcommon.tar.gz /usr/local/src
ADD nginx-1.13.6.tar.gz /usr/local/src
# 安装 libfastcommon
WORKDIR /usr/local/src/libfastcommon
RUN ./make.sh && ./make.sh install
# 安装 FastDFS
WORKDIR /usr/local/src/fastdfs-5.11
RUN ./make.sh && ./make.sh install
# 配置 FastDFS 跟踪器
ADD tracker.conf /etc/fdfs
RUN mkdir -p /fastdfs/tracker
# 配置 FastDFS 存储
ADD storage.conf /etc/fdfs
RUN mkdir -p /fastdfs/storage
# 配置 FastDFS 客户端
ADD client.conf /etc/fdfs
# 配置 fastdfs-nginx-module
ADD config /usr/local/src/fastdfs-nginx-module/src
# FastDFS 与 Nginx 集成
WORKDIR /usr/local/src/nginx-1.13.6
RUN ./configure --add-module=/usr/local/src/fastdfs-nginx-module/src
RUN make && make install
ADD mod_fastdfs.conf /etc/fdfs
WORKDIR /usr/local/src/fastdfs-5.11/conf
RUN cp http.conf mime.types /etc/fdfs/
# 配置 Nginx
ADD nginx.conf /usr/local/nginx/conf
COPY entrypoint.sh /usr/local/bin/
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
WORKDIR /
EXPOSE 8888
CMD ["/bin/bash"]
- 解压
FastDFS.zip
,并将文件夹中的内容复制到environment
下。FastDFS.zip
在我的阿里云盘下。 - 执行
chmod +x entrypoint.sh
赋予执行权限。 - 修改
tracker.confg
中的base_path
配置
base_path=/fastdfs/tracker
- 修改
storage.conf
中的基础配置,注意tracker_server
为跟踪器地址,http.server_port
为nginx监听端口
base_path=/fastdfs/storage
store_path0=/fastdfs/storage
tracker_server=192.168.3.37:22122
http.server_port=8888
- 修改
client.conf
,FastDFS客户端配置
base_path=/fastdfs/tracker
tracker_server=192.168.3.37:22122
- 修改
mod_fastdfs.conf
(nginx插件)
connect_timeout=10
tracker_server=192.168.3.37:22122
url_have_group_name = true
store_path0=/fastdfs/storage
- nginx.conf
user root;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8888;
server_name localhost;
location ~/group([0-9])/M00 {
ngx_fastdfs_module;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
- 在
environment
文件夹同级下创建docker-compose.yml
version: '3.1'
services:
fastdfs:
build: environment
restart: always
container_name: fastdfs
volumes:
- ./storage:/fastdfs/storage
network_mode: host
- 启动
docker-compose up -d
二、Springboot 集成
- 依赖
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27.0.0</version>
</dependency>
- 工具类
package com.chorain.framework.service;
/**
* @Description 文件存储服务接口
* @Author yin.jinbiao
* @Date 2021/7/13 17:23
* @Version 1.0
*/
public interface StorageService {
/**
* 上传文件
*
* @param data 文件的二进制内容
* @param extName 扩展名
* @return 上传成功后返回生成的文件id;失败,返回null
*/
String upload(byte[] data, String extName);
/**
* 删除文件
*
* @param fileId 被删除的文件id
* @return 删除成功后返回0,失败后返回错误代码
*/
int delete(String fileId);
}
package com.chorain.framework.service.impl;
import com.chorain.framework.service.StorageService;
import lombok.extern.slf4j.Slf4j;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @Description 文件服务实现
* @Author yin.jinbiao
* @Date 2021/7/13 17:24
* @Version 1.0
*/
@Slf4j
public class FastDFSStorageServiceImpl implements StorageService, InitializingBean {
private TrackerClient trackerClient;
@Value("${storage.fastdfs.tracker_server}")
private String trackerServer;
@Override
public String upload(byte[] data, String extName) {
TrackerServer trackerServer = null;
StorageServer storageServer = null;
StorageClient1 storageClient1 = null;
try {
NameValuePair[] meta_list = null; // new NameValuePair[0];
trackerServer = trackerClient.getConnection();
if (trackerServer == null) {
log.error("getConnection return null");
}
storageServer = trackerClient.getStoreStorage(trackerServer);
storageClient1 = new StorageClient1(trackerServer, storageServer);
String fileid = storageClient1.upload_file1(data, extName, meta_list);
log.debug("uploaded file <{}>", fileid);
return fileid;
} catch (Exception ex) {
log.error("Upload fail", ex);
return null;
} finally {
if (storageServer != null) {
try {
storageServer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (trackerServer != null) {
try {
trackerServer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
storageClient1 = null;
}
}
@Override
public int delete(String fileId) {
TrackerServer trackerServer = null;
StorageServer storageServer = null;
StorageClient1 storageClient1 = null;
int index = fileId.indexOf('/');
String groupName = fileId.substring(0, index);
try {
trackerServer = trackerClient.getConnection();
if (trackerServer == null) {
log.error("getConnection return null");
}
storageServer = trackerClient.getStoreStorage(trackerServer, groupName);
storageClient1 = new StorageClient1(trackerServer, storageServer);
int result = storageClient1.delete_file1(fileId);
return result;
} catch (Exception ex) {
log.error("Delete fail", ex);
return 1;
} finally {
if (storageServer != null) {
try {
storageServer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (trackerServer != null) {
try {
trackerServer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
storageClient1 = null;
}
}
@Override
public void afterPropertiesSet() throws Exception {
File confFile = File.createTempFile("fastdfs", ".conf");
PrintWriter confWriter = new PrintWriter(new FileWriter(confFile));
confWriter.println("tracker_server=" + trackerServer);
confWriter.close();
ClientGlobal.init(confFile.getAbsolutePath());
confFile.delete();
TrackerGroup trackerGroup = ClientGlobal.g_tracker_group;
trackerClient = new TrackerClient(trackerGroup);
log.info("Init FastDFS with tracker_server : {}", trackerServer);
}
}
package com.chorain.framework.common.factory;
import com.chorain.framework.service.StorageService;
import com.chorain.framework.service.impl.FastDFSStorageServiceImpl;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import java.util.HashMap;
import java.util.Map;
/**
* @Description 文件存储服务工厂类
* @Author yin.jinbiao
* @Date 2021/7/13 17:29
* @Version 1.0
*/
public class StorageFactory implements FactoryBean<StorageService> {
@Autowired
private AutowireCapableBeanFactory acbf;
/**
* 存储服务的类型,目前仅支持fastdfs
*/
@Value("${storage.type}")
private String type;
private Map<String, Class<? extends StorageService>> classMap;
public StorageFactory() {
classMap = new HashMap<>();
// fastdfs 方式
classMap.put("fastdfs", FastDFSStorageServiceImpl.class);
}
@Override
public StorageService getObject() throws Exception {
Class<? extends StorageService> clazz = classMap.get(type);
if (clazz == null) {
throw new RuntimeException("Unsupported storage type [" + type + "], valid are " + classMap.keySet());
}
StorageService bean = clazz.newInstance();
acbf.autowireBean(bean);
acbf.initializeBean(bean, bean.getClass().getSimpleName());
return bean;
}
@Override
public Class<?> getObjectType() {
return StorageService.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
package com.chorain.framework.common.config;
import com.chorain.framework.common.factory.StorageFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Description Java 配置方式定义 StorageFactory 的 Bean 使其可以被依赖注入
* @Author yin.jinbiao
* @Date 2021/7/13 17:30
* @Version 1.0
*/
@Configuration
public class FastDFSConfiguration {
@Bean
public StorageFactory storageFactory() {
return new StorageFactory();
}
}
- 配置
fastdfs:
base:
url: http://192.168.3.37:8888/
storage:
type: fastdfs
fastdfs:
tracker_server: 192.168.10.131:22122
三、小结
FastDFS 有三个概念:
- 跟踪器 tracker server ,主要做调度工作,起负载均衡作用,每个 storage 启动后会连接 tracker,告知自己所属的 group 等信息,并保持周期性心跳。
- 存储服务器 storage server,主要提供容量和备份服务;以 group 为单位,每个 group 内可以有多台 storage server,数据互为备份。
- 客户端 client,上传下载数据的服务器。这里我们的服务代码就相当于一台客户端。
上述的 tracker 和 group 都仅有一个实例, 如果想做备份,可以加一台 storage server,group 配置相同即可。因为 相同的 group 互为备份。
所以想扩容,可以新加机器,通过增加新的 group 来实现。
多个 tracker server 的作用是相同的,Storage Server会定期的向Tracker Server发送自己的存储信息。当Tracker Server Cluster中的Tracker Server不止一个时,各个Tracker之间的关系是对等的,所以客户端上传时可以选择任意一个Tracker。
所以如果想实现负载均衡效果,可以通过添加 tracker server 来实现,修改每个 storage server 的配置,添加其 tracker_server 的配置即可。