單例模式在生產環境jedis叢集中的應用

weixin_33816946發表於2019-01-08

背景:不久前單位上線一款應用,上了生產環境之後,沒過多久,便吃掉了伺服器所有的記憶體,最後導致網站服務掛了。

 

在解決了這一問題之後,我發現這其實是典型的一單例模式,現分享一下。

之前存在問題的老程式碼如下:

這是導致問題所在的那個關鍵方法

public synchronized static JedisCluster getJedisCluster() {
    JedisPoolConfig config = new JedisPoolConfig();
    config.setMaxTotal(MAX_ACTIVE);
    config.setMaxIdle(MAX_IDLE);
    config.setMaxWaitMillis(MAX_WAIT);
    config.setTestOnBorrow(TEST_ON_BORROW);
    
    // 叢集模式
    JedisPoolConfig poolConfig = new JedisPoolConfig();
    Set<HostAndPort> nodes = new HashSet<HostAndPort>();

    HostAndPort hostAndPort1 = new HostAndPort("伺服器地址1", 埠1);
    HostAndPort hostAndPort2 = new HostAndPort("伺服器地址2", 埠2);
    HostAndPort hostAndPort3 = new HostAndPort("伺服器地址3", 埠3);

    nodes.add(hostAndPort1);
    nodes.add(hostAndPort2);
    nodes.add(hostAndPort3);

    JedisCluster jedisCluster = new JedisCluster(nodes, poolConfig);
    
    return jedisCluster;
}

以上這段程式碼是有問題的,大家看出來了嗎?

問題在於,雖然方法宣告為synchronized static,但是在併發多執行緒的情況下,並不能保證每個使用者執行緒只生成一個JedisCluster的例項。

這樣就會導致每個執行緒都會建立jedisCluster的例項,就會消耗記憶體,而且這塊記憶體又沒有被及時地釋放掉,導致多使用者併發以後,快速吃光了伺服器的記憶體。

 

解決方法就是使用單例模式,把JedisCluster作為static的類成員,且使用懶漢單例模式,程式碼如下:

public class OuterClass{
    ...
    private static JedisCluster jedisCluster = null;
    ...

    public synchronized static JedisCluster getJedisCluster() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(MAX_ACTIVE);
        config.setMaxIdle(MAX_IDLE);
        config.setMaxWaitMillis(MAX_WAIT);
        config.setTestOnBorrow(TEST_ON_BORROW);
        
        // 叢集模式
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        Set<HostAndPort> nodes = new HashSet<HostAndPort>();

        HostAndPort hostAndPort1 = new HostAndPort("伺服器地址1", 埠1);
        HostAndPort hostAndPort2 = new HostAndPort("伺服器地址2", 埠2);
        HostAndPort hostAndPort3 = new HostAndPort("伺服器地址3", 埠3);

        nodes.add(hostAndPort1);
        nodes.add(hostAndPort2);
        nodes.add(hostAndPort3);

        // 只有當jedisCluster為空時才例項化
        if (jedisCluster == null) {
            jedisCluster = new JedisCluster(nodes, poolConfig);
        }

        return jedisCluster;
    }
}

這樣就會保證即使在高併發的環境下,所有使用者執行緒還是隻會擁有一個JedisCluster的例項。

 

相關文章