我们知道,对于传统项目来说,所有的模块都在一个项目中开发,包括所有静态资源文件比如图片等,都存储在这一个tomcat服务器上。如果访问量小的话,这样做问题倒不大,但是对于互联网项目来说,用户访问量很大,这样一个tomcat服务是远远不能满足业务需求的。这就需要部署tomcat集群,有集群就需要用到负载均衡,我们一般都会使用nginx来作为负载均衡服务器。如下图所示,但是这种tomcat集群的缺点也很明显,假如我们把一张1.jpg的图片上传到了tomcat1的images目录下了,由于nginx负责均衡处理请求,当用户去请求访问这张图片的时候,第一次,Nginx把请求交给tomcat1去处理,它到自己的images目录下去找这张图片,发现是可以找到的,于是我们便能看到这张图片,当我们第二次通过Nginx去请求访问该图片时,Nginx把请求交给tomcat2区处理了,这时tomcat2去它自己的images目录下去查找这张图片,发现并没有这张图片,因此页面上便看不到图片。作为用户来讲,一次访问能看到,再刷新就看不到,再刷新能看到,很不理解,直观的感觉便是我们的系统太烂了。
针对上面提到的问题,我们对集群做下改善,我们专门搞一个图片服务器,所有的tomcat都将用户上传的图片上传到图片服务器上,tomcat本身并不保存图片。我们采用http的方式来访问图片,这样我们就需要使用http服务器,能作为http服务器的有多种选择。
第一:tomcat可以作为http服务器,但是由于tomcat的强项并不在于处理静态资源( 它的强项是处理servlet和jsp等动态请求)因此我们不选择tomcat。
第二:使用Apache作为http服务器,Apache是由c语言编写的一款服务器(注意,这里指的并不是Apache 组织,仅仅是一个服务器),这款服务器在以前用的人是很多的,现在用的人少了。
第三:使用nginx,nginx因为其独特的优势,作为http服务器是目前最火的。我们就使用nginx来统一管理这些图片,这样用户要访问图片的时候,nginx直接把图片服务器上的图片给返回就可以了,从而解决了tomcat集群资源无法共享的问题。
但是这里需要考虑一个问题,那就是作为服务器,容量肯定是有限的,当这个服务器容量满了,怎么办?还有就是图片服务器挂了,怎么办?这些都是必须要解决的问题,为了解决这两个问题,我们使用FastDFS集群来解决,FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。它的优势是可以水平扩容,FastDFS存储资源的设备是按组来区分的,当存储空间不足时,便可以通过水平增加分组并相应添加设备来达到扩容的目的,而且是没有上限的。还有个优势是高可用,也就是说FastDFS集群能够做到当提供服务的nginx发生故障时,自动切换到另一台nginx设备上,保障服务的稳定。
搭建集群比较麻烦,作为练习,我们只需搭建单机版FastDFS即可。大家可以参考http://blog.csdn.net/u012453843/article/details/69951920这篇博客进行学习。当然,如果大家嫌搭建太麻烦的话,可以直接到http://download.csdn.net/detail/u012453843/9810382这个地址下载我搭建好的FastDFS单机版服务器(我已经设置了nginx、tracker、storage开机自启动,因此大家只需要启动虚拟机即可),关于用Vmware打开已经配置好的虚拟机的方法大家可以参考http://blog.csdn.net/u012453843/article/details/70045136这篇博客进行学习。
下面我便用FastDFS-Client来进行上传和下载测试,不过我们需要先做两件事
第一件事:由于中央仓库并没有fastdfs-client的包,因此需要我们自己整,大家可以参考:http://blog.csdn.net/u012453843/article/details/70135826这篇博客学习怎样使用Eclipse从Github官网下载fastdfs-client源码并转化为maven工程以及如何打包到本地maven仓库。
第二件事:在taotao-manager-web工程的resources目录下新建一个resource文件夹并在它下面创建client.conf文件,client.conf文件中输入tracker所在的设备的IP及端口,由于我的tracker是在192.168.156.13上,因此我这里写的是"tracker_server=192.168.156.13:22122",如下图所示。
下面我们新建一个测试类来进行测试,我们在src/test/java目录下新建一个包com.taotao.fastdfs,在该包下新建一个测试类TestFastDFS.java,如下图所示。
为方便复制,现把测试类代码粘贴如下:
- package com.taotao.fastdfs;
- import org.csource.common.NameValuePair;
- import org.csource.fastdfs.ClientGlobal;
- import org.csource.fastdfs.StorageClient;
- import org.csource.fastdfs.StorageServer;
- import org.csource.fastdfs.TrackerClient;
- import org.csource.fastdfs.TrackerServer;
- import org.junit.Test;
- public class TestFastDFS {
- @Test
- public void testUploadFile() throws Exception{
- //1.向工程添加jar包
- //2.创建一个配置文件,配置tracker服务器地址
- //3.加载配置文件
- ClientGlobal.init("E:/workspace/taotao-manager-web/src/main/resources/resource/client.conf");
- //4.创建一个TrackerClient对象
- TrackerClient trackerClient = new TrackerClient();
- //5.使用TrackerClient对象获得trackerserver对象。
- TrackerServer trackerServer = trackerClient.getConnection();
- //6.创建一个StorageServer的引用,我们用null就可以
- StorageServer storageServer = null;
- //7.创建一个StorageClient对象。trackerserver、StorageServer两个参数
- StorageClient storageClient = new StorageClient(trackerServer, storageServer);
- //8.使用StorageClient对象上传文件
- NameValuePair[] metaList = new NameValuePair[3];
- metaList[0] = new NameValuePair("fileName", "2");
- metaList[1] = new NameValuePair("createTime", "2017-04-13 16:01:00");
- metaList[2] = new NameValuePair("createUser", "zhangsan");
- String[] upload_files = storageClient.upload_file("E:/images/2.jpg", "jpg", metaList);
- for(String file : upload_files){
- System.out.println(file);
- }
- }
- }
上面的代码有三点需要注意:
第一、taotao-manager-web工程还未添加junit的依赖,因此需要在pom.xml文件中添加junit的依赖,如下所示,这里之所以不用写版本是因为在taotao-parent工程中已经统一定义好版本了。
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- </dependency>
第三、我们在复制粘贴win10系统的本地文件绝对路径(E:/images/2.jpg)时,Eclipse是识别不了的,运行会报如下错误。解决方法是手动输入,而且要注意把包括双引号在内的这个路径串删除("E:/images/2.jpg"),然后手动输入一遍。
注意上面三点后,我们再执行这个测试方法便成功了,如下图所示。回显信息的第一行是该图片被保存到哪个组了,由于我们现在只是用的单机FastDFS服务器,因此现在都是group1。第二行是存放的具体位置。
既然上传上去了,现在我们试着用http的方式来访问下该图片,我们需要把group1和M00/00/00/wKicDVjvjkqAISg-AAGDL8Ay0xY563.jpg拼接到一块来访问。端口8888是我在Nginx配置的对外暴露的访问接口。这样我们便可以查看到我们刚才上传的照片了,如下图所示。
经过上面的操作,说明我们搭建的图片服务器没问题,但有个问题是,上传操作步骤繁琐,因此我们迫切需要对其进行封装,现在我把封装好的类FastDFSClient.java文件的内容粘贴如下。构造方法中的conf = conf.replace("classpath:", this.getClass().getResource("/").getPath());这句话的意思是,如果用户传入的文件路径是相对路径(相对路径 以resources目录为根目录,比如用户传入的文件路径是"classpath:applications.properties",那么需要转为绝对路径,因此需要把"classpath:"给替换掉,改为E:/workspace/taotao-manager-web/src/main/resources)。封装类中使用的Storage客户端是StorageClient1而不是StorageClient,这个客户端的好处是能够帮我们自动把文件所在的组以及存放位置拼接到一块。
- package cn.itcast.fastdfs.cliennt;
- import org.csource.common.NameValuePair;
- import org.csource.fastdfs.ClientGlobal;
- import org.csource.fastdfs.StorageClient1;
- import org.csource.fastdfs.StorageServer;
- import org.csource.fastdfs.TrackerClient;
- import org.csource.fastdfs.TrackerServer;
- public class FastDFSClient {
- private TrackerClient trackerClient = null;
- private TrackerServer trackerServer = null;
- private StorageServer storageServer = null;
- private StorageClient1 storageClient = null;
- public FastDFSClient(String conf) throws Exception {
- if (conf.contains("classpath:")) {
- conf = conf.replace("classpath:", this.getClass().getResource("/").getPath());
- }
- ClientGlobal.init(conf);
- trackerClient = new TrackerClient();
- trackerServer = trackerClient.getConnection();
- storageServer = null;
- storageClient = new StorageClient1(trackerServer, storageServer);
- }
- /**
- * 上传文件方法
- * <p>Title: uploadFile</p>
- * <p>Description: </p>
- * @param fileName 文件全路径
- * @param extName 文件扩展名,不包含(.)
- * @param metas 文件扩展信息
- * @return
- * @throws Exception
- */
- public String uploadFile(String fileName, String extName, NameValuePair[] metas) throws Exception {
- String result = storageClient.upload_file1(fileName, extName, metas);
- return result;
- }
- public String uploadFile(String fileName) throws Exception {
- return uploadFile(fileName, null, null);
- }
- public String uploadFile(String fileName, String extName) throws Exception {
- return uploadFile(fileName, extName, null);
- }
- /**
- * 上传文件方法
- * <p>Title: uploadFile</p>
- * <p>Description: </p>
- * @param fileContent 文件的内容,字节数组
- * @param extName 文件扩展名
- * @param metas 文件扩展信息
- * @return
- * @throws Exception
- */
- public String uploadFile(byte[] fileContent, String extName, NameValuePair[] metas) throws Exception {
- String result = storageClient.upload_file1(fileContent, extName, metas);
- return result;
- }
- public String uploadFile(byte[] fileContent) throws Exception {
- return uploadFile(fileContent, null, null);
- }
- public String uploadFile(byte[] fileContent, String extName) throws Exception {
- return uploadFile(fileContent, extName, null);
- }
- }
我们新建一个工具包com.taotao.utils,然后把我们的封装类FastDFSClient.java放到下面。我们来测测这个工具类是否好使,我们再新建个测试方法testFastDFSClient,如下图所示。
代码如下
- @Test
- public void testFastDFSClient() throws Exception{
- FastDFSClient fastDFSClient = new FastDFSClient("E:/workspace/taotao-manager-web/src/main/resources/resource/client.conf");
- String imgPath = fastDFSClient.uploadFile("E:/images/2.jpg");
- System.out.println(imgPath);
- }
我们来访问这个图片,如下图所示。