从零手写实现 nginx-09-compress http 文件压缩

前言

大家好,我是老马。很高兴遇到你。

我们为 java 开发者实现了 java 版本的 nginx

https://github.com/houbb/nginx4j

如果你想知道 servlet 如何处理的,可以参考我的另一个项目:

手写从零实现简易版 tomcat minicat

手写 nginx 系列

如果你对 nginx 原理感兴趣,可以阅读:

从零手写实现 nginx-01-为什么不能有 java 版本的 nginx?

从零手写实现 nginx-02-nginx 的核心能力

从零手写实现 nginx-03-nginx 基于 Netty 实现

从零手写实现 nginx-04-基于 netty http 出入参优化处理

从零手写实现 nginx-05-MIME类型(Multipurpose Internet Mail Extensions,多用途互联网邮件扩展类型)

从零手写实现 nginx-06-文件夹自动索引

从零手写实现 nginx-07-大文件下载

从零手写实现 nginx-08-范围查询

从零手写实现 nginx-09-文件压缩

从零手写实现 nginx-10-sendfile 零拷贝

从零手写实现 nginx-11-file+range 合并

从零手写实现 nginx-12-keep-alive 连接复用

从零手写实现 nginx-13-nginx.conf 配置文件介绍

从零手写实现 nginx-14-nginx.conf 和 hocon 格式有关系吗?

从零手写实现 nginx-15-nginx.conf 如何通过 java 解析处理?

从零手写实现 nginx-16-nginx 支持配置多个 server

什么是 http 压缩

HTTP压缩是一种网络优化技术,用于减少在客户端和服务器之间传输的数据量。

通过压缩响应内容(如HTML、CSS、JavaScript、图片等),可以加快加载速度,减少带宽消耗,提升用户体验。

HTTP压缩通常在服务器端进行,客户端接收到压缩后的数据后进行解压缩。

以下是HTTP压缩的一些关键点:

  1. 压缩算法

    • GZIP:最常用的压缩格式,广泛支持各种压缩级别。
    • DEFLATE:与GZIP类似,但通常不包含文件元数据。
    • Brotli:一种较新的压缩算法,提供比GZIP更好的压缩比率,被现代浏览器支持。
    • 其他:如Zstandard(Zstd),也是一种高效的压缩算法,但浏览器支持度较低。
  2. HTTP头信息

    • Content-Encoding:响应头,指示数据使用的压缩格式。
    • Accept-Encoding:请求头,客户端通过此头通知服务器它支持的压缩格式。
    • Vary:响应头,用于指示响应内容会根据不同的请求头(如Accept-Encoding)而变化。
  3. 服务器配置

    • 服务器需要配置相应的模块或中间件来处理HTTP压缩。例如,在Nginx中,可以使用gzip模块,在Apache中可以使用mod_deflate
  4. 内容类型

    • 并非所有内容类型都适合压缩。文本内容(如HTML、CSS、JavaScript)通常会从压缩中受益,而已经压缩的媒体类型(如JPEG、GIF图片,MP3音频)则不会。
  5. 压缩级别

    • 服务器可以配置不同的压缩级别,以平衡压缩比率和CPU负载。压缩级别越高,压缩比率越好,但CPU负载也越大。
  6. 缓存处理

    • 压缩内容应该被适当地缓存。如果客户端支持条件请求(如ETag或Last-Modified),缓存的压缩内容可以被重复使用。
  7. 安全性

    • 在某些情况下,压缩可能会暴露敏感信息,尤其是当内容是动态生成的。因此,需要确保压缩的内容不会泄露敏感数据。
  8. 配置示例

    • 以下是Nginx配置GZIP压缩的示例:
      gzip on;
      gzip_vary on;
      gzip_proxied any;
      gzip_comp_level 5;
      gzip_min_length 256;
      gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    • 这个配置启用了GZIP压缩,根据Accept-Encoding头变化响应不同的内容,对所有代理请求进行压缩,设置压缩级别为5(快速压缩),只压缩大于256字节的响应,并且指定了可以被压缩的内容类型。
  9. 测试和验证

    • 使用工具(如Chrome开发者工具或在线服务)来测试和验证压缩是否按预期工作。
  10. 客户端支持

    • 客户端(浏览器)必须支持并发送Accept-Encoding头,才能接收压缩内容。

HTTP压缩是一个强大的工具,可以显著提高网站性能。然而,它也增加了服务器的CPU负载,因此需要根据服务器的处理能力和网站的需求来适当配置。

如果一个 txt 文件很大,指定压缩响应。那么 response 中的 Content-Type 应该是什么?响应中会有哪些信息标识压缩相关的?

对于一个大型的文本文件(如.txt),如果服务器配置了压缩响应,那么Content-Type通常设置为text/plain,因为.txt文件是纯文本格式。

