자체 애플리케이션 로그를 확인해보니 로그 페이지에 들어간 후 로드하는 데 항상 몇 초가 걸리는 것을 발견했기 때문에(인터페이스는 페이지 매김이 없음) 네트워크 패널을 열어서 확인했습니다.
그제서야 인터페이스에서 반환된 데이터가 압축되지 않았다는 것을 알았습니다. 인터페이스가 Nginx 리버스 프록시를 사용했고 Nginx가 자동으로 이 레이어를 수행하는 데 도움이 될 것이라고 생각했습니다. 이 내용은 나중에 살펴보겠습니다.
백엔드는
가능합니다.여기 Node.Service가 있습니다.
이 기사에서는 HTTP 데이터 압축에 대한 지식과 Node 측의 실제HTTP数据压缩
Node侧的实践
. 다음 클라이언트는 모두 브라우저
클라이언트가 서버에 대한 요청을 시작할 때 요청 헤더에 accept-encoding
필드를 추가합니다. 이 필드의 값支持的压缩内容编码
응답 헤더에 content-encoding
추가하여 콘텐츠의实际压缩使用的编码算法
LZ77
deflate
哈夫曼编码(Huffman Coding)
哈夫曼编码(Huffman Coding)
기반의 데이터 압축 알고리즘 .
gzip
은 DEFLATE
기반으로 한 알고리즘입니다
. br
Brotli
를 참조합니다. 이 데이터 형식은 압축 비율을 더욱 향상시키는 것을 목표로 합니다. deflate
와 비교하여 텍스트 압축은 압축 밀도를 20%
증가시킬 수 있지만 압축 및 압축 해제 속도는 거의 변하지 않습니다
에는 Gzip
, Deflate/Inflate
및 Brotli를 사용하여 구현된 압축 기능을 제공하는 zlib 模块
포함되어 있습니다 Brotli
Deflate/Inflate
Brotli
에 따른 다양한 사용 방법을 나열하기 위해 gzip
사용합니다. 같은 방식이지만 API는
stream
기반으로 합니다.
buffer
기반 작업
여러 필수 모듈 소개
const zlib = require('zlib') const fs = 요구('fs') const 스트림 = require('스트림') const testFile = '테스트/origin.log' const targetFile = `${testFile}.gz` Const decodeFile = `${testFile}.un.gz`
/압축 결과 보기, 여기에서는 du
명령을 사용하여 압축 풀기 전후의 결과를 직접 계산합니다.
# du -ah 테스트 실행 # 결과는 다음과 같습니다. 108K test/origin.log.gz 220만 개의 테스트/origin.log 220만 개의 테스트/origin.log.un.gz 4.6M은
createGzip
및 createUnzip
사용하여
流(stream)
기반 작업을zlib
API는 Node.js 내부 스레드 풀을 사용하고 비동기식으로 간주될 수 있으므로방법 1: 인스턴스에서 pipe
메서드를 직접 사용하여 스트림을 전송합니다.
// 압축 const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) readStream.pipe(zlib.createGzip()).pipe(writeStream) // 압축 해제 const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) readStream.pipe(zlib.createUnzip()).pipe(writeStream)
방법 2: stream
의 pipeline
사용하면 콜백에서 별도로 다른 처리를 수행할 수 있습니다.
// 압축 const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) stream.pipeline(readStream, zlib.createGzip(), writeStream, err => { 만약 (오류) { console.error(err); } }) // 압축 해제 const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) stream.pipeline(readStream, zlib.createUnzip(), writeStream, err => { 만약 (오류) { console.error(err); } })
방법 3: Promise pipeline
방법
const { promisify } = require('util') const 파이프라인 = promisify(stream.pipeline) // 압축 const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) 파이프라인(readStream, zlib.createGzip(), writeStream) .catch(err => { console.error(err); }) // 압축 해제 const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) 파이프라인(readStream, zlib.createUnzip(), writeStream) .catch(err => { console.error(err); })
Buffer
기반 작업은gzip
및 unzip
API를 활용합니다. 이 두 가지 방법에는同步
및异步
유형
gzip
gzipSync
unzip
unzipSync
방법 1: readStream
Buffer
로 변환한 다음 추가 작업을 수행합니다.
// Compression const buff = [] readStream.on('데이터', (청크) => { 버프.푸시(청크) }) readStream.on('end', () => { zlib.gzip(Buffer.concat(buff), targetFile, (err, resBuff) => { 만약(오류){ console.error(err); 프로세스.종료() } fs.writeFileSync(대상파일,resBuff) }) })
// 압축 const buff = [] readStream.on('데이터', (청크) => { 버프.푸시(청크) }) readStream.on('end', () => { fs.writeFileSync(targetFile,zlib.gzipSync(Buffer.concat(buff))) })
방법 2: readFileSync
통해 직접 읽기
// 압축 const readBuffer = fs.readFileSync(testFile) const decodeBuffer = zlib.gzipSync(readBuffer) fs.writeFileSync(대상파일,decodeBuffer) // 압축 해제 const readBuffer = fs.readFileSync(targetFile) const decodeBuffer = zlib.gzipSync(decodeFile) fs.writeFileSync(targetFile,decodeBuffer)
파일 압축 외에도 전송된 내용을 직접 압축 해제해야 하는 경우가 있습니다.
압축된 텍스트 내용을 예로 들어 보겠습니다.
// 테스트 데이터 const testData = fs. readFileSync( testFile, { 인코딩: 'utf-8' })
流(stream)
작업을 기반으로string
=> buffer
=> stream
string
=> buffer
const buffer = Buffer.from(testData)
buffer
=> stream
const 변환Stream을고려해보세요.
= 새 스트림.PassThrough() 변환스트림.쓰기(버퍼) // 또는 const 변환스트림 = 새 스트림.듀플렉스() TransformStream.push(Buffer.from(testData)) 변환Stream.push(null)
다음은 파일에 쓰는 예입니다. 물론 HTTP的Response
(나중에 별도로 소개할 예정)과 같은 다른 스트림에도 쓸 수 있습니다
. .pipe(zlib.createGzip()) .pipe(fs.createWriteStream(targetFile))은또한 Buffer.from을 사용하여 Buffer 작업
const buffer = Buffer.from(testData)을
Buffer
으로Buffer.from
을 buffer
로 변환한
다음 변환을 위해 동기화 API를 직접 사용합니다. content
const result = zlib.gzipSync(buffer)는
파일을 쓸 수 있고, HTTP Server
에서는 압축된 내용을 직접 반환할 수도 있습니다.
fs.writeFileSync(targetFile, result)
여기서는 Node에서 http
모듈을 직접 사용하여 생성합니다. 간단한 서버
다른 Node Web
프레임워크에서의 시연의 경우 처리 아이디어는 비슷합니다. 물론 일반적으로 한 번의 클릭으로 액세스할 수 있는 기성 플러그인이 있습니다.
const http = 요구('http') const { 패스스루, 파이프라인 } = require('스트림') const zlib = 요구('zlib') //테스트 데이터 const testTxt = '테스트 데이터 123'.repeat(1000) const 앱 = http.createServer((req, res) => { const { URL } = 요청 // 지원되는 압축 알고리즘 읽기 const acceptEncoding = req.headers['accept-encoding'].match(/(br|deflate|gzip)/g) //기본 응답 데이터 유형 res.setHeader('Content-Type', 'application/json; charset=utf-8') // 여러 예제 경로 const 경로 = [ ['/gzip', () => { if (acceptEncoding.includes('gzip')) { res.setHeader('content-encoding', 'gzip') // 동기화 API를 사용하여 텍스트 콘텐츠를 직접 압축합니다. res.end(zlib.gzipSync(Buffer.from(testTxt))) 반품 } res.end(testTxt) }], ['/수축', () => { if (acceptEncoding.includes('deflate')) { res.setHeader('content-encoding', 'deflate') // 스트림을 기반으로 한 단일 작업 const OriginStream = new PassThrough() OriginStream.write(Buffer.from(testTxt)) OriginStream.pipe(zlib.createDeflate()).pipe(res) OriginStream.end() 반품 } res.end(testTxt) }], ['/br', () => { if (acceptEncoding.includes('br')) { res.setHeader('content-encoding', 'br') res.setHeader('Content-Type', 'text/html; charset=utf-8') // 스트림을 기반으로 한 다중 쓰기 작업 const OriginStream = new PassThrough() 파이프라인(originStream, zlib.createBrotliCompress(), res, (err) => { 만약 (오류) { console.error(err); } }) OriginStream.write(Buffer.from('<h1>BrotliCompress</h1>')) OriginStream.write(Buffer.from('<h2>테스트 데이터</h2>')) OriginStream.write(Buffer.from(testTxt)) OriginStream.end() 반품 } res.end(testTxt) }] ] const 경로 = 경로.찾기(v => url.startsWith(v[0])) if (경로) { 경로[1]() 반품 } // 맨 위로 res.setHeader('Content-Type', 'text/html; charset=utf-8') res.end(`<h1>404: ${url}</h1> <h2>등록된 경로</h2> <ul> ${routes.map(r => `<li><a href="${r[0]}">${r[0]}</a></li>`).join('') } </ul> `) res.end() }) app.listen(3000)