环境: Nodejs/express + Apache
如题,如果在后端的express server中,需要读取server上的大文件或者需要返回的数据为比较大的对象时(比如几十M的数据时),考虑用stream/pipe的方式处理data,流方式提供一些实用的高效的工具来处理比较大的data的处理,之后可以直接输出给response; 但是需要了解的是,最好是以压缩比如gzip或者deflate的方式返回给浏览器。
或者请求的所有数据统统以压缩的形式返回给浏览器,所以最主要的前提是,根据用户的浏览器判断,当前这个client端的browser的请求是支持哪种类型的解压缩,比如gzip或者是deflate等,这里压缩的倍率大概为,压缩前: 50M左右, 压缩后3M左右,基本会有10倍多的压缩率,所以在网络间传输时性能会有很大提升.
这里的例子是用nodejs的,我理解用其他的语言比如java,道理也是一样的,语言只是封装和实现了tcp,http协议,优化的最后都是要了解到这些。
>>为什么要压缩,这里讲的很清楚 --> https://blog.csdn.net/liangxw1/article/details/84835199
// 用stream/pipe的方式返回data在response里,需要在http的response header中的 Accept-Encoding: gzip或者deflate,可以做到在server端压缩,在browser client端解压缩data并显示. 代码如下
module.exports.getTest = function (req, res) {
// A
// filepath, 这里filepath路径对应的文件为 *.json.gz的类型
// 注,这里因为已经把json数据压缩成*.json.gz了,所以直接用stream/pipe读到response返回即可,
// 因为浏览器自己可以解析(解压缩)*.json.gz文件为json数据
//
// 这里我认为是这样的: 当需要处理读取大文件或者比较大的数据时,用正常的res.json(data)返回数据,这可能会有性能问题,
// 但是用stream/pipe这种流的方式处理大文件或者数据时,是有优势的,可以提高处理性能,
// 但是,这种流的方式放到response后,只用如下 Apache的配置并没有起到gzip或者deflate的压缩效果,也许需要如下设置??还不确定,***有时间在本机测试一下***
// AddOutputFilterByType DEFLATE application/octet-stream??
// 所以这里就 显示的 用如下的res.writeHead的方式手动设置content-encoding,即可让这里压缩的*.json.gz传输给浏览器并解压缩,的节省带宽,提高性能的效果
fs.exists(filepath, function (exists) {
if (exists === true) {
const srcReadStream = fs.createReadStream(filepath);
// Success with response body
// res.status(200);
// 注,这里只是例子代码,这里应该根据用户的浏览器所支持的解压缩类型是gzip还是deflate来设置content-encoding
res.writeHead(200, { 'content-encoding': 'gzip' });
srcReadStream.pipe(res);
srcReadStream.on('close', function () {
// It can go here after close event
srcReadStream.destroy();
});
srcReadStream.on('error', function (err) {
srcReadStream.destroy();
});
} else {
...
}
});
}
查看相关资料,让数据在返回的response中以压缩的形式返回,至少可以在如下两个地方做:
在nodejs/express中,compression这个插件可以做到压缩后在传输给client browser. 之后browser可以正确解压缩并显示
但是这种方式,个人不是很推荐,因为对nodejs来说大量的处理压缩的动作可能会影响性能(??),另外如果可以在Apache服务器上做,应该会更好,首先对nodejs的压力小,代码不用改,侵入性小,在Apache上配置也相对灵活
2. 在Apache服务器上,启用如下配置
https://httpd.apache.org/docs/2.4/mod/mod_deflate.html
https://varvy.com/pagespeed/enable-compression.html
LoadModule deflate_module modules/mod_deflate.so
// 下面的各项需要压缩什么类型的数据就打开相应的行
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html text/plain text/xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
// 下面的配置,暂时不理解,待深入调查??
DeflateFilterNote Input instream
DeflateFilterNote Output outstream
DeflateFilterNote Ratio ratio
如果不用stream/pipe的方式返回data在response,而是用expess的res.json(data), 用上面的#1,#2两种方式都可以做到客户端和server端压缩数据在传输和解压缩的效果.
下面这种方式的stream/pipe方式是可以的,这个类似于上面第一种方式
// 参考: https://stackoverflow.com/questions/3894794/node-js-gzip-compression
// server example
// Running a gzip operation on every request is quite expensive.
// It would be much more efficient to cache the compressed buffer.
var zlib = require('zlib');
var http = require('http');
var fs = require('fs');
http.createServer(function(request, response) {
var raw = fs.createReadStream('sample-drag.html');
var acceptEncoding = request.headers['accept-encoding'];
if (!acceptEncoding) {
acceptEncoding = '';
}
// Note: this is not a conformant accept-encoding parser.
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
if (acceptEncoding.match(/\bgzip\b/)) {
response.writeHead(200, { 'content-encoding': 'gzip' });
raw.pipe(zlib.createGzip()).pipe(response);
} else if (acceptEncoding.match(/\bdeflate\b/)) {
response.writeHead(200, { 'content-encoding': 'deflate' });
raw.pipe(zlib.createDeflate()).pipe(response);
} else {
response.writeHead(200, {});
raw.pipe(response);
}
}).listen(1337);
// 另一个例子
exports.helloWorld = function helloWorld(req, res) {
const zlib = require('zlib');
// Obtain JSON stream from your source...
res.status(200);
res.set('Content-Type', 'text/plain');
res.set('Content-Encoding', 'gzip');
json.pipe(zlib.createGzip()).pipe(res);
};