自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

釋放你九成的帶寬和內(nèi)存:GZIP在解決Redis大Key方面的應(yīng)用

數(shù)據(jù)庫(kù) Redis
如里你的Redis緩存中存在大量的大Key,可能先達(dá)到瓶頸的不是Redis的讀寫(xiě)性能,很可能是你的帶寬,此時(shí)只需要簡(jiǎn)單的使用GZIP壓縮就能你給不僅節(jié)省88%的Redis內(nèi)存空間還大大減少了數(shù)據(jù)的傳輸量和節(jié)省了帶寬資源,而且還能使用的C端用戶(hù)的資源來(lái)解壓,這個(gè)ROI是非常高的。

引言

目前主流HTTP協(xié)議接口都是使用JSON格式做數(shù)據(jù)交換的,JSON數(shù)據(jù)格式有著結(jié)構(gòu)簡(jiǎn)單、可讀性高、跨平臺(tái),易解析等優(yōu)點(diǎn),同時(shí)也存在著冗余數(shù)據(jù)會(huì)占用非常多的儲(chǔ)存空間的問(wèn)題,這大大增加了JSON格式數(shù)據(jù)在存儲(chǔ)、傳輸過(guò)程中的性能消耗。所以對(duì)JSON格式數(shù)據(jù)壓縮后再傳輸、存儲(chǔ)就變的非常的有價(jià)值,如對(duì)JSON格式數(shù)據(jù)使用GZIP壓縮算法可以實(shí)現(xiàn)90%左右的壓縮率,更小的空間可以節(jié)省存儲(chǔ)成本和降低傳輸帶寬成本,本文介紹GZIP壓縮算法在優(yōu)化Redis使用大KEY字段中的應(yīng)用,通過(guò)簡(jiǎn)單壓縮可以節(jié)省88%的內(nèi)存空間和帶寬資源。

HTTP協(xié)議開(kāi)啟GZIP

HTTP協(xié)議標(biāo)準(zhǔn)中是直接支持GZIP壓縮算法的,通過(guò)響應(yīng)頭Content-Encoding: gzip來(lái)表明響應(yīng)內(nèi)容使用了GZIP壓縮,當(dāng)客戶(hù)端收到數(shù)據(jù)后會(huì)使用GZIP算法對(duì)Body內(nèi)容進(jìn)行解壓。

RFC 1952 - IETF(互聯(lián)網(wǎng)工程任務(wù)組)標(biāo)準(zhǔn)化的Gzip文件格式規(guī)范,

RFC 2616 - HTTP 1.1 協(xié)議規(guī)范,其中包括對(duì) Content-Encoding 頭的定義

在Nginx中可以通過(guò) gzip on開(kāi)啟GZIP壓縮功能:

gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

在Springboot中可以通過(guò)server.compression.enabled開(kāi)啟GZIP壓縮功能:

server:
  port: 80
  compression:
    enabled: true
    mime-types:  application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain
    min-response-size: 2KB
  • enabled,開(kāi)啟或關(guān)閉
  • mime-types,壓縮的數(shù)據(jù)類(lèi)型
  • min-response-size,最小壓縮大小

測(cè)試GZIP

為了測(cè)試開(kāi)啟GZIP前后的對(duì)比效果我們寫(xiě)一個(gè)簡(jiǎn)單的接口:

@GetMapping("/list")
public ResponseEntity<ApiResult> list() {
    return renderOk(getData());
}

我們返回1000條JSON格式的用戶(hù)信息:

private List<UserVo> getData() {
    return IntStream.range(1, 1000).mapToObj(x -> new UserVo(x,x+"+email@q63.com",x+"_公眾號(hào)",x+"_趙俠客")).collect(Collectors.toList());
}
@Data
@AllArgsConstructor
public class UserVo {
    private Integer id;
    private String username;
    private String email;
    private String trueName;
}

在未開(kāi)啟GZIP前接口返回?cái)?shù)據(jù)的大小是92.8KB, Content-Encoding為空,在開(kāi)啟GZIP后接口返回的數(shù)據(jù)大小為11.5KB,Content-Encoding為gzip,接口返回?cái)?shù)量降低了88%。圖片

當(dāng)然我們也可以在接口中通過(guò)手動(dòng)添加content-encoding響應(yīng)頭,然后通過(guò)手動(dòng)調(diào)用GZIPOutputStream對(duì)返回?cái)?shù)據(jù)進(jìn)行GZIP壓縮:

