当前位置: 首页 > news >正文

12 分布式锁加入看门狗

1、看门狗的流程图

在这里插入图片描述

2、看门狗的代码实现
/****类说明:Redis的key-value结构*/
public class LockItem {private final String key;private final String value;public LockItem(String key, String value) {this.key = key;this.value = value;}public String getKey() {return key;}public String getValue() {return value;}
}
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;/****类说明:存放到延迟队列的元素,比标准的delay的实现要提前一点时间*/
public class ItemVo<T> implements Delayed{/*到期时刻  20:00:35,234*/private long activeTime;/*业务数据,泛型*/private T data;/*传入的数值代表过期的时长,单位毫秒,需要乘1000转换为毫秒和到期时间* 同时提前100毫秒续期,具体的时间可以自己决定*/public ItemVo(long expirationTime, T data) {super();this.activeTime = expirationTime+System.currentTimeMillis()-100;this.data = data;}public long getActiveTime() {return activeTime;}public T getData() {return data;}/*** 返回元素到激活时刻的剩余时长*/public long getDelay(TimeUnit unit) {long d = unit.convert(this.activeTime- System.currentTimeMillis(),unit);return d;}/**按剩余时长排序*/public int compareTo(Delayed o) {long d = (getDelay(TimeUnit.MILLISECONDS)-o.getDelay(TimeUnit.MILLISECONDS));if (d==0){return 0;}else{if (d<0){return -1;}else{return  1;}}}}

