Menggunakan kompresi GZIP dengan Spring Boot / MVC / JavaConfig dengan RESTful

96

Kami menggunakan Spring Boot / MVC dengan java-config berbasis anotasi untuk rangkaian RESTfullayanan dan kami ingin mengaktifkan HTTP GZIPkompresi aliran secara selektif pada beberapa respons API.

Saya tahu saya dapat melakukan ini secara manual di controller dan a byte[] @ResponseBody, namun kami lebih suka mengandalkan infrastruktur SpringMVC (filter / dll) dan secara otomatis melakukan konversi dan kompresi JSON (yaitu metode mengembalikan POJO).

Bagaimana cara mengaktifkan kompresi GZIP di ResponseBody atau instans Tomcat yang disematkan, dan dengan cara yang kami dapat mengompresi hanya beberapa respons secara selektif?

Terima kasih!

NB: Saat ini kami tidak memiliki konfigurasi berbasis XML.

pengguna3182614
sumber
Anda harus memeriksa GzipFilter .
kira
2
jangan gunakan kompresi HTTP dengan HTTPS kecuali Anda tahu apa yang Anda lakukan
Neil McGuigan

Jawaban:

189

Sisa dari jawaban ini sudah ketinggalan zaman dan / atau terlalu rumit untuk sesuatu yang seharusnya IMO sederhana (berapa lama gzip telah ada sekarang? Lebih lama dari Java ...) Dari dokumen:

Dalam application.properties 1.3+

# 🗜️🗜️🗜️
server.compression.enabled=true
# opt in to content types
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css
# not worth the CPU cycles at some point, probably
server.compression.min-response-size=10240 

Dalam application.properties 1.2.2 - <1.3

server.tomcat.compression=on
server.tomcat.compressableMimeTypes=application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css

Lebih tua dari 1.2.2:

@Component
public class TomcatCustomizer implements TomcatConnectorCustomizer {

  @Override
  public void customize(Connector connector) {
    connector.setProperty("compression", "on");
    // Add json and xml mime types, as they're not in the mimetype list by default
    connector.setProperty("compressableMimeType", "text/html,text/xml,text/plain,application/json,application/xml");
  }
}

Perhatikan juga ini HANYA akan berfungsi jika Anda menjalankan tomcat tertanam:

Jika Anda berencana untuk menerapkan ke kucing jantan yang tidak tertanam, Anda harus mengaktifkannya di server.xml http://tomcat.apache.org/tomcat-9.0-doc/config/http.html#Standard_Implementation

Catatan Produksi IRL:

Juga untuk menghindari semua ini, pertimbangkan untuk menggunakan pengaturan proxy / load balancer di depan Tomcat dengan nginx dan / atau haproxy atau serupa karena ini akan menangani aset statis dan gzip JAUH lebih efisien dan mudah daripada model threading Java / Tomcat.

Anda tidak ingin melempar 'cat ke dalam bak mandi karena sibuk mengompresi barang alih-alih melayani permintaan (atau lebih mungkin memutar utas / memakan CPU / tumpukan duduk menunggu IO database terjadi saat menjalankan tagihan AWS Anda yang mengapa Java / Tomcat tradisional mungkin bukan ide yang baik untuk memulai tergantung pada apa yang Anda lakukan tetapi saya ngelantur ...)

referensi: https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/howto.html#how-to-enable-http-response-compression

https://github.com/spring-projects/spring-boot/issues/2031

