【分布式-Redis应用】Spring中Redis使用项目实战(持续更新...)

2023-11-14

@[TOC](目录标题)

场景及代码示例

1、list集合存储到Redis以及读取

  import org.springframework.data.redis.core.StringRedisTemplate;

  @Autowired
  private StringRedisTemplate redisTemplate;
  
  // 存入缓存,过期时间1h
  String cacheValue = JSONObject.parseArray(JSON.toJSONString(lsit)).toJSONString();
  redisTemplate.opsForValue().set(key, cacheValue, 1, TimeUnit.HOURS);

 /* 缓存中读取 */
 String childTemplatesStr = redisTemplate.opsForValue().get(key);
 List<TemplateInfo> cacheChildTemplates = JSONObject.parseArray(childTemplatesStr, TemplateInfo.class);

分布式锁及代码示例

参考文章:分布式系统 - 分布式锁及实现方案

1、Redisson实现分布式锁

关键点声明

各加锁方法对比
● 申明方法有无InterruptedException:方法申明时抛出 InterruptedException,表示当前方法在等待时,支持其他线程通过调用interrupt方法,中断当前线程方法的执行。
● 有无leaseTime:设置leaseTime即设置锁的过期时间,若无或leaseTime=-1L,则通过watchDog自动续锁。
● 有无waitTime:设置锁阻塞时的最大阻塞时间。
● lock和tryLock:往往lock是获取不到锁时阻塞,tryLock获取不到锁时也会立即返回,但如果包含waitTime参数,则另算。
● lock.unlock():如果直接lock.unlock(),可能会抛出java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id. 从报错信息可知:尝试解锁,而不是由当前线程按节点 ID 锁定。 这是因为redisson的unlock方法内部对释放锁的线程进行校验,当发现不是当前线程释放锁的时候会报以上错误解决方式:unlock之前判断是否是当前线程(lock.isLocked() && lock.isHeldByCurrentThread()

有个Redisson分布式锁实现原理可参考文章:https://segmentfault.com/a/1190000040813126

Redisson分布式锁代码模板

1、
设置等待的时间,加锁的时间:
● 因为设置了等待的时间waitTime,那么当没有获取到锁的时候,会最大等待500ms。
● 因为设置了加锁的时间leaseTime,那么当1500ms之后,如果业务还没有执行完毕,则会释放锁。

RLock lock = redissonClient.getLock("LOCK_KEY");
long waitTime=500L;
long leaseTime=15000L;


try {
     boolean isLock = lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);
	 if (!isLock){
       log.info("lock conflict");
       return;
    }
     // do something ...
} catch (InterruptedException e) {
    log.warn("InterruptedException", e);
     Thread.currentThread().interrupt();
} 
} catch (Exception e) {
    log.error("error", e);
    throw e;
}finally {
    //判断要解锁的key是否已被锁定;判断要解锁的key是否被当前线程持有
	if (lock.isLocked() && lock.isHeldByCurrentThread()) {
    	lock.unlock();
	}
}

2、
没有设置等待时间,加锁的时间:
● 因为没有设置等待时间,当获取不到锁的时候,不会阻塞等待,而是直接返回。
● 因为没有设置加锁的时间,默认redisson会给30s的加锁时间。

RLock lock = redissonClient.getLock("LOCK_KEY");


try {
	boolean isLock = lock.tryLock();
	if (!isLock){
	    log.info("lock conflict");
	    return;
	}
	 // do something ...
} catch (InterruptedException e) {
    log.warn("InterruptedException", e);
     Thread.currentThread().interrupt();
} 
} catch (Exception e) {
    log.eror("error", e);
} finally {
    //判断要解锁的key是否已被锁定;判断要解锁的key是否被当前线程持有
	if (lock.isLocked() && lock.isHeldByCurrentThread()) {
    	lock.unlock();
	}
}

2、Redis实现分布式锁

加锁: set NX PX + 重试 + 重试间隔

向Redis发起如下命令:

SET productId:lock 0xx9p03001 NX PX 30000 

其中,"productId"由自己定义,可以是与本次业务有关的id,"0xx9p03001"是一串随机值,必须保证全局唯一(原因在后文中会提到),“NX"指的是当且仅当key(也就是案例中的"productId:lock”)在Redis中不存在时,返回执行成功,否则执行失败。"PX 30000"指的是在30秒后,key将被自动删除。执行命令后返回成功,表明服务成功的获得了锁。


@Override
public boolean lock(String key, long expire, int retryTimes, long retryDuration) {
    // use JedisCommands instead of setIfAbsense
    boolean result = setRedis(key, expire);

    // retry if needed
    while ((!result) && retryTimes-- > 0) {
        try {
            log.debug("lock failed, retrying..." + retryTimes);
            Thread.sleep(retryDuration);
        } catch (Exception e) {
            return false;
        }

        // use JedisCommands instead of setIfAbsense
        result = setRedis(key, expire);
    }
    return result;
}

private boolean setRedis(String key, long expire) {
    try {
        RedisCallback<String> redisCallback = connection -> {
            JedisCommands commands = (JedisCommands) connection.getNativeConnection();
            String uuid = SnowIDUtil.uniqueStr();
            lockFlag.set(uuid);
            return commands.set(key, uuid, NX, PX, expire); // 看这里
        };
        String result = redisTemplate.execute(redisCallback);
        return !StringUtil.isEmpty(result);
    } catch (Exception e) {
        log.error("set redis occurred an exception", e);
    }
    return false;
}

加锁:setIfAbsent

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    public void method() {
        // eventId就是订单id
        log.info("[MallOrderMsgConsumer]. eventId:[{}], message:[{}]", event.getEventId(), JsonUtils.toJson(event));
        try {
            String orderId = event.getEventId();
            AssertUtil.isTrue(StringUtils.hasText(orderId));
            /* 幂等处理,缓存存储1天 */
            Boolean lock = stringRedisTemplate.opsForValue()
                    .setIfAbsent(AiToolConstant.MALL_ORDER_LOCK_KEY_PREFIX + orderId, orderId, 24, TimeUnit.HOURS);
            log.info("[MallOrderMsgConsumer]. lock:[{}], eventId:[{}]", lock, orderId);
            if (BooleanUtils.isTrue(lock)) {
                log.info("[MallOrderMsgConsumer]. start handler. message:[{}]", JsonUtils.toJson(event));
                /* 核心业务处理 */
                handler(event);
                log.info("[MallOrderMsgConsumer]. end handler. message:[{}]", JsonUtils.toJson(event));
            }
        } catch (Exception exception) {
            log.error("[MallOrderMsgConsumer]. ERROR. eventId:[{}], message:[{}]",
                    event.getEventId(), JsonUtils.toJson(event), exception);
        } finally {
         // 根据业务场景,判断是否解锁
    }

解锁:采用lua脚本

在删除key之前,一定要判断服务A持有的value与Redis内存储的value是否一致。如果贸然使用服务 A持有的key来删除锁,则会误将服务B的锁释放掉。

if redis.call("get", KEYS[1])==ARGV[1] then
	return redis.call("del", KEYS[1])
else
	return 0
end
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【分布式-Redis应用】Spring中Redis使用项目实战(持续更新...) 的相关文章

  • 我们可以有条件地声明 spring bean 吗?

    有没有一种方法可以有条件地声明 Spring bean 例如
  • 什么是内部类的合成反向引用

    我正在寻找应用程序中的内存泄漏 我正在使用的探查器告诉我寻找这些类型的引用 但我不知道我在寻找什么 有人可以解释一下吗 Thanks Elliott 您可以对 OUTER 类进行合成反向引用 但不能对内部类实例进行合成 e g class
  • 无法使用 datastax java 驱动程序通过 UDT 密钥从 cassandra 检索

    我正在尝试使用用户定义的类型作为分区键将对象存储在 cassandra 中 我正在使用 datastax java 驱动程序进行对象映射 虽然我能够插入到数据库中 但无法检索该对象 如果我更改分区键以使用非 udt 例如文本 我就能够保存和
  • 我对线程失去了理智

    我想要这个类的对象 public class Chromosome implements Runnable Comparable
  • 在光标所在行强制关闭!

    嘿 我正在尝试创建一个应用程序来查找存储在 SQlite 数据库中的 GPS 数据 但我面临一个问题 我构建了一个 DbAdapter 类来创建数据库 现在我尝试使用以下函数从另一个类获取所有数据上的光标 public Cursor fet
  • 为什么解析这个 JSON 会抛出错误?

    我正在尝试解析这个 JSONObject query yahoo count 1 results rate Name USD INR id USDINR Time 12 19pm Date 10 31 2015 Bid 65 405 Ask
  • 如何使用双重调度来分析图形基元的交集?

    我正在分析图形基元 矩形 直线 圆形等 的交互并计算重叠 相对方向 合并等 这被引用为双重调度的一个主要示例 例如维基百科 http en wikipedia org wiki Double dispatch 自适应碰撞算法通常要求 不同的
  • StackExchange.Redis Get 函数抛出 TimeoutException

    我在用着StackExchange Redis与 C 和StackExchangeRedisCacheClient Get函数抛出以下异常 myCacheClient Database StringGet txtKey Text myCac
  • 使用 Spring / JPA 写入 Postgres 数据库的 JSON 列

    我有一个名为 test 的表 其中包含 Postgres 9 3 中 json 类型的列 sample column 我正在尝试使用 Spring JPA 将以下内容写入列中 name 更新的名称 我在其他帖子中读到 我需要添加自定义转换器
  • 使用@Transactional注解批量插入

    在我的 Spring 应用程序中 我想一次性在数据库中插入近 1500 条记录 我在后端使用 Spring 4 X 和普通休眠 在我的服务层中 我使用 Transactional 注释 现在 在某个时间点之后插入记录时 我遇到内存不足错误
  • 如何自定义舍入形式

    我的问题可能看起来很简单 但仍然无法得到有效的东西 我需要自定义 Math round 舍入格式或其他格式以使其工作如下 如果数字是 1 6 他应该四舍五入到 1 如果大于或等于 1 7 他应该四舍五入到 2 0 对于所有其他带有 6 的小
  • UseCompressedOops JVM 标志有什么作用以及何时应该使用它?

    HotSpot JVM 标志是什么 XX UseCompressedOops我应该做什么以及什么时候使用它 在 64 位 Java 实例上使用它 与不使用它 时 我会看到什么样的性能和内存使用差异 去年大多数 HotSpot JVM 都默认
  • Joshua Bloch 的构建器设计模式有何改进?

    早在 2007 年 我就读过一篇关于 Joshua Blochs 所采用的 构建器模式 的文章 以及如何修改它以改善构造函数和 setter 的过度使用 特别是当对象具有大量属性 其中大部分属性是可选的 时 本文对此设计模式进行了简要总结
  • Java 8 Stream,获取头部和尾部

    Java 8 引入了Stream http download java net jdk8 docs api java util stream Stream html类似于 Scala 的类Stream http www scala lang
  • 从三点求圆心的算法是什么?

    我在圆的圆周上有三个点 pt A A x A y pt B B x B y pt C C x C y 如何计算圆心 在Processing Java 中实现它 我找到了答案并实施了一个可行的解决方案 pt circleCenter pt A
  • Tomcat 6 未从 WEB-INF/lib 加载 jar

    我正在尝试找出我的 tomcat 环境中的配置问题 我们的生产服务器正在运行 tomcat 安装并从共享 NFS 挂载读取战争 然而 当我尝试使用独立的盒子 及其配置 进行同样的战争时 我收到下面发布的错误 有趣的是 如果我将 WEB IN
  • java实现excel价格、收益率函数[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 我所有的 java 应用程序现在都会抛出 java.awt.headlessException

    所以几天前我有几个工作Java应用程序使用Swing图书馆 JFrame尤其 他们都工作得很好 现在他们都抛出了这个异常 java awt headlessexception 我不知道是什么改变了也许我的Java版本不小心更新了 谢谢你尽你
  • Selenium 单击在 Internet Explorer 11 上不起作用

    我尝试在 Internet Explorer 上单击 selenium 但它不起作用 我努力了element click moveToElement element click build perform javascript没事了 事实上
  • 使用 DBCP 配置 Tomcat

    在闲置一段时间 几个小时 后 我们收到了 CommunicationsException 来自 DBCP 错误消息 在异常中 位于这个问题的末尾 但我没有看到任何配置文件中定义的 wait timeout 我们应该看哪里 在 tomcat

随机推荐

  • 不同文件格式与图像类型

    文件格式 通常有BMP 8 位 24 位 32 位 PNG JPG JPEG GIF PPM PNM 和 TIF 图像类型 单通道图像 每个像素由单个值表示 包括二值 单色 图像 每个像素由一个 0 1 位 和灰度图像 每个像素由 8 位表
  • Kotlin:Android世界的Swift

    转载自 http www infoq com cn news 2015 06 Android JVM JetBrains Kotlin Kotlin 是一门与 Swift 类似的静态类型 JVM 语言 由 JetBrains 设计开发并开源
  • 2019年7月3日星期三 恒指/美原油/美黄金 走势分析

    财经早餐 2019年7月3日重点关注的财经数据与事件 09 45 中国6月财新服务业PMI 15 50 法国6月服务业PMI终值 15 55 德国6月服务业PMI终值 16 00 欧元区6月服务业PMI终值 16 30 英国6月服务业PMI
  • malloc和free,brk和sbrk和mmap和munmap的使用和关系以及内存分配的原理

    目录 一 使用 1 1 malloc和free 2 brk和sbrk 2 1 sbrk 2 2 brk 3 mmap munmap 二 关系 三 内存分配原理 四 malloc底层 一 使用 1 1 malloc和free 参数 申请内存大
  • Android进阶——AIDL详解

    看这篇之前先看看binder Android Binder机制 1 简介 AIDL Android 接口定义语言 可以使用它定义客户端与服务端进程间通信 IPC 的编程接口 在 Android 中 进程之间无法共享内存 用户空间 不同进程之
  • 【第13篇】Bert论文翻译

    文章目录 摘要 1 简介 2 相关工作 2 1 无监督的基于特征的方法 2 2 无监督微调方法 2 3 从监督数据中迁移学习 3 Bert 3 1 预训练BERT 3 2 微调BERT 4 实验 4 1 GLUE 4 2 SQuAD v1
  • 应该选择使用哪个版本的 JDK?

    要构建和运行 Java 应用程序 就需要安装 JDK 环境 OpenJDK 是 Java SE 规范的开源软件 但它只是源代码 二进制发行版由不同的供应商提供 适用于许多受支持的平台 这些发行版在许可证 商业支持 支持的平台和更新频率方面有
  • All in AI,现在开始算不算太晚?

    编者按 目前大模型近乎可以帮助人类处理方方面面的事情 如对话 写文章 写代码等等 在大模型 狂飙 趋势下 想要从事AI领域的小伙伴可能会犹疑 现在进入AI领域会不会已经太晚了 本文作者结合自身转型经历和对AI市场的研判 阐述了进入人工智能领
  • 软件设计能力提升之设计匠艺

    简介 课程将高效率的软件项目质量管理 UML 重构设计与功能实现 单元测试几个课程合而为一 我们称之为设计匠艺 这几个课程当然可以分开来讲 但是如果把他们组织在一起 将会形成一个有效的开发闭环 事实上 在实际的开发过程中 它们本身就是一个整
  • 荣耀play4tpro能升级鸿蒙系统吗,荣耀Play4T Pro的Magic UI系统有哪些优化和升级

    一个好的体验度离不开一个好的系统支持 那咱们的这个荣耀Play4T Pro手机所采用的系统有哪些提升和改变呢 其实这个手机还是搭载了了大家比较熟悉的Magic UI系统 凭借华为方舟编译器先进的技术 系统的应用执行效率提升29 系统操作流畅
  • token 应该存在 Cookie、SessionStorage 还是 LocalStorage 中?

    在Web开发中 token 令牌 可以存储在多个地方 包括cookie sessionStorage和localStorage 每种存储方式都有其优点和缺点 选择哪种方式取决于你的应用需求 1 Cookie 将token存储在cookie中
  • WSL和VM的不兼容问题

    目前的问题是鱼与熊掌不可兼得 所以每次只能用一个 假如要用wsl2 则执行 dism exe online enable feature featurename VirtualMachinePlatform all norestart ws
  • SQL 常用优化实践

    对查询进行优化 要尽量避免全表扫描 首先应考虑在 where 及 order by 涉及的列上建立索引 应尽量避免在 where 子句中对字段进行 null 值判断 否则将导致引擎放弃使用索引而进行全表扫描 如 select id from
  • 神位纷争服务器维护,神位纷争steam版3月12日版本更新公告

    Steam上的神位纷争即将迎来重大版本更新 以下更新也将在之后的移动端版本中体现 本次更新将在3月12日下午推送至Steam版本的 神位纷争 另外需要补充一句的是 神位纷争HD已可免费游玩了 欢迎各位玩家前往体验 系统 1 对战系统相关改动
  • 软件版本号的意思是什么?从PC到安卓,带你了解版本号命名逻辑

    猫三科技杂烩 18 12 2114 49 很多朋友在安装软件的时候都会注意到软件下方一行意义不明的版本号 有的软件会用x x x逻辑命名版本号 有的软件则是x x x x 而windows系统的版本号例如1803就更让人摸不到头脑了 那么这
  • Transformer——《Attention is all you need》

    本文是Google 机器翻译团队在2017 年发表 提出了一个新的简单的网络模型 Transformer 该模型基于纯注意力机制 Attention mechanisms 完全抛弃了RNN和CNN网络结构 在机器翻译任务上取得了很好的效果
  • 设计和开发如何获取真实的客户产品需求

    某富翁想要娶老婆 有三个人选 富翁给了三个女孩各一千元 请她们把房间装满 第一个女孩买了很多棉花 装满房间的1 2 第二个女孩买了很多气球 装满房间3 4 第三个女孩买了蜡烛 让光线充满房间 最终 富翁选了胸部最大的那个 这个故事告诉我们
  • 五种开源协议的具体区别

    1 BSD开源协议 original BSD license FreeBSD license Original BSD license 修改源码后可以选择闭源 修改后的文档不用进行版权说明 衍生软件的广告不能用你的名字促销 2 Apache
  • 爬虫Scrapy框架之学习使用(三):信号(Signals)

    Extension for collecting core stats like items scraped and start finish times import datetime from scrapy import signals
  • 【分布式-Redis应用】Spring中Redis使用项目实战(持续更新...)

    TOC 目录标题 场景及代码示例 1 list集合存储到Redis以及读取 import org springframework data redis core StringRedisTemplate Autowired private S