Centos7下的Redis集群搭建与Jedis连接

来源:互联网 发布:如何根据数据拟合曲线 编辑:程序博客网 时间:2024/06/08 14:52

centos下的redis安装(Redis官网)

  • Redis集群知识
    • Redis集群介绍
    • Redis 集群的数据分片
    • Redis 集群的主从复制模型
    • Redis 一致性保证
    • 搭建并使用Redis集群
    • 搭建集群
    • Creating a Redis Cluster using the create-cluster script
    • 使用集群
  • shell中尝试连接redis单个服务器并用info获得信息
  • Jedis连接redis集群出现问题cant get resource from jedisPool
  • Eclipse准备用maven获得最新版本的jedis的jar包用java程序连接redis服务器
  • 查阅Jedis文档如何连接集群和单机不一样

Redis集群知识

参考Redis集群知识中文文档

Redis集群介绍

Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。

Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误.

Redis 集群通过分区来提供一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情况下继续处理命令. Redis 集群的优势:

  • 自动分割数据到不同的节点上。
  • 整个集群的部分节点失败或者不可达的情况下能够继续处理命令。

Redis 集群的数据分片

Redis 集群没有使用一致性hash, 而是引入了哈希槽的概念.

Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么:

  • 节点 A 包含 0 到 5500号哈希槽.
  • 节点 B 包含5501 到 11000 号哈希槽.
  • 节点 C 包含11001 到 16384号哈希槽.

这种结构很容易添加或者删除节点. 比如如果我想新添加个节点D, 我需要从节点 A, B, C中得部分槽到D上. 如果我像移除节点A,需要将A中得槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可. 由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.

Redis 集群的主从复制模型

为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品.

在我们例子中具有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用.

然而如果在集群创建的时候(或者过一段时间)我们为每个节点添加一个从节点A1,B1,C1,那么整个集群便有三个master节点和三个slave节点组成,这样在节点B失败后,集群便会选举B1为新的主节点继续服务,整个集群便不会因为槽找不到而不可用了

不过当B和B1 都失败后,集群是不可用的.

Redis 一致性保证

Redis 并不能保证数据的强一致性. 这意味这在实际中集群在特定的条件下可能会丢失写操作.

第一个原因是因为集群是用了异步复制. 写操作过程:

  • 客户端向主节点B写入一条命令.
  • 主节点B向客户端回复命令状态.
  • 主节点将写操作复制给他得从节点 B1, B2 和 B3.

主节点对命令的复制工作发生在返回命令回复之后, 因为如果每次处理命令请求都需要等待复制操作完成的话, 那么主节点处理命令请求的速度将极大地降低 —— 我们必须在性能和一致性之间做出权衡。 注意:Redis 集群可能会在将来提供同步写的方法。 Redis 集群另外一种可能会丢失命令的情况是集群出现了网络分区, 并且一个客户端与至少包括一个主节点在内的少数实例被孤立。

举个例子 假设集群包含 A 、 B 、 C 、 A1 、 B1 、 C1 六个节点, 其中 A 、B 、C 为主节点, A1 、B1 、C1 为A,B,C的从节点, 还有一个客户端 Z1 假设集群中发生网络分区,那么集群可能会分为两方,大部分的一方包含节点 A 、C 、A1 、B1 和 C1 ,小部分的一方则包含节点 B 和客户端 Z1 .

Z1仍然能够向主节点B中写入, 如果网络分区发生时间较短,那么集群将会继续正常运作,如果分区的时间足够让大部分的一方将B1选举为新的master,那么Z1写入B中得数据便丢失了.

注意, 在网络分裂出现期间, 客户端 Z1 可以向主节点 B 发送写命令的最大时间是有限制的, 这一时间限制称为节点超时时间(node timeout), 是 Redis 集群的一个重要的配置选项:

搭建并使用Redis集群

搭建集群的第一件事情我们需要一些运行在 集群模式的Redis实例. 这意味这集群并不是由一些普通的Redis实例组成的,集群模式需要通过配置启用,开启集群模式后的Redis实例便可以使用集群特有的命令和特性了.

下面是一个最少选项的集群的配置文件:

port 7000cluster-enabled yescluster-config-file nodes.confcluster-node-timeout 5000appendonly yes

文件中的 cluster-enabled 选项用于开实例的集群模式, 而 cluster-conf-file 选项则设定了保存节点配置文件的路径, 默认值为 nodes.conf.节点配置文件无须人为修改, 它由 Redis 集群在启动时创建, 并在有需要时自动进行更新。

要让集群正常运作至少需要三个主节点,不过在刚开始试用集群功能时, 强烈建议使用六个节点: 其中三个为主节点, 而其余三个则是各个主节点的从节点。

