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. (尚006)Vue计算属性之set与get

    test004.html <!DOCTYPE html><html lang="en"><head> <meta charset=&quo ...

  2. admin站点管理

    admin中的显示 class Saltstack_GroupAdmin(admin.ModelAdmin): list_display = ['group_name','salt_minion_id ...

  3. C# 异常 抛异常的时候 同时抛出 传入的参数

    abp的审计日志都把这些功能实现了 可以借鉴 抛异常的时候 同时抛出 传入的参数 大致这样实现,aop,方法执行先,先把参数写入到栈中,抛异常时,栈中自然就有此时的参数了. 可用于重现该异常. 获取把 ...

  4. PHP-FPM 的工作整理

    1.php-fpm的配置文件 根据命令找到路径修改配置文件 ps -ef|grep php-fpm vim /home/php/etc/php-fpm.conf ;;;;;;;;;;;;;;;;;; ...

  5. 洛谷 P3143 [USACO16OPEN]钻石收藏家Diamond Collector 题解

    P3143 [USACO16OPEN]钻石收藏家Diamond Collector 题目描述 Bessie the cow, always a fan of shiny objects, has ta ...

  6. Sage Math中的语法

    1.赋值后不能立即输出,而需要停顿.x= 3  不能输出显示,而 x= 3; x 可以显示. 2.可以用分号连续书写多行. 3.矩阵可以用 mtx[i, j]引用,但是行列号通常从0开始,维度n, m ...

  7. Pytest权威教程11-模块及测试文件中集成doctest测试

    目录 模块及测试文件中集成doctest测试 编码 使用doctest选项 输出格式 pytest-specific 特性 返回: Pytest权威教程 模块及测试文件中集成doctest测试 编码 ...

  8. Mysql中EXISTS关键字用法、总结

    在做教务系统的时候,一个学生(alumni_info)有多个教育经历(alumni_education),使用的数据库是mysql,之前使用左链接查询的,发现数据量才只有几万条时,查询就很慢了,早上想 ...

  9. sturts标签中

    struts中的s标签中textfield和textarea中不需要加上value和中间值,通过name可以直接取到. textfield和textarea标签没有value属性,需要加上${ylqx ...

  10. [C++] typeid关键字使用方法

    typeid 关键字的作用就是获取一个表达式是类型,返回表达式的类型 表达式可以是类型名称.变量名.数字.字符串.指针.结构体等 #include <iostream> using nam ...