John Culviner
sumber
Pendekatan Anda untuk versi yang lebih lama dari 1.2.2 tidak akan berfungsi karena Spring Boot tidak mencari TomcatConnectorCustomizercontoh dalam konteks aplikasi; mereka harus terdaftar secara terprogramTomcatEmbeddedServletContainerFactory
Andy Wilkinson
Terimakasih atas peringatannya. Saya akhirnya menyerah karena tampaknya boot statis / dinamis / tomcat / vs masih menjadi masalah. Ini jauh lebih sulit dari yang seharusnya ... Nginx reverse proxy FTW!
John Culviner
3
Di SpringBoot, properti baru adalah server.compression.enabled = true dan server.compression.mime-types = XXX, YYY github.com/spring-projects/spring-boot/wiki/…
blacelle
2
Jika untuk boot musim semi kami memiliki beberapa pengontrol istirahat yang semuanya mengembalikan respons JSON. Bisakah kita menerapkan zip secara selektif pada beberapa pengontrol?
3
Anda juga harus menyebutkan ukuran respons minimum untuk kompresi (misal: 10KB) jika tidak, ini menjadi overhead bagi server untuk mengompresi setiap permintaan (misal: 0,5KB). server.compression.min-response-size=10240
UsamaAmjad
13

Tentang versi terbaru dalam application.ymlkonfigurasi:

---

spring:
  profiles: dev

server:
  compression:
    enabled: true
    mime-types: text/html,text/css,application/javascript,application/json

---
M. Reza Nasirloo
sumber
Laporan bug yang sesuai dan perbaikan di mana ini diubah adalah github.com/spring-projects/spring-boot/issues/2737
McLovin
12

Ini pada dasarnya adalah solusi yang sama seperti yang disediakan @ andy-wilkinson, tetapi pada Spring Boot 1.0 metode customize (...) memiliki parameter ConfigurableEmbeddedServletContainer .

Hal lain yang perlu disebutkan adalah bahwa Tomcat hanya mengompresi jenis konten text/html, text/xmldan text/plainsecara default. Di bawah ini adalah contoh yang mendukung kompresi application/jsonjuga:

@Bean
public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
    return new EmbeddedServletContainerCustomizer() {
        @Override
        public void customize(ConfigurableEmbeddedServletContainer servletContainer) {
            ((TomcatEmbeddedServletContainerFactory) servletContainer).addConnectorCustomizers(
                    new TomcatConnectorCustomizer() {
                        @Override
                        public void customize(Connector connector) {
                            AbstractHttp11Protocol httpProtocol = (AbstractHttp11Protocol) connector.getProtocolHandler();
                            httpProtocol.setCompression("on");
                            httpProtocol.setCompressionMinSize(256);
                            String mimeTypes = httpProtocol.getCompressableMimeTypes();
                            String mimeTypesWithJson = mimeTypes + "," + MediaType.APPLICATION_JSON_VALUE;
                            httpProtocol.setCompressableMimeTypes(mimeTypesWithJson);
                        }
                    }
            );
        }
    };
}
matsev
sumber
Saya mencoba menambahkan ini ke Konfigurasi Java saya dan menemukan bahwa kompresi tampaknya tidak berfungsi sama sekali. Saya menggunakan Spring Boot dengan Tomcat sebagai container yang disematkan, dan bertanya-tanya apakah ada hal tambahan yang perlu saya atur selain konfigurasi ini?
Michael Coxon
2
Coba verifikasi dengan menentukan Accept-Encoding: gzip,deflateheader, jika Anda menggunakan curl:curl -i -H 'Accept-Encoding: gzip,deflate' http://url.to.your.server
matsev
9

Spring Boot 1.4 Gunakan ini untuk Javascript HTML Json semua kompresi.

server.compression.enabled: true
server.compression.mime-types: application/json,application/xml,text/html,text/xml,text/plain,text/css,application/javascript
Ronny Shibley
sumber
Bagaimana cara kami memverifikasi kompresi ini?
Bhargav
@Bhargav Lihat header respon dari respon api Anda. Ini harus berisi header Content-Encoding::gzip
Sumit Jha
6

Mengaktifkan GZip di Tomcat tidak berfungsi di Proyek Spring Boot saya. Saya menggunakan CompressingFilter ditemukan di sini .

