1. 程式人生 > >Redis學習筆記(五)jedis(JedisCluster)操作Redis叢集 redis-cluster

Redis學習筆記(五)jedis(JedisCluster)操作Redis叢集 redis-cluster

redis系列文章目錄

版本說明
jedis2.9.0
redis3.2.5

參考資料:redis命令參考。 不過這個稍微有一點點老

java(JedisCluster)操作redis叢集

這裡只是幾個簡單的demo,直接上程式碼吧,沒啥好說的

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.*;

import
java.io.IOException; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; /** * Created by liubenlong on 2016/12/20. */ public class RedisTest { JedisCluster jedisCluster = null; static String prefix = "luffi:lbl"; static String KEY_SPLIT = ":"
; //用於隔開快取字首與快取鍵值 String nameKey = prefix + KEY_SPLIT + "name"; /** * 因為是測試,這裡沒有寫單例 */ @Before public void before(){ String[] serverArray = "redis叢集地址".split(","); Set<HostAndPort> nodes = new HashSet<>(); for (String ipPort : serverArray) { String[] ipPortPair = ipPort.split(":"
); nodes.add(new HostAndPort(ipPortPair[0].trim(), Integer.valueOf(ipPortPair[1].trim()))); } //注意:這裡超時時間不要太短,他會有超時重試機制。而且其他像httpclient、dubbo等RPC框架也要注意這點 jedisCluster = new JedisCluster(nodes, 1000, 1000, 1, "redis叢集密碼", new GenericObjectPoolConfig()); // 大多數測試都是使用【nameKey】測試的,所以在啟動之前先把這個key刪掉 jedisCluster.del(nameKey); } /** * 釋出 */ @Test public void publish(){ System.out.println(jedisCluster.publish("channel1", "ss")); } /** * 訂閱 */ @Test public void psubscribe(){ // jedisCluster.psubscribe(new JedisPubSubListener(), "channel1");//帶萬用字元 jedisCluster.subscribe(new JedisPubSubListener(), "channel1"); } /** * 簡單字串讀寫 */ @Test public void setStringData(){ System.out.println(jedisCluster.set(nameKey, "張三")); System.out.println(jedisCluster.get(nameKey)); } /** * setnx : 如果key存在,返回0,如果不存在,則設定成功。 * setnx的意思是set if not exist. */ @Test public void setnxTest(){ System.out.println(jedisCluster.setnx(nameKey, "張三"));//key不存在,返回值為1 System.out.println(jedisCluster.get(nameKey)); System.out.println(jedisCluster.setnx(nameKey, "張三"));//已經存在,返回值為0 System.out.println(jedisCluster.get(nameKey)); } /** * 簡單字串讀寫,帶過期時間 */ @Test public void setexTest() throws InterruptedException { System.out.println(jedisCluster.setex(nameKey, 3, "張三"));//時間單位是秒 for(int i = 0 ; i < 5 ; i ++){ System.out.println(jedisCluster.get(nameKey));//過期以後redis叢集自動刪除 Thread.sleep(1000); } } /** * 操作子字串 */ @Test public void setrangeTest() throws InterruptedException { System.out.println(jedisCluster.set(nameKey, "[email protected]")); System.out.println(jedisCluster.get(nameKey));//結果:[email protected] //從offset=8開始替換字串value的值 System.out.println(jedisCluster.setrange(nameKey, 8, "abc"));//結果:85202688abcq.com System.out.println(jedisCluster.get(nameKey)); System.out.println(jedisCluster.setrange(nameKey, 8, "abcdefghhhhhh"));//結果:85202688abcdefghhhhhh System.out.println(jedisCluster.get(nameKey)); //查詢子串,返回startOffset到endOffset的字元 System.out.println(jedisCluster.getrange(nameKey, 2, 5));//結果:2026 } /** * 批量操作key * keySlot演算法中,如果key包含{},就會使用第一個{}內部的字串作為hash key,這樣就可以保證擁有同樣{}內部字串的key就會擁有相同slot。 * 參考 http://brandnewuser.iteye.com/blog/2314280 * redis.clients.util.JedisClusterCRC16#getSlot(java.lang.String) * * 注意:這樣的話,本來可以hash到不同的slot中的資料都放到了同一個slot中,所以使用的時候要注意資料不要太多導致一個slot資料量過大,資料分佈不均勻! * * MSET 是一個原子性(atomic)操作,所有給定 key 都會在同一時間內被設定,某些給定 key 被更新而另一些給定 key 沒有改變的情況,不可能發生。 */ @Test public void msetTest() throws InterruptedException { /** * jedisCluster.mset("sf","d","aadf","as"); * 直接這樣寫,會報錯:redis.clients.jedis.exceptions.JedisClusterException: No way to dispatch this command to Redis Cluster because keys have different slots. * 這是因為key不在同一個slot中 */ String result = jedisCluster.mset("{" + prefix + KEY_SPLIT + "}" + "name", "張三", "{" + prefix + KEY_SPLIT + "}" + "age", "23", "{" + prefix + KEY_SPLIT + "}" + "address", "adfsa", "{" + prefix + KEY_SPLIT + "}" + "score", "100"); System.out.println(result); String name = jedisCluster.get("{" + prefix + KEY_SPLIT + "}" + "name"); System.out.println(name); Long del = jedisCluster.del("{" + prefix + KEY_SPLIT + "}" + "age"); System.out.println(del); List<String> values = jedisCluster.mget("{" + prefix + KEY_SPLIT + "}" + "name", "{" + prefix + KEY_SPLIT + "}" + "age", "{" + prefix + KEY_SPLIT + "}" + "address"); System.out.println(values); } /** * MSETNX 命令:它只會在所有給定 key 都不存在的情況下進行設定操作。 * http://doc.redisfans.com/string/mset.html */ @Test public void msetnxTest() throws InterruptedException { Long msetnx = jedisCluster.msetnx( "{" + prefix + KEY_SPLIT + "}" + "name", "張三", "{" + prefix + KEY_SPLIT + "}" + "age", "23", "{" + prefix + KEY_SPLIT + "}" + "address", "adfsa", "{" + prefix + KEY_SPLIT + "}" + "score", "100"); System.out.println(msetnx); System.out.println(jedisCluster.mget( "{" + prefix + KEY_SPLIT + "}" + "name", "{" + prefix + KEY_SPLIT + "}" + "age", "{" + prefix + KEY_SPLIT + "}" + "address", "{" + prefix + KEY_SPLIT + "}" + "score"));//[張三, 23, adfsa, 100] //name這個key已經存在,由於mset是原子的,該條指令將全部失敗 msetnx = jedisCluster.msetnx( "{" + prefix + KEY_SPLIT + "}" + "phone", "110", "{" + prefix + KEY_SPLIT + "}" + "name", "張三", "{" + prefix + KEY_SPLIT + "}" + "abc", "asdfasfdsa"); System.out.println(msetnx); System.out.println(jedisCluster.mget( "{" + prefix + KEY_SPLIT + "}" + "name", "{" + prefix + KEY_SPLIT + "}" + "age", "{" + prefix + KEY_SPLIT + "}" + "address", "{" + prefix + KEY_SPLIT + "}" + "score", "{" + prefix + KEY_SPLIT + "}" + "phone", "{" + prefix + KEY_SPLIT + "}" + "abc"));//[張三, 23, adfsa, 100, null, null] } /** * getset:設定key值,並返回舊值 */ @Test public void getsetTest() throws InterruptedException { System.out.println(jedisCluster.set(nameKey, "zhangsan")); System.out.println(jedisCluster.get(nameKey)); System.out.println(jedisCluster.getSet(nameKey, "lisi")); System.out.println(jedisCluster.get(nameKey)); } /** * append: 追加. 其返回值是追加後資料的長度 */ @Test public void appendTest() throws InterruptedException { System.out.println(jedisCluster.append(nameKey, "aa")); System.out.println(jedisCluster.get(nameKey)); System.out.println(jedisCluster.append(nameKey, "lisi")); System.out.println(jedisCluster.get(nameKey)); } /** * incrf: * 將 key 中儲存的數字值增一。 如果 key 不存在,那麼 key 的值會先被初始化為 0 ,然後再執行 INCR 操作。 如果值包含錯誤的型別,或字串型別的值不能表示為數字,那麼返回一個錯誤。 本操作的值限制在 64 位(bit)有符號數字表示之內。 這是一個針對字串的操作,因為 Redis 沒有專用的整數型別,所以 key 內儲存的字串被解釋為十進位制 64 位有符號整數來執行 INCR 操作。 返回值: 執行 INCR 命令之後 key 的值。 這裡有問題,最終資料結果大於10000 後續在研究 TODO 這是因為設定的超時時間太小了,他去重試了,所以最終結果大於10000 */ @Test public void incrTest() throws InterruptedException { /** * 測試執行緒安全 */ jedisCluster.del("incrNum"); final AtomicInteger atomicInteger = new AtomicInteger(0); final CountDownLatch countDownLatch = new CountDownLatch(10); ExecutorService executorService = Executors.newFixedThreadPool(10); for(int i = 0 ; i < 10 ; i ++){ executorService.submit(new Runnable() { @Override public void run() { //每個執行緒增加1000次,每次加1 for(int j = 0 ; j < 1000 ; j ++){ atomicInteger.incrementAndGet(); jedisCluster.incr("incrNum"); } countDownLatch.countDown(); } }); } countDownLatch.await(); System.out.println(jedisCluster.get("incrNum")); System.out.println(atomicInteger); } /** * * @throws InterruptedException */ @Test public void hashTest() throws InterruptedException { String hashKey = "hashKey"; jedisCluster.del(hashKey); System.out.println(jedisCluster.hset(hashKey, "program_zhangsan", "111")); System.out.println(jedisCluster.hexists(hashKey, "program_zhangsan")); System.out.println(jedisCluster.hset(hashKey, "program_zhangsan", "222")); System.out.println(jedisCluster.hset(hashKey, "program_wangwu", "333")); System.out.println(jedisCluster.hset(hashKey, "program_lisi", "444")); System.out.println("hkeys:" + jedisCluster.hkeys(hashKey)); System.out.println(jedisCluster.hgetAll(hashKey)); System.out.println(jedisCluster.hincrBy(hashKey, "program_zhangsan", 2)); System.out.println(jedisCluster.hmget(hashKey, "program_zhangsan", "program_lisi")); jedisCluster.hdel(hashKey, "program_wangwu"); System.out.println(jedisCluster.hgetAll(hashKey)); System.out.println("hsetnx:" + jedisCluster.hsetnx(hashKey, "program_lisi", "666")); System.out.println("hvals:" + jedisCluster.hvals(hashKey)); System.out.println("expire:" + jedisCluster.expire(hashKey, 3)); for(int i = 0 ; i < 5 ; i ++){ System.out.println(jedisCluster.hgetAll(hashKey)); Thread.sleep(1000); } } /** * 模擬先進先出佇列 * 生產者 消費者 */ @Test public void queue() throws InterruptedException { String key = prefix + KEY_SPLIT + "queue"; jedisCluster.del(key); System.out.println(jedisCluster.lpush(key, "1", "2", "3")); System.out.println(jedisCluster.lpush(key, "4")); System.out.println(jedisCluster.lpush(key, "5")); System.out.println("lrange:" + jedisCluster.lrange(key, 0, -1)); System.out.println("lindex[2]:" + jedisCluster.lindex(key, 2)); //在“3”的前面插入“100” System.out.println("linsert:" + jedisCluster.linsert(key, Client.LIST_POSITION.BEFORE, "3", "100")); System.out.println("lrange:" + jedisCluster.lrange(key, 0, -1)); //寫進去的順序是12345,讀取出來的也是12345 for(int i = 0 ; i< 6 ; i ++){ System.out.println(jedisCluster.rpop(key)); } // 如果想達到生產者消費者那種模式需要使用阻塞式佇列才可。這個另外寫多個客戶端測試。 } /** * Set集合 */ @Test public void setTest() throws InterruptedException { String keyA = "{" + prefix + KEY_SPLIT + "set}a"; String keyB = "{" + prefix + KEY_SPLIT + "set}b"; jedisCluster.del(keyA); jedisCluster.del(keyB); System.out.println(jedisCluster.sadd(keyA, "a", "b", "c"));//給集合新增資料 System.out.println(jedisCluster.sadd(keyA, "a"));//給集合新增資料.集合是不可以重複的 System.out.println(jedisCluster.sadd(keyA, "d"));//給集合新增資料 System.out.println(jedisCluster.smembers(keyA));//返回集合所有資料 System.out.println(jedisCluster.scard(keyA));//返回集合長度 System.out.println("c是否在集合A中:" + jedisCluster.sismember(keyA, "c"));//判斷 member 元素是否集合 key 的成員。 /* 從 Redis 2.6 版本開始, SRANDMEMBER 命令接受可選的 count 引數: 如果 count 為正數,且小於集合基數,那麼命令返回一個包含 count 個元素的陣列,陣列中的元素各不相同。如果 count 大於等於集合基數,那麼返回整個集合。 如果 count 為負數,那麼命令返回一個數組,陣列中的元素可能會重複出現多次,而陣列的長度為 count 的絕對值。 */ System.out.println(jedisCluster.srandmember(keyA));//返回集合中的一個隨機元素。 System.out.println(jedisCluster.spop(keyA)); //移除並返回集合中的一個隨機元素。 System.out.println(jedisCluster.smembers(keyA));//返回集合所有資料 System.out.println("---------"); /* SMOVE 是原子性操作。 如果 source 集合不存在或不包含指定的 member 元素,則 SMOVE 命令不執行任何操作,僅返回 0 。否則, member 元素從 source 集合中被移除,並新增到 destination 集合中去。 當 destination 集合已經包含 member 元素時, SMOVE 命令只是簡單地將 source 集合中的 member 元素刪除。 當 source 或 destination 不是集合型別時,返回一個錯誤。 注:不可以在redis-cluster中使用SMOVE:redis.clients.jedis.exceptions.JedisClusterException: No way to dispatch this command to Redis Cluster because keys have different slots. 解決辦法可以參考上面的mset命令,使用“{}”來講可以設定的同一個slot中 */ System.out.println(jedisCluster.smove(keyA, keyB, "a"));//返回集合所有資料 System.out.println("keyA: "+jedisCluster.smembers(keyA));//返回集合所有資料 System.out.println("keyB: "+jedisCluster.smembers(keyB));//返回集合所有資料 System.out.println(jedisCluster.sadd(keyB, "a", "f", "c"));//給集合新增資料 System.out.println(jedisCluster.sdiff(keyA, keyB));//差集 keyA-keyB System.out.println(jedisCluster.sinter(keyA, keyB));//交集 System.out.println(jedisCluster.sunion(keyA, keyB));//並集 } /** * sortedSet集合 */ @Test public void sortedSetTest() throws InterruptedException { String keyA = "{"+prefix + KEY_SPLIT + "sortedSet}a"; String keyB = "{"+prefix + KEY_SPLIT + "sortedSet}b"; String keyC = "{"+prefix + KEY_SPLIT + "sortedSet}c"; jedisCluster.del(keyA); jedisCluster.del(keyB); System.out.println(jedisCluster.zadd(keyA, 10, "aa")); Map<String, Double> map = new HashMap<>(); map.put("b", 8.0); map.put("c", 4.0); map.put("d", 6.0); System.out.println(jedisCluster.zadd(keyA, map)); System.out.println(jedisCluster.zcard(keyA));//返回有序集 key 的數量。 System.out.println(jedisCluster.zcount(keyA, 3, 8));//返回有序集 key 中score某個範圍的數量。 System.out.println("zrange: "+jedisCluster.zrange(keyA, 0, -1));//返回有序集 key 中,指定區間內的成員。按score從小到大 System.out.println("zrevrange: "+jedisCluster.zrevrange(keyA, 0, -1));//返回有序集 key 中,指定區間內的成員。按score從大到小 System.out.println("zrangeWithScores: "+jedisCluster.zrangeWithScores(keyA, 0, -1));//返回有序集 key 中,指定區間內的成員。按score從小到大.包含分值 System.out.println("zscore: "+jedisCluster.zscore(keyA, "aa")); /* 返回有序集 key 中,所有 score 值介於 min 和 max 之間(包括等於 min 或 max )的成員。有序整合員按 score 值遞增(從小到大)次序排列。 具有相同 score 值的成員按字典序(lexicographical order)來排列(該屬性是有序集提供的,不需要額外的計算)。 */ System.out.println("zrangeByScore: "+jedisCluster.zrangeByScore(keyA, 3, 8)); System.out.println("zrank: "+jedisCluster.zrank(keyA, "c"));//返回有序集 key 中成員 member 的排名。其中有序整合員按 score 值遞增(從小到大)順序排列。 System.out.println("zrevrank: "+jedisCluster.zrevrank(keyA, "c"));//返回有序集 key 中成員 member 的排名。其中有序整合員按 score 值遞增(從大到小)順序排列。 System.out.println("zrem: "+jedisCluster.zrem(keyA, "c", "a"));//移除有序集 key 中的一個或多個成員,不存在的成員將被忽略。 System.out.println("zrange: "+jedisCluster.zrange(keyA, 0, -1)); System.out.println("zremrangeByRank: "+jedisCluster.zremrangeByRank(keyA, 1, 2));//按下標刪除 System.out.println("zrange: "+jedisCluster.zrange(keyA, 0, -1)); System.out.println("zremrangeByScore: "+jedisCluster.zremrangeByScore(keyA, 1, 3));//按評分刪除 System.out.println("zrange: "+jedisCluster.zrange(keyA, 0, -1)); /* 接下來這幾個操作,需要使用"{}"使得key落到同一個slot中才可以 */ System.out.println("-------"); System.out.println(jedisCluster.zadd(keyB, map)); System.out.println("zrange: "+jedisCluster.zrange(keyB, 0, -1)); /* ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] 計算給定的一個或多個有序集的並集,其中給定 key 的數量必須以 numkeys 引數指定,並將該並集(結果集)儲存到 destination 。 預設情況下,結果集中某個成員的 score 值是所有給定集下該成員 score 值之 和 。 WEIGHTS 使用 WEIGHTS 選項,你可以為 每個 給定有序集 分別 指定一個乘法因子(multiplication factor),每個給定有序集的所有成員的 score 值在傳遞給聚合函式(aggregation function)之前都要先乘以該有序集的因子。 如果沒有指定 WEIGHTS 選項,乘法因子預設設定為 1 。 AGGREGATE 使用 AGGREGATE 選項,你可以指定並集的結果集的聚合方式。 預設使用的引數 SUM ,可以將所有集合中某個成員的 score 值之 和 作為結果集中該成員的 score 值;使用引數 MIN ,可以將所有集合中某個成員的 最小 score 值作為結果集中該成員的 score 值;而引數 MAX 則是將所有集合中某個成員的 最大 score 值作為結果集中該成員的 score 值。 */ System.out.println("zunionstore: "+jedisCluster.zunionstore(keyC, keyA, keyB));//合併keyA和keyB並儲存到keyC中 System.out.println("zrange: "+jedisCluster.zrange(keyC, 0, -1)); System.out.println("zinterstore: "+jedisCluster.zinterstore(keyC, keyA, keyB));//交集 System.out.println("zrange: "+jedisCluster.zrange(keyC, 0, -1)); } /** * 列表 排序 */ @Test public void sort() throws InterruptedException { String key = prefix + KEY_SPLIT + "queue"; jedisCluster.del(key); System.out.println(jedisCluster.lpush(key, "1", "5", "3", "20", "6")); System.out.println(jedisCluster.lrange(key, 0, -1)); System.out.println(jedisCluster.sort(key)); System.out.println(jedisCluster.lrange(key, 0, -1)); } /** * lua指令碼 */ @Test public void script() throws InterruptedException { /* * 其中 "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 是被求值的 Lua 指令碼,數字 2 指定了鍵名引數的數量, * key1 和 key2 是鍵名引數,分別使用 KEYS[1] 和 KEYS[2] 訪問,而最後的 first 和 second 則是附加引數,可以通過 ARGV[1] 和 ARGV[2] 訪問它們。 * * 注意,這裡一些操作不適用於redis-cluster,主要還是因為不同的key被分配到了不同的slot中 */ Object eval = jedisCluster.eval("return {KEYS[1],ARGV[1],ARGV[2]}", 1, "lua", "key1", "dd"); System.out.println(eval); //腳本里使用的所有鍵都應該由 KEYS 陣列來傳遞: //因為:所有的 Redis 命令,在執行之前都會被分析,籍此來確定命令會對哪些鍵進行操作。因此,對於 EVAL 命令來說,必須使用正確的形式來傳遞鍵,才能確保分析工作正確地執行 System.out.println(jedisCluster.eval("return redis.call('set', KEYS[1], ARGV[1])", 1, "luaTest", "cv")); System.out.println(jedisCluster.get("luaTest")); //注意這裡需要指定KEY,因為這裡lua指令碼也是和slot掛鉤的 String scriptLoad = jedisCluster.scriptLoad("return redis.call('get', KEYS[1])", "luaTest");//載入指令碼 System.out.println(scriptLoad);//返回的SHA1校驗和,後續可以直接使用這個進行操作。 System.out.println(jedisCluster.scriptExists(scriptLoad, "luaTest"));//檢查是否存在 System.out.println(jedisCluster.evalsha(scriptLoad, 1, "luaTest"));//執行lua指令碼 System.out.println(jedisCluster.scriptFlush("luaTest".getBytes()));//刪除KEY as 上的所有lua指令碼 System.out.println(jedisCluster.scriptExists(scriptLoad, "luaTest")); System.out.println(jedisCluster.evalsha(scriptLoad, 1, "luaTest"));//指令碼已經刪除,返回錯誤:NOSCRIPT No matching script. Please use EVAL. } /** * redis中的lua指令碼做了很多限制,防止隨機性的發生。比如lua指令碼中返回的總是有序的集合。 * 詳情見 http://doc.redisfans.com/script/eval.html - 純函式指令碼 */ @Test public void scriptFuc() throws InterruptedException { String key = "luaTest"; System.out.println(jedisCluster.del(key)); System.out.println(jedisCluster.sadd(key, "10","3","7","40","6")); System.out.println(jedisCluster.smembers(key));//這裡怎麼返回的值是有序的? [3, 6, 7, 10, 40] System.out.println(jedisCluster.eval("return redis.call('smembers', KEYS[1])", 1, key));//根據字母序排序 [10, 3, 40, 6, 7] } /** * 使用lua指令碼記錄日誌 * @throws InterruptedException */ @Test public void redisLog() throws InterruptedException { //這裡後面必須要有key??? System.out.println(jedisCluster.eval("redis.log(redis.LOG_WARNING, 'Something is wrong with this script.')", 1, "afd")); } /** * 模擬先進先出 定長 佇列 * 參考 http://www.cnblogs.com/stephen-liu74/archive/2012/02/14/2351859.html * https://www.v2ex.com/t/65663 */ @Test public void queue1() throws InterruptedException { String key = prefix + KEY_SPLIT + "queue"; Long del = jedisCluster.del(key); System.out.println(del); //我們定義佇列長度是5 int length = 5; System.out.println(jedisCluster.lpush(key, "1", "2", "3", "4", "5", "6", "7")); List<String> list = jedisCluster.lrange(key, 0, -1);//列印所有資料 System.out.println(list); Long llen = jedisCluster.llen(key); System.out.println("目前佇列長度:" + llen); /** * 該命令將僅保留指定範圍內的元素 * 每次lpush以後,就用ltrim進行擷取,已達到定長佇列(或定長list)的目的 * * 注意: * 這裡根據實際業務需求,超長了不一定擷取丟掉,也可以進行分流限流報警處理或者其他阻塞操作 */ System.out.println(jedisCluster.ltrim(key, 0, length - 1)); System.out.println(jedisCluster.lrange(key, 0, -1)); } /** * 分散式互斥鎖 * 一般是通過 set nx ex 實現的 * 但是這樣不完美,具體參考 http://ifeve.com/redis-lock/ */ @Test public void lock() throws InterruptedException{ String key = prefix + KEY_SPLIT + "lock"; /** * 複合命令: SET KEY VALUE [EX seconds] [PX milliseconds] [NX|XX] */ System.out.println(jedisCluster.set(key, "本機ID", "nx", "ex", 3)); for (int i = 0 ; i < 6 ; i ++) { System.out.println(jedisCluster.get(key)); Thread.sleep(1000); } } @After public void after(){ try { if(jedisCluster != null) jedisCluster.close(); } catch (IOException e) { e.printStackTrace(); } } }

JedisPubSubListener .java

import redis.clients.jedis.JedisPubSub;

public class JedisPubSubListener extends JedisPubSub {
    // 取得訂閱的訊息後的處理  
    public void onMessage(String channel, String message) {  
        System.out.println(channel + "=" + message);  
    }  

    // 初始化訂閱時候的處理  
    public void onSubscribe(String channel, int subscribedChannels) {  
        System.out.println(channel + "=" + subscribedChannels);  
    }  

    // 取消訂閱時候的處理  
    public void onUnsubscribe(String channel, int subscribedChannels) {  
        System.out.println(channel + "=" + subscribedChannels);  
    }  

    // 初始化按表示式的方式訂閱時候的處理  
    public void onPSubscribe(String pattern, int subscribedChannels) {  
        System.out.println(pattern + "=" + subscribedChannels);  
    }  

    // 取消按表示式的方式訂閱時候的處理  
    public void onPUnsubscribe(String pattern, int subscribedChannels) {  
        System.out.println(pattern + "=" + subscribedChannels);  
    }  

    // 取得按表示式的方式訂閱的訊息後的處理  
    public void onPMessage(String pattern, String channel, String message) {  
        System.out.println(pattern + "=" + channel + "=" + message);  
    }  
}