Saat memeriksa log aplikasi saya sendiri, saya menemukan bahwa memuat selalu memerlukan beberapa detik setelah memasuki halaman log (antarmuka tidak diberi halaman), jadi saya membuka panel jaringan untuk memeriksa
Baru kemudian saya menemukan bahwa data yang dikembalikan oleh antarmuka tidak dikompresi. Saya pikir antarmuka tersebut menggunakan proxy terbalik Nginx, dan Nginx akan secara otomatis membantu saya melakukan lapisan ini (saya akan membahasnya nanti, secara teoritis layak untuk
backend
).di sini adalah Layanan Node.
Artikel ini akan berbagi pengetahuan tentang HTTP数据压缩
Node侧的实践
. Semua klien berikut mengacu pada
Saat klien memulai permintaan ke server, klien akan menambahkan bidang accept-encoding
ke header permintaan, yang nilainya支持的压缩内容编码
实际压缩使用的编码算法
dengan menambahkan content-encoding
ke header respons.
deflate
menggunakan algoritma LZ77
dan哈夫曼编码(Huffman Coding)
algoritma kompresi data berdasarkan哈夫曼编码(Huffman Coding)
.
gzip
adalah algoritma berdasarkan DEFLATE
br
mengacu pada Brotli
. Format data ini bertujuan untuk lebih meningkatkan rasio kompresi. Dibandingkan dengan deflate
, kompresi teks dapat meningkatkan kepadatan kompresi sebesar 20%
, sementara kecepatan kompresi dan dekompresinya kira-kira tidak berubah
Node.js berisi zlib 模块
, yang menyediakan fungsi kompresi yang diimplementasikan menggunakan Gzip
, Deflate/Inflate
, dan Brotli
Deflate/Inflate
Brotli
, gzip
digunakan sebagai contoh untuk membuat daftar berbagai metode penggunaan sesuai dengan skenario dengan cara yang sama, tetapi API
didasarkan pada stream
yang beroperasi
Operasi berbasis buffer
Perkenalkan beberapa modul yang diperlukan
const zlib = require('zlib') const fs = memerlukan('fs') const aliran = memerlukan('aliran') const testFile = 'tes/asal.log' const targetFile = `${testFile}.gz` Const decodeFile = `${testFile}.un.gz`tampilan hasil
, disini gunakan perintah du
untuk langsung menghitung hasil sebelum dan sesudah dekompresi
# Jalankan tes du -ah # Hasilnya sebagai berikut 108K tes/origin.log.gz 2,2 juta tes/asal.log 2,2 juta tes/origin.log.un.gz 4.6M menguji
流(stream)
menggunakan createGzip
dan createUnzip
zlib
, kecuali yang sinkron secara eksplisit, menggunakan kumpulan thread internal Node.js dan dapat dianggap asinkronMetode 1: Langsung gunakan metode pipe
pada instance untuk mentransfer aliran
// Kompresi const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) readStream.pipe(zlib.createGzip()).pipe(writeStream) // Dekompresi const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) readStream.pipe(zlib.createUnzip()).pipe(writeStream)
Metode 2: Menggunakan pipeline
pada stream
, Anda dapat melakukan pemrosesan lain secara terpisah di callback
// Compression const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) stream.pipeline(readStream, zlib.createGzip(), writeStream, err => { jika (salah) { konsol.kesalahan(err); } }) // Dekompresi const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) stream.pipeline(readStream, zlib.createUnzip(), writeStream, err => { jika (salah) { konsol.kesalahan(err); } })
Metode 3: Metode pipeline
janji
const { berjanji } = memerlukan('util') const pipeline = menjanjikan(stream.pipeline) // Kompres const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) saluran pipa(readStream, zlib.createGzip(), writeStream) .menangkap(err => { konsol.kesalahan(err); }) // Dekompresi const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) saluran pipa(readStream, zlib.createUnzip(), writeStream) .menangkap(err => { konsol.kesalahan(err); })
Buffer
menggunakan API gzip
dan unzip
. Kedua metode ini mencakup kompresi tipe同步
dan异步
gzip
gzipSync
unzip
unzipSync
metode 1: Konversi readStream
ke Buffer
, lalu lakukan operasi lebih lanjut
// Kompresi const buff = [] readStream.on('data', (potongan) => { buff.push(potongan) }) readStream.on('akhir', () => { zlib.gzip(Buffer.concat(buff), targetFile, (err, resBuff) => { jika(salah){ konsol.kesalahan(err); proses.keluar() } fs.writeFileSync(targetFile,resBuff) }) })
// kompresi const buff = [] readStream.on('data', (potongan) => { buff.push(potongan) }) readStream.on('akhir', () => { fs.writeFileSync(targetFile,zlib.gzipSync(Buffer.concat(buff))) })
Metode 2: Baca langsung melalui readFileSync
// Const terkompresi readBuffer = fs.readFileSync(testFile) const decodeBuffer = zlib.gzipSync(readBuffer) fs.writeFileSync(targetFile,decodeBuffer) // Dekompresi const readBuffer = fs.readFileSync(targetFile) const decodeBuffer = zlib.gzipSync(decodeFile) fs.writeFileSync(targetFile,decodeBuffer)
Selain kompresi file, terkadang diperlukan dekompresi langsung konten yang dikirimkan.
Berikut adalah konten teks terkompresi sebagai contoh
// Data pengujian const testData = fs. readFileSync( testFile, { coding: 'utf-8' })
流(stream)
, pertimbangkan saja konversi string
=> buffer
=> stream
string
=> buffer
const buffer = Buffer.from(testData)
buffer
=> stream
const transformStream = aliran baru.PassThrough() transformStream.write(buffer) // atau const transformStream = aliran baru.Duplex() transformStream.push(Buffer.from(testData)) transformStream.push(null)
Berikut adalah contoh penulisan ke sebuah file. Tentu saja, ini juga dapat ditulis ke stream lain, seperti HTTP的Response
(akan diperkenalkan secara terpisah nanti)
transformStream. .pipe(zlib.createGzip()) .pipe(fs.createWriteStream(targetFile))
juga menggunakan Buffer.from
untuk mengubah string menjadi buffer
Buffer
const buffer = Buffer.from(testData)
dan kemudian langsung menggunakan API sinkronisasi untuk konversi content
const result = zlib.gzipSync(buffer)
dapat menulis file, dan di HTTP Server
konten terkompresi juga dapat dikembalikan secara langsung
fs.writeFileSync(targetFile, result)
Di sini kita langsung menggunakan modul http
di Node untuk membuat Server sederhana Untuk demonstrasi
dalam kerangka Node Web
lainnya, ide pemrosesannya serupa. Tentu saja, umumnya ada plug-in siap pakai yang dapat diakses dengan satu klik.
const http = memerlukan('http') const { PassThrough, saluran pipa } = memerlukan('aliran') const zlib = memerlukan('zlib') //Data pengujian const testTxt = 'Data pengujian 123'.repeat(1000) const app = http.createServer((req, res) => { const { url } = persyaratan // Baca algoritme kompresi yang didukung const AcceptEncoding = req.headers['accept-encoding'].match(/(br|deflate|gzip)/g) //Tipe data respons default res.setHeader('Tipe Konten', 'application/json; charset=utf-8') // Beberapa contoh rute const rute = [ ['/gzip', () => { if (acceptEncoding.includes('gzip')) { res.setHeader('pengkodean konten', 'gzip') // Gunakan API sinkronisasi untuk mengompres konten teks secara langsung res.end(zlib.gzipSync(Buffer.from(testTxt))) kembali } res.end(testTxt) }], ['/kempis', () => { if (acceptEncoding.includes('deflate')) { res.setHeader('pengkodean konten', 'kempis') // Operasi tunggal berdasarkan aliran const originStream = new PassThrough() originStream.write(Buffer.from(testTxt)) originStream.pipe(zlib.createDflate()).pipe(res) aliran asal.akhir() kembali } res.end(testTxt) }], ['/br', () => { if (acceptEncoding.includes('br')) { res.setHeader('pengkodean konten', 'br') res.setHeader('Jenis Konten', 'teks/html; charset=utf-8') // Beberapa operasi penulisan berdasarkan aliran const originStream = new PassThrough() pipa(originStream, zlib.createBrotliCompress(), res, (err) => { jika (salah) { konsol.kesalahan(err); } }) originStream.write(Buffer.from('<h1>BrotliCompress</h1>')) originStream.write(Buffer.from('<h2>Data pengujian</h2>')) originStream.write(Buffer.from(testTxt)) aliran asal.akhir() kembali } res.end(testTxt) }] ] rute const = rute.find(v => url.startsWith(v[0])) jika (rute) { rute[1]() kembali } // Kembali ke atas res.setHeader('Tipe Konten', 'teks/html; charset=utf-8') res.end(`<h1>404: ${url}</h1> <h2>Rute terdaftar</h2> <ul> ${routes.map(r => `<li><a href="${r[0]}">${r[0]}</a></li>`).join('') } </ul> `) res.end() }) aplikasi.dengarkan(3000)