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

广西庆海建设发展有限公司网站网站建设公司骗人

广西庆海建设发展有限公司网站,网站建设公司骗人,哪里购买网站空间好,广州做网站技术背景 Redis分布式锁很有用处#xff0c;在秒杀、抢购、订单、限流特别是一些用到异步分布式并行处理任务时频繁的用到#xff0c;可以说它是一个BS架构的应用中最高频使用的技术之一。 但是我们经常会碰到这样的一个问题#xff0c;那就是我们都按照标准做了但有时运行着、… 背景 Redis分布式锁很有用处在秒杀、抢购、订单、限流特别是一些用到异步分布式并行处理任务时频繁的用到可以说它是一个BS架构的应用中最高频使用的技术之一。 但是我们经常会碰到这样的一个问题那就是我们都按照标准做了但有时运行着、运行着就是没锁住的问题。 一旦出了这样的问题特别难调试以及排查因为在异步并行的环境下计算机代码的执行是乱序的而且有一个“概率”问题。往往测10次结果都是对的。此时测试团队以为这次交付没有问题了于是布署上线而上了线后会产生要么一次都不对或者前10次对的第11次就是不对的。 要知道锁的问题出了事不是小事。一旦出事对用户来说就和“死机”一样死活无法操作了亦或者时操作的结果乱通知、乱扣钱、随机不能下单此时后台唯有找到锁键值然后人为的把这个键值给“剁”掉才能解决。 因此今天就借着刚排查的2个生产问题我们把锁的机制彻底的了解一下。 Redis锁的正确使用方式 //使用RedissonClient锁Autowired private RedissonClient redissonSentinel;//申明锁 RLock lock null; lock redissonSentinel.getLock(lockKey);if (lock ! null lock.isLocked()) {//已经有一个任务在进行了因此不能执行此时系统需要根据业务逻辑或进入等待或者return } try{lock.tryLock(0, TimeUnit.SECONDS);// 用自续约锁上锁接着下面就是做某事了 }catch(Exception e){}finally { //直接按此解锁永不出错也不要在finally里去判断而是强制在finally里关闭-大厂佳实践try {lock.unlock();} catch (Exception e) {} } 以上是一个标准的Redis分布式锁的标准公式下面给出配置 redis:password: 111111nodes: 192.1.0.11:7001redisson:nodes: redis://192.1.0.11:27001,redis://192.1.0.12:27001,redis://192.1.0.13:7001:27003sentinel: nodes: 192.1.0.11:27001,192.1.0.12:27001,192.1.0.13:27001master: master1subscriptionsPerConnection: 50 #分布式锁必设此参数可以考虑放大它占用redis连接subscriptionConnectionPoolSize: 200 #分布式锁必设此参数可以考虑放大它占用redis连接 千万不要忘了这两关键字很多人不设的话那么会出现生产上订单、并发一多直接会抛出redis锁连接不够用的错 subscriptionsPerConnectionsubscriptionConnectionPoolSize 生产典型问题 下面我们就来看自以为锁住了但是在生产上随机的“飘”的问题要么锁死要么就没锁住的具体案例来讲解Redis分布式锁的一些坑吧。 每个用户只可以有一个文件导出没但没锁住 具体场景 每个用户在一个数据展式面板里查看数据看到了自己要的数据就可以选择1万条做导出导出时用户可以关闭当前页面甚至退出后台任务导完后会以消息形式通知到用户用户在自己的个人头像上可以看到一个小红点闪出。 需求 根据需求这是一个云上的SAAS应用我们对普通用户只提供同时只可以有一个导出任务在后台运行的机制。 当后台己有一条任务正在导出时用户此时在数据面板里就算点几十次“导出”都因该提醒用户“当前您有一个导出任务正在进行中”。 实际有问题代码 我看了一下代码还挺公整的它是这么判断的。 //使用RedissonClient锁 Service public class ExportService{Autowiredprivate RedissonClient redissonSentinel;Asyncpublic void exportTask(){String redisLockEXPORT_TASK_LOCK_KEY:companyId:loginId//申明锁RLock lock null; lock redissonSentinel.getLock(lockKey);if (lock ! null lock.isLocked()) {//已经有一个任务在进行了因此不能执行此时系统需要根据业务逻辑或进入等待或者return}try{lock.tryLock(0, TimeUnit.SECONDS);// 用自续约锁上锁接着下面就是做某事了}catch(Exception e){}finally { //直接按此解锁永不出错也不要在finally里去判断而是强制在finally里关闭-大厂佳实践try {lock.unlock();} catch (Exception e) {}}} } 对锁认识上的误区 我一眼就看出了问题但我没有声张我让开发和他的Leader以及我们的架构师一起来看。我这么提出问题让他们自己开动脑筋去想这个问题。 1. 首先我们看到这个锁用companyIdloginId的确是可以做到锁的这个key唯一 2. 但实际是没锁住因为前端用户在一个任务没有导完后再点按钮有时可以并发出两条导出任务有时只能并发出一条这是事实那么肯定不是这个key唯一的问题 3. 我们一起打开redis客户端用命令来查看服务器在导出任务时锁产生的情况的确是看到产生的这个锁的key对于不同的人是唯一的key 我的问题是锁的key是唯一的就一定会被锁住吗 三个人搔搔头回答我可能吧 哈哈问题就出在这。 都以为只要锁的key是唯一这个key被锁住了那另一个操作带着同样的lock key进来获取到的状态就一定是“已经上锁”。 这个认知上错误了 对于Redis分布式锁正确的认知 锁是存在于服务器上的它不存在于客户端同一个key来锁固然没错但是我们看到了这个方法是一个被标为Async的。 于是同一个客户点击一次就会生成一个Service类的exportTask进程。再点击一次又生成了一个Service类的exportTask进程。 当有10个exportTask进程时我们虽然用的都是同一个lock key String redisLockEXPORT_TASK_LOCK_KEY:companyId:loginId 但是别忘了这个Service方法的完整运行机制是怎么样的 Asyncpublic void exportTask(){String redisLockEXPORT_TASK_LOCK_KEY:companyId:loginId//申明锁RLock lock null; lock redissonSentinel.getLock(lockKey);if (lock ! null lock.isLocked()) {//已经有一个任务在进行了因此不能执行此时系统需要根据业务逻辑或进入等待或者return}try{lock.tryLock(0, TimeUnit.SECONDS);// 用自续约锁上锁 看到没 每次都要RLock locknull再lock redissonSentinel.getLock(lockKey)一下。 此时后台有10个exportTask这10个exportTask彼此都在实例化自己的锁、锁语句。这下好玩了因为是异步的是乱序的所以此时发生了这么一件肉眼不可见的事 exportTask1刚锁住正在操作导出还没有操作完时。exportTask2进程被创建时把这个锁的状态给置成“初始化状态了“。exportTask2于是在exportTask1还没有完成任务和释放锁时就又可以接着执行了。 这就是我们说的“没锁住”。 嘿嘿这一切是Async惹的祸。 如何改 但我们又必须让这个方法是一个Async的因此怎么办 把这个锁“上浮”到controller层。 在RestController层的public ResponseBean exportDataAPI方法里如下申明 RLock uploadLock null;uploadLock exportService.canLock(companyId);if (uploadLock ! null !uploadLock.isLocked()) {message 导出中;exportService.exportTask(ut, data, uploadLock);}else{logger.info(有一个任务已经在导出当前步骤不执行)} 在Service方法中放入一个canLock方法如下 public RLock canLock(int companyId) {StringBuilder lockKeySB new StringBuilder();lockKeySB.append(FoodTrainLLMConstants.LLM_UPLOAD_LOCK).append(companyId);RLock lock redissonSentinel.getLock(lockKeySB.toString());return lock;} 然后我们在Service中如此改写原有逻辑 //使用RedissonClient锁 Service public class ExportService{Autowiredprivate RedissonClient redissonSentinel;Asyncpublic void exportTask(Rlock lock){try{lock.tryLock(0, TimeUnit.SECONDS);// 用自续约锁上锁接着下面就是做某事了}catch(Exception e){}finally { //直接按此解锁永不出错也不要在finally里去判断而是强制在finally里关闭-大厂佳实践try {lock.unlock();} catch (Exception e) {}}} } 这就是把锁“上浮”让其真正把一整个“进程”给锁住于是这个问题就可以被解决了。 小结 这种错误一般在于非Async中就算一开始写成了错误的那种写法你也发现不了这是因为一切都是同步的。 而一旦但有了Async后上述生产问题就发生了。 多线程中希望每个用户只可以存在一个查询任务但实际没有锁住 这是另一个场景但是其也发生在一个Async方法中。 即在一个Asynce标注的方法中还有一个while而要锁是锁的while中的步骤。 于是我们看到了这样的代码 Async public void backendQueryImageTask(String ut, int companyId, String loginId, String userInputPrompt, String taskId) throws Exception { while (System.currentTimeMillis() - startTime timeoutMillis) {String lockKey Query_Image_Redis_Lock_PREFIX companyId : uid;RLock lock null; lock redissonSentinel.getLock(lockKey);if (lock ! null lock.isLocked()) {logger.info(当前是backendQueryImageTask图片还在生成中另一个任务在查询中当前任务不进行);continue;}try {lock.tryLock(0, TimeUnit.SECONDS);// 上锁 。。。。。。catch (Exception e) {logger.error(Error in backendQueryImageTask, e);return;} finally {try {lock.unlock();} catch (Exception e) {}} 实际代码问题 这个问题和第一个问题其实是一样因为锁是运行在服务器端的它的状态不是维持在客户端。因此当这个方法如果是Asynce时代表着后台会存在若干进程而我们这次的需求是在每一个进程里再有一个while而在while中运行时必须锁住。 但实际没有锁住也正是因为每一次循环时另一个进程把同一把本己锁住正在执行任务的锁的状态给连续的做了这样的操作 RLock locknull - redissonSentinel.getLock(lockKey); 这就破坏了还在上锁的服务器上的同一把锁的状态导致了这个锁失效。 如何改 String lockKey Query_Image_Redis_Lock_PREFIX companyId : uid;RLock lock null; lock redissonSentinel.getLock(lockKey);while (System.currentTimeMillis() - startTime timeoutMillis) {if (lock ! null lock.isLocked()) {logger.info(当前是backendQueryImageTask图片还在生成中另一个任务在查询中当前任务不进行);continue;}try {lock.tryLock(0, TimeUnit.SECONDS);// 上锁//do something} catch (Exception e) {logger.error(Error in backendQueryImageTask, e);return;} finally {try {lock.unlock();} catch (Exception e) {}}} 把锁的申明外置到while循环外即可成功达到我们的要求。 总结 锁一定是存在于服务器的锁要锁的这个范围本身是异步运行的因为如果是同步操作也没有这个锁的必要了。正因为是异步所以服务器上的锁的对象是同一个而当这个对象在异步并行时是乱序的因此就会存在一个子进程“污染”到了另一个子进程里的锁对象。 为了成功把一组进程、业务原子方法锁住这个锁的范围必须控制在其外层且这个锁的初始状态只可以被初始化一次。 此处我们考虑第一个例子中为什么把锁放在controller方法中这个锁就可以成功锁住Service里的方法呢 这是因为每次用户在Controller方法中就算RLock locknull时此时初始化的lock不是同一个对象这是Controller方法的特性。 而只有当redissonSentinel.getLock(lockKey);时才会去拿服务端的锁而此时这把锁的状态如果还没有被释放那么就一定是被锁住的。 附redisson自续约锁的概念 当我们这样操作时 lock.tryLock(0, TimeUnit.SECONDS);// 上锁 很多人会习惯性的在参数里把这个0改成30或者60。这样做反而是画蛇添足、错误的做法。 因为加上了一个确切的数字后就会有问题你怎么知道这个方法正好执行了30秒或者是60秒就一定完成了呢如果这个方法是需要62秒怎么办你不是把方法给打断了。 因此Redisson特有的自续约锁就是把这个值设成0。于是在后台Redisson锁会先给到锁30秒时间。 当第20多秒还没有碰到有用户调用finally里的unlock时它会再给这个key延续30秒。。。再没执行完再给它30秒。 直到碰到finally块里被显示的调用了unlock那么代表任务结束这把锁的状态才会变成“释放”。
http://www.hkea.cn/news/14525611/