核心代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;import javax.annotation.PreDestroy;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;/*** 分布式锁,附带看门狗线程的实现:加锁,保持锁1秒*/
@Component
public class RedisDistLockWithDog implements Lock {//加锁的时间private final static int LOCK_TIME = 1*1000;private final static String LOCK_TIME_STR = String.valueOf(LOCK_TIME);//key的开头,用于标记是分布式锁使用private final static String RS_DISTLOCK_NS = "tdln2:";//释放锁的lua,释放的时候保持原子性private final static String RELEASE_LOCK_LUA ="if redis.call('get',KEYS[1])==ARGV[1] then\n" +"        return redis.call('del', KEYS[1])\n" +"    else return 0 end";/*还有并发问题,考虑ThreadLocal*/private ThreadLocal<String> lockerId = new ThreadLocal<>();private Thread ownerThread;private String lockName = "lock";@Autowiredprivate JedisPool jedisPool;public String getLockName() {return lockName;}public void setLockName(String lockName) {this.lockName = lockName;}public Thread getOwnerThread() {return ownerThread;}public void setOwnerThread(Thread ownerThread) {this.ownerThread = ownerThread;}//加锁的入口@Overridepublic void lock() {//自璇while(!tryLock()){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}@Overridepublic void lockInterruptibly() throws InterruptedException {throw new UnsupportedOperationException("不支持可中断获取锁!");}//具体的加锁逻辑@Overridepublic boolean tryLock() {Thread t=Thread.currentThread();/*说明本线程正在持有锁*/if(ownerThread==t) {return true;}else if(ownerThread!=null){/*说明本进程中有别的线程正在持有分布式锁*/return false;}Jedis jedis = null;try {jedis = jedisPool.getResource();/*每一个锁的持有人都分配一个唯一的id,也可采用snowflake算法*/String id = UUID.randomUUID().toString();SetParams params = new SetParams();params.px(LOCK_TIME); //加锁时间1sparams.nx();//synchronized是为了防止本地多个线程争抢synchronized (this){//加锁if ((ownerThread==null)&&"OK".equals(jedis.set(RS_DISTLOCK_NS+lockName,id,params))) {lockerId.set(id);setOwnerThread(t);if(expireThread == null){//看门狗线程启动expireThread = new Thread(new ExpireTask(),"expireThread");expireThread.setDaemon(true);//设置为守护线程expireThread.start();}//往延迟阻塞队列中加入元素(让看门口可以在过期之前一点点的时间去做锁的续期)delayDog.add(new ItemVo<>((int)LOCK_TIME,new LockItem(lockName,id)));System.out.println(Thread.currentThread().getName()+"已获得锁----");return true;}else{System.out.println(Thread.currentThread().getName()+"无法获得锁----");return false;}}} catch (Exception e) {throw new RuntimeException("分布式锁尝试加锁失败!",e);} finally {jedis.close();}}@Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {throw new UnsupportedOperationException("不支持等待尝试获取锁!");}@Overridepublic void unlock() {if(ownerThread!=Thread.currentThread()) {throw new RuntimeException("试图释放无所有权的锁!");}Jedis jedis = null;try {jedis = jedisPool.getResource();Long result = (Long)jedis.eval(RELEASE_LOCK_LUA,Arrays.asList(RS_DISTLOCK_NS+lockName),Arrays.asList(lockerId.get()));System.out.println(result);if(result.longValue()!=0L){System.out.println("Redis上的锁已释放!");}else{System.out.println("Redis上的锁释放失败!");}} catch (Exception e) {throw new RuntimeException("释放锁失败!",e);} finally {if(jedis!=null) jedis.close();lockerId.remove();setOwnerThread(null);}}@Overridepublic Condition newCondition() {throw new UnsupportedOperationException("不支持等待通知操作!");}/*看门狗线程*/private Thread expireThread;//通过delayDog 避免无谓的轮询,减少看门狗线程的轮序次数   阻塞延迟队列   刷1  没有刷2private static DelayQueue<ItemVo<LockItem>> delayDog = new DelayQueue<>();//续锁逻辑:判断是持有锁的线程才能续锁private final static String DELAY_LOCK_LUA ="if redis.call('get',KEYS[1])==ARGV[1] then\n" +"        return redis.call('pexpire', KEYS[1],ARGV[2])\n" +"    else return 0 end";private class ExpireTask implements Runnable{@Overridepublic void run() {System.out.println("看门狗线程已启动......");while(!Thread.currentThread().isInterrupted()) {try {LockItem lockItem = delayDog.take().getData();//只有元素快到期了才能take到  0.9sJedis jedis = null;try {jedis = jedisPool.getResource();Long result = (Long)jedis.eval(DELAY_LOCK_LUA,Arrays.asList(RS_DISTLOCK_NS+lockItem.getKey ()),Arrays.asList(lockItem.getValue(),LOCK_TIME_STR));if(result.longValue()==0L){System.out.println("Redis上的锁已释放,无需续期!");}else{delayDog.add(new ItemVo<>((int)LOCK_TIME,new LockItem(lockItem.getKey(),lockItem.getValue())));System.out.println("Redis上的锁已续期:"+LOCK_TIME);}} catch (Exception e) {throw new RuntimeException("锁续期失败!",e);} finally {if(jedis!=null) jedis.close();}} catch (InterruptedException e) {System.out.println("看门狗线程被中断");break;}}System.out.println("看门狗线程准备关闭......");}}//    @PostConstruct
//    public void initExpireThread(){
//
//    }@PreDestroypublic void closeExpireThread(){if(null!=expireThread){expireThread.interrupt();}}
}

测试:

@SpringBootTest
public class TestRedisDistLockWithDog {@Autowiredprivate RedisDistLockWithDog redisDistLockWithDog;private int count = 0;@Testpublic void testLockWithDog() throws InterruptedException {int clientCount =3;CountDownLatch countDownLatch = new CountDownLatch(clientCount);ExecutorService executorService = Executors.newFixedThreadPool(clientCount);for (int i = 0;i<clientCount;i++){executorService.execute(() -> {try {redisDistLockWithDog.lock(); //锁的有效时间1秒System.out.println(Thread.currentThread().getName()+"准备进行累加。");Thread.sleep(2000);count++;} catch (InterruptedException e) {e.printStackTrace();} finally {redisDistLockWithDog.unlock();}countDownLatch.countDown();});}countDownLatch.await();System.out.println(count);}@Testpublic void testTryLock2() {int clientCount =1000;for (int i = 0;i<clientCount;i++) {if (redisDistLockWithDog.tryLock()) {System.out.println("已获得锁!");redisDistLockWithDog.unlock();} else {System.out.println("未能获得锁!");}}}}
3、数据同步问题

在redis的主从部署架构下,由于主从之间的数据同步是异步线程来进行的,所以存在以下场景需要考虑:
当分布式锁加在主库上的时候,数据还没有同步至从库的时候,主库便宕机了,当哨兵机制从新指定了一个从库为主库的时候,此时从节点上并没有锁.
解决方案:红锁
在这里插入图片描述
在这里插入图片描述

相关文章:

12 分布式锁加入看门狗

1、看门狗的流程图 2、看门狗的代码实现 /****类说明&#xff1a;Redis的key-value结构*/ public class LockItem {private final String key;private final String value;public LockItem(String key, String value) {this.key key;this.value value;}public String getKey…...

怎么判断list是否为null

List<Entity> baseMess new ArrayList<>(); baseMess motiveService.getBaseMessage(machine.get(i),preDate,nowDate); System.out.println("获取Size"baseMess.size()); baseMess.removeIf(Objects::isNull); System.out.println("获取Size"…...

11.数据公式中使用2个 $$ a =b $$,是什么意思?

在 LaTeX 中&#xff0c;双美元符号 $$ 用于进入和退出独立的数学模式&#xff0c;也就是数学公式模式。在 $$ 中的文本将被视为数学公式&#xff0c;并以数学排版的方式显示。 具体地说&#xff0c;$$ 的使用是为了在文档中创建居中显示的独立数学公式。这意味着公式将单独占…...

设计模式-14-迭代器模式

经典的设计模式有23种&#xff0c;但是常用的设计模式一般情况下不会到一半&#xff0c;我们就针对一些常用的设计模式进行一些详细的讲解和分析&#xff0c;方便大家更加容易理解和使用设计模式。 1-原理和实现 迭代器模式&#xff08;Iterator Design Pattern&#xff09;&a…...

防雷接地+防雷工程施工综合方案

一、地凯科技防雷工程接地概述 防雷接地工程是指在建筑物或其他设施上安装防雷装置&#xff0c;以防止雷电对人员、设备和建筑物造成危害的工程。防雷装置主要包括避雷针&#xff08;网&#xff09;、引下线、接地体&#xff08;网&#xff09;等部分&#xff0c;其中接地体&a…...

排序算法--选择排序

实现逻辑 ① 第一轮从下标为 1 到下标为 n-1 的元素中选取最小值&#xff0c;若小于第一个数&#xff0c;则交换 ② 第二轮从下标为 2 到下标为 n-1 的元素中选取最小值&#xff0c;若小于第二个数&#xff0c;则交换 ③ 依次类推下去…… void print_array(int a[], int n){f…...

【Web】Ctfshow SSRF刷题记录1

核心代码解读 <?php $url$_POST[url]; $chcurl_init($url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $resultcurl_exec($ch); curl_close($ch); ?> curl_init()&#xff1a;初始curl会话 curl_setopt()&#xff1a;会…...

【算法挨揍日记】day30——300. 最长递增子序列、376. 摆动序列

300. 最长递增子序列 300. 最长递增子序列 题目解析&#xff1a; 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#…...

ROS2对比ROS1的一些变化与优势(全新安装ROS2以及编译错误处理)《1》

1、概述 我们在前面介绍的ROS&#xff0c;都是ROS1的版本&#xff0c;近期对机器狗进行学习的时候&#xff0c;发现版本是ROS2了&#xff0c;也发现平时习惯的一些命令都有了变化&#xff0c;改变还是挺大的&#xff0c;不过熟悉之后还是很习惯ROS2的写法。 ROS2不是在ROS1的基…...

基于单片机PM2.5监测系统仿真设计

**单片机设计介绍&#xff0c; 基于单片机PM2.5监测系统仿真设计 文章目录 一 概要简介设计目标系统组成工作流程仿真设计结论 二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 # 基于单片机PM2.5监测系统仿真设计介绍 简介 PM2.5&#xff08;可吸…...

CRM系统中的联系人是什么?如何进行联系人管理?

上手CRM系统前掌握专业术语是必要的功课&#xff0c;在第一次使用CRM系统时小编和大家一样&#xff0c;分不清楚线索、联系人、客户、商机之间的关系&#xff0c;今天我们就来着重分享一下CRM中联系人是什么&#xff1f;如何进行联系人管理&#xff1f; CRM系统联系人是指能够…...

uniapp:如何实现点击图片可以全屏展示预览

这个需要使用uniapp中的api&#xff1a;uni.previewImage&#xff0c;使用方法如下 1、html <template><view><image src"图片路径" click"preview"></image></view> </template> 2、JavaScript <script> e…...

python运行hhsearch二进制命令的包装器类

hhsearch 是 HMM-HMM&#xff08;Hidden Markov Model to Hidden Markov Model&#xff09;比对方法的一部分&#xff0c;属于 HMMER 软件套件。它用于进行蛋白质序列的高效比对&#xff0c;特别适用于检测远缘同源性。 以下是 hhsearch 的一些主要特点和用途&#xff1a; HMM…...

Java 网络编程、e-mail、多线程编程

一、Java 网络编程&#xff1a; 网络编程时指编写运行在多个设备的程序&#xff0c;这些设备通过网络连接起来。 Java.net包中的J2SE的API包含有类和接口&#xff0c;提供低层次的通信细节。 java.net 包中提供了两种常见的网络协议的支持&#xff1a; TCP&#xff1a;TCP&…...

为虚幻引擎开发者准备的Unity指南

目录 1.前言2.编辑器2.1 Scene 视图&#xff08;视口&#xff09;2.2 Game 视图 (Play in Editor)2.3.Hierarchy 窗口 (World Outliner)2.4 Project 窗口(Content Browser)2.5 Inspector (Details)2.6 Console&#xff08;消息视图/输出日志&#xff09;2.7 Modes 面板在哪里&a…...

Vue 2使用element ui 表格不显示

直接修改package.json文件 把这两个依赖修改成对应的 删除node_modules 重新安装依赖 重启...

C++学习 --文件

文件操作步骤&#xff1a; 1&#xff0c; 包含头文件#include<fstream> 2&#xff0c; 创建流对象&#xff1a;ofstream ofs 3&#xff0c; 打开文件&#xff1a;ofs.open("文件路径"&#xff0c; 打开方式) 4&#xff0c; 写数据&#xff1a;ofs <<…...

java/Android:将字符串按数量分割

分割成数组 import java.util.Arrays;/*** Java将字符串按照指定长度分割成字符串数组*/ public class StringUtils {public static void main(String[] args){String data "227d77a7a244c7b2be3180f2d46be352f56ddf92866692f2cac797358097e5a3e90f6d20bb96bc516a4ab9c0…...

JVM 监控命令详解

文章目录 JDK 中与常用命令行工具jpsjstatjinfojmap导出 dump 文件查看堆内存信息 jstack JVM 可视化分析工具 JDK 中与常用命令行工具 jps 查看当前服务器正在执行的 Java 进程 $> jps 7584 Application 16433 AdminApplication 14209 Jps 5813 Bootstrap 5575 TestApplic…...

TEE威胁评分与评级

目录 一、攻击潜力 1.1 攻击潜力网格 1、实际经过的时间: 2、访问TOE(目标设备)...

c++面向对象:对象的赋值

对象初始化&#xff1a;构造函数和复制构造函数在设计一个类时&#xff0c;往往要设计构造函数。一般对象的初始化使用构造函数初始化&#xff0c;如果没有构造函数则会使用默认构造函数。还可以用复制构造函数来通过一个已有对象初始化一个新的对象。设计一个类来表现对象的初…...

Windows 11安装的 OOBEKEYBOARD 错误

在虚拟机中尝试安装Windows 11遇到错误&#xff0c;提示OOBEKEYBOARD。 参考了一些处理方法&#xff1a; 转发OOBEKEYBOARD &#xff01;&#xff01;解决Windows 10安装过程中的错误-CSDN博客&#xff0c;但是没有解决问题。 最后通过该文下Windows 11跳过微软账户登录的三…...

扩散模型在图像编辑中的应用与优化实践

1. 扩散模型与图像编辑的技术融合去年我在处理一批商业摄影素材时&#xff0c;客户突然要求将照片中的阴天背景替换成阳光明媚的沙滩场景。传统Photoshop处理需要数小时精细修图&#xff0c;而使用扩散模型技术&#xff0c;我在15分钟内就输出了自然逼真的合成效果。这种技术革…...

Quixel Mixer本地材质库管理全攻略:从下载、整理到备份,告别资源混乱

Quixel Mixer本地材质库管理全攻略&#xff1a;从下载、整理到备份&#xff0c;告别资源混乱 打开Quixel Mixer的Local Library时&#xff0c;你是否经常面对满屏未分类的材质球感到无从下手&#xff1f;当硬盘空间告急时&#xff0c;又是否在纠结哪些材质可以安全删除&#xf…...

从Docker镜像到生产部署:企业级Web应用容器化实战指南

1. 项目概述与核心价值最近在折腾一个企业官网项目&#xff0c;客户对性能和稳定性要求极高&#xff0c;同时希望有一套清晰、可维护的代码架构。在技术选型阶段&#xff0c;我偶然在Docker Hub上发现了tentechtop/tentech-official这个镜像。起初&#xff0c;它只是一个简单的…...

MySql基础知识精简版

一、建表1、分析需求明确关系&#xff0c;作用&#xff1a;提前在建表的时候保留有相关的字段&#xff0c;方便进行相互的校验查询表里边必须有一个 ID 字段的话代表是唯一标识字段查询两个不同的表时&#xff0c;需要表进行关联绑定&#xff0c;建立一对一关联&#xff0c;设立…...

openclaw v2026.5.6 最新更新:修复 OpenAI Codex OAuth 路由、插件请求、调试代理与 Web Fetch 超时问题

一、版本概览 openclaw 在 2026年5月7日发布了 v2026.5.6 版本。 这次更新虽然看起来是一个小版本&#xff0c;但实际涉及多个关键修复&#xff0c;特别是 Doctor/OpenAI Codex 路由回退问题、插件/runtime fetch 请求头兼容问题、debug proxy 请求重放问题、以及 web fetch 超…...

clawpier爬虫框架:声明式配置应对动态网页抓取难题

1. 项目概述&#xff1a;一个现代化的网络爬虫框架最近在做一个数据采集相关的项目&#xff0c;需要从几个结构比较复杂的网站上抓取一些动态加载的内容。用传统的requestsBeautifulSoup组合&#xff0c;遇到JavaScript渲染的页面就有点力不从心&#xff0c;上Selenium或者Play…...

AgentScaffold:为AI编程助手构建持久化知识图谱与治理框架

1. 项目概述&#xff1a;为AI编程助手装上“记忆”与“纪律”如果你和我一样&#xff0c;深度使用过Cursor、Claude Code这类AI编程助手&#xff0c;那你一定经历过这种“甜蜜的烦恼”&#xff1a;每次开启一个新会话&#xff0c;它都像一张白纸&#xff0c;需要重新读取你的项…...

从语音到智能体:构建语音交互式AI系统的架构与实践

1. 项目概述&#xff1a;从语音到智能体的桥梁最近在探索AI智能体&#xff08;Agent&#xff09;的落地应用时&#xff0c;我遇到了一个非常有意思的开源项目&#xff1a;thom-heinrich/voice2agent。这个项目直译过来就是“语音到智能体”&#xff0c;它的核心目标非常明确——…...