Redis筆記整理(二):Java API使用與Redis分佈式集羣環境搭建

[TOC]java


Redis Java API使用(一):單機版本Redis API使用

Redis的Java API經過Jedis來進行操做,所以首先須要Jedis的第三方庫,由於使用的是Maven工程,因此先給出Jedis的依賴:node

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

基本代碼示例

Redis能提供的命令,Jedis也都提供了,並且使用起來很是相似,因此下面只是給出了部分操做的代碼。web

package com.uplooking.bigdata;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Redis操做之java API
 *  jedis 是咱們操做Redis的java api的入口
 *  一個Jedis對象,就表明了一個Redis的鏈接
 * CRUD
 */
public class RedisTest {

    private Jedis jedis;
    private String host;
    private int port;
    @Before
    public void setUp() {
        host = "uplooking01";
        port = 6379;
        jedis = new Jedis(host, port);
    }

    @Test
    public void testCRUD() {
        //後去全部的key的集合
        Set<String> keys = jedis.keys("*");
//        jedis.select(index); 指定要執行操做的數據庫,默認操做的是0號數據
        System.out.println(keys);
        //string
        System.out.println("**************String**************");
        //刪除redis中的key nam1
        Long del = jedis.del("nam1");
        System.out.println(del == 1L ? "刪除成功~" : "刪除失敗~");
        List<String> mget = jedis.mget("name", "age");
        System.out.println(mget);
        //hash
        System.out.println("**************Hash**************");
        Map<String, String> person = jedis.hgetAll("person");
        //keyset
        //entryset
        for (Map.Entry<String, String> me : person.entrySet()) {
            String field = me.getKey();
            String value = me.getValue();
            System.out.println(field + "---" + value);
        }
        //list
        System.out.println("**************List**************");
        List<String> seasons = jedis.lrange("season", 0, -1);
        for (String season : seasons) {
            System.out.println(season);
        }
        //set
        System.out.println("**************Set**************");
        Set<String> nosql = jedis.smembers("nosql");
        for (String db : nosql) {
            System.out.println(db);
        }
        //zset
        System.out.println("**************Zset**************");
        Set<String> website = jedis.zrange("website", 0, -1);
        for (String ws : website) {
            System.out.println(ws);
        }
    }

    @After
    public void cleanUp() {
        jedis.close();
    }
}

JedisPool工具類開發

前面的代碼是每次都創建一個Jedis的鏈接,這樣比較消耗資源,可使用JedisPool來解決這個問題,同時爲了提升後面的開發效率,能夠基於JedisPool來開發一個工具類。redis

JedisUtil.java

package com.uplooking.bigdata.common.util.redis;

import com.uplooking.bigdata.constants.redis.JedisConstants;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.io.IOException;
import java.util.Properties;

/**
 * Redis Java API 操做的工具類
 * 主要爲咱們提供 Java操做Redis的對象Jedis 模仿相似的數據庫鏈接池
 *
 * JedisPool
 */
public class JedisUtil {

    private JedisUtil() {}
    private static JedisPool jedisPool;
    static {
        Properties prop = new Properties();
        try {
            prop.load(JedisUtil.class.getClassLoader().getResourceAsStream("redis/redis.properties"));
            JedisPoolConfig poolConfig = new JedisPoolConfig();

            //jedis鏈接池中最大的鏈接個數
            poolConfig.setMaxTotal(Integer.valueOf(prop.getProperty(JedisConstants.JEDIS_MAX_TOTAL)));
            //jedis鏈接池中最大的空閒鏈接個數
            poolConfig.setMaxIdle(Integer.valueOf(prop.getProperty(JedisConstants.JEDIS_MAX_IDLE)));
            //jedis鏈接池中最小的空閒鏈接個數
            poolConfig.setMinIdle(Integer.valueOf(prop.getProperty(JedisConstants.JEDIS_MIN_IDLE)));
            //jedis鏈接池最大的等待鏈接時間 ms值
            poolConfig.setMaxWaitMillis(Long.valueOf(prop.getProperty(JedisConstants.JEDIS_MAX_WAIT_MILLIS)));

            //表示jedis的服務器主機名
            String host = prop.getProperty(JedisConstants.JEDIS_HOST);
            String JEDIS_PORT = "jedis.port";
            int port = Integer.valueOf(prop.getProperty(JedisConstants.JEDIS_PORT));
            //表示jedis的服務密碼
            String password = prop.getProperty(JedisConstants.JEDIS_PASSWORD);

            jedisPool = new JedisPool(poolConfig, host, port, 10000);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 提供了Jedis的對象
     * @return
     */
    public static Jedis getJedis() {
        return jedisPool.getResource();
    }

    /**
     * 資源釋放
     * @param jedis
     */
    public static void returnJedis(Jedis jedis) {
        jedis.close();
    }
}

JedisConstants.java

package com.uplooking.bigdata.constants.redis;

/**
 * 專門用於存放Jedis的常量類
 */
public interface JedisConstants {

    //表示jedis的服務器主機名
    String JEDIS_HOST = "jedis.host";
    //表示jedis的服務的端口
    String JEDIS_PORT = "jedis.port";
    //表示jedis的服務密碼
    String JEDIS_PASSWORD = "jedis.password";

    //jedis鏈接池中最大的鏈接個數
    String JEDIS_MAX_TOTAL = "jedis.max.total";
    //jedis鏈接池中最大的空閒鏈接個數
    String JEDIS_MAX_IDLE = "jedis.max.idle";
    //jedis鏈接池中最小的空閒鏈接個數
    String JEDIS_MIN_IDLE = "jedis.min.idle";

     //jedis鏈接池最大的等待鏈接時間 ms值
    String JEDIS_MAX_WAIT_MILLIS = "jedis.max.wait.millis";

}

redis.properties

##########################################
###
### redis的配置文件
###
##########################################
###表示jedis的服務器主機名
jedis.host=uplooking01
###表示jedis的服務的端口
jedis.port=6379
###表示jedis的服務密碼
jedis.password=uplooking

###jedis鏈接池中最大的鏈接個數
jedis.max.total=60
###jedis鏈接池中最大的空閒鏈接個數
jedis.max.idle=30
###jedis鏈接池中最小的空閒鏈接個數
jedis.min.idle=5

###jedis鏈接池最大的等待鏈接時間 ms值
jedis.max.wait.millis=30000

後面就能夠很是方便地使用這個工具類來進行Redis的操做:sql

// 得到Jedis鏈接對象
Jedis jedis = JedisUtil.getJedis();
// 釋放Jedis對象資源
JedisUtil.returnJedis(jedis);

Redis分佈式集羣環境搭建

Redis集羣理論知識

Redis集羣是一個分佈式Redis存儲架構,能夠在多個節點之間進行數據共享,解決Redis高可用、可擴展等問題。
   Redis集羣提供瞭如下兩個好處
   1.將數據自動切分(split)到多個節點
   2.當集羣中的某一個節點故障時,redis還可繼續處理客戶端的請求

   一個Redis集羣包含16384個哈希槽(hash slot),數據庫中的每一個數據都屬於這16384個哈希槽中的一個。
   集羣使用公式CRC16(key)%16384來計算key屬於哪個槽。集羣中的每個節點負責處理一部分哈希槽。

集羣中的主從複製
   集羣中的每一個節點都有1個到N個複製品,其中一個爲主節點,其他爲從節點,若是主節點下線了,
   集羣就會把這個主節點的一個從節點設置爲新的主節點,繼續工做。這個集羣就不會由於一個主節點的下線而沒法正常工做。
   若是某一個主節點和它全部的從節點都下線的話,redis集羣就中止工做了。
   Redis集羣不保證數據的強一致性,在特定的狀況下,redis集羣會丟失已經執行過的命令。
   使用異步複製(asynchronous replication)是Redis集羣可能會丟失寫命令的其中一個緣由,
   有時候因爲網絡緣由,若是網絡斷開時間太長,redis集羣就會啓用新的主節點,以前發給主節點的數據聚會丟失。

上面的理論知識,在完成下面Reids主從複製環境和分佈式環境的搭建後,相信會有很是直觀的理解。數據庫

Redis主從複製集羣安裝

這裏使用三臺設備,環境說明以下:vim

uplooking01 master
uplooking02 slave
uplooking03 slave

即uplooking01爲主節點,02和03爲從節點,主節點主要負責寫,從節點主要負責讀,不能寫。
另外在兩臺從服務器上還會配置使用密碼,測試一下使用密碼時的鏈接方式(注意主服務器沒有設置密碼)。
下面的配置會對這些需求有所體現。

uplooking01 redis.conf配置以下:api

bind uplooking01
daemonize yes(後臺運行)
logfile /opt/redis-3.2.0/logs/redis.log(日誌文件,目錄必須存在)

uplooking02 redis.conf配置以下:ruby

bind uplooking02
daemonize yes(後臺運行)
logfile /opt/redis-3.2.0/logs/redis.log(日誌文件,目錄必須存在)

slave-read-only yes
requirepass uplooking

slaveof uplooking01 6379

uplooking03 redis.conf配置以下:bash

bind uplooking03
daemonize yes(後臺運行)
logfile /opt/redis-3.2.0/logs/redis.log(日誌文件,目錄必須存在)

slave-read-only yes
requirepass uplooking

slaveof uplooking01 6379

上面的配置完成後,在兩臺從服務器上分別啓動redis即完成了主從複製集羣的配置,須要注意以下問題:

1.認證的問題
    若是須要鏈接uplooking02或者uplooking03,那麼須要加上密碼,不然沒法完成認證。
    有兩種方式:
        能夠在鏈接時就指定密碼:redis-cli -h uplooking03 -a uplooking
        也能夠先鏈接,到終端後再認證:auth uplooking

2.數據讀寫的問題
    讀:三臺服務器上都能完成數據的讀
    寫:只能在主節點上完成數據的寫入

3.Java API的使用問題
    在前面使用的代碼中,若是鏈接的是從服務器,則還須要配置密碼
    以咱們開發的JedisPool工具類爲例,在建立JedisPool時須要指定密碼:
    jedisPool = new JedisPool(poolConfig, host, port, 10000, password);

Redis分佈式集羣安裝部署

集羣說明:

1.前面搭建的只是主從複製的集羣,這意味着,數據在三臺機器上都是同樣的,其目的只是爲了讀寫分離,提升讀的效率
    同時也能夠起到冗餘的做用,主節點一旦出現故障,從節點能夠替換,但顯然,這只是集羣,而不是分佈式。

2.可是可能會出現一個問題,就是當數據量過大時,全部的數據都保存在同一個節點上
    (雖然兩臺作了備份,但由於保存的數據都是同樣的,因此看作一個節點),
    單臺服務器的數據存儲壓力會很大,所以,能夠考慮使用分佈式的環境來保存,這就是Redis的分佈式集羣。
    分佈式:數據分紅幾份保存在不一樣的設備上
    集羣:對於相同的數據,都會有至少一個副本進行保存。
    這能夠類比hadoop中的hdfs或者是kafka中的partition(topic能夠設置partition數量和副本因子)

3.在Redis中,搭建分佈式集羣環境至少須要6個節點,所以出於設備的考慮,這裏會在同一臺設備上操做
    也就是說,這裏搭建的是僞分佈式環境,3個爲主節點,另外3個分別爲其從節點,用來保存其副本數據。
    根據前面的理論知識,在分佈式環境中,key值會進行以下的計算:
        CRC16(16) % 16384
    來計算key值屬於哪個槽,而對於咱們的環境,每一個主節點的槽位數量大概是16384 / 3 = 5461

1.解壓安裝包

[uplooking@uplooking01 ~]$ mkdir -p app/redis-cluster
[uplooking@uplooking01 ~]$ tar -zxvf soft/redis-3.2.0.tar.gz -C app/redis-cluster/

2.編譯安裝

[uplooking@uplooking01 ~]$ cd app/redis-cluster/redis-3.2.0/
[uplooking@uplooking01 redis-3.2.0]$ pwd
/home/uplooking/app/redis-cluster/redis-3.2.0
[uplooking@uplooking01 redis-3.2.0]$ make
[uplooking@uplooking01 redis-3.2.0]$ make install PREFIX=/home/uplooking/app/redis-cluster/redis-3.2.0

3.建立Redis節點

[uplooking@uplooking01 redis-cluster]$ mv redis-3.2.0/ 7000
[uplooking@uplooking01 redis-cluster]$ cp -r 7000 7001
[uplooking@uplooking01 redis-cluster]$ cp -r 7000 7002
[uplooking@uplooking01 redis-cluster]$ cp -r 7000 7003
[uplooking@uplooking01 redis-cluster]$ cp -r 7000 7004
[uplooking@uplooking01 redis-cluster]$ cp -r 7000 7005

4.修改各個節點的配置

以7000爲例:

daemonize   yes                             //配置redis後臺運行
bind    uplooking01                         //綁定主機uplooking01
logfile "/home/uplooking/app/redis-cluster/7000/redis-7000.log"     //注意目錄要存在
pidfile /var/run/redis-7000.pid             //pidfile文件對應7000,7002,7003
port    7000                                //端口
cluster-enabled yes                         //開啓集羣  把註釋#去掉
cluster-config-file nodes-7000.conf         //集羣的配置  配置文件首次啓動自動生成
cluster-node-timeout    15000               //請求超時  設置15秒夠了
appendonly  yes                             //aof日誌開啓  有須要就開啓,它會每次寫操做都記錄一條日誌

在其它的節點上,只須要修改成7001,7002…便可。

技巧:配置完成7000後,能夠直接複製到其它節點,cp redis.conf ../7001,而後再充分利用vim中的1,$s///g將7000替換爲其它數字,如7001等。

5.啓動各個節點

先建立一個批量啓動的腳本:

[uplooking@uplooking01 redis-cluster]$ cat start-all.sh
#!/bin/bash

cd 7000
bin/redis-server ./redis.conf
cd ..

cd 7001
bin/redis-server ./redis.conf
cd ..

cd 7002
bin/redis-server ./redis.conf
cd ..

cd 7003
bin/redis-server ./redis.conf
cd ..

cd 7004
bin/redis-server ./redis.conf
cd ..

cd 7005
bin/redis-server ./redis.conf
cd ..

而後再執行腳本啓動。

6.查看服務

[uplooking@uplooking01 redis-cluster]$ ps -ef | grep redis
500       1460     1  0 01:17 ?        00:00:01 bin/redis-server uplooking01:7000 [cluster]
500       1464     1  0 01:17 ?        00:00:01 bin/redis-server uplooking01:7001 [cluster]
500       1468     1  0 01:17 ?        00:00:01 bin/redis-server uplooking01:7002 [cluster]
500       1472     1  0 01:17 ?        00:00:01 bin/redis-server uplooking01:7003 [cluster]
500       1474     1  0 01:17 ?        00:00:01 bin/redis-server uplooking01:7004 [cluster]
500       1480     1  0 01:17 ?        00:00:01 bin/redis-server uplooking01:7005 [cluster]
500       3233  1018  0 01:53 pts/0    00:00:00 grep redis

7.建立集羣(核心)

如今就是要使用前面準備好的redis節點,將其串聯起來搭建集羣。官方提供了一個工具:redis-trib.rb($REDIS_HOME/src 使用ruby編寫的一個程序,因此須要安裝ruby):

$ sudo yum -y install ruby ruby-devel rubygems rpm-build

再用gem這個命令安裝redis接口(gem是ruby的一個工具包):

gem install redis [ -v 3.2.0] #[]中爲可選項制定具體的軟件版本

# 在我安裝時,提示ruby版本須要>=2.2.2,可是上面接上redis接口的版本後就沒有問題了。

接下來運行一下redis-trib.rb:

[uplooking@uplooking01 7000]$ src/redis-trib.rb create --replicas 1 192.168.56.101:7000 192.168.56.101:7001 192.168.56.101:7002 192.168.56.101:7003 192.168.56.101:7004 192.168.56.101:7005
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
192.168.56.101:7000
192.168.56.101:7001
192.168.56.101:7002
Adding replica 192.168.56.101:7003 to 192.168.56.101:7000
Adding replica 192.168.56.101:7004 to 192.168.56.101:7001
Adding replica 192.168.56.101:7005 to 192.168.56.101:7002
M: 497bce5118057198afb0511cc7b88479bb0c3938 192.168.56.101:7000
   slots:0-5460 (5461 slots) master
M: f0568474acad5c707f25843add2d68455d2cbbb2 192.168.56.101:7001
   slots:5461-10922 (5462 slots) master
M: ebe86ea74af5612e6393c8e5c5b3363928a4b7b2 192.168.56.101:7002
   slots:10923-16383 (5461 slots) master
S: c99c55ab3fcea2d65ca3be5b4786390a6e463ea2 192.168.56.101:7003
   replicates 497bce5118057198afb0511cc7b88479bb0c3938
S: 0a847801493a45d32487d701cd0fe37790d4b2f9 192.168.56.101:7004
   replicates f0568474acad5c707f25843add2d68455d2cbbb2
S: 7f9e4bec579fda23a574a62d362a04463140bbc2 192.168.56.101:7005
   replicates ebe86ea74af5612e6393c8e5c5b3363928a4b7b2
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join......
>>> Performing Cluster Check (using node 192.168.56.101:7000)
M: 497bce5118057198afb0511cc7b88479bb0c3938 192.168.56.101:7000
   slots:0-5460 (5461 slots) master
M: f0568474acad5c707f25843add2d68455d2cbbb2 192.168.56.101:7001
   slots:5461-10922 (5462 slots) master
M: ebe86ea74af5612e6393c8e5c5b3363928a4b7b2 192.168.56.101:7002
   slots:10923-16383 (5461 slots) master
M: c99c55ab3fcea2d65ca3be5b4786390a6e463ea2 192.168.56.101:7003
   slots: (0 slots) master
   replicates 497bce5118057198afb0511cc7b88479bb0c3938
M: 0a847801493a45d32487d701cd0fe37790d4b2f9 192.168.56.101:7004
   slots: (0 slots) master
   replicates f0568474acad5c707f25843add2d68455d2cbbb2
M: 7f9e4bec579fda23a574a62d362a04463140bbc2 192.168.56.101:7005
   slots: (0 slots) master
   replicates ebe86ea74af5612e6393c8e5c5b3363928a4b7b2
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

仔細查看其提示,會對Redis分佈式集羣有一個更加清晰的理解。另外須要注意的是,因爲redis-trib.rb 對域名或主機名支持很差,故在建立集羣的時候要使用ip:port的方式。

8.測試

[uplooking@uplooking01 7000]$ bin/redis-cli -h uplooking01 -p 7000 -c
uplooking01:7000> set name xpleaf
-> Redirected to slot [5798] located at 192.168.56.101:7001
OK
192.168.56.101:7001> get name
"xpleaf"
192.168.56.101:7001>
[uplooking@uplooking01 7000]$ bin/redis-cli -h uplooking01 -p 7004 -c
uplooking01:7004> get name
-> Redirected to slot [5798] located at 192.168.56.101:7001
"xpleaf"
192.168.56.101:7001>
[uplooking@uplooking01 7000]$ bin/redis-cli -h uplooking01 -p 7004 -c
uplooking01:7004> set name yyh
-> Redirected to slot [5798] located at 192.168.56.101:7001
OK
192.168.56.101:7001> get name
"yyh"
[uplooking@uplooking01 7000]$ bin/redis-cli -h uplooking01 -p 7002 -c
uplooking01:7002> keys *
(empty list or set)
uplooking01:7002> get name
-> Redirected to slot [5798] located at 192.168.56.101:7001
"yyh"

上面的測試能夠充分說明下面幾個問題:

1.分佈式
    數據是分佈式存儲的,根據key的不一樣會保存到不一樣的主節點上。

2.數據備份
    從節點是做爲備份節點的,跟前面的主從複製集羣同樣,只是用來讀數據,當須要寫或修改數據時,須要切換到主節點上。

Redis Java API使用(二):Cluster API使用

前面的代碼只適合操做單機版本的Redis,若是使用的是分佈式的Redis集羣,那麼就須要修改一下代碼,這裏,咱們直接開發一個工具類JedisClusterUtil,以下:

package com.uplooking.bigdata.common.util.redis;

import redis.clients.jedis.*;

import java.io.IOException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

/**
 * Redis Java API 操做的工具類
 *  專門負責redis的cluster模式
 */
public class JedisClusterUtil {

    private JedisClusterUtil() {}
    private static JedisCluster jedisCluster;
    static {
        Set<HostAndPort> nodes = new HashSet<HostAndPort>();
        nodes.add(new HostAndPort("uplooking01", 7000));
        nodes.add(new HostAndPort("uplooking01", 7001));
        nodes.add(new HostAndPort("uplooking01", 7002));
        nodes.add(new HostAndPort("uplooking01", 7003));
        nodes.add(new HostAndPort("uplooking01", 7004));
        nodes.add(new HostAndPort("uplooking01", 7005));
        jedisCluster = new JedisCluster(nodes);//獲得的是redis的集羣模式
    }

    /**
     * 提供了Jedis的對象
     * @return
     */
    public static JedisCluster getJedis() {
        return jedisCluster;
    }

    /**
     * 資源釋放
     * @param jedis
     */
    public static void returnJedis(JedisCluster jedis) {
        try {
            jedis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在使用時須要注意的是,JedisCluster在使用mget等API操做時,是不容許同時在多個節點上獲取數據的,例如:List<String> mget = jedis.mget("name", "age");,若是name和age分別在不一樣的節點上,則會報異常,因此不建議使用此種方式來獲取數據。

相關文章
相關標籤/搜索