1. 程式人生 > 資料庫 >Redis 學習——客戶端

Redis 學習——客戶端

文章目錄

第三章 客戶端

1.開啟遠端連線

Redis 預設是不支援遠端連線的,需要手動開啟,需要配置 redis.conf 檔案:
在這裡插入圖片描述
警告說,如果打算直接暴露到網上,誰都可以連線,是危險的,建議強制直接配置的 IPV4 地址,這樣只有配置的地址才能連線,由於我的 Redis 在 CentOS 虛擬機器的 Ubuntu 系統中,所以我需要暴露到網上,此時比較危險,所以我設定了 redis 的連線密碼,密碼:javaboy
在這裡插入圖片描述

2. Jedis

建立一個 Maven 專案,並在 github 上查詢 Jedis 的,新增到專案中
在這裡插入圖片描述
在這裡插入圖片描述

  • 使用 Jedis
public class MyJedis {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("192.168.21.129");
        jedis.auth("javaboy");
        String ping = jedis.ping();
        System.out.println(ping);
        String set = jedis.set("name", "xuanjinnan");
        System.out.println(set);
        String name = jedis.get("name");
        System.out.println(name);
    }
}

直接 new 就可以,地址是 ubuntu 執行 ip addr ,ens33 中的內容
在這裡插入圖片描述
執行檢視控制檯,說明連線上 Redis ,並能正常使用了
在這裡插入圖片描述
期間,第一次沒連上,重啟了一下 Redis 就好了
在這裡插入圖片描述
對於 Jedis 而言,一旦連上 Redis 服務,接下來操作都很容易,因為方法的 API 與 Redis 命令高度一致,所以 Jedis 中的方法見名知義直接使用即可

2.1連線池

在實際用中,Jedis 例項一般通過連線池來獲取,由於 Jedis 不是現場安全的,所以當我們使用 Jedis 物件時,要從連線池中獲取 Jedis,使用完成後再還給連線池,執行緒池是執行緒安全的

// 第一步,建立一個連線池
        JedisPool jedisPool = new JedisPool("192.168.21.129");
        // 第二步,從連線池中獲取一個 Jedis 連線
        Jedis jedis = jedisPool.getResource();
        String javaboy = jedis.auth("javaboy");
        System.out.println(javaboy);
        // 第三步,執行 Jedis 操作
        String ping = jedis.ping();
        System.out.println(ping);
        // 第四步,歸還連線
        jedis.close();

執行結果
在這裡插入圖片描述
每個 Jedis Resouce 都是要歸還的資源,如果第三步出錯,導致第四步沒有執行,會有記憶體洩露的風險,所以我們要對程式碼進行優化,保證第四步能夠執行

// 第一步,建立一個連線池
        JedisPool jedisPool = new JedisPool("192.168.21.129");
        // 第二步,從連線池中獲取一個 Jedis 連線
        Jedis jedis = jedisPool.getResource();
        String javaboy = jedis.auth("javaboy");
        System.out.println(javaboy);
        // 第三步,執行 Jedis 操作
        try {
            String ping = jedis.ping();
            System.out.println(ping);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 第四步,歸還連線
            if(jedis != null){
                jedis.close();
            }

        }

通過 finally,我們可以確保 Jedis 一定被關閉
還可以利用 JDK 1.7 的資源釋放語法糖進行簡寫:

// 第一步,建立一個連線池
        JedisPool jedisPool = new JedisPool("192.168.21.129");
        // 第二步,從連線池中獲取一個 Jedis 連線
        try(Jedis jedis = jedisPool.getResource()){
            String javaboy = jedis.auth("javaboy");
            System.out.println(javaboy);
            // 第三步,執行 Jedis 操作
            String ping = jedis.ping();
            System.out.println(ping);
        }

這段程式碼的作用和 try - catch - finally 是一樣的
但是又有一個弊端,每次一定要執行 close() 方法,如果忘了呢,就有可能資源釋放不了,無法做到強約束,我們可以通過設計模式,把 第三部 Jedis 操作抽象出去,整個過程方法起名叫 excute,第三部抽象出一個方法叫 callJedis,那麼程式碼可以首先是

public class Redis {
    