@GetMapping("/gzip")
public void gzip(HttpServletResponse response) throws IOException {
    response.setContentType("application/json;charset=utf-8");
    response.setHeader("content-encoding", "gzip");
    try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(response.getOutputStream())) {
        IOUtils.write(JsonUtils.toJson(getData()), gzipOutputStream);
    }
}

Redis緩存壓縮

為了增加接口的響應(yīng)速度我們通常會(huì)使用Redis當(dāng)緩存,基本邏輯是先查Redis有沒(méi)有數(shù)據(jù)如果有直接返回,如果沒(méi)有會(huì)查數(shù)據(jù)庫(kù),然后再存入Redis,以下是一個(gè)簡(jiǎn)單的使用Redis當(dāng)緩存的接口:

@Resource
private RedissonClient redissonClient;
public static final String REDIS_KEY = "REDIS_KEY";

@GetMapping("/redis")
public void redis(HttpServletResponse response) throws IOException {
    RBucket<String> bucket = redissonClient.getBucket(REDIS_KEY);
    String data = bucket.get();
    if (data == null) {
         data=JsonUtils.toJson(getData());
        redissonClient.getBucket(REDIS_KEY).set(data,100L, TimeUnit.SECONDS);
    }
    response.setContentType("application/json");
    IOUtils.write(data, response.getOutputStream());
}

我們分析一下這樣個(gè)接口的基本數(shù)據(jù)流:

  • 第一次從數(shù)據(jù)庫(kù)服務(wù)器查出92.8KB的數(shù)據(jù)傳輸?shù)絎EB服務(wù)器中
  • 將92.8KB的數(shù)據(jù)從WEB服務(wù)器傳輸?shù)絉edis服務(wù)器中
  • 后面如果命中緩存將92.8KB數(shù)據(jù)從Redis服務(wù)器傳輸?shù)絎EB服務(wù)器
  • 最后將92.8KB數(shù)據(jù)從WEB服務(wù)器返回給用戶(hù)瀏覽器

使用Redis當(dāng)緩存加速接口使用Redis當(dāng)緩存加速接口

使用ZIP優(yōu)化Redis緩存:

public static final String GZIP_REDIS_KEY = "GZIP_REDIS_KEY";

@GetMapping("/gzipRedis")
public void gzipRedis(HttpServletResponse response) throws IOException {
    RBucket<byte[]> bucket = redissonClient.getBucket(GZIP_REDIS_KEY);
    byte[] data = bucket.get();
    if (data == null) {
        String jsnotallow=JsonUtils.toJson(getData());
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) {
            IOUtils.write(json, gzipOutputStream, String.valueOf(StandardCharsets.UTF_8));
            gzipOutputStream.finish();
            data= byteArrayOutputStream.toByteArray();
            redissonClient.getBucket(GZIP_REDIS_KEY).set(data,100L, TimeUnit.SECONDS);
        }
    }
    response.setContentType("application/json");
    response.setHeader("content-encoding", "gzip");
    IOUtils.write(data, response.getOutputStream());
}

使用GZIP壓縮后的緩存接口使用GZIP壓縮后的緩存接口

我們?cè)俜治鲆幌乱陨鲜褂肎ZIP壓縮后的數(shù)據(jù)傳輸:

  • 第一次從數(shù)據(jù)庫(kù)服務(wù)器查出92.8KB的數(shù)據(jù)傳輸?shù)絎EB服務(wù)器中
  • 將11.5KB的GZIP數(shù)據(jù)從WEB服務(wù)器傳輸?shù)絉edis服務(wù)器中
  • 后面命中緩存將11.5KB數(shù)據(jù)從Redis服務(wù)器傳輸?shù)絎EB服務(wù)器
  • 最后將11.KB數(shù)據(jù)從WEB服務(wù)器返回給用戶(hù)瀏覽器

GZIP壓縮后的Redis緩存GZIP壓縮后的Redis緩存

單次接口請(qǐng)求好像感覺(jué)不到這個(gè) GZIP壓縮帶來(lái)的好處,接下來(lái)我們壓測(cè)一下看看會(huì)不會(huì)有差距。

壓力測(cè)試

壓測(cè)可以使用ab (Apache Benchmark) 工具,ab工具是 Apache HTTP server 的一部分,在 macOS使用Homebrew包管理器可以快速安裝上ab :

