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

缪斯设计网站网络营销推广策划方案书

缪斯设计网站,网络营销推广策划方案书,重庆建站模板源码,做化工的外贸网站都有什么地方文章目录 分布式锁基本原理和实现方式对比Redis分布式锁的实现核心思路实现分布式锁版本一Redis分布式锁误删情况说明解决Redis分布式锁误删问题分布式锁的原子性问题分布式锁-Redission分布式锁-redission可重入锁原理分布式锁-redission锁重试和WatchDog机制分布式锁-redissi… 文章目录 分布式锁基本原理和实现方式对比Redis分布式锁的实现核心思路实现分布式锁版本一Redis分布式锁误删情况说明解决Redis分布式锁误删问题分布式锁的原子性问题分布式锁-Redission分布式锁-redission可重入锁原理分布式锁-redission锁重试和WatchDog机制分布式锁-redission锁的MutiLock原理 分布式锁 基本原理和实现方式对比 分布式锁满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁只要大家使用的是同一把锁那么我们就能锁住线程不让线程进行让程序串行执行这就是分布式锁的核心思路 那么分布式锁他应该满足的条件呢 可见性多个线程都能看到相同的结果注意这个地方说的可见性并不是并发编程中指的内存可见性只是说多个进程之间都能感知到变化的意思 互斥互斥是分布式锁的最基本的条件使得程序串行执行 高可用程序不易崩溃时时刻刻都保证较高的可用性 高性能由于加锁本身就让性能降低所有对于分布式锁本身需要他就较高的加锁性能和释放锁性能 安全性安全也是程序中必不可少的一环 常见的分布式锁有三种 Mysqlmysql本身就带有锁机制但是由于mysql性能本身一般所以采用分布式锁的情况下其实使用mysql作为分布式锁比较少见 Redisredis作为分布式锁是非常常见的一种使用方式现在企业级开发中基本都使用redis或者zookeeper作为分布式锁利用setnx这个方法如果插入key成功则表示获得到了锁如果有人插入成功其他人插入失败则表示无法获得到锁利用这套逻辑来实现分布式锁 Zookeeperzookeeper也是企业级开发中较好的一个实现分布式锁的方案 Redis分布式锁的实现核心思路 实现分布式锁时需要实现的两个基本方法 获取锁 互斥确保只能有一个线程获取锁非阻塞尝试一次成功返回true失败返回false 释放锁 手动释放超时释放获取锁时添加一个超时时间 核心思路 我们利用redis 的setNx 方法当有多个线程进入时我们就利用该方法第一个线程进入时redis 中就有这个key 了返回了1如果结果是1则表示他抢到了锁那么他去执行业务然后再删除锁退出锁逻辑没有抢到锁的哥们等待一定时间后重试即可 实现分布式锁版本一 加锁逻辑 锁的基本接口 SimpleRedisLock 利用setnx方法进行加锁同时增加过期时间防止死锁此方法可以保证加锁和增加过期时间具有原子性 我们的方法是把存在线程中的用户的id作为redis中的中的键这样我们就可以作为为每一个用户设置单独的锁而且我们也会为每个锁设置单的过期时间从而防止死锁具体代码可以看下面 private static final String KEY_PREFIXlock: Override public boolean tryLock(long timeoutSec) {// 获取线程标示String threadId Thread.currentThread().getId()// 获取锁Boolean success stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX name, threadId , timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success); }Redis分布式锁误删情况说明 逻辑说明 持有锁的线程在锁的内部出现了阻塞导致他的锁自动释放这时其他线程线程2来尝试获得锁就拿到了这把锁然后线程2在持有锁执行过程中线程1反应过来继续执行而线程1执行过程中走到了删除锁逻辑此时就会把本应该属于线程2的锁进行删除这就是误删别人锁的情况说明 解决方案解决方案就是在每个线程释放锁的时候去判断一下当前这把锁是否属于自己如果属于自己则不进行锁的删除假设还是上边的情况线程1卡顿锁自动释放线程2进入到锁的内部执行逻辑此时线程1反应过来然后删除锁但是线程1一看当前这把锁不是属于自己于是不进行删除锁逻辑当线程2走到删除锁逻辑时如果没有卡过自动释放锁的时间点则判断当前这把锁是属于自己的于是删除这把锁。 解决Redis分布式锁误删问题 需求修改之前的分布式锁实现满足在获取锁时存入线程标示可以用UUID表示 在释放锁时先获取锁中的线程标示判断是否与当前线程标示一致 如果一致则释放锁如果不一致则不释放锁 核心逻辑在存入锁时放入自己线程的标识在删除锁时判断当前这把锁的标识是不是自己存入的如果是则进行删除如果不是则不进行删除。 具体代码如下加锁 private static final String ID_PREFIX UUID.randomUUID().toString(true) -; Override public boolean tryLock(long timeoutSec) {// 获取线程标示String threadId ID_PREFIX Thread.currentThread().getId();// 获取锁Boolean success stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX name, threadId, timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success); }释放锁 public void unlock() {// 获取线程标示String threadId ID_PREFIX Thread.currentThread().getId();// 获取锁中的标示String id stringRedisTemplate.opsForValue().get(KEY_PREFIX name);// 判断标示是否一致if(threadId.equals(id)) {// 释放锁stringRedisTemplate.delete(KEY_PREFIX name);} }分布式锁的原子性问题 更为极端的误删逻辑说明 线程1现在持有锁之后在执行业务逻辑过程中他正准备删除锁而且已经走到了条件判断的过程中比如他已经拿到了当前这把锁确实是属于他自己的正准备删除锁但是此时他的锁到期了那么此时线程2进来但是线程1他会接着往后执行当他卡顿结束后他直接就会执行删除锁那行代码相当于条件判断并没有起到作用这就是删锁时的原子性问题之所以有这个问题是因为线程1的拿锁比锁删锁实际上并不是原子性的我们要防止刚才的情况发生 这个问题可以使用lua脚本实现但是在java中我们一般会用redission这个第三方库。 分布式锁-Redission 引入依赖 dependencygroupIdorg.redisson/groupIdartifactIdredisson/artifactIdversion3.13.6/version /dependency配置Redisson客户端 Configuration public class RedissonConfig {Beanpublic RedissonClient redissonClient(){// 配置Config config new Config();config.useSingleServer().setAddress(redis://192.168.150.101:6379).setPassword(123321);// 创建RedissonClient对象return Redisson.create(config);} } 使用Redission的分布式锁 Resource private RedissionClient redissonClient;Test void testRedisson() throws Exception{//获取锁(可重入)指定锁的名称RLock lock redissonClient.getLock(anyLock);//尝试获取锁参数分别是获取锁的最大等待时间(期间会重试)锁自动释放时间时间单位boolean isLock lock.tryLock(1,10,TimeUnit.SECONDS);//判断获取锁成功if(isLock){try{System.out.println(执行业务); }finally{//释放锁lock.unlock();}}}业务代码更改 在 VoucherOrderServiceImpl 注入RedissonClient Resource private RedissonClient redissonClient;Override public Result seckillVoucher(Long voucherId) {// 1.查询优惠券SeckillVoucher voucher seckillVoucherService.getById(voucherId);// 2.判断秒杀是否开始if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {// 尚未开始return Result.fail(秒杀尚未开始);}// 3.判断秒杀是否已经结束if (voucher.getEndTime().isBefore(LocalDateTime.now())) {// 尚未开始return Result.fail(秒杀已经结束);}// 4.判断库存是否充足if (voucher.getStock() 1) {// 库存不足return Result.fail(库存不足);}Long userId UserHolder.getUser().getId();//创建锁对象 这个代码不用了因为我们现在要使用分布式锁//SimpleRedisLock lock new SimpleRedisLock(order: userId, stringRedisTemplate);RLock lock redissonClient.getLock(lock:order: userId);//获取锁对象boolean isLock lock.tryLock();//加锁失败if (!isLock) {return Result.fail(不允许重复下单);}try {//获取代理对象(事务)IVoucherOrderService proxy (IVoucherOrderService) AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);} finally {//释放锁lock.unlock();}}分布式锁-redission可重入锁原理 在Lock锁中他是借助于底层的一个voaltile的一个state变量来记录重入的状态的比如当前没有人持有这把锁那么state0假如有人持有这把锁那么state1如果持有这把锁的人再次持有这把锁那么state就会1 如果是对于synchronized而言他在c语言代码中会有一个count原理和state类似也是重入一次就加一释放一次就-1 直到减少成0 时表示当前这把锁没有被人持有。 在redission中我们的也支持支持可重入锁 在分布式锁中他采用hash结构用来存储锁其中大key表示表示这把锁是否存在用小key表示当前这把锁被哪个线程持有所以接下来我们一起分析一下当前的这个lua表达式 这个地方一共有3个参数 KEYS[1] 锁名称 ARGV[1] 锁失效时间 ARGV[2] id “:” threadId; 锁的小key exists: 判断数据是否存在 name是lock是否存在,如果0就表示当前这把锁不存在 redis.call(‘hset’, KEYS[1], ARGV[2], 1);此时他就开始往redis里边去写数据 写成一个hash结构 Lock{ ​ id “:” threadId : 1 } 如果当前这把锁存在则第一个条件不满足再判断 redis.call(‘hexists’, KEYS[1], ARGV[2]) 1 此时需要通过大key小key判断当前这把锁是否是属于自己的如果是自己的则进行 redis.call(‘hincrby’, KEYS[1], ARGV[2], 1) 将当前这个锁的value进行1 redis.call(‘pexpire’, KEYS[1], ARGV[1]); 然后再对其设置过期时间如果以上两个条件都不满足则表示当前这把锁抢锁失败最后返回pttl即为当前这把锁的失效时间 分布式锁-redission锁重试和WatchDog机制 抢锁过程中获得当前线程通过tryAcquire进行抢锁该抢锁逻辑和之前逻辑相同 1、先判断当前这把锁是否存在如果不存在插入一把锁返回null 2、判断当前这把锁是否是属于当前线程如果是则返回null 所以如果返回是null则代表着当前已经抢锁完毕或者可重入完毕但是如果以上两个条件都不满足则进入到第三个条件返回的是锁的失效时间同学们可以自行往下翻一点点你能发现有个while( true) 再次进行tryAcquire进行抢锁 long threadId Thread.currentThread().getId(); Long ttl tryAcquire(-1, leaseTime, unit, threadId); // lock acquired if (ttl null) {return; }接下来会有一个条件分支因为lock方法有重载方法一个是带参数一个是不带参数如果带带参数传入的值是-1如果传入参数则leaseTime是他本身所以如果传入了参数此时leaseTime ! -1 则会进去抢锁抢锁的逻辑就是之前说的那三个逻辑 if (leaseTime ! -1) {return tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG); }如果是没有传入时间则此时也会进行抢锁 而且抢锁时间是默认看门狗时间 commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout() ttlRemainingFuture.onComplete((ttlRemaining, e) 这句话相当于对以上抢锁进行了监听也就是说当上边抢锁完毕后此方法会被调用具体调用的逻辑就是去后台开启一个线程进行续约逻辑也就是看门狗线程 RFutureLong ttlRemainingFuture tryLockInnerAsync(waitTime,commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(),TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG); ttlRemainingFuture.onComplete((ttlRemaining, e) - {if (e ! null) {return;}// lock acquiredif (ttlRemaining null) {scheduleExpirationRenewal(threadId);} }); return ttlRemainingFuture;此逻辑就是续约逻辑注意看commandExecutor.getConnectionManager().newTimeout 此方法 Method( new TimerTask() {},参数2 参数3 ) 指的是通过参数2参数3 去描述什么时候去做参数1的事情现在的情况是10s之后去做参数一的事情 因为锁的失效时间是30s当10s之后此时这个timeTask 就触发了他就去进行续约把当前这把锁续约成30s如果操作成功那么此时就会递归调用自己再重新设置一个timeTask()于是再过10s后又再设置一个timerTask完成不停的续约 那么大家可以想一想假设我们的线程出现了宕机他还会续约吗当然不会因为没有人再去调用renewExpiration这个方法所以等到时间之后自然就释放了。 private void renewExpiration() {ExpirationEntry ee EXPIRATION_RENEWAL_MAP.get(getEntryName());if (ee null) {return;}Timeout task commandExecutor.getConnectionManager().newTimeout(new TimerTask() {Overridepublic void run(Timeout timeout) throws Exception {ExpirationEntry ent EXPIRATION_RENEWAL_MAP.get(getEntryName());if (ent null) {return;}Long threadId ent.getFirstThreadId();if (threadId null) {return;}RFutureBoolean future renewExpirationAsync(threadId);future.onComplete((res, e) - {if (e ! null) {log.error(Cant update lock getName() expiration, e);return;}if (res) {// reschedule itselfrenewExpiration();}});}}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);ee.setTimeout(task); }分布式锁-redission锁的MutiLock原理 为了提高redis的可用性我们会搭建集群或者主从现在以主从为例 此时我们去写命令写在主机上 主机会将数据同步给从机但是假设在主机还没有来得及把数据写入到从机去的时候此时主机宕机哨兵会发现主机宕机并且选举一个slave变成master而此时新的master中实际上并没有锁信息此时锁信息就已经丢掉了。 为了解决这个问题redission提出来了MutiLock锁每个节点的地位都是一样的 这把锁加锁的逻辑需要写入到每一个主丛节点上只有所有的服务器都写入成功此时才是加锁成功假设现在某个节点挂了那么他去获得锁的时候只要有一个节点拿不到都不能算是加锁成功就保证了加锁的可靠性。
http://www.hkea.cn/news/14421079/

相关文章:

  • 附近建网站公司郑州网站建设灵秀
  • 区块链技术网站开发站长工具查询视频
  • 用境外服务器做网站网站后台设置
  • 建设大型购物网站培训机构咨询
  • 对网站外部的搜索引擎优化小程序代理设置
  • 太原做企业网站的网站建设竞标
  • 七里港网站建设建设完网站成功后需要注意什么
  • 沈阳企业网站开发wordpress数据库调用
  • 做网站制作利润有多少建设刷会员网站
  • 哈尔滨制作手机网站wordpress购物模版
  • 郏县网站制作哪家公司好福州的网站建设
  • 上传网站到百度周村网站建设yx718
  • 开发网站需要注意企业开发网站用什么技术
  • php旅游网站开发小结网站搜索显示图片
  • 电子商务网站建设评价写好网页怎么建成网站
  • 网站中的公司地址怎么做wordpress固定链接 404 nginx
  • php 调试网站遵义网吧
  • 网站后台上传模板商铺营销推广方案
  • dede自动一键更新网站网站推广方式大全
  • 广州网站制作培训关键词推广价格
  • 松江网站关键词优化网站的管理页面
  • 南昌网站推广策划赣州市人才网
  • 贵州两学一做教育网站民治营销网站
  • 建设网站的公司要什么资质吗泉州 网站建设
  • 网站的建设与维护需要资质吗学python能干嘛
  • 做淘宝网站需要多少钱中国建筑官网电话
  • 网站内容建设是什么泰州网站建设解决方案
  • 织梦网站模版怎么用wordpress 自定义栏目 删除
  • 网页设计中用div做网站例子郑州三牛网站建设
  • 网站如何做才会有流量上海工商网