当响应被压缩时,响应头中会包含一些标识压缩相关的信息:

  1. Content-Encoding: 这个响应头用于指示响应体使用的压缩格式。例如,如果使用了GZIP压缩,Content-Encoding将被设置为gzip。如果使用了Brotli压缩,它将被设置为br

    Content-Encoding: gzip
  2. VaryVary响应头用于指示响应内容会根据不同的请求头而变化。当启用压缩时,服务器可能会设置Vary头为Accept-Encoding,表示响应内容会根据请求中的Accept-Encoding头变化。

    Vary: Accept-Encoding
  3. Content-LengthContent-Length响应头指示响应体的大小(以字节为单位)。当响应被压缩时,Content-Length将反映压缩后的大小。

    Content-Length: 1200

    注意:在某些情况下,如果服务器使用分块传输编码(chunked transfer encoding),Content-Length可能不会被设置。

当客户端接收到包含Content-Encoding头的响应时,它知道需要对接收到的数据进行解压缩,以恢复原始内容。

以下是一个示例HTTP响应,假设服务器对一个大型文本文件的响应进行了GZIP压缩:

HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Encoding: gzip
Content-Length: 1200
Vary: Accept-Encoding
Date: Wed, 31 Dec 2024 12:00:00 GMT
Server: YourServer/1.0<压缩的响应体>

在这个示例中:

  • Content-Type: text/plain; charset=UTF-8 表示响应体是一个文本文件,使用UTF-8编码。
  • Content-Encoding: gzip 表示响应体被GZIP压缩。
  • Content-Length 表示压缩后的响应体大小。
  • Vary: Accept-Encoding 表示响应内容可能会根据请求头中的Accept-Encoding变化。

客户端在接收到这个响应后,会根据Content-Encoding头来解压缩响应体,然后以原始的文本格式进行处理或显示。

核心实现

