文件服务器FastDFS 消息队列中间件RabbitMQ
新标签页 (chinaunix.net)
FastDFS - Browse Files at SourceForge.net
一、FastDFS
Tracker和Storage:
tracker用来管理所有的storage,只是管理服务器,负责负载均衡。
storage是存储服务器,每一个storage服务器都是一个单独的个体,storage服务器之间没有交互关系。
在FastDFS中根目录包含256个一级目录、每个一级目录中包含256个二级子目录,在二级子目录中存储图片。存储图片时服务器会返回相应的group和remote,访问文件时通过这两个键值对获取图片。
①、在虚拟机中使用docker安装FastDFS
docker load -i fastdfs.tar //在docker中加载镜像源文件
mkdir -p /opt/fdfs/tracker
docker run -d --network=host --name tracker -v /opt/fdfs/tracker:/var/fdfs delron/fastdfs tracker
mkdir -p /opt/fdfs/storage
docker run -d --network=host --name storage -e TRACKER_SERVER=192.168.222.128:22122 -v /opt/fdfs/storage:/var/fdfs -e GROUP_NAME=group1 delron/fastdfs storage
//重启服务器需要删除文件。
rm -rf /opt/fdfs/tracker/data/*.pid
rm -rf /opt/fdfs/storage/data/*.pid
//更改服务器的磁盘限制
docker exec -it tracker bash
cd /etc/fdfs
vi /etc/fdfs/tracker.conf
reserved_storage_space = 10K //磁盘剩余多少空间时关闭上传文件功能
②、java代码使用FastDFS
1)首先编写FastDFS配置信息fdfs.properties
fastdfs.connect_timeout_in_seconds=10 fastdfs.network_timeout_in_seconds=30 fastdfs.charset=UTF-8 # tracker???????????????? fastdfs.tracker_servers=192.168.222.128:22122 # tracker????????tracker.conf??http?????????? fastdfs.http_tracker_http_port=8080
2)FastDFS 操作工具类
封装了加载配置文件fdfs.properties的静态代码块,还有上传和下载的方法。
package com.xja.util; import org.csource.common.NameValuePair; import org.csource.fastdfs.*; import java.io.InputStream; import java.util.Properties; /** * FastDFS Java客户端工具 */ public final class FastDFSUtils { /** * 定义静态属性,Properties和StorageClient */ private final static Properties PROPERTIES; private final static StorageClient STORAGE_CLIENT; /** * 静态初始化代码块,初始化静态属性 * 静态初始化代码块有异常如何处理? * 处理的时候,try。。catch。。 抛出一个Error,终止虚拟机。 */ static{ try { PROPERTIES = new Properties(); // 读取配置文件 PROPERTIES.load( FastDFSUtils.class .getClassLoader() .getResourceAsStream("fdfs.properties") ); // 使用ClientGlobal初始化FastDFS客户端配置 ClientGlobal.initByProperties(PROPERTIES); // 创建Tracker客户端对象 TrackerClient trackerClient = new TrackerClient(); // 基于Tracker客户端对象,获取Tracker服务器对象 TrackerServer trackerServer = trackerClient.getConnection(); // 基于Tracker服务器和客户端对象,获取Storage服务器对象 StorageServer storageServer = trackerClient.getStoreStorage(trackerServer); // 创建Storage客户端对象 STORAGE_CLIENT = new StorageClient(trackerServer, storageServer); }catch (Exception e){ throw new ExceptionInInitializerError(e); } } /** * 删除文件 * int delete_file(String 卷名, String 路径及文件名); * 返回值: 0代表成功,其他数字代表错误编码 */ public static int remote(String group, String remote){ try { return STORAGE_CLIENT.delete_file(group, remote); }catch (Exception e){ e.printStackTrace(); return -1; } } /** * 查询某文件的元数据 * @param group 卷名 * @param remote 路径及文件名 * @return 返回文件的元数据数组。发生错误返回null */ public static NameValuePair[] getMetaData(String group, String remote){ try{ return STORAGE_CLIENT.get_metadata(group, remote); }catch (Exception e){ e.printStackTrace(); return null; } } /** * 下载文件工具方法 * 下载方法 * byte[] download_file(String 卷名, String 路径及文件名) * 返回要下载的文件内容 * @param group 卷名 * @param remote 路径及文件名 * @return 返回下载的文件内容,发生错误返回null */ public static byte[] download(String group, String remote){ try { return STORAGE_CLIENT.download_file(group, remote); }catch (Exception e){ e.printStackTrace(); return null; } } /** * 上传文件的工具方法 * 一定保存文件到FastDFS,一定保存至少一个元数据(文件原始名称) * @param inputStream 要上传的文件的输入流 * @param fileName 上传文件的原始名称 * @param metaProperties 上传文件的元数据,成对提供,如: 名,值,名,值 * @return */ public static String[] uploadFile(InputStream inputStream, String fileName, String... metaProperties){ try { int length = inputStream.available(); byte[] datas = new byte[length]; inputStream.read(datas, 0, length); // 处理元数据 NameValuePair[] nameValuePairs = null; if (metaProperties.length % 2 == 0) { // 参数数量满足要求,开始处理 nameValuePairs = new NameValuePair[metaProperties.length / 2 + 1]; for (int i = 0; i < nameValuePairs.length; i = i + 2) { nameValuePairs[i / 2] = new NameValuePair(metaProperties[i], metaProperties[i + 1]); } } else { nameValuePairs = new NameValuePair[1]; } nameValuePairs[nameValuePairs.length - 1] = new NameValuePair("fileName", fileName); // 获取文件后缀 String extName = getExtName(fileName); // 上传文件到FastDFS String[] result = STORAGE_CLIENT.upload_file(datas, extName, nameValuePairs); for (String s : result) { System.out.println("s = " + s); //s = group1 //s = M00/00/00/wKjegGbqfoKACfLIAAAnJ2OoZs8411.txt } System.out.println("============= " + nameValuePairs[0]+ "/n"+ nameValuePairs[1]); //============= org.csource.common.NameValuePair@4ae28001/norg.csource.common.NameValuePair@3e472f5d return result; }catch (Exception e){ // 发生任何异常,上传文件失败。返回null e.printStackTrace(); return null; } } /** * 截取文件后缀 * @param fileName * @return */ private static String getExtName(String fileName){ if(fileName.lastIndexOf(".") > -1){ // 文件名称中包含字符 . return fileName.substring(fileName.lastIndexOf(".") + 1); }else{ // 文件名称中不包含字符 . return ""; } } /** * 提供获取Storage客户端对象的工具方法 */ public static StorageClient getStorageClient(){ return STORAGE_CLIENT; } private FastDFSUtils(){} }
3)编写上传和下载路由
下载路由中需要加上内容类型和响应头,否则可能出现访问下载却出现在线预览图片
@Controller public class LoginController { @RequestMapping("/index") public String uploadPage(){ return "index"; } @RequestMapping("/upload") public String upload(MultipartFile file, HttpSession session) throws IOException { InputStream inputStream = file.getInputStream(); String originalFilename = file.getOriginalFilename(); String[] strings = FastDFSUtils.uploadFile(inputStream, originalFilename, "s", ""); System.out.println(strings); return "a"; } @RequestMapping("/download") public void download(String group,String remote,HttpServletResponse response) throws IOException { byte[] datas = FastDFSUtils.download(group, remote); response.setContentType("application/octet-stream"); // 设置下载文件的附件名称 NameValuePair[] metaData = FastDFSUtils.getMetaData(group, remote); String fileName = ""; for (NameValuePair metaDatum : metaData) { if ("fileName".equals(metaDatum.getName())) fileName = metaDatum.getValue(); } response.setHeader("content-disposition", "attachment;filename="+fileName.toString()); // 输出要下载的文件内容到客户端 // byte[] datas = (byte[]) result.get("datas"); response.getOutputStream().write(datas, 0, datas.length); response.getOutputStream().flush(); } }
4)上传一张图片,在浏览器中访问图片
这里的端口号为8888,fastdfs中内置有nginx服务器,请求从nginx服务器转发到storage服务器.
二、RabbitMQ
publisher项目使用RabbitMQ软件将消息推送至交换机,交换机根据路由键将消息推送至相应队列中。Consumer项目中的监听器时刻监听提前设置好的监听队列,如果有消息进入队列中,会调用单元方法将消息中的数据取出消费,消费成功后返回信息在队列中删除消息
如果消息在Consumer项目中拿取数据或者消费过程中出现错误,这个时候不会被删除,而是会多次尝试再次获取 消息 消费。达到一定次数,停止尝试。
1)虚拟机使用docker安装RabbitMQ
0、docker命令:
docker pull rabbitmq:management docker run -d --name rabbitmq -p 15672:15672 -p 5672:5672 --restart=always -e DEFAULT_USER=wollo -e DEFAULT_PASS=wollo rabbitmq:management
①、 访问 http:192.168.222.128:15672: //Docker宿主机IP:15672
这个是RabbitMQ提供的可视化界面,类似于Navicat。
②、使用可视化界面操作RabbitMQ:
创建交换机、创建队列、将交换机和队列绑定并设置路由键
2)使用java代码连接操作RabbitMQ