一、单机时代的图片服务器架构html
初创时期因为时间紧迫,开发人员水平也颇有限等缘由。因此一般就直接在website文件所在的目录下,创建1个upload子目录,用于保存用户上传的图片文件。若是按业务再细分,能够在upload目录下再创建不一样的子目录来区分。例如:upload\QA,upload\Face等java
优势:实现起来最简单,无需任何复杂技术,就能成功将用户上传的文件写入指定目录。保存数据库记录和访问起来却是也很方便。linux
缺点:上传方式混乱,严重不利于网站的扩展。nginx
二、单独立文件服务器web
随着公司的业务不断的发展,将服务和文件放在同一服务器下面的弊端愈来愈明显;这个时候就该上线独立的图片服务器系统;经过ftp或者ssh工具将文件上传到图片服务器的某个目录下面,在经过ngnix或者apache服务器来作图片的访问,给图片服务器配置独立的子域名,例如 img.xx.com。在业务处理文件时经过ftp或者ssh将文件上传到文件服务器,返回给程序一个独立域名的图片url地址,网站正常访问的时候就经过这个URL地址来访问文件。算法
优势:图片访问是很消耗服务器资源的(由于会涉及到操做系统的上下文切换和磁盘I/O操做)。分离出来后,Web/App服务器能够更专一发挥动态处理的能力;独立存储,更方便作扩容、容灾和数据迁移;方便作图片访问请求的负载均衡,方便应用各类缓存策略(HTTP Header、Proxy Cache等),也更加方便迁移到CDN。spring
缺点:单机存在性能瓶颈,容灾、垂直扩展性稍差数据库
三、分布式文件系统apache
业务继续发展,单独单台的服务器存储和响应也很快到达了瓶颈,新的业务要求,文件访问高响应性,高可用性来响应业务对系统的要求。分布式文件系统,通常分为三块内容来配合,服务的存储、访问的仲裁系统,文件存储系统,文件的容灾系统来构成,总裁系统至关于文件服务器的大脑,根据必定的算法来决定文件存储的位置,文件存储系统负责报错文件,容灾系统负责文件系统和本身的相互备份。缓存
优势:扩展能力: 毫无疑问,扩展能力是一个分布式文件系统最重要的特色;高可用性: 在分布式文件系统中,高可用性包含两层,一是整个文件系统的可用性,二是数据的完整和一致性;弹性存储: 能够根据业务须要灵活地增长或缩减数据存储以及增删存储池中的资源,而不须要中断系统运行
缺点:系统复杂度稍高,须要更多服务器
一、什么是FastDFS
FastDFS是一个开源的轻量级分布式文件系统。它解决了大数据量存储和负载均衡等问题。特别适合以中小文件(建议范围:4KB < file_size <500MB)为载体的在线服务,如相册网站、视频网站等等。在UC基于FastDFS开发向用户提供了:网盘,社区,广告和应用下载等业务的存储服务。
二、FastDFS架构和原理
FastDFS服务端有三个角色:跟踪服务器(tracker server)、存储服务器(storage server)和客户端(client)。
tracker server:跟踪服务器,主要作调度工做,起负载均衡的做用。在内存中记录集群中全部存储组和存储服务器的状态信息,是客户端和数据服务器交互的枢纽。相比GFS中的master更为精简,不记录文件索引信息,占用的内存量不多。
storage server:存储服务器(又称:存储节点或数据服务器),文件和文件属性(meta data)都保存到存储服务器上。Storage server直接利用OS的文件系统调用管理文件。
client:客户端,做为业务请求的发起方,经过专有接口,使用TCP/IP协议与跟踪器服务器或存储节点进行数据交互。
Tracker至关于FastDFS的大脑,不管是上传仍是下载都是经过tracker来分配资源;客户端通常可使用ngnix等静态服务器来调用或者作一部分的缓存;存储服务器内部分为卷(或者叫作组),卷于卷之间是平行的关系,能够根据资源的时候状况随时增长,卷内服务器文件相互同步备份,以达到容灾的目的
上传机制:
首先客户端请求Tracker服务获取到存储服务器的ip地址和端口,而后客户端根据返回的IP地址和端口号请求上传文件,存储服务器接收到请求后,生产文件file_id而且将文件内容写入磁盘返回给客户端file_id和路径信息、文件名,客户端保存相关信息上传完毕
下载机制:
客户端带上文件名信息请求Tracker服务获取到存储服务器的ip地址和端口,而后客户端根据返回的IP地址和端口号请求下载文件,存储服务器接收到请求后返回文件给客户端。
三、如何搭建fastDFS
请参考如下文章:
地址:http://www.linux178.com/storage/fastdfs-nginx-cache.html
四、使用java调用fastDFS
如下代码是一个spring mvc中一个完整的上传请求
@RequestMapping(value = "/upload", method = RequestMethod.POST) @ResponseBody public Object upload(@RequestParam MultipartFile file) { UploadResponse res = new UploadResponse(); try { if(file.isEmpty()){ res.setRet_code(UserCodeEnum.ERR_FILE_NULL.getCode()); res.setRet_msg(UserCodeEnum.ERR_FILE_NULL.getDesc()); }else{ logger.info("UserController-upload-request-file=" + file.getOriginalFilename()); String tempFileName = file.getOriginalFilename(); //fastDFS方式 ClassPathResource cpr = new ClassPathResource("fdfs_client.conf"); ClientGlobal.init(cpr.getClassLoader().getResource("fdfs_client.conf").getPath()); byte[] fileBuff = file.getBytes(); String fileId = ""; String fileExtName = tempFileName.substring(tempFileName.lastIndexOf(".")); //创建链接 TrackerClient tracker = new TrackerClient(); TrackerServer trackerServer = tracker.getConnection(); StorageServer storageServer = null; StorageClient1 client = new StorageClient1(trackerServer, storageServer); //设置元信息 NameValuePair[] metaList = new NameValuePair[3]; metaList[0] = new NameValuePair("fileName", tempFileName); metaList[1] = new NameValuePair("fileExtName", fileExtName); metaList[2] = new NameValuePair("fileLength", String.valueOf(file.getSize())); //上传文件 fileId = client.upload_file1(fileBuff, fileExtName, metaList); res.setHead_img(UserConstants.FILE_IMG_URL+fileId); res.setRet_code(UserCodeEnum.SUCCESS.getCode()); res.setRet_msg(UserCodeEnum.SUCCESS.getDesc()); } logger.info("UserController-upload-response-" + JsonUtils.o2j(res)); } catch (Exception e) { res.setRet_code(UserCodeEnum.ERR_UNKNOWN.getCode()); res.setRet_msg(UserCodeEnum.ERR_UNKNOWN.getDesc()); logger.error("UserController-upload-error", e); } return res; }
fastDFS java客户端配置文件fdfs_client.conf配置以下:
connect_timeout = 30 network_timeout = 60 charset = ISO8859-1 http.tracker_http_port = 8090 http.anti_steal_token = no http.secret_key = 123456 tracker_server = 192.168.11.***:22122
参考: