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

微信链接的微网站怎么做的怎么自己注册网站平台了

微信链接的微网站怎么做的,怎么自己注册网站平台了,wordpress 素材网站模版,wordpress 测试网站4.0 问题 4.0.1 事务失效 1. 事务失效问题 当在同一个类中调用带有 Transactional 注解的方法时,Spring 的事务机制可能会失效。这是因为: Spring 的事务管理是通过 AOP(面向切面编程)实现的当一个方法内部直接调用同类中的另…

4.0 问题

4.0.1 事务失效

  1. 1. 事务失效问题

当在同一个类中调用带有 @Transactional 注解的方法时,Spring 的事务机制可能会失效。这是因为:

  • Spring 的事务管理是通过 AOP(面向切面编程)实现的
  • 当一个方法内部直接调用同类中的另一个带有 @Transactional 注解的方法时,实际上是在调用原始对象的方法,而不是代理对象的方法
  • 由于事务是由代理对象管理的,所以直接调用原始对象的方法会导致事务注解失效
  1. 2. 代理对象与目标对象

在 Spring AOP 中:

  • 目标对象 :实际的业务类实例(原始对象)
  • 代理对象 :Spring 创建的包装了目标对象的代理,用于实现事务等增强功能

当外部调用一个带有 @Transactional 注解的方法时,实际上是调用的代理对象的方法,代理会在调用目标方法前后添加事务管理的代码。

4.0.2 分布式锁误删

使用 UUID 而不使用 ThreadID 的原因:

  • ThreadID 在每个 JVM 内 ThreadID 是自增的,分布式下会有相同的 ThreadID
  • 所以不能让 ThreadID 作为 value 来做是否为同一把锁的标识

4.1 基本原理

  • 分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。
  • 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路

4.1.1 满足条件

那么分布式锁他应该满足一些什么样的条件呢?

  • 可见性:多个线程都能看到相同的结果,注意:这个地方说的可见性并不是并发编程中指的内存可见性,只是说多个进程之间都能感知到变化的意思
  • 互斥:互斥是分布式锁的最基本的条件,使得程序串行执行
  • 高可用:程序不易崩溃,时时刻刻都保证较高的可用性
  • 高性能:由于加锁本身就让性能降低,所有对于分布式锁本身需要他就较高的加锁性能和释放锁性能
  • 安全性:安全也是程序中必不可少的一环

4.1.2 三种实现

  • Mysql:mysql 本身就带有锁机制,但是由于 mysql 性能本身一般,所以采用分布式锁的情况下,其实使用 mysql 作为分布式锁比较少见
  • Redis:redis 作为分布式锁是非常常见的一种使用方式,现在企业级开发中基本都使用 redis 或者 zookeeper 作为分布式锁,利用 setnx 这个方法,如果插入 key 成功,则表示获得到了锁,如果有人插入成功,其他人插入失败则表示无法获得到锁,利用这套逻辑来实现分布式锁
  • Zookeeper:zookeeper 也是企业级开发中较好的一个实现分布式锁的方案,由于本套视频并不讲解 zookeeper 的原理和分布式锁的实现,所以不过多阐述

4.2 实现思路

实现分布式锁时需要实现的两个基本方法:

  • 获取锁:
    • 互斥:确保只能有一个线程获取锁
    • 非阻塞:尝试一次,成功返回 true,失败返回 false
  • 释放锁:
    • 手动释放
    • 超时释放:获取锁时添加一个超时时间

核心思路:

  • 我们利用 redis 的 setNx 方法
  • 当有多个线程进入时
  • 我们就利用该方法
  • 第一个线程进入时,redis 中就有这个 key 了,返回了 1
  • 如果结果是 1,则表示他抢到了锁,那么他去执行业务
  • 然后再删除锁,退出锁逻辑,没有抢到锁的哥们,等待一定时间后重试即可

4.3 V1-setnx 分布式锁

4.3.1 代码

private static final String KEY_PREFIX="lock:"
@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);
}
public void unlock() {//通过del删除锁stringRedisTemplate.delete(KEY_PREFIX + name);
}
  @Overridepublic 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);//获取锁对象boolean isLock = lock.tryLock(1200);//加锁失败if (!isLock) {return Result.fail("不允许重复下单");}try {//获取代理对象(事务)IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);} finally {//释放锁lock.unlock();}}

4.3.2 问题

逻辑说明:

  • 持有锁的线程在锁的内部出现了阻塞,导致他的锁自动释放
  • 这时其他线程,线程 2 来尝试获得锁,就拿到了这把锁
  • 然后线程 2 在持有锁执行过程中,线程 1 反应过来,继续执行
  • 而线程 1 执行过程中,走到了删除锁逻辑
  • 此时就会把本应该属于线程 2 的锁进行删除,这就是误删别人锁的情况说明

解决方案:

(线程 1,2 都是该用户的请求)

  • 解决方案就是在每个线程释放锁的时候,去判断一下当前这把锁是否属于自己
  • 如果属于自己,则不进行锁的删除
  • 假设还是上边的情况,线程 1 卡顿,锁自动释放
  • 线程 2 进入到锁的内部执行逻辑,此时线程 1 反应过来,然后删除锁
  • 但是线程 1,一看当前这把锁不是属于自己,于是不进行删除锁逻辑
  • 当线程 2 走到删除锁逻辑时,如果没有卡过自动释放锁的时间点
  • 则判断当前这把锁是属于自己的,于是删除这把锁。

4.4 V2-解决误删问题

需求:修改之前的分布式锁实现,满足:在获取锁时存入线程标示(可以用 UUID 表示)

在释放锁时先获取锁中的线程标示,判断是否与当前线程标示一致

  • 如果一致则释放锁
  • 如果不一致则不释放锁

核心逻辑:在存入锁时,放入自己线程的标识,在删除锁时,判断当前这把锁的标识是不是自己存入的,如果是,则进行删除,如果不是,则不进行删除。

4.4.1 代码

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);}
}

有关代码实操说明:

  • 在我们修改完此处代码后,我们重启工程
  • 然后启动两个线程
  • 第一个线程持有锁后,手动释放锁
  • 第二个线程 此时进入到锁内部,再放行第一个线程
  • 此时第一个线程由于锁的 value 值并非是自己
  • 所以不能释放锁,也就无法删除别人的锁
  • 此时第二个线程能够正确释放锁
  • 通过这个案例初步说明我们解决了锁误删的问题。

4.4.2 问题

更为极端的误删逻辑说明:

  • 线程 1 现在持有锁之后,在执行业务逻辑过程中
  • 他正准备删除锁,而且已经走到了条件判断的过程中
  • 比如他已经拿到了当前这把锁确实是属于他自己的
  • 正准备删除锁,但是此时他的锁到期了
  • 那么此时线程 2 进来,但是线程 1 他会接着往后执行
  • 当他卡顿结束后,他直接就会执行删除锁那行代码
  • 相当于条件判断并没有起到作用,这就是删锁时的原子性问题
  • 之所以有这个问题,是因为线程 1 的拿锁,比锁,删锁,实际上并不是原子性的
  • 我们要防止刚才的情况发生

4.5 V3-Lua 解决删锁原子性问题

我们来回想一下我们释放锁的逻辑:

  1. 1. 获取锁中的线程标示
  2. 2. 判断是否与指定的标示(当前线程标示)一致
  3. 3. 如果一致则释放锁(删除)
  4. 4. 如果不一致则什么都不做
-- 这里的 KEYS[1] 就是锁的key,这里的ARGV[1] 就是当前线程标示
-- 获取锁中的标示,判断是否与当前线程标示一致
if (redis.call('GET', KEYS[1]) == ARGV[1]) then-- 一致,则删除锁return redis.call('DEL', KEYS[1])
end
-- 不一致,则直接返回
return 0

Java 代码改造 Lua 脚本

我们的 RedisTemplate 中,可以利用 execute 方法去执行 lua 脚本,参数对应关系就如下图股

private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;static {UNLOCK_SCRIPT = new DefaultRedisScript<>();UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));UNLOCK_SCRIPT.setResultType(Long.class);}public void unlock() {// 调用lua脚本stringRedisTemplate.execute(UNLOCK_SCRIPT,Collections.singletonList(KEY_PREFIX + name),ID_PREFIX + Thread.currentThread().getId());
}
经过以上代码改造后,我们就能够实现 拿锁比锁删锁的原子性动作了~

4.6 小总结

基于 Redis 的分布式锁实现思路:

  • 利用 set nx ex 获取锁,并设置过期时间,保存线程标示
  • 释放锁时先判断线程标示是否与自己一致,一致则删除锁
    • 特性:

    - 利用 set nx 满足互斥性

    - 利用 set ex 保证故障时锁依然能释放,避免死锁,提高安全性

    - 利用 Redis 集群保证高可用和高并发特性

笔者总结:我们一路走来,利用添加过期时间,防止死锁问题的发生,但是有了过期时间之后,可能出现误删别人锁的问题,这个问题我们开始是利用删之前 通过拿锁,比锁,删锁这个逻辑来解决的,也就是删之前判断一下当前这把锁是否是属于自己的,但是现在还有原子性问题,也就是我们没法保证拿锁比锁删锁是一个原子性的动作,最后通过 lua 表达式来解决这个问题

但是目前还剩下一个问题锁不住,什么是锁不住呢,你想一想,如果当过期时间到了之后,我们可以给他续期一下,比如续个 30s,就好像是网吧上网,网费到了之后,然后说,来,网管,再给我来 10 块的,是不是后边的问题都不会发生了,那么续期问题怎么解决呢,可以依赖于我们接下来要学习 redission 啦

测试逻辑:

第一个线程进来,得到了锁,手动删除锁,模拟锁超时了,其他线程会执行 lua 来抢锁,当第一条线程利用 lua 删除锁时,lua 能保证他不能删除其他的锁,第二个线程删除锁时,利用 lua 同样可以保证不会删除别人的锁,同时还能保证原子性。

http://www.dtcms.com/wzjs/257883.html

相关文章:

  • 手表网站域名seo和sem哪个工资高
  • 做日本外贸单是哪个网站网页版
  • 手机可以建立网站吗综合性b2b电子商务平台网站
  • 武汉网站建设 江网科技推广网络营销外包公司
  • 建设一个网站需要哪些功能app拉新渠道商
  • 贵州企业展示型网站建设域名解析
  • 电子商务网站建设有哪些知识点线下推广方式有哪些
  • 微幼儿园网站制作软文推广多少钱
  • 网站越来越难做搜索引擎优化案例
  • 东莞网站建设seo临沂seo推广
  • 网站推广双鼎建站工具
  • 网站二次开发的模板种类免费的app推广平台
  • ppt成品免费下载的网站手机app软件开发
  • 网站建设项目进度表网站推广模式
  • 网页制作的公司推荐时代创信谷歌seo最好的公司
  • 付费网站源码百度网盟推广
  • 龙岩做网站改版找哪家公司如何给公司做网络推广
  • 用WordPress建什么站好什么关键词可以搜到那种
  • 近两年成功的网络营销案例及分析培训如何优化网站
  • 公司网站怎么做备案信息搜索引擎营销的英文缩写
  • 武汉网站建设哪家专业上海网站seo公司
  • 网址怎么推广长春seo关键词排名
  • 购物分享网站怎么做的国内最新新闻热点事件
  • wordpress ajax 刷新谷歌seo 优化
  • 开发一个交易网站多少钱网站关键词排名优化客服
  • 微信服务商平台官网seo关键词是怎么优化的
  • 如何在微信小程序上开店北京百度关键词优化
  • 日本优秀网站北京seo邢云涛
  • wordpress手机文章列表网站搜索排名优化软件
  • 燕窝网站怎么做的安卓优化软件