go任务调度9(op实现分布式乐观锁)

package main

import (
"go.etcd.io/etcd/clientv3"
"time"
"fmt"
"context"
)

func main() {
var (
config clientv3.Config
client *clientv3.Client
err error
lease clientv3.Lease
leaseGrantResp *clientv3.LeaseGrantResponse
leaseId clientv3.LeaseID
keepRespChan <-chan *clientv3.LeaseKeepAliveResponse
keepResp *clientv3.LeaseKeepAliveResponse
ctx context.Context
cancelFunc context.CancelFunc
kv clientv3.KV
txn clientv3.Txn
txnResp *clientv3.TxnResponse
)

// 客户端配置
config = clientv3.Config{
Endpoints: []string{"0.0.0.0:2379"},
DialTimeout: 5 * time.Second,
}

// 建立连接
if client, err = clientv3.New(config); err != nil {
fmt.Println(err)
return
}

// lease实现锁自动过期(上锁之后,如果节点宕机,锁会一直占用,所以要过期机制,也要续租机制):
// op操作
// txn事务: if else then

// 1, 上锁 (创建租约, 自动续租, 拿着租约去抢占一个key)
lease = clientv3.NewLease(client)

// 申请一个5秒的租约
if leaseGrantResp, err = lease.Grant(context.TODO(), 5); err != nil {
fmt.Println(err)
return
}

// 拿到租约的ID
leaseId = leaseGrantResp.ID

// 准备一个用于取消自动续租的context
ctx, cancelFunc = context.WithCancel(context.TODO())

// 确保函数退出后, 自动续租会停止
defer cancelFunc() //终止自动续租协程(goroutine)
defer lease.Revoke(context.TODO(), leaseId) //告诉etcd把租约直接释放掉,更直接,立即删除,锁就释放了

// 5秒后会取消自动续租
if keepRespChan, err = lease.KeepAlive(ctx, leaseId); err != nil {
fmt.Println(err)
return
}

// 处理续约应答的协程
go func() {
for {
select {
case keepResp = <- keepRespChan:
if keepRespChan == nil {
fmt.Println("租约已经失效了")
goto END
} else { // 每秒会续租一次, 所以就会受到一次应答
fmt.Println("收到自动续租应答:", keepResp.ID)
}
}
}
END:
}()

// if 不存在key, then 设置它, else 抢锁失败
kv = clientv3.NewKV(client)

// 创建事务
txn = kv.Txn(context.TODO())

// 定义事务

// 如果key不存在(创建版本是0说明没有被创建)
txn.If(clientv3.Compare(clientv3.CreateRevision("/cron/lock/job9"), "=", 0)).
Then(clientv3.OpPut("/cron/lock/job9", "xxx", clientv3.WithLease(leaseId))).
Else(clientv3.OpGet("/cron/lock/job9")) // 否则抢锁失败

// 提交事务
if txnResp, err = txn.Commit(); err != nil {
fmt.Println(err)
return // 没有问题
}

// 判断是否抢到了锁
if !txnResp.Succeeded {
fmt.Println("锁被占用:", string(txnResp.Responses[0].GetResponseRange().Kvs[0].Value))
return
}

// 2, 处理业务

fmt.Println("处理任务")
time.Sleep(5 * time.Second)

// 3, 释放锁(取消自动续租, 释放租约)
// 上面的defer 会把租约释放掉, 关联的KV就被删除了
}


(右边的先执行,左边的后执行,左边会提示锁已被占用)

原文地址:https://blog.51cto.com/5660061/2382042

时间: 2024-11-09 03:02:48

go任务调度9(op实现分布式乐观锁)的相关文章

【转】MySQL乐观锁在分布式场景下的实践

背景 在电商购物的场景下,当我们点击购物时,后端服务就会对相应的商品进行减库存操作.在单实例部署的情况,我们可以简单地使用JVM提供的锁机制对减库存操作进行加锁,防止多个用户同时点击购买后导致的库存不一致问题. 但在实践中,为了提高系统的可用性,我们一般都会进行多实例部署.而不同实例有各自的JVM,被负载均衡到不同实例上的用户请求不能通过JVM的锁机制实现互斥. 因此,为了保证在分布式场景下的数据一致性,我们一般有两种实践方式:一.使用MySQL乐观锁:二.使用分布式锁. 本文主要介绍MySQL

Redis分布式锁----乐观锁的实现,以秒杀系统为例

本文使用redis来实现乐观锁,并以秒杀系统为实例来讲解整个过程. 乐观锁      大多数是基于数据版本(version)的记录机制实现的.即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个"version"字段来实现读取出数据时,将此版本号一同读出,之后更新时,对此版本号加1.此时,将提交数据的版本号与数据库表对应记录的当前版本号进行比对,如果提交的数据版本号大于数据库当前版本号,则予以更新,否则认为是过期数据.redis中可以使用watch命令

Java锁?分布式锁?乐观锁?行锁?

Tomcat的锁 Tomcat是这个系统的核心组成部分, 每当有用户请求过来,Tomcat就会从线程池里找个线程来处理,有的执行登录,有的查看购物车,有的下订单,看着属下们尽心尽职地工作,完成人类的请求,Tomcat就很有成就感. 与此同时,它也很得意,所有的业务逻辑尽在掌握.MySQL算啥!不就是一个保存数据的地方吗? Redis算啥!不就是一个加快速度的缓存吗? 没有他们,我也能找到替代品,而我不可替代的, Tomcat经常这么想. 昨天MySQL偶然说起隔壁机器入驻了一个叫做Node.js

web开发中的两把锁之数据库锁:(高并发--乐观锁、悲观锁)

这篇文章讲了 1.同步异步概念(消去很多疑惑),同步就是一件事一件事的做:sychronized就是保证线程一个一个的执行. 2.我们需要明白,锁机制有两个层面,一种是代码层次上的,如Java中的同步锁,典型的就是同步关键字synchronized ( 线    程级别的).另一个就是数据库层次上的,比较典型的就是悲观锁和乐观锁. 3.常见并发同步案例分析   附原文链接 http://www.cnblogs.com/xiohao/p/4385508.html 对于我们开发的网站,如果网站的访问

乐观锁解决高并发

对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了.而并发问题是绝大部分的程序员头疼的问题, 但话又说回来了,既然逃避不掉,那我们就坦然面对吧~今天就让我们一起来研究一下常见的并发和同步吧. 为了更好的理解并发和同步,我们需要先明白两个重要的概念:同步和异步    1.同步和异步的区别和联系          所谓同步,可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是出于阻塞的,只有接收到 返回的值或消息后才往下执行其它的命令. 异步

乐观锁、悲观锁

乐观锁: 总认为不会产生并发问题,因此不会上锁,更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作来实现 version: 数据上有数据版本号version字段,每次更新version值加一 CAS操作方式:compare and set, 三个参数,数据所在的内存值,预期值,新值,当需要更新时,判断内存值与之前取到的值是否相等,若相等,用新值更新,否则不断尝试 悲观锁: 总是假设最坏的情况,每次取数据都认为其他线程会修改,所以都会加锁(读锁,写锁,行锁),当其他

一篇文章带你解析,乐观锁与悲观锁的优缺点

乐观锁与悲观锁 概述 乐观锁 总是假设最好的情况,每次去读数据的时候都认为别人不会修改,所以不会上锁, 但是在更新的时候会判断一下在此期间有没有其他线程更新该数据, 可以使用版本号机制和CAS算法实现. 乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁. 在Java中java.util.concurrent.atomic包下面的原子变量类就是基于CAS实现的乐观锁. 悲观锁 总是假设最坏的情况,每次去读数据的时候都认为别

悲观锁与乐观锁的一些使用

1 首先我们来了解一下 乐观锁与悲观锁的区别 2 3 乐观锁的思路一般是表中增加版本字段,更新时where语句中增加版本的判断,算是一种CAS(Compare And Swep)操作, 4 商品库存场景中number起到了版本控制(相当于version)的作用( AND number=#{number}). 5 6 悲观锁之所以是悲观,在于他认为本次操作会发生并发冲突,所以一开始就对商品加上锁(SELECT … FOR UPDATE), 7 然后就可以安心的做判断和更新,因为这时候不会有别人更新

JVM+分布式+算法+锁+MQ+微服务+数据库 面试题

JAVA基础 1.JAVA中的几种基本数据类型是什么,各自占用多少字节 Java基本数据类型有8种: 名词解释: bit:位,计算机存储数据的最小单位,二进制数中的一个 位数. byte:字节,计算机存储数据的基本单位,一个字节由8位二进制数组成.通常一个汉字占两个字节. 2.String类能被继承吗,为什么. 不可以,因为String类有final修饰符,而final修饰的类是不能被继承的,实现细节不允许改变. 关于final修饰符,介绍如下: 根据程序上下文环境,Java关键字final有“