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);
}
}
執行結果: