1. 前言

分布式锁的场景,大家应该都有遇到过。比如对可靠性有较高要求的系统中,我们需要做主备切换。这时我们可以利用分布式锁,来做选主动作,抢到锁作为主,执行对应的任务,剩余的实例作为备份

redis和zookeeper都可以用来做分布式锁,典型的如redis,可以使用SETNX命令来实现分布式锁。本文将介绍基于consul的分布式锁实现

2. 例子

测试例子
test_lock.go

package main

import (
"github.com/hashicorp/consul/api"
"log"
"strconv"
"sync"
"time"
) func main() {
wg := &sync.WaitGroup{}
for i := 0; i < 3; i++ {
wg.Add(1)
go tryLock("mylock", "session"+strconv.Itoa(i), wg)
}
wg.Wait() } func tryLock(key string, sessionName string, wg *sync.WaitGroup) {
defer wg.Done()
// Get a new client
config := &api.Config{
Address: "dev1:8500",
Scheme: "http",
} client, err := api.NewClient(config)
if err != nil {
panic(err)
} opts := &api.LockOptions{
Key: key,
SessionName: sessionName,
} lock, err := client.LockOpts(opts)
log.Println(sessionName, "try to get lock obj")
for i := 0; i < 3; i++ {
leaderCh, err := lock.Lock(nil)
if err != nil {
log.Println("err", err, sessionName)
}
if leaderCh == nil{
log.Println("err", err, sessionName)
continue
}
log.Println(sessionName, "lock and sleep")
time.Sleep(5 * time.Second)
err = lock.Unlock()
if err != nil {
log.Fatal("err", err)
}
log.Println(sessionName, "unlock")
time.Sleep(5 * time.Second)
}
}

3. 原理


consul中锁的主要是依赖KV Store和Session相关API

3.1 创建session

PUT /v1/session/create 创建1个session

3.2 加锁

PUT /v1/kv/{key}?acquire={sessionId}

这里的key是锁的名称
如果成功加锁,则consul返回true, 否则返回false
注意:获取失败,接口并不阻塞,如果想要加锁,需要再次发起请求
可以使用 GET /v1/kv/{key} 获取锁信息

[
{
"LockIndex": 91,
// 锁的名称
"Key": "mylock",
"Flags": 3304740253564472344,
"Value": null,
// sessionId
// 从这里可以看出当前是那个session持有锁
"Session": "c090b464-23f3-bce1-d999-6163ba6eb91f",
"CreateIndex": 2588219,
"ModifyIndex": 2590269
}
]

3.3 保持会话

PUT /v1/session/renew/{sessionId}

锁是有生命周期,它的生命周期是与session的生命周期一致 因此对于锁的持有者,它需要周期性的执行renew session,以确保session关联的锁不被释放。

hashicorp/consul/api的实现是 每隔TTL/2 执行1次renew

3.4 释放锁

有2种方式可以释放锁
1) 主动释放

PUT /v1/kv/{key}?release={sessionId}

2)被动释放(session超时或者被check为invalidate)
锁释放以后

[
{
"LockIndex": 109,
"Key": "mylock",
"Flags": 3304740253564472344,
"Value": null,
// 可以看到已经没有session信息里
"CreateIndex": 2588219,
"ModifyIndex": 2592871
}
]

4. 注意

1个client释放锁之后,其它client无法立刻获得锁,这可能是由于lock-delay设置引起的。

The final nuance is that sessions may provide a lock-delay. This is a time duration, between 0 and 60 seconds. When a session invalidation takes place, Consul prevents any of the previously held locks from being re-acquired for the lock-delay interval; this is a safeguard inspired by Google’s Chubby. The purpose of this delay is to allow the potentially still live leader to detect the invalidation and stop processing requests that may lead to inconsistent state. While not a bulletproof method, it does avoid the need to introduce sleep states into application logic and can help mitigate many issues. While the default is to use a 15 second delay, clients are able to disable this mechanism by providing a zero delay value.

为防止由于网络波动等原因,session的状态被错误的检查为invalidate 导致锁被释放。此时如果其它client需要加锁,则需要等待lock-delay,才能再次加锁成功。(主动释放没有这个问题)
可以将lock-delay设置成0,表示不启用lock-delay机制

3. 参考资料

    1. 基于redis的分布式锁实现
    2. SETNX
    3. 基于Consul的分布式锁实现