brew install httpd
ab -V
ab -n 100 -c 10 http://localhost/list

其中:

  • -n 100 表示總共請(qǐng)求 100 次。
  • -c 10  表示并發(fā) 10 個(gè)請(qǐng)求。

未壓縮走Redis壓縮結(jié)果:

ab -n 100000 -c 10 http://localhost/redis

Finished 100000 requests
Document Length:        92476 bytes
Concurrency Level:      10
Time taken for tests:   194.917 seconds
Complete requests:      100000
Failed requests:        0
Total transferred:      9258100000 bytes
HTML transferred:       9247600000 bytes
Requests per second:    513.04 [#/sec] (mean)
Time per request:       19.492 [ms] (mean)
Time per request:       1.949 [ms] (mean, across all concurrent requests)
Transfer rate:          46384.34 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    8 249.5      0   19514
Processing:     4   12  19.8     10     754
Waiting:        4   11  19.8     10     754
Total:          4   19 250.4     10   19525
Percentage of the requests served within a certain time (ms)
  50%     10
  66%     11
  75%     11
  80%     12
  90%     12
  95%     15
  98%     27
  99%    134
 100%  19525 (longest request)

使用GZIP壓縮后走Redis緩存壓測(cè)結(jié)果:

ab -n 100000 -c 10 http://localhost/gzipRedis

Finished 100000 requests
Document Length:        11091 bytes
Concurrency Level:      10
Time taken for tests:   194.927 seconds
Complete requests:      100000
Failed requests:        0
Total transferred:      1122000000 bytes
HTML transferred:       1109100000 bytes
Requests per second:    513.01 [#/sec] (mean)
Time per request:       19.493 [ms] (mean)
Time per request:       1.949 [ms] (mean, across all concurrent requests)
Transfer rate:          5621.09 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   12 410.4      0   19608
Processing:     3    7  20.0      4     802
Waiting:        3    7  19.9      4     801
Total:          3   19 410.9      4   19613

Percentage of the requests served within a certain time (ms)
  50%      4
  66%      9
  75%      9
  80%      9
  90%     10
  95%     10
  98%     11
  99%     19
 100%  19613 (longest request)

總結(jié)

對(duì)比使用GZIP壓縮我們可以得出以下幾點(diǎn):

  • 測(cè)試中10萬(wàn)請(qǐng)求在194S完成,緩存時(shí)間是100S,服務(wù)器端只做了二次查數(shù)據(jù)庫(kù)和GZIP壓縮然后存數(shù)Redis
  • 兩次GZIP和之后的數(shù)據(jù)傳輸消耗資源可以忽略不計(jì)
  • 未壓縮10萬(wàn)請(qǐng)求從Redis傳輸了8.6GB數(shù)據(jù)到WEB服務(wù)器,又從WEB服務(wù)器傳輸8.6GB給用戶(hù)瀏覽器,
  • 壓縮10萬(wàn)請(qǐng)求從Redis傳輸了1GB數(shù)據(jù)到WEB服務(wù)器,又從WEB服務(wù)器傳輸1GB給用戶(hù)瀏覽器,節(jié)省數(shù)據(jù)傳輸15.2GB,節(jié)省率88%
  • 未壓縮數(shù)據(jù)傳輸速度達(dá)到45M/S,壓縮后5.4M/S,節(jié)省帶寬88%
  • 如果Redis中大JSON都使用GZIP壓縮理論上可以節(jié)省Redis內(nèi)存達(dá)到88%
  • 因?yàn)橹苯邮褂胓zip返回,所有解壓計(jì)算在用戶(hù)瀏覽器端完成,不消耗服務(wù)器CPU資源

請(qǐng)求10萬(wàn)次數(shù)據(jù)傳輸流程請(qǐng)求10萬(wàn)次數(shù)據(jù)傳輸流程

綜合上所述如里你的Redis緩存中存在大量的大Key,可能先達(dá)到瓶頸的不是Redis的讀寫(xiě)性能,很可能是你的帶寬,此時(shí)只需要簡(jiǎn)單的使用GZIP壓縮就能你給不僅節(jié)省88%的Redis內(nèi)存空間還大大減少了數(shù)據(jù)的傳輸量和節(jié)省了帶寬資源,而且還能使用的C端用戶(hù)的資源來(lái)解壓,這個(gè)ROI是非常高的。


責(zé)任編輯:武曉燕 來(lái)源: 趙俠客
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)