首先, 让我们进入一个新目录, 并创建六个以端口号为名字的子目录, 稍后我们在将每个目录中运行一个 Redis 实例: 命令如下:

mkdir cluster-testcd cluster-testmkdir 7000 7001 7002 7003 7004 7005

在文件夹 7000 至 7005 中, 各创建一个 redis.conf 文件, 文件的内容可以使用上面的示例配置文件, 但记得将配置中的端口号从 7000 改为与文件夹名字相同的号码。

从 Redis Github 页面 的 unstable 分支中取出最新的 Redis 源码, 编译出可执行文件 redis-server , 并将文件复制到 cluster-test 文件夹, 然后使用类似以下命令, 在每个标签页中打开一个实例:

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

实例打印的日志显示, 因为 nodes.conf 文件不存在, 所以每个节点都为它自身指定了一个新的 ID :

[82462] 26 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1

实例会一直使用同一个 ID , 从而在集群中保持一个独一无二(unique)的名字。

搭建集群

现在我们已经有了六个正在运行中的 Redis 实例, 接下来我们需要使用这些实例来创建集群, 并为每个节点编写配置文件。

通过使用 Redis 集群命令行工具 redis-trib , 编写节点配置文件的工作可以非常容易地完成: redis-trib 位于 Redis 源码的 src 文件夹中, 它是一个 Ruby 程序, 这个程序通过向实例发送特殊命令来完成创建新集群, 检查集群, 或者对集群进行重新分片(reshared)等工作。

【Ruby安装】

./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 \127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005

这个命令在这里用于创建一个新的集群, 选项–replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。

【如果报错:/usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in require': cannot load such file -- redis (LoadError)
from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in
require’,请参考这里,是因为ruby中和redis对接的库没有安装,用ruby的gem命令:gem install redis一下就好】

【进一步如果gem install redis命令报错redis requires Ruby version >= 2.2.2问题,那么是ruby版本不够,参考这里,安装curl,再用curl安装RVM,用RVM安装最新的ruby】

之后跟着的其他参数则是这个集群实例的地址列表,3个master3个slave redis-trib 会打印出一份预想中的配置给你看, 如果你觉得没问题的话, 就可以输入 yes , redis-trib 就会将这份配置应用到集群当中,让各个节点开始互相通讯,最后可以得到如下信息:

[OK] All 16384 slots covered

这表示集群中的 16384 个槽都有至少一个主节点在处理, 集群运作正常。

Creating a Redis Cluster using the create-cluster script

If you don’t want to create a Redis Cluster by configuring and executing individual instances manually as explained above, there is a much simpler system (but you’ll not learn the same amount of operational details).

Just check utils/create-cluster directory in the Redis distribution. There is a script called create-cluster inside (same name as the directory it is contained into), it’s a simple bash script. In order to start a 6 nodes cluster with 3 masters and 3 slaves just type the following commands:

create-cluster startcreate-cluster create

Reply to yes in step 2 when the redis-trib utility wants you to accept the cluster layout.

You can now interact with the cluster, the first node will start at port 30001 by default. When you are done, stop the cluster with:

create-cluster stop.

Please read the README inside this directory for more information on how to run the script.

使用集群

Redis 集群现阶段的一个问题是客户端实现很少。

以下是一些我知道的实现:

  • redis-rb-cluster 是我(@antirez)编写的 Ruby 实现, 用于作为其他实现的参考。 该实现是对 redis-rb 的一个简单包装, 高效地实现了与集群进行通讯所需的最少语义(semantic).
  • redis-py-cluster 看上去是 redis-rb-cluster 的一个 Python 版本, 这个项目有一段时间没有更新了(最后一次提交是在六个月之前), 不过可以将这个项目用作学习集群的起点。
  • 流行的 Predis 曾经对早期的 Redis 集群有过一定的支持, 但我不确定它对集群的支持是否完整, 也不清楚它是否和最新版本的 Redis 集群兼容 (因为新版的 Redis 集群将槽的数量从 4k 改为 16k 了).
  • 使用最多的时java客户端, Jedis 最近添加了对集群的支持, 详细请查看项目README中Jedis Cluster部分.
  • StackExchange.Redis 提供对 C# 的支持(并且包括大部分 .NET 下面的语言,比如: VB, F#等等)
  • thunk-redis 提供对 Node.js 和 io.js的支持。
  • Redis unstable 分支中的 redis-cli 程序实现了非常基本的集群支持, 可以使用命令 redis-cli -c 来启动。

测试 Redis 集群比较简单的办法就是使用 redis-rb-cluster 或者 redis-cli , 接下来我们将使用 redis-cli 为例来进行演示:

