解决了大容量的文件存储和高并发访问的问题,文件存取时实现了负载均衡。
FastDFS服务端只有两个角色,tracker server和storage server。
所有同角色服务器集群节点都是平等的,不存在主从关系(Master-Slave)。
存储服务器采用分组方式,同组内存储服务器上的文件完全相同(备份);不同分组的存储服务器管理不同的文件(扩容 RAID)。
不同组的storage server之间不会相互通信。
由storage server主动向tracker server报告状态信息,tracker server之间不会相互通信。

(1)拉取镜像
docker pull delron/fastdfs
(2)创建tracker service宿主机目录
mkdir -p /opt/fdfs/tracker
(3)创建tracker service容器,默认端口号22122
docker run -d --network=host --name tracker -v /opt/fdfs/tracker:/var/fdfs delron/fastdfs tracker
(4)创建storage service宿主机目录
mkdir -p /opt/fdfs/storage
(5)创建storage service并启动容器,默认端口号23000
docker run -d --network=host --name storage -e TRACKER_SERVER=192.168.146.130:22122 -v /opt/fdfs/storage:/var/fdfs -e GROUP_NAME=group1 delron/fastdfs storage
(6)重启问题解决
rm -rf /opt/fdfs/tracker/data/*.pid
rm -rf /opt/fdfs/storage/data/*.pid
(1)导入依赖
cn.bestwu
fastdfs-client-java
1.27
org.apache.commons
commons-lang3
3.4
(2)在src/main/resources目录中创建properties类型配置文件,命名不限。如:fdfs.properties
fastdfs.connect_timeout_in_seconds=10
fastdfs.network_timeout_in_seconds=30
fastdfs.charset=UTF-8
# tracker服务器地址,多个服务器,逗号分隔
fastdfs.tracker_servers=192.168.130.146:22122
# tracker服务器中配置文件tracker.conf中的http端口配置。必须相同。
fastdfs.http_tracker_http_port=8080
(3)创建工具类com.bjsxt.utils.FastDFSUtils
- /**
- * 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);
- 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(){}
- }
(4)service层
- @Override
- public boolean upload(MultipartFile file) {
- try {
- //将文件存入到Storage service中,返回值为长度为2的数组,一个为存储的仓库,一个为存储的文件路径
- String[] strings = FastDFSUtils.uploadFile(file.getInputStream(), file.getOriginalFilename(), "owner", "admin");
-
- if(strings==null){
- return false;
- }
- //将文件信息存储到数据库中
- MyFile myFile = new MyFile();
- myFile.setFileName(file.getOriginalFilename());
- myFile.setGroupName(strings[0]);
- myFile.setRemoteName(strings[1]);
- myFile.setLength(file.getSize());
- myFile.setCreateTime(new Date());
- int insert = uploadMapper.insert(myFile);
- if(insert != 1){
- return false;
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- return true;
- }
(1)service层
- /**
- * 文件上传service
- * @param groupName 存储的主机
- * @param remoteName 存储的文件路径及文件名
- * @return
- */
- @Override
- public Map
download(String groupName, String remoteName) { - HashMap
map = new HashMap<>(); - //获取文件二进制数组
- byte[] download = FastDFSUtils.download(groupName, remoteName);
- //获取文件元数据
- NameValuePair[] metaData = FastDFSUtils.getMetaData(groupName, remoteName);
- if(download != null && metaData != null){
- map.put("data",download);
- for (NameValuePair metaDatum : metaData) {
- if(metaDatum.getName().equals("fileName")){
- map.put("fileName", metaDatum.getValue());
- break;
- }
- }
- }
- return map;
- }
(2)controller
- @RequestMapping("/download")
- public void download(String groupName, String remoteName, HttpServletResponse response) throws IOException {
- Map
download = uploadService.download(groupName, remoteName); - // 设置响应头
- response.setContentType("application/octet-stream");
- // 设置下载文件的附件名称
- response.setHeader("content-disposition", "attachment;filename="+download.get("fileName").toString());
- // 输出要下载的文件内容到客户端
- byte[] datas = (byte[]) download.get("data");
- response.getOutputStream().write(datas, 0, datas.length);
- }
- @Override
- public boolean delete(MyFile file) {
- //删除数据库文件
- int delete = uploadMapper.delete(file);
- //删除文件
- int remote = FastDFSUtils.remote(file.getGroupName(), file.getRemoteName());
- return delete>0&&remote==0;
- }
(1)Nginx简介
Nginx (engine x) 是一个高性能的HTTP和反向代理服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。
Nginx 是一个很强大的高性能Web和反向代理服务,它具有很多非常优越的特性:在连接高并发的情况下,Nginx是Apache服务不错的替代品:Nginx在美国是做虚拟主机生意的老板们经常选择的软件平台之一。
(2)代理方式
正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。
反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
两者间的区别
位置不同 正向代理,架设在客户机和目标主机之间; 反向代理,架设在服务器端;
代理对象不同 正向代理,代理客户端,服务端不知道实际发起请求的客户端; 反向代理,代理服务端,客户端不知道实际提供服务的服务端;
(3)Nginx常用场景
Http协议代理
只要支持HTTP协议访问的内容,都可以由Nginx进行代理。Nginx只支持HTTP协议的代理,其他协议不支持。
搭建虚拟主机
Nginx可以监听所安装的主机的某个端口,对外支持这个端口的HTTP访问。当接收到外部HTTP请求后把本机中资源返回给客户端。今天的课程内容就是使用Nginx的搭建虚拟主机功能,外部请求图片时,把图片信息响应给请求发。
负载均衡
Nginx可以代理多个主机,内置负载均衡策略。
(4)使用
使用http请求,ip+存储主机+存储路径及文件名