优化文件下载性能,缓存MongoDB GRIDFS文件(实践讨论稿)

来源:互联网 发布:淘宝客用返利网推广 编辑:程序博客网 时间:2024/05/22 11:30

  当前的多机集群系统中,文件(附件)的上传、下载服务是通过网络文件系统(NFS),在NFS的应用中,本地NFS的客户端应用可以透明地读写位于远端NFS服务器上的文件,就像访问本地文件一样,以此形成集成负载均衡方式环境下的共享盘功能,如下图所示。
这里写图片描述

  在系统中,文件管理使用MongoDB Gridfs,如下图所示,文件下载过程中,先把MongoDB中的文件读取到NFS中,再通过系统Web服务下载文件,实际上是通过NFS缓存文件共享方式提供下载服务。
这里写图片描述

  下面示例为Web服务端下载JavaScript代码。

//下载附件function downloadFile(fileId) {    $.cordys.utils.sendCordysAjax({        method: 'GetFileById',        namespace: 'http://unicom.com/common/attachment',        parameters: {            fileId: fileId,            gridFSName: window.isTransWkfl ? window.opener.bizRvsnNumber : window.bizRvsnNumber        }    }).done(function (response) {        var downloadFileUrl = response['tuple']['old']['C_MONGODB_FILE']['FILE_PATH'];        if (downloadFileUrl) {            window.open(downloadFileUrl);        }    }).fail(function (returnData) {        alert('附件下载失败!');        console.error('error' + returnData)    });}

  下面示例为下载Webservice代码。

    public static com.unicom.common.attachment.C_MONGODB_FILE getFileByIdImp(String fileId, String gridFSName) {        try {            DB db = MongoDBUtil.getDB();            GridFS gridFS = new GridFS(db, gridFSName);            ObjectId objId = new ObjectId(fileId);            GridFSDBFile gridFSDBFile = (GridFSDBFile) gridFS.findOne(objId);            C_MONGODB_FILE fileDetail = new C_MONGODB_FILE();            if (gridFSDBFile != null) {                SimpleDateFormat sdfFileName = new SimpleDateFormat("yyyy-MM-dd_hh-mm-ss.SSS");                String fileSize = Float.toString(gridFSDBFile.getLength());                String fileType = gridFSDBFile.getContentType();                String fileName = sdfFileName.format(new Date()) + "." + fileType;                InputStream inputStream = gridFSDBFile.getInputStream();                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");                String datePath = sdf.format(new Date());                String downloadPath = EIBProperties.getInstallDir() + File.separator + "webroot" + File.separator + "shared" + File.separator + "download" + File.separator + datePath + File.separator;                String fileFullName = downloadPath + fileName;                String filePath = "/cordys/download/" + datePath + "/" + fileName;                String fileUploader = (String) gridFSDBFile.get("fileUploader");                File file = new File(downloadPath);                if (!file.exists()) {                    file.mkdirs();                }                file = new File(fileFullName);                if (!file.exists()) {                    file.createNewFile();                } else {                    file.delete();                    file.createNewFile();                }                OutputStream outputStream = new FileOutputStream(file);                int byteCount = 0;                while ((byteCount = inputStream.read()) != -1) {                    outputStream.write(byteCount);                }                outputStream.flush();                inputStream.close();                outputStream.close();                fileDetail.setFILE_ID(fileId);                fileDetail.setFILE_NAME(fileName);                fileDetail.setFILE_TYPE(fileType);                fileDetail.setFILE_SIZE(fileSize);                fileDetail.setFILE_CONTENT(filePath);                fileDetail.setFILE_PATH(filePath);                fileDetail.setFILE_UPLOADER(fileUploader);                return fileDetail;            } else {                return null;            }        } catch (UnknownHostException e) {            return null;        } catch (IOException e) {            return null;        } finally {        }    }

  在使用过程中出现如下图性能问题,对于大附件,容易出现下载失败的问题(实质为超时)。
这里写图片描述

分析原因

  在负载均衡集群环境中,并发量大或文件体积大的时候,NFS系统可能存在瓶颈问题,特别是下载操作时,多出一步从MongoDB读取文件写入NFS的过程。如果写入时间过长,则将造成Web服务端请求超时。
  上传附件过程,一般体现不出来。主要是上传附件到缓存NFS后,再写入MongoDB,而此时已经是异步操作,上传的响应已经反馈了。

解决方案

1、临时解决方案,延长Webservice超时时长;

2、优化NFS缓存文件管理,减少“从MongoDB读取文件写入NFS”的次数。优化方案设计图如下:

这里写图片描述

3、每台服务器上都生成可下载的缓存文件,抛弃文件共享盘。

设计思路

  按方案2的设计如下,首先创建个附件缓存表,记录上传、下载过程中的缓存文件,方便多次重复使用文件。

1、附件缓存表结构

字段名称 类型 说明 文件ID 字符 上传时间 时间 为了与上传附件复用 初次下载时间 时间 文件名称 字符 显示出的中文名称,方便维护 文件缓存路径 字符 文件相对路径和名称 组织DN 字符 为了区分租户

  第一次下载失败(超时),但是在异步情况下,缓存文件已经产生,下次下载时,就不必再从MongoDB中读取写到缓存中,提高了系统性能。

2、缓存使用过程。

Created with Raphaël 2.1.0客户端(JS)客户端(JS)WEB服务WEB服务缓存服务缓存服务数据库(MySQL)数据库(MySQL)下载文件按文件ID查询缓存文件查询缓存记录返回缓存文件路径从NFS中下载文件

3、生成缓存并下载过程

Created with Raphaël 2.1.0客户端(JS)客户端(JS)WEB服务WEB服务缓存服务缓存服务文件下载服务文件下载服务MongoDB服务MongoDB服务下载文件未查询到缓存文件按文件ID查询文件读取文件增加/修改缓存数据记录缓存并反馈车票

  欢迎讨论分享。

参考:

集群环境下文件上传方法与运维(Uploading a File to a Service) 肖永威 2016.08

0 0
原创粉丝点击