玩转CONSUL(2)–分布式锁的更多相关文章

  1. consul实现分布式锁

    分布式一致性问题: 分布式的CAP理论告诉我们"任何一个分布式系统都无法同时满足一致性(Consistency).可用性(Availability)和分区容错性(Partition tole ...

  2. 服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

    一.基于key/value实现 我们在构建分布式系统的时候,经常需要控制对共享资源的互斥访问.这个时候我们就涉及到分布式锁(也称为全局锁)的实现,基于目前的各种工具,我们已经有了大量的实现方式,比如: ...

  3. 基于Redis的分布式锁真的安全吗?

    说明: 我前段时间写了一篇用consul实现分布式锁,感觉理解的也不是很好,直到我看到了这2篇写分布式锁的讨论,真的是很佩服作者严谨的态度, 把这种分布式锁研究的这么透彻,作者这种技术态度真的值得我好 ...

  4. Java分布式锁看这篇就够了

    ### 什么是锁? 在单进程的系统中,当存在多个线程可以同时改变某个变量(可变共享变量)时,就需要对变量或代码块做同步,使其在修改这种变量时能够线性执行消除并发修改变量. 而同步的本质是通过锁来实现的 ...

  5. 利用consul在spring boot中实现最简单的分布式锁

    因为在项目实际过程中所采用的是微服务架构,考虑到承载量基本每个相同业务的服务都是多节点部署,所以针对某些资源的访问就不得不用到用到分布式锁了. 这里列举一个最简单的场景,假如有一个智能售货机,由于机器 ...

  6. 基于Redis实现分布式锁(1)

    转自:http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部 ...

  7. Java分布式锁,搞懂分布式锁实现看这篇文章就对了

    随着微处理机技术的发展,人们只需花几百美元就能买到一个CPU芯片,这个芯片每秒钟执行的指令比80年代最大的大型机的处理机每秒钟所执行的指令还多.如果你愿意付出两倍的价钱,将得到同样的CPU,但它却以更 ...

  8. 【转】Redis学习笔记(四)如何用Redis实现分布式锁(1)—— 单机版

    原文地址:http://bridgeforyou.cn/2018/09/01/Redis-Dsitributed-Lock-1/ 为什么要使用分布式锁 这个问题,可以分为两个问题来回答: 为什么要使用 ...

  9. 基于Redis实现分布式锁实战

    背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端 ...

随机推荐

  1. 自用java购物

    @RequestMapping("listgoodscart") public ResultEntity listGoodsCart(@RequestParam(name = &q ...

  2. Codeforces Round #557 题解【更完了】

    Codeforces Round #557 题解 掉分快乐 CF1161A Hide and Seek Alice和Bob在玩捉♂迷♂藏,有\(n\)个格子,Bob会检查\(k\)次,第\(i\)次检 ...

  3. CF1163E Magical Permutation【线性基,构造】

    题目描述:输入一个大小为\(n\)的正整数集合\(S\),求最大的\(x\),使得能构造一个\(0\)到\(2^x-1\)的排列\(p\),满足\(p_i\oplus p_{i+1}\in S\) 数 ...

  4. 《挑战30天C++入门极限》C++的iostream标准库介绍(1)

        C++的iostream标准库介绍(1) 我们从一开始就一直在利用C++的输入输出在做着各种练习,输入输出是由iostream库提供的,所以讨论此标准库是有必要的,它与C语言的stdio库不同 ...

  5. MySQL 基础之一

    为了满足关系范式:通常一个大的数据集会拆成在一个库(集合内)中多张表表来存储,每一张表就是由行和列组成的二维关系,表与表之间也有关系,查询有时候需要做表链接,而表链接这个过程需要依赖于索引到各种算法来 ...

  6. Tkinter 之事件绑定

    import tkinter as tk window = tk.Tk() # 设置窗口大小 winWidth = 600 winHeight = 400 # 获取屏幕分辨率 screenWidth ...

  7. docker run 参数含义

    -a stdin: 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项: -d: 后台运行容器,并返回容器ID: -i: 以交互模式运行容器,通常与 -t 同时使用:     ...

  8. Assignment6:白盒测试方法

    常用的软件测试方法有两大类:静态测试方法和动态测试方法. 其中软件的静态测试不要求在计算机上实际执行所测程序,主要以一些人工的模拟技术对软件进行分析和测试:而软件的动态测试是通过输入一组预先按照一定的 ...

  9. google chrome 浏览器插件

    如果感觉浏览器(chrome)的背景是白色太亮太刺眼,可以先在 设置->外观->主题背景 里选择 oceanic,将浏览器头部颜色设置为海蓝色.然后再安装插件 “眼睛护航”,改变所有网页的 ...

  10. argmin ,argmax函数

    在数学中,ARG MAX(或ARGMAX)代表最大值,即给定参数的点集,给定表达式的值达到其最大值: 换一种说法, 是f(x)具有最大值M的x的值的集合.例如,如果f(x)是1- | x |,那么它在 ...