Redis學習筆記14、 JedisPool連接池學習

一、介紹

1、Jedis開源庫提供了一個負責管理Jedis連接對象的池,名爲JedisPool類,位於redis.clients.jedis包中。爲了防止數據庫連接的頻繁創建、銷燬帶來的性能損耗。

二、JedisPool的配置

JedisPoolConfig配置類,位於redis.clients.jedis包中。這個連接池的配置類負責配置JedisPool的參數。JedisPoolConfig配置類涉及到很多與連接管理和使用有關的參數。

1、maxTotal:資源池中最大的連接數 默認:8

2、maxIdle:資源池允許最大空閒的連接數 默認:8

3、minIdle:資源池確保最少空閒的連接數 默認0

開啓JedisPool空閒連接的有效性檢測,如果空閒連接無效,就銷燬。銷燬連接後,連接數量就少了,如果小於minIdle數量,就新建連接,維護數量不少於minIdle的數量。minIdle確保了線程池中有最小的空閒Jedis實例的數量。

4、blockWhenExhausted:當資源池用盡後,調用者是否要等待,默認值爲true。當爲true時,maxWaitMillis纔會生效。

5、maxWaitMillis:當資源池連接用盡後,調用者的最大等待時間(單位爲毫秒)。默認值爲-1,表示永不超時。

6、testOnBorrow:向資源池借用連接時,是否做有效性檢測(ping命令),如果是無效連接,會被移除,默認值爲false,表示不做檢測。

  1. 如果爲true,則得到的Jedis實例均是可用的。
  1. 在業務量小的應用場景,建議設置爲true,確保連接可用;
  1. 在業務量很大的應用場景,建議設置爲false(默認值),少一次ping命令的開銷,有助於提升性能。

7、testOnReturn:向資源池歸還連接時,是否做有效性檢測(ping命令),如果是無效連接,會被移除,默認值爲false,表示不做檢測。

8、testWhileIdle:如果爲true,表示用一個專門的線程對空閒的連接進行有效性的檢測掃描,如果有效性檢測失敗,即表示無效連接,會從資源池中移除。

選項存在一個附加條件,需要配置項timeBetweenEvictionRunsMillis的值大於0;否則,testWhileIdle不會生效。

9、timeBetweenEvictionRunsMillis:表示兩次空閒連接掃描的活動之間,要睡眠的毫秒數,默認爲30000毫秒,也就是30秒鐘。

10、minEvictableIdleTimeMillis:表示一個Jedis連接至少停留在空閒狀態的最短時間,然後才能被空閒連接掃描線程進行有效性檢測,默認值爲60000毫秒,即60秒。

  1. 一條Jedis連接只有在空閒60秒後,纔會參與空閒線程的有效性檢測。
  1. 選項存在一個附加條件,需要在timeBetweenEvictionRunsMillis大於0時纔會生效。也就是說,如果不啓動空閒檢測線程,這個參數也沒有什麼意義。

11、numTestsPerEvictionRun:表示空閒檢測線程每次最多掃描的Jedis連接數,默認值爲-1,表示掃描全部的空閒連接。

12、jmxEnabled:是否開啓jmx監控,默認值爲true。

四、JedisPool創建和預熱

1、創建JedisPool連接池,創建一個JedisPoolConfig配置實例

  1. 配置
package com.example.actuatordemo.redis.conf;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

/**
 * @author haoxiansheng
 */
public class JedisPoolConfig extends GenericObjectPoolConfig {

    /**
     * 有個實際的問題:如何推算一個連接池的最大連接數maxTotal呢?
     * 實際上,這是一個很難精準回答的問題,主要是依賴的因素比較多。
     * 大致的推算方法是:業務QPS/單連接的QPS = 最大連接數。如何推算單個Jedis連接的QPS呢?
     * 假設一個Jedis命令操作的時間約爲5ms(包含borrow +return +Jedis執行命令 + 網絡延遲),那麼,單個Jedis連接的QPS大約是100/5 =200。
     * 如果業務期望的QPS是100000,則需要的最大連接數爲100000/200 =500。
     * <p>
     * 在實際的生產場景中,還要預留一些資源,通常來講所配置的maxTotal要比理論值大一些。
     * 如果連接數確實太多,可以考慮Redis集羣,那麼單個Redis節點的最大連接數的公式爲:
     * maxTotal= 預估的連接數 / nodes節點數。
     * <p>
     * 在併發量不大時,maxTotal設置過高會導致不必要的連接資源的浪費。可以根據實際總QPS和nodes節點數,合理評估每個節點所使用的最大連接數。
     * <p>
     * <p>
     * 再看一個問題:如何推算連接池的最大空閒連接數maxIdle值呢?實際上,maxTotal只是給出了一個連接數量的上限,maxIdle實際上纔是業務可用的最大連接數,
     * 從這個層面來說,maxIdle不能設置過小,否則會有創建、銷燬連接的開銷。
     * 使得連接池達到最佳性能的設置是maxTotal = maxIdle,應儘可能地避免由於頻繁地創建和銷燬Jedis連接所帶來的連接池性能的下降。
     */
    public JedisPoolConfig() {
        this.setTestWhileIdle(true);
        this.setMinEvictableIdleTimeMillis(60000L);
        this.setTimeBetweenEvictionRunsMillis(30000L);
        this.setNumTestsPerEvictionRun(-1);
    }
}

  1. 創建pool
