百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

API接口优化!基于Spring Boot 实现Deflate压缩技术

lipiwang 2025-05-26 17:16 14 浏览 0 评论

环境:SpringBoot3.2.5



1. 简介

一个应用的性能是决定用户体验好坏的关键因素。提高性能的最有效方法之一是减少服务器与客户端之间传输的数据大小。这正是压缩技术发挥作用的地方。Spring Boot 提供了对各种压缩技术的内置支持,以优化数据传输。

在本篇文章中,我们将探讨Deflate压缩,包括它为什么重要、何时使用它以及如何在Spring Boot应用程序中实现它。通过本篇文章你将清楚地了解如何使用Deflate压缩来优化应用程序的性能。

1.1 什么是Deflate压缩?

Deflate是一种无损数据压缩算法,它结合了LZ77算法和霍夫曼编码来减小数据的大小。在Web应用程序中,Deflate被广泛用于在向客户端发送HTTP响应之前对其进行压缩。

当客户端(例如浏览器或API使用者)从服务器请求数据时,服务器可以使用Deflate对响应进行压缩,从而减小通过网络传输的数据大小。客户端在接收到数据后进行解压缩。

1.2 为什么使用Deflate压缩?

  • 提高性能
  • 网络上的数据传输速度更快。延迟降低,特别是对于使用慢速或带宽有限的连接的用户而言。
  • 节省带宽
  • 压缩减少了通过网络发送的数据量,这对于流量高或负载大的应用程序(例如,JSON或XML响应)非常重要。
  • 提升用户体验
  • 更快的响应时间带来更好的用户体验,特别是对于移动用户或从远程位置访问的应用程序的用户而言。

1.3 应用场景

  • 响应结果很大
  • 如果你的API返回大的JSON或XML响应,压缩数据可以显著减少响应时间。
  • 静态内容
  • 压缩HTML、CSS和JavaScript文件等静态资源可以改善页面加载时间

注意:如果对应响应结果比较小的(小于2kb)时候反而使用压缩技术会对性能造成影响。

2. 实战案例

2.1 Deflate过滤器

public class DeflateCompressionFilter implements Filter {


  private static final int MIN_RESPONSE_SIZE = 2 * 1024 ;


  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    HttpServletResponse resp = (HttpServletResponse) response;


    String acceptEncoding = req.getHeader("Accept-Encoding");
    // 这里我们更加请求header中的Accept-Encoding进行判断,只有包含指定的值才进行处理
    if (acceptEncoding == null || !acceptEncoding.toLowerCase().contains("deflate")) {
      chain.doFilter(request, response);
      return;
    }
    // 自定义Response包装类,我们需要对响应结果进行获取处理
    DeflateResponseWrapper responseWrapper = new DeflateResponseWrapper(resp);


    chain.doFilter(request, responseWrapper);
    // 只有响应的数据大小超过这里指定的值(2KB)才进行压缩处理
    if (responseWrapper.getContentLength() > MIN_RESPONSE_SIZE) {
      // 必须设置,否则客户端将无法解析解压数据
      resp.setHeader("Content-Encoding", "deflate");
      try (DeflaterOutputStream dos= new DeflaterOutputStream(resp.getOutputStream())) {
        dos.write(responseWrapper.getCapturedData());
      }
    } else {
      // Write the uncompressed response
      resp.getOutputStream().write(responseWrapper.getCapturedData());
    }
  }
}

关键的注释已经在源码中进行了处理。

注意:这里没有判断响应数据的类型可以根据Content-Type进行判断。

2.2 Response包装类

public class DeflateResponseWrapper extends HttpServletResponseWrapper {


  private final ByteArrayOutputStream capture;
  private ServletOutputStream outputStream;
  private PrintWriter writer;


  public DeflateResponseWrapper(HttpServletResponse response) {
    super(response);
    capture = new ByteArrayOutputStream() ;
  }
  public ServletOutputStream getOutputStream() {
    if (writer != null) {
      throw new IllegalStateException("Writer already in use");
    }
    if (outputStream == null) {
      outputStream = new ServletOutputStream() {
        public void write(int b) throws IOException {
          capture.write(b);
        }
        public void flush() throws IOException {
          capture.flush();
        }
        public void close() throws IOException {
          capture.close();
        }
        public boolean isReady() {
          return true;
        }
        public void setWriteListener(WriteListener writeListener) {
        }
      };
    }
    return outputStream;
  }
  public PrintWriter getWriter() {
    if (outputStream != null) {
      throw new IllegalStateException("OutputStream already in use");
    }
    if (writer == null) {
      writer = new PrintWriter(capture);
    }
    return writer;
  }
  public byte[] getCapturedData() {
    return capture.toByteArray();
  }
  public int getContentLength() {
    return capture.size();
  }
}

我们需要将数据先写入到内存输出流中,这样我们才能得到当前写入到响应流中的数据。

2.3 注册过滤器

在Spring Boot中我们可以通过如下方式注册过滤器,也可以通过@WedFilter的方式注册。

@Bean
FilterRegistrationBean<DeflateCompressionFilter> deflateCompressionFilter() {
  FilterRegistrationBean<DeflateCompressionFilter> registrationBean = new FilterRegistrationBean<>();
  registrationBean.setFilter(new DeflateCompressionFilter());
  // 对所有的请求都进行过去了处理
  registrationBean.addUrlPatterns("/*") ;
  registrationBean.setName("DeflateCompressionFilter") ;
  registrationBean.setOrder(1) ;
  return registrationBean ;
}

