找回密码
 立即注册
首页 业界区 业界 SpringCloud进阶--Redis与分布式

SpringCloud进阶--Redis与分布式

讹过畔 10 小时前
Redis与分布式

Redis是一个基于内存的高性能数据库!
主从复制

1.png

主从复制:将一台Redis服务器的数据复制到其他Redis服务器,前者是主节点(Master),后者是从节点(Slave),数据的复制是单向的,只能从主节点到从节点。Master以写为主,Slave以读为主。
这样的好处有:

  • 实现读写分离,提高性能
  • 在写少读多的情况下,可以安排多个从节点,这样能大幅度分担压力,就算挂掉一个,其他的也能用
具体的实施步骤如下:

  • 先修改master的redis.windows.conf文件,把端口为6001,slave的端口为6002
  1. # Accept connections on the specified port, default is 6379 (IANA #815344).# If port 0 is specified Redis will not listen on a TCP socket.port 6001
复制代码

  • 启动redis:命令如下:redis-server.exe redis.windows.conf
  • 使用info replication 命令可以查看主从状态。启动客户端后,可以使用命令查看
  • 将6002设置为从节点:先使用客户端连上6002 ,再输入命令 replicaof 127.0.0.1 6001 命令后,就会将6001服务器作为主节点,当前节点就是从节点
2.png

可以看到6002的角色是slave。从新连接6001再查看状态
3.png

此时,6001和6002已经形成了主从关系,这里有一个偏移量,反应的是从节点的同步情况

  • 主节点和从节点都会维护一个复制偏移量,主节点每次向从节点传递N个字节的时候,会将自己的复制偏移量+N.从节点收到N个字节的数据后,就会将自己的复制偏移量+N.通过2个偏移量的对比可以知道主从节点的数据是否一致,如果不一致就需要进行增量同步!
主节点可以读写。从节点只能读!当有新的从节点加入时,会马上同步主节点的数据!
当节点关闭后,从节点也可以读取数据,毕竟从节点没关闭!
整个同步流程如下:

  • 从节点执行replicaof 【ip】 [port] 命令后,从节点会保存主节点的地址信息
  • 从节点通过每秒允许的定时任务发现新的主节点后,会尝试与该节点建立网络连接,专门接收主节点发送的复制命令
  • 连接成功后,第一次会将主节点的数据进行全量复制,之后采用增量复制,持续将新来的写命令同步给从节点
怎么解除主从模式?
客户端连接6002 ,输入指令:replicaof no one 就可以了!
也可以直接在配置文件中配置主从模式:
在从节点的redis.windows.conf 文件中加入replicaof 127.0.0.1 6001 内容,就可以了,
除了作为主节点的从节点外,也可以作为从节点的从节点,比如增加一个6003 作为6002 的从节点,操作方式和上面一样,
这两种主从方式的区别如下:
4.png

但是第二种方式,一旦传播链路中出现问题,就会导致后面的从节点无法及时同步数据!
哨兵模式

上面的主从复制模式,一旦主节点故障,就会导致整个系统崩溃!要是能够监控到主节点的允许状况,就能够采取补救措施,这时就要用到哨兵模式。
5.png

哨兵会对所有的节点进行监控,如果发现主节点故障,会立即让从节点进行投票,选举一个新的主节点出来,这样就不会由于主节点故障而导致系统崩溃(要实现这样的功能,最少必须时一主一从模式,再小就没有意义了)。
如何启动一个哨兵?

  • 修改一下配置文件:先删除全部内容,然后添加:sentinel monitor ali 127.0.0.1 6001 1
第一个和第二个时固定,第三个时监控对象名称,可以随意命名,后面就是主节点的信息(ip和端口),最后的1 后面说明,然后启动服务器:redis-server.exe redis.windows.conf --sentinel
6.png

哨兵模式启动后,会字段监控主节点,还会显示哪些节点是从节点。
此时关闭主节点后,过一段时间,哨兵会将一个从节点选举为主节点,而挂掉的主节点会设置为从节点,当挂掉的节点启动后就当成从节点使用了。
那么从节点见的选举规则是什么呢?

  • 首先会根据优先级进行选择,可以在配置文件中设置,添加replica-priority配置项(默认100),值越小优先级越高
  • 如果优先级一样,选择偏移量大的
  • 要是还选不出来,那就选择runid(启动时随机生成的)最小的。
那要是哨兵挂了怎么办?
可以多设置几个哨兵。只需要将哨兵的配置复制一下,然后修改端口号。就可以同时启动多个哨兵,只不过要把最后一个值改为2,比如:
  1. sentinel monitor ali 127.0.0.1 6001 2port 2002
复制代码
这个值代表的额是,当有几个哨兵认为主节点挂掉时,就判断主节点真的挂掉了。
那再哨兵重新选举新的主节点后,Java中的redis客户端怎么感知到呢?

  • 先引入依赖:
  1.     redis.clients    jedis    4.2.1
