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

咸阳市网站建设_网站建设公司_UI设计_seo优化

网站漏洞 在线扫描,网站营销的重点,v2ex 网站建设,idc机房托管费用各位小伙伴们大家好#xff0c;欢迎来到这个小扎扎的Redis 6专栏#xff0c;在这个系列专栏中我对B站黑马的Redis教程进行一个总结#xff0c;鉴于 看到就是学到、学到就是赚到 精神#xff0c;这波依然是血赚 ┗|#xff40;O′|┛ #x1f4a1;Redis知识点速览#… 各位小伙伴们大家好欢迎来到这个小扎扎的Redis 6专栏在这个系列专栏中我对B站黑马的Redis教程进行一个总结鉴于 看到就是学到、学到就是赚到 精神这波依然是血赚 ┗|O′|┛ Redis知识点速览 分布式锁 业务逻辑分析 Redis命令 代码实现 分布式锁误删问题 问题原因分析 代码实现 Lua脚本 讲过上一节的分析可知服务器集群项目中的锁是无法精准的锁住线程资源的于是我们就是需要使用分布式锁分布式锁该如何使用又有什么注意点呢就让我们进入接下来的学习 首先使用idea模拟搭建一个tomcat服务器集群并使用Nginx对集群中的服务器实现负载均衡配置完负载均衡之后发送两次请求就会在idea的运行窗口中发现两次请求的运行是分别在两个服务器中完成这就是集群的轮询机制 分布式锁 业务逻辑分析 在单JVM虚拟机多线程执行的情况下可以使用JVM内部的锁机制来控制多进程的并发执行借此可以保证一个用户只能下一个优惠券订单。但是在分布式的情况下每一个JVM虚拟机都有一个锁监视器不同JVM里的不同线程之间的访问的并不是同一个锁监视器所以说此时再使用synchronized锁就无法满足一个用户限买一单的业务情况了于是就需要使用分布式锁 分布式锁就是满足分布式系统或集群模式下多进程可见并且互斥的锁。一般实现分布式锁的技术主要就是MySQL、Redis和ZooKeeper但是综合对比来看的话Redis作分布式锁的性能更高一些Redis是在JVM虚拟机之外的一种应用可以满足多线程都可见互斥可以使用setnx这种的互斥命令来实现但是使用Redis会存在安全性问题如果Redis崩溃的话会导致锁无法释放而出现死锁现象解决这一问题的方案就是使用TTL过期时间就算崩溃也可以实现到期自动释放。 Redis命令 使用Redis实现分布式锁的步骤主要就是使用setnx体现互斥锁然后expire过期时间防止宕机死锁但是如果服务在setnx之后expire之前宕机的话依旧会造成死锁现象。于是我们可以使用以下命令在互斥的同时设置超时时间这样的话即是在设置锁之后宕机依旧可以凭借超时时间释放锁 SET lock thread NX EX ttl超时时间代码实现 将获取锁和释放锁业务抽取出来使用接口和实现类来完成 /*** author : mereign* date : 2022/6/10 - 12:01* description : 分布式锁*/ public interface ILock {/*** 尝试获取锁* param timeoutSec 锁的超时时间* return 是否成功获取锁*/boolean tryLock(long timeoutSec);/*** 释放锁*/void unLock(); }/*** author : mereign* date : 2022/6/10 - 13:31* description : 分布式锁的实现类*/ public class SimpleRedisLock implements ILock {private String name;/***先获取StringRedisTemplate对象才能使用代码操作Redis*/private StringRedisTemplate stringRedisTemplate;public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {this.name name;this.stringRedisTemplate stringRedisTemplate;}Overridepublic boolean tryLock(long timeoutSec) {// 获取当前操作线程的标识long threadId Thread.currentThread().getId();// 获取锁Boolean res stringRedisTemplate.opsForValue().setIfAbsent(RedisConstants.KEY_PREFIX name, threadId , timeoutSec, TimeUnit.SECONDS);// res是Boolean的包装类返回结果的时候涉及到拆箱问题有可能存在结果为null的情况此时就需要返回结果与true的比较避免了空指针风险return Boolean.TRUE.equals(res);}Overridepublic void unLock() {// 释放锁stringRedisTemplate.delete(RedisConstants.KEY_PREFIX name);} }定义了分布式锁的获取和释放接下来就是在一人一单业务代码中将锁机制升级成多线程锁了主要修改的代码为就是5~14行由单体的synchronized锁改为使用自定义的Redis锁并根据不同线程获取锁的不同结果定义了不同的业务 public Result secKillVoucher(Long voucherId) {// 单用户id(拦截器中做登录验证的用户id)Long userId UserHolder.getUser().getId();// 创建锁对象SimpleRedisLock lock new SimpleRedisLock(order: userId, stringRedisTemplate);// 获取锁boolean isLock lock.tryLock(1200);// 判断是否获取锁成功if (!isLock) {// 获取锁失败返回错误或者重试return Result.fail(不允许重复下单 );}// 获取锁成功继续下单的业务逻辑try {// 查询优惠券SeckillVoucher seckillVoucher seckillVoucherService.getById(voucherId);// 获取时间 判断秒杀活动是否开始或者结束if (seckillVoucher.getBeginTime().isAfter(LocalDateTime.now())) {return Result.fail(活动暂未开始);} else if (seckillVoucher.getEndTime().isBefore(LocalDateTime.now())) {return Result.fail(活动已经结束);}// 判断库存是否充足if (seckillVoucher.getStock() 1) {return Result.fail(库存不足活动结束);}// user_id和voucher_id联合查询订单数int count query().eq(user_id, userId).eq(voucher_id, voucherId).count();// 订单数为1 就说明已经下过单了if (count 0) {return Result.fail(您已经购买过该商品了);}// 扣减库存boolean update seckillVoucherService.update().setSql(stock stock - 1).eq(voucher_id, voucherId).gt(stock, 0).update();if (!update) {return Result.fail(库存不足);}// 创建订单 并返回idVoucherOrder order new VoucherOrder();// 订单id(redis全局唯一id) 下单用户id(拦截器中做登录验证的用户id) 优惠券id(直接传过来的id)long orderId generator.nextId(order);order.setId(orderId);order.setUserId(userId);order.setVoucherId(voucherId);save(order);return Result.ok(orderId);} finally {// 释放锁lock.unLock();} }分布式锁误删问题 问题原因分析 这个问题出现在Redis锁设置的超时时间上由于设置了超时时间所以可能出现一下情况即当线程1获取到锁之后执行下单业务但是由于业务堵塞锁已经超出TTL时间自动释放此时线程2趁机获取Redis锁成功执行下单业务线程2的下单业务执行到一半时线程1完成下单使用del命令释放锁此时线程1释放的是线程2的锁于是现在锁又处于闲置状态于是线程3来获取Redis锁成功执行下单业务此时一共有同一个用户的两个线程在同时操作 为了解决以上出现的问题需要在每次释放锁之前都通过锁的线程标识(Redis锁对应的值)判断一下是不是自己的锁如果是就使用del命令释放锁否则就不做操作。但是有一点值得注意之前锁的线程标识使用的是线程的name这样的话很容易就造成不同JVM虚拟机里的线程name冲突影响判断于是可以使用UUID随机生成一组数字加上线程name作为线程的标识这样更能确保唯一性 代码实现 综上所述一共有两处需要改进的地方一个是使用UUID加线程name作为线程标识(主要修改的是获取锁方法加上UUID的获取)一个是在使用del释放锁之前判断一下是否是自己的锁 public static final String ID_PREFIX UUID.randomUUID(true) -;public boolean tryLock(long timeoutSec) {// 获取当前操作线程的标识String threadId RedisConstants.ID_PREFIX Thread.currentThread().getId();// 获取锁Boolean res stringRedisTemplate.opsForValue().setIfAbsent(RedisConstants.KEY_PREFIX name, threadId, timeoutSec, TimeUnit.SECONDS);// res是Boolean的包装类返回结果的时候涉及到拆箱问题有可能存在结果为null的情况此时就需要返回结果与true的比较避免了空指针风险return Boolean.TRUE.equals(res); }public void unLock() {// 获取当前操作线程的标识String threadId RedisConstants.ID_PREFIX Thread.currentThread().getId();// 通过锁名 获取redis中存储的锁对应的标识String rid stringRedisTemplate.opsForValue().get(RedisConstants.KEY_PREFIX name);if (threadId.equals(rid)) {// 释放锁stringRedisTemplate.delete(RedisConstants.KEY_PREFIX name);} }Lua脚本 Redis提供了Lua脚本功能在一个脚本中编写多条Redis命令确保多条命令执行时的原子性。Lua是一种编程语言它的基本语法大家可以参考网站https://www.runoob.com/lua/lua-tutorial.html 使用Redis命令调用脚本的常见命令可以是 EVAL “redis.call(‘set’, ‘key’, ‘value’)” num 上述命令解释为EVAL是调用后面双引号中就是所调用的脚本语句而最后的num即脚本语句中的KEYS类型参数的个数num之外的就是ARGV(value)类型的参数。比如说接下来这一个语句就代表着setname为Rose其中KEYS类型的参数有1个就是num后面的第一个name剩下的都是ARGV(value)类型的数据其中调用的是KEYS[1]和ARGV[2]也就是name和Rose EVAL “redis.call(‘set’, ‘KEYS[1]’, ‘ARGV[2]’)” 1 name age Rose
http://www.lebaoying.cn/news/105809.html

