现在的位置: 首页 > 算法 > 正文

分布式锁用Redis还是Zookeeper

2020年01月13日 算法 ⁄ 共 3253字 ⁄ 字号 评论关闭

  由于这一部分是讲解 Redis,并没有涉及到分布式架构方面的东西,所以我很纠结要不要写这篇文章,最终我还是决定在这里说一下 Redis 实现分布式锁,在后续文章中还是会涉及到分布式架构的相关知识,这里就当一个引子,主要还是讲解如何使用 Redis 来实现,我会将写的方法封装成一个工具类,对代码进行讲解,由于没有分布式环境,所有没有办法进行测试。

  一、程序客户端之 Java 客户端 Jedis

  既然到了代码层级,那么我们就需要先讲解一下程序客户端之 Java 客户端 Jedis,我们需要使用 Java 代码来连接 Redis,那么我们先来看一下 Jedis 如何使用吧。

  1、程序客户端之 Java 客户端 Jedis

  1)添加依赖包 

  redis.clients

  jedis

  2.9.0

  2)单实例连接

  这里我们先创建 RedisUtils 类,并给创两个常量:HOST 和 PORT,就是 Redis 服务地址

  public class RedisUtils {

  public static String HOST = "192.168.1.216";

  public static int PORT = 6379;

  public static void main(String[] args) {

  }

  }

  然后我们来编写单连接实例,代码很简单,获取到 jedis 对象后,就可以使用对应方法操作命令了

  public static void singleConnect() {

  Jedis jedis = new Jedis(HOST, PORT);

  String result = jedis.get("k2");

  System.out.println(result);

  jedis.close();

  }

  public static void main(String[] args) {

  singleConnect();

  }

  但是我们在 main() 方法中执行的时候,是会报错的,错误信息如下:(连接失败)

  造成这种错误的常见的原因无非两种:

  (1)没有配置防火墙

  为了方便,我直接关闭了防火墙,没有去配置出入站规则,练习这样做是没有问题的,但是正是环境中切记不要关闭防火墙。使用以下命令关闭防火墙:(注意:CentOS 6 中是这个命令,而在 CentOS 7 中有所差异,请自行百度使用)

  service iptables stop

  (2)redis.conf 配置文件中的 bind 127.0.0.1 没有修改

  我们进入 Redis 的 bin 目录下,打开 redis.conf 配置文件,使用 :set nu 命令,在 vim 中显示行号,找到第61行,并进行修改为 bind 192.168.1.216:然后在执行 main() 方法,就 OK 了。

  3)连接池连接

  Redis 的连接池与关系型数据库的连接池具有相同的作用,这不是我们讲解的重点,直接给出代码。

  public static void poolConnect() {

  JedisPool pool = new JedisPool(HOST, PORT);

  Jedis jedis = pool.getResource();

  String result = jedis.get("k2");

  System.out.println(result);

  jedis.close();

  pool.close();

  }

  二、Redis 实现分布式锁

  1、锁的处理

  单应用中使用锁:单进程多线程(synchronize、Lock)

  分布式应用中使用锁:多进程

  2、分布式锁的实现方式

  数据库的乐观锁

  基于 Zookeeper 的分布式锁

  基于 Redis 的分布式锁

  分布式锁有多种实现方式,本章节我们只讲解 Redis 如何实现分布式锁。

  3、分布式锁的注意事项

  互斥性:在任意时刻,只有一个客户端能持有锁。

  同一性:加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

  避免死锁:即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

  4、实现分布式锁

  初始化操作

  public static String HOST = "192.168.1.216";

  public static int PORT = 6379;

  private static JedisPool pool;

  static {

  JedisPoolConfig config = new JedisPoolConfig();

  config.setMinIdle(5);

  pool = new JedisPool(config, HOST, PORT);

  }

  public static Jedis getJedis() {

  return pool.getResource();

  }

  1)获取锁

  获取锁有两种方式,一种是使用 set 命令,另一种是使用 setnx 命令,下面我们会对这两种方式分别进行说明。

  (1)使用 set 命令实现

  /**

  * 使用redis的set命令实现获取分布式锁

  *

  * @param lockKey 锁

  * @param requestId 请求ID,保证同一性

  * @param timeout 过期时间,避免死锁

  * @return

  */

  public static boolean getLockBySet(String lockKey, String requestId, int timeout) {

  // 获取jedis对象,负责与远程redis服务器进行链接

  Jedis jedis = getJedis();

  String result = jedis.set(lockKey, requestId, "NX", "EX", timeout);

  if (result == "OK") {

  return true;

  }

  return false;

  }

  (2)使用 setnx 命令实现

  /**

  * 使用redis的setnx命令实现获取分布式锁

  *

  * @param lockKey 锁

  * @param requestId 请求ID,保证同一性

  * @param timeout 过期时间,避免死锁

  * @return

  */

  public static synchronized boolean getLockBySetnx(String lockKey, String requestId, int timeout) {

  // 获取jedis对象,负责与远程redis服务器进行链接

  Jedis jedis = getJedis();

  Long result = jedis.setnx(lockKey, requestId);

  if (result == 1) {

  // 设置有效期,防止死锁

  jedis.expire(lockKey, timeout);

  return true;

  }

  return false;

  }

  2)释放锁

  (1)使用 del 命令实现

  /**

  * 使用del命令释放锁

  *

  * @param lockKey 锁

  * @param requestId 请求ID,保证同一性

  */

  public static void releaseLockByDel(String lockKey, String requestId) {

  // 获取jedis对象,负责与远程redis服务器进行链接

  Jedis jedis = getJedis();

  // 保证同一性

  if (requestId.equals(jedis.get(lockKey))) {

  jedis.del(lockKey);

  }

  小结

  本章节主要是对 Redis 如何实现分布式锁进行了讲解,虽然给出了代码,但由于环境限制,并没有进行演示,在后面分布式架构的章节中,我们会重新对这一部分进行演示,本章节主要是为了让读者了解 Redis 实现分布式锁的方式,了解 Redis 是可以用来实现分布式锁的。本章节所用代码可在下方码云地址中查看。

抱歉!评论已关闭.