package com.example.actuatordemo.redis.conf;

import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.ArrayList;
import java.util.List;

/**
 * @author haoxiansheng
 * 創建JedisPool連接池的一般步驟爲創建一個JedisPoolConfig配置實例;
 * JedisPoolConfig、Redis IP、Redis端口和其他可選選項(如超時時間、Auth密碼)爲參數,構造一個JedisPool連接池。
 */
@Slf4j
public class JredisPoolBuilder {
    public static final int MAX_IDLE = 50;

    public static final int MAX_TOTAL = 50;

    private static JedisPool pool = null;


    // 創建連接池
    private static JedisPool buildPool() {
        if (pool == null) {
            long start = System.currentTimeMillis();
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxIdle(MAX_IDLE);
            config.setMaxWaitMillis(1000 * 10);
            // 在brrow 一個jedis 實例 是否提前進行有效檢測操作;
            // 如果爲true,則得到的jedis 實例均是可用的
            config.setTestOnBorrow(true);
            // new JedisPool(config, ADDR ,PORT, TIMEOUT);
            pool = new JedisPool(config, "127.0.0.1", 6379, 10000);
            long end = System.currentTimeMillis();
            log.info("buildPool毫秒數=>{}", (end - start));
        }
        return pool;
    }

    // 連接池的預熱
    private static void hotPool() {
        long start = System.currentTimeMillis();
        List<Jedis> minIdleJedisList = new ArrayList<>(MAX_IDLE);
        Jedis jedis = null;
        for (int i = 0; i < MAX_IDLE; i++) {
            try {
                jedis = pool.getResource();
                minIdleJedisList.add(jedis);
                jedis.ping();
            } catch (Exception e) {
                log.info("e=>{}", e.getMessage());
            } finally {

            }

            for (int j = 0; j < MAX_IDLE; j++) {
                try {
                    jedis = minIdleJedisList.get(i);
                    jedis.close();
                    ;
                } catch (Exception e) {
                    log.error("e=>{}", e.getMessage());
                } finally {

                }
            }
            long end = System.currentTimeMillis();
            log.info("hotPool毫秒數=>{}", (end - start));
        }
    }
    static {
        // 創建連接池
        buildPool();
        // 預熱連接池
        hotPool();
    }



    public static Jedis getJedis() {
        return pool.getResource();
    }

}

  1. 使用
package com.example.actuatordemo.redis.conf;

import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;

/**
 * @author haoxiansheng
 */
@Slf4j
public class JredisPoolTest {
    public static final int NUM = 200;

    public static final String ZEST_KEY = "zset1";

    // 測試刪除
    public static void main(String[] args) {

    }

    public static void testSet() {
        testDel();
        try (Jedis redis = JredisPoolBuilder.getJedis()) {
            int loop = 0;
            long start = System.currentTimeMillis();
            while (loop < NUM) {
                redis.zadd(ZEST_KEY, loop, "field-" + loop);
                loop++;
            }
            long end = System.currentTimeMillis();
            log.info("設置Zset=>{}次,毫秒數=>{}", loop, (end - start));
        }
    }

    public static void testDel() {
        Jedis redis = null;
        try {
            redis = JredisPoolBuilder.getJedis();
            long start = System.currentTimeMillis();
            redis.del(ZEST_KEY);
            long end = System.currentTimeMillis();
            log.info("刪除zset1 毫秒數=>{}", (end - start));
        } finally {
            // 關閉 連接池
            if (redis != null) {
                redis.close();
            }
        }
    }


}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章