    private static JedisPool pool = new JedisPool( "192.168.21.129");

    public void execute(CallWithJedis callWithJedis){
        try(Jedis jedis = pool.getResource()){
            jedis.auth("javaboy");
            callWithJedis.callJedis(jedis);
        }
    }
}

定義 CallWithJedis 介面 和 callJedis 抽象方法,callJedis 就是實際執行的 Jedis 操作方法:

public interface CallWithJedis {
    void callJedis(Jedis jedis);
}

使用時:

Redis redis = new Redis();
        redis.execute(jedis -> {
            System.out.println(jedis.ping());
        });

這就是最終的優化方案

  • Jedis 的構造方法還有一個帶有 GenericObjectPoolConfig 類的,可以配置其他連線池的其他屬性,下面是使用 GenericObjectPoolConfig 類後的程式碼:
public class Redis {
    private static GenericObjectPoolConfig<String> config ;
    static {
        config= new GenericObjectPoolConfig<String>();
        // 最大空閒數量
        config.setMaxIdle(300);
        // 最大執行緒數
        config.setMaxTotal(1000);
        // 最大連線等待時間,-1 表示無限制
        config.setMaxWaitMillis(30000);
        // 在空閒時校驗有效性
        config.setTestOnBorrow(true);
    }
    private static JedisPool pool = new JedisPool(config, "192.168.21.129", 6379, 3000, "javaboy");

    public void execute(CallWithJedis callWithJedis){
        try(Jedis jedis = pool.getResource()){
            callWithJedis.callJedis(jedis);
        }
    }
}

在分散式中有許多情況要重試,比如 Spring Cloud Config,同樣,獲取 Jedis 資源也可能失敗,我們這時候可以加上重試功能,這裡重試三次

public class Redis {
    private static GenericObjectPoolConfig<String> config ;
    static {
        config= new GenericObjectPoolConfig<String>();
        // 最大空閒數量
        config.setMaxIdle(300);
        // 最大執行緒數
        config.setMaxTotal(1000);
        // 最大連線等待時間,-1 表示無限制
        config.setMaxWaitMillis(30000);
        // 在空閒時校驗有效性
        config.setTestOnBorrow(true);
    }
    private static JedisPool pool = new JedisPool(config, "192.168.21.129", 6379, 3000, "javaboy");

    public void execute(CallWithJedis callWithJedis){
        Jedis jedis = null;
        try{
            jedis = pool.getResource();
            callWithJedis.callJedis(jedis);
        }catch (Exception e0){
            try {
                Thread.sleep(2000);
                jedis = pool.getResource();
                callWithJedis.callJedis(jedis);
            } catch (Exception e1) {
                try {
                    Thread.sleep(2000);
                    jedis = pool.getResource();
                    callWithJedis.callJedis(jedis);
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }

        }finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }
}

重試的程式碼醜陋,也可能有很多可以優化的地方,僅作為重試想法 demo,看看即可

3.Lettuce


Lettus 和 Jedis 比較

  • 1.Jedis 在實現過程中是直接連線 Redis 的,在多個執行緒之間共享一個 Jedis 例項,這是執行緒不安全的,如果想在多執行緒場景下使用 Jedis,就需要使用連線池,這樣,每個執行緒都有自己的 Jedis 例項;弊端是佔用較多的資源
  • 2.Lettuce 是基於現在很流行的 Netty Nio 框架構建的,所以克服了 Jedis 執行緒不安全的問題,Lettuce 支援同步、非同步及響應式呼叫,多個執行緒可以共享一個連線例項

Lettuce簡單使用

  • 建立一個普通的 Maven 專案,新增 Lettuce 依賴
    在這裡插入圖片描述
    在這裡插入圖片描述
public class LettusTest {
    public static void main(String[] args) {
        RedisClient redisClient = RedisClient.create("redis://[email protected]");
        StatefulRedisConnection<String, String> connect = redisClient.connect();
        RedisCommands<String, String> sync = connect.sync();// 同步
        String set = sync.set("name", "xuanjinnan");
        System.out.println(set);
        String name = sync.get("name");
        System.out.println(name);
    }
}

執行結果:
在這裡插入圖片描述