/*** 文件压缩** @since 0.8.0* @author 老马啸西风*/
public class NginxRequestDispatchFileCompress extends AbstractNginxRequestDispatchFullResp {private static final Log logger = LogFactory.getLog(AbstractNginxRequestDispatchFullResp.class);@Overrideprotected FullHttpResponse buildFullHttpResponse(FullHttpRequest request,final NginxConfig nginxConfig,NginxRequestDispatchContext context) {final File targetFile = context.getFile();logger.info("[Nginx] match compress file, path={}", targetFile.getAbsolutePath());// 压缩内容byte[] compressData = getCompressData(context);// 创建一个带有GZIP压缩内容的ByteBufByteBuf compressedContent = Unpooled.copiedBuffer(compressData);FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, compressedContent);// 设置压缩相关的响应头response.headers().set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());response.headers().set(HttpHeaderNames.CONTENT_TYPE, InnerMimeUtil.getContentTypeWithCharset(targetFile, context.getNginxConfig().getCharset()));// 检查请求是否接受GZIP编码if (request.headers().contains(HttpHeaderNames.ACCEPT_ENCODING) &&request.headers().get(HttpHeaderNames.ACCEPT_ENCODING).contains(HttpHeaderValues.GZIP)) {// 添加Vary头,告知存在多个版本的响应response.headers().set(HttpHeaderNames.VARY, HttpHeaderNames.ACCEPT_ENCODING);}return response;}public static byte[] getCompressData(NginxRequestDispatchContext context) {final File targetFile = context.getFile();byte[] inputData = FileUtil.getFileBytes(targetFile);// 使用try-with-resources语句自动关闭资源try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) {// 写入要压缩的数据gzipOutputStream.write(inputData);// 强制刷新输出流以确保所有数据都被压缩和写入gzipOutputStream.finish();// 获取压缩后的数据return byteArrayOutputStream.toByteArray();} catch (IOException e) {logger.error("[Nginx] getCompressData failed", e);throw new Nginx4jException(e);}}}

测试

测试代码

public static void main(String[] args) {NginxGzipConfig gzipConfig = new NginxGzipConfig();gzipConfig.setGzip("on");gzipConfig.setGzipMinLength(256);Nginx4jBs.newInstance().nginxGzipConfig(gzipConfig).init().start();
}

页面访问

http://192.168.1.12:8080/c.txt

可以直接返回 c.txt 的内容。

而且后端命中了压缩处理。

[INFO] [2024-05-26 21:01:26.319] [nioEventLoopGroup-3-1] [c.g.h.n.s.r.d.h.AbstractNginxRequestDispatchFullResp.buildFullHttpResponse] - [Nginx] match compress file, path=D:\data\nginx4j\c.txt
[INFO] [2024-05-26 21:01:26.324] [nioEventLoopGroup-3-1] [c.g.h.n.u.InnerMimeUtil.getContentTypeWithCharset] - file=D:\data\nginx4j\c.txt, contentType=text/plain; charset=UTF-8
[INFO] [2024-05-26 21:01:26.327] [nioEventLoopGroup-3-1] [c.g.h.n.s.r.d.h.AbstractNginxRequestDispatchFullResp.doDispatch] - [Nginx] channelRead writeAndFlush DONE response=DefaultFullHttpResponse(decodeResult: success, version: HTTP/1.1, content: UnpooledHeapByteBuf(freed))
HTTP/1.1 200 OK
content-encoding: gzip
content-type: text/plain; charset=UTF-8
vary: accept-encoding
content-length: 696
[INFO] [2024-05-26 21:01:26.328] [nioEventLoopGroup-3-1] [c.g.h.n.s.h.NginxNettyServerHandler.channelRead0] - [Nginx] channelRead writeAndFlush DONE id=40a5effffe257be0-00006394-00000001-0af1170bb1114311-b3ae0da9

http 信息

请求基本信息

Request URL:
http://192.168.1.12:8080/c.txt
Request Method:
GET
Status Code:
200 OK
Remote Address:
192.168.1.12:8080
Referrer Policy:
strict-origin-when-cross-origin

请求头

GET /c.txt HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Host: 192.168.1.12:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36

响应头

HTTP/1.1 200 OK
content-encoding: gzip
content-type: text/plain; charset=UTF-8
vary: accept-encoding
content-length: 696

可见,虽然返回的是 gzip 内容,但是浏览器会自动解压处理。

小结

本节我们实现了文件的压缩处理,这个对于文件的传输性能提升比较大。

当然,压缩+解压本身也是对性能有损耗的。要结合具体的压缩比等考虑。

下一节,我们考虑实现一下 cors 的支持。

我是老马,期待与你的下次重逢。

开源地址

为了便于大家学习,已经将 nginx 开源

https://github.com/houbb/nginx4j

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/23803.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算欧几里得距离

任务描述 本关实现一个函数来计算欧几里得距离。 相关知识 K-means 算法的核心思想是&#xff0c;将数据集中的样本聚类为多个簇集&#xff0c;簇内样本距离较近&#xff0c;簇间样本距离较远。由此可见&#xff0c;其最基本的运算是判断样本&#xff08;如书籍、电影、用户…

澳大利亚和德国媒体投放-国外新闻发稿-海外软文推广

德国媒体 Firmenpresse德国新闻 Firmenpresse德国新闻是一家备受欢迎的新闻发布平台&#xff0c;其好友搜索引擎在收录网站方面表现出色。如果您希望更好地将您的新闻传播给德国受众&#xff0c;Firmenpresse德国新闻将是一个理想的选择。 Frankfurt Stadtanzeiger法兰克福城…

电气灭火产品调查:全氟己酮自自动灭火贴多少钱一个?

根据国家消防救援局于透露&#xff0c;今年年初&#xff0c;河南南阳、江西新余、江苏南京接连发生重大火灾事故&#xff0c;截至日前&#xff0c;全国共接报火灾45万起&#xff0c;住宅、宾馆餐饮店、电动车火灾数量相比去年同期均有所上升。从引 发火灾的原因来看&#xff0c…

k8s-mysql主从部署

一.环境信息 mysql版本 :8.0 k8s 版本1.22 使用nfs作为共享存储 二.配置mysql主节点yaml apiVersion: v1 kind: ConfigMap metadata:name: mysql-master-confignamespace: mysqllabels:app: mysql-master-config data:my.cnf: |[client]default-character-setutf8[mysql]d…

MySQL Doublewrite Buffer 有了解过吗?

引言&#xff1a;在数据库管理中&#xff0c;确保数据的完整性和一致性是至关重要的。然而&#xff0c;在持久化数据到磁盘的过程中&#xff0c;可能会遇到各种意外情况&#xff0c;如断电或系统崩溃&#xff0c;从而导致部分数据写入&#xff0c;而另一部分数据未能成功写入&a…

揭秘!如何从精益生产转向智能制造

企业在“工业4.0、智能制造、互联网”等概念满天飞的环境下迷失了方向&#xff0c;不知该如何下手&#xff0c;盲目跟风。 君不见&#xff0c;很多企业在“工业4.0、智能制造、互联网”等概念满天飞的环境下迷失了方向&#xff0c;不知该如何下手&#xff0c;盲目跟风&#xf…

D365 使用 X++ 设置采购行的财务维度组合

文章目录 前言一、代码 前言 使用 X 设置采购行的财务维度组合 一、代码 PurchLine purchLine;DimensionAttributeValueSetStorage dimensionAttributeValueSetStorage;DimensionAttributeValue dimensionAttributeValue;DimensionAt…

2024下《系统集成项目管理工程师》50个高频考点汇总!值得收藏

宝子们&#xff01;5月软考考完了&#xff0c;终于可以考系统集成了&#xff01; 整理了50个高频考点&#xff0c;涵盖全书90%考点&#xff0c;先把这个存下&#xff01;再慢慢看书&#xff0c;边看书边背这个 1、信息安全的基本要素有&#xff1a; &#xff08;1&#xff09…

迈入智能新纪元:智慧机房运维系统引领行业变革

在数字化飞速发展的今天&#xff0c;机房作为信息时代的“心脏”&#xff0c;其稳定运行对于企业的业务连续性至关重要。然而&#xff0c;传统的机房运维模式面临着诸多挑战&#xff0c;如响应速度慢、故障定位难、资源浪费大等问题。智慧机房运维系统&#xff0c;它将以智能化…

Java【问题 07】SSH不同版本使用jsch问题处理(7.4升级9.7及欧拉原生8.8)

SSH不同版本使用jsch问题处理 1.问题一2.问题二2.1 说明2.2 解决 3.问题三 1.问题一 # 1.系统 cat /etc/os-release # 系统信息 NAME"openEuler" VERSION"22.03 (LTS-SP1)" ID"openEuler" VERSION_ID"22.03" PRETTY_NAME"openEu…

nginx快速删除一行

使用光标定位到要删除的行&#xff0c;连续按两次键盘上的字母d&#xff0c;则可删除&#xff1b; 如果原先在编辑模式下&#xff0c;按esc键退出编辑模式&#xff0c;然后定位到要删除的位&#xff0c;按2次d&#xff0c;则可快速删除&#xff0c;删除后想继续编辑&#xff0…

延时任务工具类

自定义工具类 package com.sxfoundation.task;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.task.TaskRejectedException; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.spri…

重磅:吴恩达最新的机器学习书籍《Machine Learning Yearning》两年磨一剑

《Machine Learning Yearning》是吴恩达历时两年打磨而成的机器学习和深度学习实践宝典。这本书旨在为读者提供实战经验&#xff0c;以帮助他们在机器学习项目中取得成功。 吴恩达通过自身多年的实践经验&#xff0c;为读者提供了宝贵的指导&#xff0c;涵盖了从项目构建到调试…

AndroidX Navigation 反复创建Fragment问题修复

目录 解决办法如何使用参考文档解决办法 自定义 FragmentNavigator,替换系统的,系统对应的 FragmentNavigator Key 为 "fragment",代码如下,可直接拷贝使用。 import android.content.Context import android.util.Log import androidx.annotation.IdRes impor…

ts类型声明文件、内置声明文件

1. ts类型声明文件 在ts中以d.ts为后缀的文件就是类型声明文件&#xff0c;主要作用是为js模块提供类型信息支持&#xff0c;从而获得类型提示 1.1 第三方包用ts编写的&#xff0c;会自动生成一个 .d.ts文件&#xff0c;进行类型声明 1.2 有些包不是用ts编写的&#xff0c;在…

【HashMap】CAS的定义及优缺点

CAS&#xff08;Compare-And-Swap&#xff0c;比较并交换&#xff09;是一种原子操作&#xff0c;用于实现无锁&#xff08;lock-free&#xff09;的并发数据结构。它是现代处理器支持的一种硬件指令&#xff0c;能够保证在多线程环境下进行变量更新时的原子性。CAS 操作包含三…

LangChain真的好用吗?谈一下LangChain封装FAISS的一些坑

前言 最近在做一个知识库问答项目&#xff0c;就是现在大模型浪潮下比较火的 RAG 应用。LangChain 可以说是 RAG 最受欢迎的工具&#xff0c;因此我首选 LangChain 来快速构建我的应用。坦白来讲 LangChain 本身一套对于组件的定义已经让我感觉很复杂&#xff0c;为什么采用 f…

Java Web学习笔记6——盒子模型

视频标签&#xff1a;<video> src: 规定视频的URL controls&#xff1a;显示播放控件 width&#xff1a;播放器的宽度 height&#xff1a;播放器的高度 音频标签&#xff1a;<audio> src: 规定音频的URL controls: 显示播放控件 段落标签&#xff1a;<p&g…

npm yarn 更换国内源以及node历史版本下载地址

npm 更换国内源 npm config set registryhttps://registry.npmmirror.com npm config set electron_mirrorhttps://registry.npmmirror.com/electron/yarn 更换国内源 yarn config set registry https://registry.npmmirror.comnode历史版本下载地址 https://nodejs.org/dow…

Git版本控制:核心概念、操作与实践

Git是一种分布式版本控制系统&#xff0c;被广泛应用于软件开发过程中。本文将介绍Git的核心概念、常用操作以及最佳实践&#xff0c;帮助读者掌握Git的基本技巧&#xff0c;提高团队协作效率。 一、引言 在软件开发过程中&#xff0c;版本控制是至关重要的。它能帮助我们跟踪…