$ redis-cli -c -p 7000
redis 127.0.0.1:7000> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
redis 127.0.0.1:7002> set hello world
-> Redirected to slot [866] located at 127.0.0.1:7000
OK
redis 127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
“bar”
redis 127.0.0.1:7000> get hello
-> Redirected to slot [866] located at 127.0.0.1:7000
“world”

【记住,用redis-cli登录集群一定要加-c参数,否则默认为单独使用你制定单个服务器的服务,这样你操作时一旦涉及到数据在别的服务器上的情况,都会报错,而以集群模式-c登录,则是默认整个集群一起为你服务】

注意: 如果你是使用脚本创建的集群节点,那么默认端口可能是从30001开始。

redis-cli 对集群的支持是非常基本的, 所以它总是依靠 Redis 集群节点来将它转向(redirect)至正确的节点。一个真正的(serious)集群客户端应该做得比这更好: 它应该用缓存记录起哈希槽与节点地址之间的映射(map), 从而直接将命令发送到正确的节点上面。这种映射只会在集群的配置出现某些修改时变化, 比如说, 在一次故障转移(failover)之后, 或者系统管理员通过添加节点或移除节点来修改了集群的布局(layout)之后, 诸如此类。

shell中尝试连接redis单个服务器,并用info获得信息。

./redis-cli -h 127.0.0.1 -p 7000info

Jedis连接redis集群出现问题can’t get resource from jedisPool

这个情况很多,逐一排查:

参考这里

Eclipse准备:用maven获得最新版本的jedis的jar包,用java程序连接redis服务器

参考这里

  • 首先安装eclipse(官网下载,同时会要求本地已经又JDK环境)和maven(官网下载,并且需要配置环境变量,直接将maven目录下的bin路径放到~/.bash_profile中即可)。
  • 在命令行下测试maven安装成功后(mvn -v能输出maven版本信息),随机新建一个文件夹,文件夹下新建一个pom.xml文件,加入如下内容:
 <?xml version="1.0"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>temp.download</groupId>    <artifactId>temp-download</artifactId>    <version>1.0-SNAPSHOT</version>     <dependencies><!-- 需要下载什么jar包 添加相应依赖 其余部分无需在意--><!-- https://mvnrepository.com/artifact/redis.clients/jedis --><dependency>    <groupId>redis.clients</groupId>    <artifactId>jedis</artifactId>    <version>2.9.0</version></dependency>    </dependencies></project>

dependency的内容直接去maven中央仓库,搜索jedis,点进去你想要的那个版本的jedis,复制下来它的依赖写法就行了。maven中央仓库jedis

  • 命令行下进入pom文件所在目录,执行命令

    mvn -f pom.xml dependency:copy-dependencies

PS:如果发现下载jar包依赖的速度很慢或者各种错误,可能是网络连接原因,可以尝试调整一下maven的下载镜像,参考这里。

另外,第一次使用maven的话,一般要下载大量maven本身的依赖包,所以速度会比较慢,不过用国内镜像的话一般很快也能解决。

搞定后,在目录下会生成一个target文件夹,里面就有目标jar包。

查阅Jedis文档,如何连接集群(和单机不一样)

package main;import java.io.IOException;import java.util.HashSet;import java.util.Map;import java.util.Set;import redis.clients.jedis.HostAndPort;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisCluster;import redis.clients.jedis.JedisPool;import redis.clients.jedis.JedisPoolConfig;public class Main {    public static void main(String[] args) {        /**         * 单机测试(没问题)         *///      Jedis jedis=new Jedis("192.168.179.128",10000);//      jedis.set("hello", "123456");//      System.out.println(jedis.get("hello"));//      jedis.close();        /**         * 集群测试         */        Set<HostAndPort> set=new HashSet<HostAndPort>();        set.add(new HostAndPort("192.168.179.128", 7000));        set.add(new HostAndPort("192.168.179.128", 7001));        set.add(new HostAndPort("192.168.179.128", 7002));        set.add(new HostAndPort("192.168.179.128", 7003));        set.add(new HostAndPort("192.168.179.128", 7004));        set.add(new HostAndPort("192.168.179.128", 7005));        JedisCluster jc= new JedisCluster(set);        Map<String, JedisPool> nodes = jc.getClusterNodes();        Set<String> keySet = nodes.keySet();        for(String key: keySet){            JedisPool jedisPool = nodes.get(key);            Jedis resource = jedisPool.getResource();            //获得单个服务器信息            String info = resource.info();            //获得集群信息            String clusterInfo = resource.clusterInfo();            System.out.println(info);            System.out.println("===============================");        }        System.out.println(clusterInfo);    }}

PS:写在末尾的话,不要总是尝试去用最新版本的软件或者库,因为往往可能存在很多坑还没被人踩,你就很可能踩,搭建redis集群过程中就因为一些版本问题忙活好一阵儿找不到原因~~