相关文章:

  • 网站建设的上机报告wordpress 文章 目录
  • 在线营销型网站制作广州网站推广模板
  • 手机网站 普通网站在线课堂手机网站模板
  • php 建设网站制作开发公司的一般利润率2020
  • 有没有傻瓜式建设网站千库网app官方下载
  • 宜昌网站建设一流专业建设标准
  • wordpress 类似建站wordpress 获取文章文字
  • 南京app网站开发公司项目网站基础设施建设
  • 组件化网站建设wordpress标签生成
  • 网站怎么做效果好江苏建设厅网站首页
  • 天津网站建设制作设计海门网站制作
  • 合肥网站制作哪家有名做招聘网站需要什么
  • 摄影赚钱的网站共享互助医疗网站建设
  • 衣服商业网站建设策划书网站团队
  • 昌吉州住房和城乡建设局网站一站式营销推广平台
  • 网站建设 比选网站建设注意问题
  • 做短视频网站首页页面设计模板
  • 西安做网站的公司报价电子商务具体是做什么的
  • 可信赖的大良网站建设汽车之家网页版地址
  • 中国的网站域名是什么意思阿克苏网站设计
  • 求2021没封的良心网址吉林网站seo
  • 简单个人网站模板互联网营销师培训教材
  • 买网站主机重庆外贸网站建设公司
  • 怎么做微信推送 网站网站建设用什么代码
  • 旅游网站建设费用预算怎么建设婚恋网站
  • 公司做外贸的网站网站用html做框架asp做主页
  • 贺州网站建设邵阳市中高风险地区
  • 甘肃网站建设外贸soho通过网站开发客户
  • 汕尾手机网站建设报价国外的设计网站推荐
  • 建设银行信用卡中心网站ppt链接网站怎么做的