因为项目的需要,这两天在项目中整合了FastDFS,用于大批量的上传图片。按照惯例我们先介绍整合,然后再介绍一下FastDFS的相关情况。
FastDFS 与Spring Boot的整合
相关配置
在pom文件中添加依赖
<!-- fastdfs --> <dependency> <groupId>org.csource</groupId> <artifactId>fastdfs-client-java</artifactId> <version>1.2.RELEASE</version> </dependency> <!--对象池--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.4.2</version> </dependency>
配置属性文件,命名为fastdfs_client.conf,放在resources下
## fastdfs为前缀的是FastDFS的配置 connect_timeout = 2 network_timeout = 30 charset = utf-8 # TrackerServer port http.tracker_http_port = 80 # token 防盗链功能 http.anti_steal_token = no # 密钥 http.secret_key = FastDFS1234567890 # 文件服务器地址 http.access.url=你fastdfs的服务器 # Tracker Server tracker_server = 你tracker的服务器 tracker_server = 你tracker的服务器
文件上传
操作文件的工具类FastDfsUtil类
public class FastDfsUtil implements Serializable{ private static final Logger log = LoggerFactory.getLogger(FastDfsUtil.class); private static final long serialVersionUID = -4230645563489067923L; /** * 文件上传 * @param is * @param fileName * @return */ public static String uploadFile(InputStream is, String fileName){ TrackerServer trackerServer = TrackerServerPool.borrowObject(); StorageClient1 storageClient = new StorageClient1(trackerServer, null); try { //读取流 byte[] content = new byte[is.available()]; is.read(content, 0, content.length); //文件扩展名 String ext = FilenameUtils.getExtension(fileName); //mata list是表文件的描述 NameValuePair[] mata_list = new NameValuePair[1]; mata_list[0] = new NameValuePair("fileName", fileName); // 上传 String path = storageClient.upload_file1(content, ext, mata_list); return path; } catch (Exception e) { log.error("上传图片到fastDFS失败={}", e.getMessage()); throw new UploadException("上传图片到fastDFS失败"); } finally{ // 返还对象 TrackerServerPool.returnObject(trackerServer); } } }
此处用到了工厂模式以及对象池
4. 对象池类TrackerServerPool类
/** * TrackerServer 对象池 * <p> * * @author xiang.wei * @version 1.0 */ public class TrackerServerPool { /** * org.slf4j.Logger */ private static Logger logger = LoggerFactory.getLogger(TrackerServerPool.class); /** * TrackerServer 配置文件路径 */ private static final String FASTDFS_CONFIG_PATH = "fastdfs_client.conf"; /** * 最大连接数 default 8. * 需要在application.properties中配置 */ @Value("${max_storage_connection}") private static int maxStorageConnection; /** * TrackerServer 对象池. * GenericObjectPool 没有无参构造 */ private static GenericObjectPool<TrackerServer> trackerServerPool; private TrackerServerPool(){}; private static synchronized GenericObjectPool<TrackerServer> getObjectPool(){ if(trackerServerPool == null){ try { // 加载配置文件 ClassPathResource resource = new ClassPathResource(FASTDFS_CONFIG_PATH); ClientGlobal.init(resource.getClassLoader().getResource(FASTDFS_CONFIG_PATH).getPath()); } catch (IOException e) { logger.info("加载fastDFS配置文件出错={}",e.getMessage()); } catch (Exception e) { logger.info("加载fastDFS配置文件出错={}",e.getMessage()); } // Pool配置 GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); poolConfig.setMinIdle(2); if(maxStorageConnection > 0){ poolConfig.setMaxTotal(maxStorageConnection); } trackerServerPool = new GenericObjectPool<>(new TrackerServerFactory(), poolConfig); } return trackerServerPool; } /** * 获取 TrackerServer * @return TrackerServer * @throws FastDFSException */ public static TrackerServer borrowObject(){ TrackerServer trackerServer = null; try { trackerServer = getObjectPool().borrowObject(); } catch (Exception e) { e.printStackTrace(); } return trackerServer; } /** * 回收 TrackerServer * @param trackerServer 需要回收的 TrackerServer */ public static void returnObject(TrackerServer trackerServer){ getObjectPool().returnObject(trackerServer); } }
工厂类 TrackerServerFactory类
public class TrackerServerFactory extends BasePooledObjectFactory<TrackerServer> { @Override public TrackerServer create() throws Exception { // TrackerClient TrackerClient trackerClient = new TrackerClient(); // TrackerServer TrackerServer trackerServer = trackerClient.getConnection(); return trackerServer; } @Override public PooledObject<TrackerServer> wrap(TrackerServer trackerServer) { return new DefaultPooledObject<TrackerServer>(trackerServer); } }
以上就配置完了,可以传入有效的图片测试下。
FastDFS 的介绍
FastDFS是一个开源的高性能分布式文件系统(DFS)。它的主要功能包括:文件存储,文件同步和文件访问,以及高容量和负载均衡。主要解决了海量数据存储问题,特别适合以中小文件(建议范围:4KB < file_size < 500MB) 为载体的在线服务。
FastDFS系统有三个角色:跟踪服务器(Tracker Server)、存储服务器(Storage Server)和客户端(Client) 。
Tracker Server: 跟踪服务器,主要做调度工作,起到均衡的作用;负责管理所有的storage server和group,每个storage在启动后会连接Tracker,告知自己所属group等信息,并保持周期性心跳。
Storage Server:存储服务器,主要提供容量和备份服务;以group为单位,每个group内有多台storage server,数据互为备份。
Client:客户端,上传下载数据的服务器,也就是我们自己的项目所部署在的服务器。
FastDFS的存储策略
为了支持大容量,存储节点(服务器)采用了分卷(或分组)的组织方式。存储系统由一个或多个卷组成,卷与卷之间的文件是相互独立的。所有卷的文件容量累加就是存储系统中的文件容量。一个卷可以由一台或者多台存储服务器组成,一个卷下的存储服务器中的文件都是相同的,卷中的多台存储服务器起到了冗余备份和负载均衡的作用。
在卷中增加服务器时,同步已有的文件由系统自动完成,同步完成后,系统自动将新增服务器切换到线上提供服务。当存储空间不足或即将耗尽时,可以动态添加卷。只需要增加一台或多台服务器,并将它们配置为一个新的卷,这样就可以扩大了存储系统的容量。
FastDFS的上传过程
FastDFS向使用者提供基本文件访问接口,不如upload,download等等,以客户端库的方式提供给用户使用。
storage server会定期的向Tracker Server发送自己的存储信息。当Tracker Server Cluster中的Tracker Server不止一个时,各个Tracker之间的关系是对等的。所以客户端上传时可以选择任意一个Tracker。
当Tracker收到客户端上传文件的请求时,会为该文件分配一个可以存储文件的group,当选定了group后就要决定给客户端分配group中的哪一个storage server。当分配好storage server后,客户端向stroage发送写文件请求,storage 将会为文件分配一个数据存储目录。然后为文件分配一个 field,最后根据以上的信息生成文件名存储文件。
生成文件名:
当文件存储到某个子目录后,即认为该文件存储成功,接下来会为该文件生成一个文件名,文件名由group、存储目录、两级子目录、fileid、文件后缀名拼接而成。
例如:
group1/M00/02/1E/wKjScFs8PP6AOeKAAAFQYpr9pVQ027.jpg 组名 磁盘 目录 文件名
FastDFS的文件同步
写文件时,客户端将文件写至group内一个storage server即认为写文件成功,storage server写完文件后,会由后台线程将文件同步至同group内其他的storage server。
每个storage写文件后,同时会写一份 binlog,binlog里不包含文件数据,只包含文件名等元信息,这份binlog用于后台同步,storage会记录向group内其他storage同步的进度,以便重启后能接上次的进度继续同步;进度以时间戳的方式记录,所以最好能保持集群内所有server的时钟保持同步。
storage的同步进度会作为元数据的一部分汇报到tracker上,tracker在选择读storage的时候以同步进度作为参考。
FastDFS的文件下载
客户端uploadfile成功后,会拿到一个storage生成的文件名,接下来客户端更加这个文件名即可访问到该文件。
引用
https://www.cnblogs.com/chiangchou/p/fastdfs.html#_label0_0
https://blog.csdn.net/u013378306/article/details/74852355