相关文章:

  • 免费cms建站五指哈尔滨网站建设赚钱么
  • 网站建设外包公司招聘住房城乡建设部执业资格注册中心网站
  • 导购 网站模板wordpress 国内云
  • 网站怎么做舆情监测网页设计你若安好便是晴天作业
  • 个人域名的网站百度制作网页需要多少钱
  • 做妇产科网站wap网站下载
  • 陕西网站制小游戏网页版入口
  • seo自学网站建设网站建设多少钱
  • 山西大同网站建设wordpress 新建文件
  • 男同志做爰网站如何提高wordpress速度
  • 禹城做网站的公司阳江北京网站建设
  • 浙江省工程建设质量管理协会 网站建设银行金牛支行网站
  • site 危险网站企业网站源码带后台管理
  • 全中文网站开发玩具 东莞网站建设 技术支持
  • 网站里面如何做下载的app手机零售网站 关键词
  • 郑州专业网站建设价格wordpress自定义分类名称
  • 东莞微客巴巴做网站泰安网络营销网站建设
  • 阿里巴巴做特卖的网站网站正在建设中模板
  • 网站ipv6改造怎么做wordpress导出导入
  • 公司网站栏目wordpress写了文章 文章无法打开
  • 网站建设素材收集通知网站建设必备的功能模块
  • 电影网站怎么做seo怎样建设单位网站
  • 如何将网站提交给谷歌网站建设会遇到哪些难题
  • asp响应式h5网站源码国内建站源码
  • 国外做ppt的网站有哪些辽宁营商环境建设网站
  • 学校网站设计的目的郑州一建第七项目部
  • 旅游网站设计方案wordpress 制作404
  • 网站开发用的框架中信建设有限责任公司投资部执行总监
  • 湖北专业的网站制作代理商做电商网站需要会些什么问题
  • 网页制作软件哪个好用杭州优化建筑设计