4.2闯袒、Java客戶(hù)端Jedis

Java客戶(hù)端Jedis

Java有很多優(yōu)秀的Redis客戶(hù)端(詳見(jiàn):http://redis.io/clients#java ),
這里介紹使用較為廣泛的客戶(hù)端Jedis游岳,本節(jié)將按照以下幾個(gè)方面對(duì)Jedis進(jìn)行介
紹:

- 獲取Jedis
- Jedis的基本使用
- Jedis連接池使用
- Jedis中Pipleline使用
- Jedis的Lua腳本使用
  1. 獲取Jedis

    Jedis屬于Java的第三方開(kāi)發(fā)包政敢,在Java中獲取第三方開(kāi)發(fā)包通常有兩種方式:

    • 直接下載目標(biāo)版本的Jedis-${version}.jar包加入到項(xiàng)目中。
    • 使用集成構(gòu)建工具胚迫,例如maven喷户、gradle等將Jedis目標(biāo)版本的配置加入到項(xiàng)
      目中。

    通常在實(shí)際項(xiàng)目中使用第二種方式访锻,但如果只是想測(cè)試一下Jedis褪尝,第一種方法
    也是可以的。在寫(xiě)本書(shū)時(shí)期犬,Jedis最新發(fā)布的穩(wěn)定版本2.8.2河哑,以Maven為例
    子,在項(xiàng)目中加入下面的依賴(lài)即可:

    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.8.2</version>
    </dependency>
    

    對(duì)于第三方開(kāi)發(fā)包龟虎,版本的選擇也是至關(guān)重要的璃谨,因?yàn)镽edis更新速度比較快,
    如果客戶(hù)端跟不上服務(wù)端的速度鲤妥,有些特性和bug不能及時(shí)更新睬罗,不利于日常開(kāi)
    發(fā)。通常來(lái)講選取第三方開(kāi)發(fā)包有如下兩個(gè)策略:

    • 選擇比較穩(wěn)定的版本旭斥,也就是盡可能選擇穩(wěn)定的里程碑版本,這些版本已經(jīng)經(jīng)
      過(guò)多次alpha古涧,beta的修復(fù)垂券,基本算是穩(wěn)定了。

    • 選擇更新活躍的第三方開(kāi)發(fā)包羡滑,例如Redis3.0有了Redis Cluster新特性菇爪,
      但是如果使用的客戶(hù)端一直不支持,并且維護(hù)的人也比較少柒昏,這種就謹(jǐn)慎選擇凳宙。

    本節(jié)介紹的Jedis基本滿(mǎn)足上述兩個(gè)特點(diǎn),下面將對(duì)Jedis的基本使用方法進(jìn)行
    介紹职祷。

  2. Jedis的基本使用方法

    Jedis的使用方法非常簡(jiǎn)單氏涩,只要下面三行代碼就可以實(shí)現(xiàn)get功能:

    # 1. 生成一個(gè)Jedis對(duì)象届囚,這個(gè)對(duì)象負(fù)責(zé)和指定Redis實(shí)例進(jìn)行通信
    Jedis jedis = new Jedis("127.0.0.1", 6379);
    # 2. jedis執(zhí)行set操作
    jedis.set("hello", "world");
    # 3. jedis執(zhí)行g(shù)et操作,value="world"
    String value = jedis.get("hello");
    

    可以看到初始化Jedis需要兩個(gè)參數(shù):Redis實(shí)例的IP和端口是尖,除了這兩個(gè)參數(shù)
    外意系,還有一個(gè)包含了四個(gè)參數(shù)的構(gòu)造函數(shù)是比較常用的:

    Jedis(final String host, final int port, final int connectionTimeout, final int soTImeout)
    

    參數(shù)說(shuō)明:

    • host:Redis實(shí)例的所在機(jī)器的IP。
    • port:Redis實(shí)例的端口饺汹。
    • connectionTimeout:客戶(hù)端連接超時(shí)蛔添。
    • soTimeout:客戶(hù)端讀寫(xiě)超時(shí)。

    如果想看一下執(zhí)行如果:

    String setResult = jedis.set("hello", "world");
    String getResult = jedis.get("hello");
    System.out.println(setResult);
    System.out.println(getResult);
    

    輸入結(jié)果為:

    OK
    world
    

    可以看到j(luò)edis.set的返回結(jié)果是OK兜辞,和redis-cli的執(zhí)行效果是一樣的迎瞧,只不
    過(guò)結(jié)果類(lèi)型變?yōu)榱薐ava的數(shù)據(jù)類(lèi)型。上面的這種寫(xiě)法只是為了演示使用逸吵,在實(shí)
    際項(xiàng)目中比較推薦使用try catch finally的形式來(lái)進(jìn)行來(lái)進(jìn)行代碼的書(shū)寫(xiě):
    一方面可以在Jedis出現(xiàn)異常的時(shí)候(本身是網(wǎng)絡(luò)操作)凶硅,將異常進(jìn)行捕獲或者
    拋出;另一個(gè)方面無(wú)論執(zhí)行成功或者失敗胁塞,將Jedis連接關(guān)閉掉咏尝,在開(kāi)發(fā)中關(guān)閉
    不用的連接資源是一種好的習(xí)慣,代碼類(lèi)似如下:

    Jedis jedis = null;
    try{
        jedis = new Jedis("127.0.0.1", 6379);
        jedis.get("hello");
    }catch (Exception e){
        logger.error(e.getMessage(), e);
    }finally{
        if(jedis != null){
            jedis.close();
        }
    }
    

    下面用一個(gè)例子說(shuō)明Jedis對(duì)于Redis五種數(shù)據(jù)結(jié)構(gòu)的操作啸罢,為了節(jié)省篇幅编检,所
    有返回結(jié)果放在注釋中。

    //1.string
    //輸出結(jié)果:OK
    jedis.set("hello", "world");
    //輸出結(jié)果:world
    jedis.get("hello");
    //輸出結(jié)果:1
    jedis.incr("counter");
    
    //2.hash
    jedis.hset("myhash", "f1", "v1");
    jedis.hset("myhash", "f2", "v2");
    //輸出結(jié)果:{f1=v1, f2=v2}
    jedis.hgetAll("myhash");
    
    //3.list
    jedis.rpush("mylist", "1");
    jedis.rpush("mylist", "2");
    jedis.rpush("mylist", "3");
    //輸出結(jié)果:{1, 2, 3}
    jedis.lrange("mylist", 0, -1);
    
    //4.set
    jedis.sadd("myset", "a");
    jedis.sadd("myset", "b");
    jedis.sadd("myset", "a");
    //輸出結(jié)果:{b, a}
    jedis.smemebers{b, a};
    
    //5.zset
    jedis.zadd("myzset", 99, "tom");
    jedis.zadd("myzset", 66, "peter");
    jedis.zadd("myzset", 33, "james");
    //輸出結(jié)果:[[["james"], 33.0], [["peter"], 66.0], [["tom"],99.0]]
    jedis.zrangeWithScores("myset", 0, -1);
    

    參數(shù)除了可以是字符串扰才,Jedis還提供了字節(jié)數(shù)組的參數(shù)允懂,例如:

    public String set(final String key, String value)
    public String set(final String key, final byte[] value)
    public byte[] get(final byte[] key
    public String get(final String key)
    

    有了這些API的支持,就可以將Java對(duì)象序列化為二進(jìn)制衩匣,當(dāng)應(yīng)用需要獲取Java
    對(duì)象時(shí)蕾总, 使用get(final byte[] key)函數(shù)將字節(jié)數(shù)組取出,然后反序列化為
    Java對(duì)象即可琅捏。和很多NoSQL數(shù)據(jù)庫(kù)的客戶(hù)端不同生百,Jedis本身沒(méi)有提供序列化
    的工具,也就是說(shuō)開(kāi)發(fā)者需要自己引入序列化的工具柄延。序列化的工具有很多蚀浆,例
    如XML、Json搜吧、谷歌的Protobuf市俊、Facebook的Thrift等等,對(duì)于序列化工具的
    選擇開(kāi)發(fā)者可以根據(jù)自己需求決定滤奈。

  3. Jedis連接吃的使用方法

    上節(jié)介紹的是Jedis的直連方式摆昧,所謂直連是指Jedis每次都會(huì)新建TCP連接,使
    用后再斷開(kāi)連接蜒程,對(duì)于頻繁訪(fǎng)問(wèn)Redis的場(chǎng)景顯然不是高效的使用方式绅你。因此生
    產(chǎn)環(huán)境中一般使用連接池的方式對(duì)Jedis連接進(jìn)行管理伺帘,所有Jedis對(duì)象預(yù)先放
    在池子中(JedisPool),每次要連接Redis勇吊,只需要在池子中劫曼追,用完了再歸
    還給池子。

    客戶(hù)端連接Redis使用的是TCP協(xié)議汉规,直連的方式每次需要建立TCP連接礼殊,而連接
    池的方式可以預(yù)先初始化號(hào)Jedis連接,所以每次只需要從Jedis連接池借用即
    可针史,而借用和歸還操作是在本地進(jìn)行的晶伦,只有少量的并發(fā)同步開(kāi)銷(xiāo),遠(yuǎn)遠(yuǎn)小于新
    建TCP連接的開(kāi)銷(xiāo)啄枕。另外智聯(lián)的方式無(wú)法限制Jedis對(duì)象的個(gè)數(shù)婚陪,在極端情況下
    可能會(huì)造成連接泄露,而連接池的形式可以有效地保護(hù)和控制資源的使用频祝。但是
    直連的方式也并不是一無(wú)是處泌参,下表給出兩種方式各自的優(yōu)劣勢(shì)。

    方式 優(yōu)點(diǎn) 缺點(diǎn)
    直連 簡(jiǎn)單方便常空,適用于少量長(zhǎng)期連接的場(chǎng)景 1.存在每次/關(guān)閉TCP連接開(kāi)銷(xiāo) 2.資源無(wú)法控制沽一,極端情況會(huì)出現(xiàn)連接泄露 3.Jedis對(duì)象線(xiàn)程不安全
    連接池 1.無(wú)需每次連接都生成Jedis對(duì)象,降低開(kāi)銷(xiāo) 2.使用連接池的形式保護(hù)和控制資源的使用 相對(duì)于直連漓糙,使用相對(duì)麻煩铣缠,尤其在資源的管理上需要很多參數(shù)來(lái)保證,一旦規(guī)劃不合理也會(huì)出現(xiàn)問(wèn)題

    Jedis提供了JedisPool這個(gè)類(lèi)作為對(duì)Jedis的連接池昆禽,同時(shí)使用了Apache的通
    用對(duì)象池工具common-pool作為資源的管理工具蝗蛙,下面是使用JedisPool操作
    Redis的代碼實(shí)例:

    1) Jedis連接池(通常JedisPool是單例的):

    //common-pool連接池配置,這里使用默認(rèn)配置醉鳖,后面小節(jié)會(huì)介紹具體配置說(shuō)明
    GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
    //初始化Jedis連接池
    JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);
    

    2)獲取Jedis對(duì)象不再是直接生成一個(gè)Jedis對(duì)象進(jìn)行直連捡硅,而是從連接池直接
    獲取,代碼如下:

    Jedis jedis = null;
    try{
        //1. 從連接池獲取jedis對(duì)象
        jedis = jedisPool.getResource();
        //2. 執(zhí)行操作
        jedis.get("hello");
    }catch (Exception e){
        logger.error(e.getMessage(), e);
    }finally{
        if(jedis != null){
            //如果適應(yīng)JedisPool盗棵,close操作不是關(guān)閉連接病曾,代表歸還連接池
            jedis.close();
        }
    }
    

    這里可以看到在finally中依然是jedis.close()操作,為什么會(huì)把連接關(guān)閉
    呢漾根,這不和連接池的原則違背了嗎?但實(shí)際上Jedis的close()實(shí)現(xiàn)方式如下:

    public void close(){
        //使用Jedis連接池
        if(dataSource != null){
            if(client.isBroken){
                this.dataSource.returnBrokenResource(this);
            }else{
                this.dataSource.returnResource(this);
            }
        }else{
            client.close();
        }
    }
    

    參數(shù)說(shuō)明:

    • dataSource != null代表使用的是連接池鲫竞,所以jedis.close()代表兌換連
      接給連接池辐怕,而且Jedis會(huì)判斷當(dāng)前連接是否已經(jīng)斷開(kāi)。

    • dataSource == null代表直連从绘,jedis.close()代表關(guān)閉連接寄疏。

    前面GenericObjectPoolConfig使用的是默認(rèn)配置是牢,實(shí)際它提供有很多參數(shù),
    例如池子中最大連接數(shù)陕截、最大空閑連接數(shù)驳棱、最小空閑李娜結(jié)束、連接活性檢測(cè)农曲,
    等等社搅,下表給出GenericObjectPoolConfig其他屬性及其含義解釋。

    參數(shù)名 含義 默認(rèn)值
    maxActive 連接池中最大連接數(shù) 8
    maxIdle 連接池中最大空閑的連接數(shù) 8
    minIdle 連接池中最少空間的李娜結(jié)束 0
    maxWaitMillis 當(dāng)連接池資源用盡后乳规,調(diào)用者的最大等待時(shí)間(單位為毫秒)形葬,一般不建議使用默認(rèn)值 -1,表示永遠(yuǎn)不超時(shí)暮的,一直等笙以。
    jmxEnabled 是否開(kāi)啟jmx監(jiān)控,如果應(yīng)用開(kāi)啟了jmx端口并且jmxEnabled設(shè)置為true冻辩,就可以通過(guò)jconsole或者jvisualvm看到關(guān)于連接池的相關(guān)統(tǒng)計(jì)猖腕,有助于了解連接池的使用情況,并且可以針對(duì)其做監(jiān)控統(tǒng)計(jì)恨闪。 true
    minEvictableIdleTimeMillis 連接的最小空間時(shí)間倘感,達(dá)到此值后空閑連接將被移除 1000L * 60L * 30毫秒 = 30分鐘
    numTestsPerEvictionRun 做空間連接檢測(cè)時(shí),每次的采樣數(shù) 3
    testOnBorrow 想連接池借用連接是否做連接有效性檢測(cè)(ping)凛剥,無(wú)效連接會(huì)被移除侠仇,每次借用多執(zhí)行一次ping命令 false
    testOnReturn 向連接池歸還連接時(shí)是否做連接有效性檢測(cè)(ping),無(wú)效連接會(huì)被移除犁珠,每次歸還多執(zhí)行一次ping命令 false
    timeBetweenEvictionRunMillis 空閑連接的檢測(cè)周期(單位為毫秒) -1:表示不做檢測(cè)
    blockWhenExhausted 當(dāng)連接池用盡后逻炊,調(diào)用者是否要等待,這個(gè)參數(shù)是和maxWaitMillis對(duì)應(yīng)的犁享,只有當(dāng)此參數(shù)為true時(shí)余素,maxWaitMillis才會(huì)生效 true
  4. Redis中Pipeline的使用方法

    下面代碼是借助Pipeline來(lái)模擬批量刪除:

    public void mdel(List<String> keys){
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        //1)生成pipeline對(duì)象
        Pipeline pipeline = jedis.pipelined();
        //2)pipeline執(zhí)行命令,注意此事命令并未真正執(zhí)行
        for(String key : keys){
            pipeline.del(key);
        }
        //3)執(zhí)行命令
        pipeline.sync();
    }
    

    除了pipeline炊昆。sync(),還可以使用pipeline.syncAndReturnAll()將
    pipeline的命令進(jìn)行返回桨吊,例如下面代碼將set和incr做了一次pipeline操
    作,并順序打印了兩個(gè)命令的結(jié)果:

    Jedis jedis = new Jedis("127.0.0.1", 6379);
    Pipeline pipeline = jedis.pipelined();
    pipeline.set("hello", "world");
    pipeline.incr("counter");
    List<Object> resultList = pipeline.syncAndReturnAll();
    for(Object object : resultList){
        System.out.println(object);
    }
    

    輸出結(jié)果為:

    OK
    1
    
  5. Jedis的Lua腳本

    Jedis中執(zhí)行Lua腳本和redis-cli十分類(lèi)似凤巨,Jedis提供了三個(gè)重要的函數(shù)實(shí)現(xiàn)
    Lua腳本的執(zhí)行:

    Object eval(String script, int key count, String ... params)
    Object evalSha(Stirng sha1, int keyCount, String ... params)
    String scriptLoad(String script)
    

    eval函數(shù)有三個(gè)參數(shù)视乐,分別是:

    • script:Lua腳本內(nèi)容。
    • keyCount:鍵的個(gè)數(shù)敢茁。
    • params:相關(guān)參數(shù)KEYS和ARGV佑淀。

    scriptLoad和evalSha函數(shù)要一起使用,首先使用scriptLoad將腳本加載到
    Redis中彰檬,evalSha函數(shù)用來(lái)執(zhí)行腳本的SHA1校驗(yàn)和伸刃,它需要三個(gè)參數(shù):

    • scriptSha:腳本的SHA1谎砾。
    • keyCount:鍵的個(gè)數(shù)。
    • params:相關(guān)參數(shù)KEYS和ARGV
  6. 重點(diǎn)注意以下幾點(diǎn):

    1) Jedis操作放在 try catch finally里更加合理捧颅。
    2) 區(qū)分直連和連接池兩種實(shí)現(xiàn)方式的優(yōu)缺點(diǎn)景图。
    3) jedis.close()方法的兩種實(shí)現(xiàn)方式。
    4) Jedis依賴(lài)了common-pool
    5) 如果key和value涉及了字節(jié)數(shù)組碉哑,需要自己選擇適合的序列化方法挚币。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谭梗,隨后出現(xiàn)的幾起案子忘晤,更是在濱河造成了極大的恐慌,老刑警劉巖激捏,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件设塔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡远舅,警方通過(guò)查閱死者的電腦和手機(jī)闰蛔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)图柏,“玉大人序六,你說(shuō)我怎么就攤上這事≡榇担” “怎么了例诀?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)裁着。 經(jīng)常有香客問(wèn)我繁涂,道長(zhǎng),這世上最難降的妖魔是什么二驰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任扔罪,我火速辦了婚禮,結(jié)果婚禮上桶雀,老公的妹妹穿的比我還像新娘矿酵。我一直安慰自己,他們只是感情好矗积,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布全肮。 她就那樣靜靜地躺著,像睡著了一般棘捣。 火紅的嫁衣襯著肌膚如雪辜腺。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音哪自,去河邊找鬼。 笑死禁熏,一個(gè)胖子當(dāng)著我的面吹牛壤巷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瞧毙,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼胧华,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了宙彪?” 一聲冷哼從身側(cè)響起矩动,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎释漆,沒(méi)想到半個(gè)月后悲没,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡男图,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年示姿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逊笆。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡栈戳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出难裆,到底是詐尸還是另有隱情子檀,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布乃戈,位于F島的核電站褂痰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏偏化。R本人自食惡果不足惜脐恩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望侦讨。 院中可真熱鬧驶冒,春花似錦、人聲如沸韵卤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沈条。三九已至需忿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背屋厘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工涕烧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人汗洒。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓议纯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親溢谤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瞻凤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容