复制代码
  1. public class Main {    public static void main(String[] args) {        // 直接使用JedisSentinelPool获取Master节点        // 填入三个哨兵地址,注意,如果连不上,就把哨兵的配置文件中的protected-mode属性改为no        JedisSentinelPool pool = new JedisSentinelPool("ali", new HashSet(Arrays.asList(                "127.0.0.1:2001",    "127.0.0.1:2002",    "127.0.0.1:2003"        )));        // 询问并得到Jedis对象,就是master节点的连接        Jedis jedis = pool.getResource();        // 向master节点写入数据        jedis.set("test", "114514");        // 再次获取Jedis对象,得到的也是master节点的连接        Jedis jedis2 = pool.getResource();        // 读取数据,验证是否成功        System.out.println(jedis2.get("test"));    }}
复制代码
集群搭建

如果服务器的内存不够用,但是redis需要继续存储内容,这时候就需要使用集群来实现扩容。
7.png

此时,面对一个写入请求,数据该写到哪个节点上呢?我们需要先明白集群的机制!
一个redis集群包含16384个插槽,集群中的每个热覅是维护一部分插槽以及插槽送映射的键值对数据,那这个插槽到底是什么呢?
插槽就是Hash计算后的一个结果,这里采用CRC16循环冗余校验,得到16个bit位的数据,就是说算出来的结果是0-65535之间,再进行取模,得到最终结果:
Redis key 的路由计算公式:slot = CRC(Key) % 16384
结果是多少就存放在对应的redis下,比如redis节点1负责0-25565的插槽,这时客户端插入了一个数据a=10.a在hash计算后结果为666.那么a就应该存到节点1。
本质上,就是通过哈希算法将如数分摊到各个节点!
搭建redis集群步骤如下:

  • 这里创建6个配置,先在配置文件中开启集群模式,注意修改端口号:
  1. cluster-enabled yes
复制代码

  • 然后输入redis-cli.exe --cluster create --cluster-replicas 1 127.0.0.1:6001 127.0.0.1:6002 127.0.0.1:6003 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003
6001 6002 6003 这三个是三个主节点。7001 7002 7003 分别是三个从节点
这里的--cluster-replicas 1 指的是每个节点配一个从节点
8.png

输入yes执行
此时,集群搭建成功!
此时在保存数据时,可以使用集群方式连接,这样无论在哪个节点插入数据都可以成功!,只需要添加 -c 表示以集群模式连接即可:
redis-cli.exe -p 6001 -c
set a 888
就可以保存成功。
此时,可以输入cluster nodes 来查看当前所有节点信息
当某一个主节点发生故障后,这个主节点的从节点就会升级为新的主节点!之后,当故障节点恢复后会变为当前主节点的从节点!
如果某个节点的主从redis都挂掉了呢?
那当前节点就不可用了,无法在此节点插入数据!
当他们都恢复后,就可以正常使用了。
如何使用Java连接到集群模式下的redis,需要用到JedisCluster对象:
  1. // 和客户端一样,随便连一个就行,也可以多写几个!JedisCluster cluster = new JedisCluster(new HostAndPort("127.0.0.1",6001));System.out.println("集群实例数量:"+cluster.getClusterNodes().size());cluster.set("test", "114514");System.out.println(cluster.get("test"));
复制代码
分布式锁

redis实现分布式锁,可以使用setnx key value 命令。
意思是,只有当指定的key不存在的时候,才能进行插入,实际上就是set if not exists
比如:setnx a 999 ,然后再输入setnx a 100 就会报错,因为已经存在key是a 的数据了!
利用这种特定,就可以在不同的服务中实现分布式锁,但是,当某个服务加了锁,但是卡顿或者崩溃了,那这个锁岂不是永远无法释放了?
此时可以加上过期时间:set a 666 EX 5 NX
这里使用的set命令。最后的NX表示使用setnx模式,和上面一样的效果。通过EX设定过期时间,这里设为5秒,表明超过5秒还没释放,就会自动删除锁!
虽然添加过期时间解决了上面的问题,但是也带来了很多麻烦,比如下面这种情况:
9.png

因此,单纯的加过期时间,会把别人加的锁给删除,要解决这种问题很简单。
现在的目标是保证任务只能删除自己加的锁,所以可以把a的值设定为任务专属值,比如使用uuid。如果在主动删除锁的时候发现值不是我们当前任务指定的,那么说明可能是因为超时,其他任务已经加锁了,此时就不能删除锁了。
10.png

此时还会有一个问题,如果在超时之前那一刹那进入到释放锁阶段,获取的值还是自己的,但是在即将执行删除之前,由于超时机制导致被删除并且其他任务也枷锁了,这时再进行删除,仍会导致删除其他任务加的锁!
11.png

本质还是因为锁的超时时间不好衡量,如果超时时间能够设定恰当,就可以避免这种问题了。
这时就需要Redisson框架,它是Redis官方推荐的Java版的Redis客户端。
Redisson内部提供了一个监控锁的看门狗,作用是再Redisson实例被关闭前,不断的延长锁的有效期!
使用步骤如下:

  • 先引入依赖:
  1.     org.redisson    redisson    3.5.0    io.netty    netty-all
复制代码

  • 编写代码:
  1. public static void main(String[] args) {    Config config = new Config();    // 连接单机模式的Redis服务器,也可以指定集群    config.useSingleServer().setAddress("redis://127.0.0.1:6379");    // 创建RedissonClient实例,内部会创建连接池    RedissonClient redisson = Redisson.create(config);    for (int i = 0; i < 10; i++) {        new Thread(()-> {            Jedis jedis = new Jedis("127.0.0.1", 6379);            // 指定锁名称,拿到锁对象            RLock lock = redisson.getLock("testLock");            for (int j = 0; j < 100; j++) {                lock.lock(); // 加锁                int a = Integer.parseInt(jedis.get("a")) +1;                jedis.set("a", String.valueOf(a));                lock.unlock(); // 解锁            }        }).start();    }}
复制代码
注意:如果存放锁的Redis服务器挂了,那么肯定会出问题,这时候,可以使用RedLock(RLock 类),它的思路是,在多个Redis服务器上保存锁,只要超过半数的Redis服务器获取到锁,那就真的获取到锁了,这样就算挂掉一部分节点,也能正常运行。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册