以上我们就完成了所有的代码,接下来我们进行测试。

2.4 测试

接下来,我们通过如下接口进行测试:

@GetMapping("/data")
public List<User> getData() {
  List<User> data = new ArrayList<>() ;
  for (long i = 0; i < 10000; i++) {
    data.add(new User(i, "姓名 - " + i, new Random().nextInt(100))) ;
  }
  return data;
}
public static record User (Long id, String name, Integer age) {}

首先,我们将请求的Accept-Encoding随意写一个值,响应结果

最后,我们在将Accept-Encoding设置为deflate,响应结果:

与压缩前相比:压缩了近6.7倍

显著提升应用程序的性能,减少带宽使用,并增强用户体验。

注意:你完全可以使用GZIP进行压缩,并且使用GZIP也是当前最为推荐流行的方式,并且兼容性要比deflate好。

如果启用GZIP?如下配置即可:

server:
  compression:
    enabled: true
    min-response-size: 1024
    mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml

而本篇文章的目标是让你了解这压缩技术的实现原理。而在上面自定义的过滤器中,我们也完全可以使用GZIP对应的输出流进行压缩数据,如下代码:

if (responseWrapper.getContentLength() > MIN_RESPONSE_SIZE) {
  // 设置响应的内容编码类型为gzip
  resp.setHeader("Content-Encoding", "gzip");
  // 使用gzip进行压缩数据
  try (GZIPOutputStream gos = new GZIPOutputStream(resp.getOutputStream())) {
    gos.write(responseWrapper.getCapturedData()) ;
  }
} else {
  resp.getOutputStream().write(responseWrapper.getCapturedData());
}

相关推荐

linux实例之设置时区的方式有哪些

linux系统下的时间管理是一个复杂但精细的功能,而时区又是时间管理非常重要的一个辅助功能。时区解决了本地时间和UTC时间的差异,从而确保了linux系统下时间戳和时间的准确性和一致性。比如文件的时间...

Linux set命令用法(linux cp命令的用法)

Linux中的set命令用于设置或显示系统环境变量。1.设置环境变量:-setVAR=value:设置环境变量VAR的值为value。-exportVAR:将已设置的环境变量VAR导出,使其...

python环境怎么搭建?小白看完就会!简简单单

很多小伙伴安装了python不会搭建环境,看完这个你就会了Python可应用于多平台包括Linux和MacOSX。你可以通过终端窗口输入"python"命令来查看本地是否...

Linux环境下如何设置多个交叉编译工具链?

常见的Linux操作系统都可以通过包管理器安装交叉编译工具链,比如Ubuntu环境下使用如下命令安装gcc交叉编译器:sudoapt-getinstallgcc-arm-linux-gnueab...

JMeter环境变量配置技巧与注意事项

通过给JMeter配置环境变量,可以快捷的打开JMeter:打开终端。执行jmeter。配置环境变量的方法如下。Mac和Linux系统在~/.bashrc中加如下内容:export...

C/C++|头文件、源文件分开写的源起及作用

1C/C++编译模式通常,在一个C++程序中,只包含两类文件——.cpp文件和.h文件。其中,.cpp文件被称作C++源文件,里面放的都是C++的源代码;而.h文件则被称...

linux中内部变量,环境变量,用户变量的区别

unixshell的变量分类在Shell中有三种变量:内部变量,环境变量,用户变量。内部变量:系统提供,不用定义,不能修改环境变量:系统提供,不用定义,可以修改,可以利用export将用户变量转为环...

在Linux中输入一行命令后究竟发生了什么?

Linux,这个开源的操作系统巨人,以其强大的命令行界面而闻名。无论你是初学者还是经验丰富的系统管理员,理解在Linux终端输入一条命令并按下回车后发生的事情,都是掌握Linux核心的关键。从表面上看...

Nodejs安装、配置与快速入门(node. js安装)

Nodejs是现代JavaScript语言产生革命性变化的一个主要框架,它使得JavaScript从一门浏览器语言成为可以在服务器端运行、开发各种各样应用的通用语言。在不同的平台下,Nodejs的安装...

Ollama使用指南【超全版】(olaplex使用方法图解)

一、Ollama快速入门Ollama是一个用于在本地运行大型语言模型的工具,下面将介绍如何在不同操作系统上安装和使用Ollama。官网:https://ollama.comGithub:http...

linux移植(linux移植lvgl)

1uboot移植l移植linux之前需要先移植一个bootlader代码,主要用于启动linux内核,lLinux系统包括u-boot、内核、根文件系统(rootfs)l引导程序的主要作用将...

Linux日常小技巧参数优化(linux参数调优)

Linux系统参数优化可以让系统更加稳定、高效、安全,提高系统的性能和使用体验。下面列出一些常见的Linux系统参数优化示例,包括修改默认配置、网络等多方面。1.修改默认配置1.1修改默认编辑器默...

Linux系统编程—条件变量(linux 条件变量开销)

条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等...

面试题-Linux系统优化进阶学习(linux系统的优化)

一.基础必备优化:1.关闭SElinux2.FirewalldCenetOS7Iptables(C6)安全组(阿里云)3.网络管理服务||NetworkManager|network...

嵌入式Linux开发教程:Linux Shell

本章重点介绍Linux的常用操作和命令。在介绍命令之前,先对Linux的Shell进行了简单介绍,然后按照大多数用户的使用习惯,对各种操作和相关命令进行了分类介绍。对相关命令的介绍都力求通俗易懂,都给...

取消回复欢迎 发表评论: