zoukankan      html  css  js  c++  java
  • Redis笔记整理(二):Java API使用与Redis分布式集群环境搭建

    Redis Java API使用(一):单机版本Redis API使用

    Redis的Java API通过Jedis来进行操作,因此首先需要Jedis的第三方库,因为使用的是Maven工程,所以先给出Jedis的依赖:

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

    基本代码示例

    Redis能提供的命令,Jedis也都提供了,而且使用起来非常类似,所以下面只是给出了部分操作的代码。

    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来开发一个工具类。

    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的操作:

    // 获得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主从复制集群安装

    这里使用三台设备,环境说明如下:

    uplooking01 master
    uplooking02 slave
    uplooking03 slave
    
    uplooking01为主节点,0203为从节点,主节点主要负责写,从节点主要负责读,不能写。
    另外在两台从服务器上还会配置使用密码,测试一下使用密码时的连接方式(注意主服务器没有设置密码)。
    下面的配置会对这些需求有所体现。

    uplooking01 redis.conf配置如下:

    bind uplooking01
    daemonize yes(后台运行)
    logfile /opt/redis-3.2.0/logs/redis.log(日志文件,目录必须存在)

    uplooking02 redis.conf配置如下:

    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配置如下:

    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分别在不同的节点上,则会报异常,所以不建议使用此种方式来获取数据。

  • 相关阅读:
    Android JNI用于驱动測试
    shell实例浅谈之三产生随机数七种方法
    WEB安全实战(二)带你认识 XSS 攻击
    前端和云端性能分析工具分析报告
    【翻译】Ext JS——高效的编码风格指南
    dubbo协议
    JavaBean对象转map
    messagePack编解码
    主流编码框架
    java编解码技术,json序列化与二进制序列化
  • 原文地址:https://www.cnblogs.com/henrylinux/p/11516962.html
Copyright © 2011-2022 走看看