@Bean
public Filter compressingFilter() {
    CompressingFilter compressingFilter = new CompressingFilter();
    return compressingFilter;
}
pengguna1127860
sumber
@ user1127860 tnx ini berfungsi tetapi bagaimanapun juga untuk mengkonfigurasi Filter ini lebih lanjut? Saya menggunakan boot musim semi dan sepertinya tidak dapat menambahkan parameter init seperti yang dikatakan manual di web.xml
Musim semi
5

Untuk mengaktifkan kompresi GZIP, Anda perlu mengubah konfigurasi instance Tomcat yang disematkan. Untuk melakukannya, Anda mendeklarasikan sebuah EmbeddedServletContainerCustomizerbean dalam konfigurasi Java Anda dan kemudian mendaftarkan sebuah bean TomcatConnectorCustomizerdengannya.

Sebagai contoh:

@Bean
public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
    return new EmbeddedServletContainerCustomizer() {
        @Override
        public void customize(ConfigurableEmbeddedServletContainerFactory factory) {
            ((TomcatEmbeddedServletContainerFactory) factory).addConnectorCustomizers(new TomcatConnectorCustomizer() {
                @Override
                public void customize(Connector connector) {
                    AbstractHttp11Protocol httpProtocol = (AbstractHttp11Protocol) connector.getProtocolHandler();
                    httpProtocol.setCompression("on");
                    httpProtocol.setCompressionMinSize(64);
                }
            });
        }
    };
}

Lihat dokumentasi Tomcat untuk detail lebih lanjut tentang berbagai opsi konfigurasi kompresi yang tersedia.

Anda mengatakan bahwa Anda ingin mengaktifkan kompresi secara selektif. Bergantung pada kriteria pemilihan Anda, maka pendekatan di atas mungkin cukup. Ini memungkinkan Anda untuk mengontrol kompresi oleh agen pengguna permintaan, ukuran respons, dan jenis mime respons.

Jika ini tidak memenuhi kebutuhan Anda maka saya yakin Anda harus melakukan kompresi di pengontrol Anda dan mengembalikan respons byte [] dengan header encoding konten gzip.

Andy Wilkinson
sumber
1
Apa perbedaan antara jawaban Anda dengan opsi untuk meletakkan pengaturan di application.properties? server.compression.enabled = true server.compression.mime-types = application / json, application / xml, text / html, text / xml, text / plain, application / javascript, text / css
lukass77
Jawaban ini ditulis sebelum konfigurasi kompresi berbasis properti tersedia. Mereka setara, tetapi pendekatan berbasis properti lebih mudah, jadi saya akan merekomendasikan untuk menggunakannya.
Andy Wilkinson
hanya ingin berbagi bahwa dalam kasus saya tomcat berada di belakang penyeimbang beban yang mendapatkan https dan kata kunci permintaan ke kucing jantan sebagai http., ketika saya menggunakan solusi application.properties respon bukan gzip tetapi ketika saya menggunakan solusi konfigurasi programatik pada konektor saya mendapatkan tanggapan gzip dengan https permintaan LB
lukass77
pertanyaan lain jika saya menggunakan solusi application.properties .. dan menentukan lebih banyak 2 konektor pada port 8081 dan 8082 .. apakah kompresi applay ke semua conectors atau hanya ke konektor 8080?
lukass77
i verfiy ini, kompersi hanya berlaku pada port 8080, bahkan jika Anda membuka lebih banyak konektor .., saya pikir bug harus dibuka pada ini untuk boot musim semi ?? .., jadi hanya solusi yang bekerja untuk saya adalah konfigurasi programatik untuk setiap konektor, bukan application.properties
lukass77
0

Saya mengalami masalah yang sama dengan proyek Spring Boot + Spring Data saya saat menjalankan file @RepositoryRestResource.

Masalahnya adalah tipe MIME yang dikembalikan; yang mana application/hal+json. Menambahkannya ke server.compression.mime-typesproperti memecahkan masalah ini bagi saya.

Semoga ini bisa membantu orang lain!

Serginho
sumber