济南润滑油网站制作,智慧团建手机登录入口电脑版,公司做英文网站,有没有卖设计的网站目录
基于Redission实现分布式锁解决商品秒杀超卖的场景#xff1a;
1.引入依赖#xff1a;
2.加上redis的配置#xff1a;
3.添加配置类#xff1a;
4.编写代码实现#xff1a;
5.模拟服务器分布式集群的情况#xff1a; 1.右键点击Copy Configuration 2.点击Modi…目录
基于Redission实现分布式锁解决商品秒杀超卖的场景
1.引入依赖
2.加上redis的配置
3.添加配置类
4.编写代码实现
5.模拟服务器分布式集群的情况 1.右键点击Copy Configuration 2.点击Modify option 3. 选择VM option(用于指定新的端口) 4.输入想要指定的端口(比如)-Dserver.port8082 点击Apply 5.出现新的进程点击启动就可以进行分布式多节点测试。
使用Jmeter进行压测 单机部署多线程高并发情况下对同一个共享资源进行读写时会出现数据错乱数据不一致的问题加锁同步锁可以解决出现数据不一致的问题其他线程进行等待持有锁的线程执行完成后才能进行正常的处理。 但是随着用户量日益增多单个服务器压力越来越大所以使用多个服务器进行分布式集群部署虽然降低了服务器的压力提高了服务器的吞吐量。但是还是会出现数据不一致的问题这时候发现是是同步锁synchronized的问题同步锁基于JVM的他只能锁住单个服务器中一个线程但是经过分布式集群部署过后每台服务器在并发的情况下只能锁住一个线程所以高并发情况下还是会出现数据错乱的情况。假如4个服务器每台服务器都处于高并发的情况然后同步锁只能锁住一个线程这时候每个服务器锁住一个线程最多可以出现4个线程同时对数据库进行操作就会造成数据不一致的问题例如常说的秒杀超卖的情况 经过查阅资料发现可以通过分布式锁可以解决然后有三种主流的分布式锁的解决方案分别是使用基于Mysql/Zookeeper/redis实现分布式锁。由于我们系统使用到了Redis考虑Zookeeper需要重新部署到服务器避免间接增加服务器的成本所以直接使用Redis来实现分布式锁。我们发现使用Redis的setNX可以很简单的实现分布式锁。 那么setNX的特性是什么呢 当一个线程进来往Redis当中通过setNX去存储一个值的时候他会根据键值key去查看是否存在value值没有就存储一个值返回true有就返回一个false注意一定要加上锁的过期时间避免线程阻塞。当用户在请求的过程当中通过setNX进行加锁完成的时候这个服务器挂掉了当其他线程进行setNX进行上锁的时候发现键当中一直会有值造成了死锁其他线程加锁不成功就会造成阻塞。所以一定要加上过期时间。 随着业务的扩展又出现了一些问题就是1.业务的处理时间超过了锁的过期时间和2.线程1可能释放了线程2所持有的锁【线程1还没将业务处理完成就释放锁导致线程2拿到锁处理自己的业务。当线程1执行完成后释放了锁但是此时线程2已经拿到了当前锁所以线程1释放的是线程2的锁。】 如何解决这些问题呢 1.加长锁的过期时间并增加子线程每10秒去确认线程是否在线。在线则将过期时间重设续命--他们所说的看门狗 2.给锁的值设置一个唯一IDUUID-使用setNx进行尝试获取锁的时候如果获取成功将锁的值设置为一个唯一的ID释放锁的时候会拿着key去获取锁的值是否与自己的唯一ID一致一致才进行释放锁从而就不会释放其他线程的锁 上述说这么多如果让我们自己写起来确实有些麻烦这时候查阅发现redis他本身已经提供了一个Redission的组件已经解决了这些问题。
那么下面我就提供一个
基于Redission实现分布式锁解决商品秒杀超卖的场景
springboot版本2.6.13
redission版本3.22.0
1.引入依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependencydependencygroupIdorg.redisson/groupIdartifactIdredisson-spring-boot-starter/artifactIdversion3.22.0/version/dependency2.加上redis的配置
server:port: 8083
spring:redis:host: 127.0.0.1password:port: 6379timeout: 1000
3.添加配置类
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;Configuration
public class RedissonConfig {Value(${spring.redis.host})private String host;Value(${spring.redis.port})private String port;Beanpublic RedissonClient getRedisson(){Config config new Config();//单机模式 依次设置redis地址和密码config.useSingleServer().setAddress(redis:// host : port).setTimeout(30000); // 设置缓存过期时间为30秒return Redisson.create(config);}
}
4.编写代码实现 import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.Objects;RestController
RequestMapping(/redisLock)
public class RedisLockController {Resourceprivate StringRedisTemplate stringRedisTemplate;Resourceprivate RedissonClient redisson;// 秒杀商品keyprivate static final String REDIS_KEY secKillProductKey:;private static final String LOCK_KEY secKillProduct;// 秒杀商品总个数private static final int PRODUCT_SIZE 1000;/*** 初始化秒杀商品总个数到redis中* 注意测试的时候先调用这个接口初始化库存*/GetMapping(/init)public void init() {// 初始化库存 将库存存到redis中stringRedisTemplate.opsForValue().set(REDIS_KEY, String.valueOf(PRODUCT_SIZE));}/*** 秒杀*/GetMapping(/secKill)public void secKill() {// 获取锁RLock lock redisson.getLock(LOCK_KEY);try {// 加锁lock.lock();int s Integer.parseInt(Objects.requireNonNull(stringRedisTemplate.opsForValue().get(REDIS_KEY)));if (s 0) {// 扣库存s--;System.out.printf(秒杀商品个数剩余 s \n);// 更新库存stringRedisTemplate.opsForValue().set(REDIS_KEY, String.valueOf(s));} else {System.out.println(活动太火爆了商品已经被抢购一空了);}} catch (Exception e) {System.out.println(Thread.currentThread().getName() 异常);e.printStackTrace();} finally {// 释放锁lock.unlock();}}
}
5.模拟服务器分布式集群的情况 同一个服务不同端口同时运行两个相同的主程序。
在service中复制一个进程,指定不同端口 1.右键点击Copy Configuration 2.点击Modify option 3. 选择VM option(用于指定新的端口) 4.输入想要指定的端口(比如)-Dserver.port8082 点击Apply 5.出现新的进程点击启动就可以进行分布式多节点测试。 使用Jmeter进行压测
首先需要先调用一下初始化的接口127.0.0.1:8082/redisLock/init或127.0.0.1:8083/redisLock/init 1.设置线程组 2.两个HTTP请求请求不同的端口8082、8083进行测试高并发多线程的情况 3.服务器打印结果 经过测试一秒钟1000个线程同时请求秒杀接收并没有出现超卖的问题。
拓展 Redis本身是一个CP一致性和分区容错性模式的数据库它通过主从复制实现高可用性当主节点挂掉时从节点会自动进行选举选出一个新的主节点继续提供服务。但是在主节点挂掉之前它可能还来不及将最新的数据同步到从节点这时就会出现数据不一致的问题。 如果redis采用主从模式进行部署当往redis中通过setNX进行加锁的过程中主节点挂了主节点的数据并没有同步到从节点当中这种怎么办 可以使用RedLock红锁解决RedLock是一个分布式锁的实现它可以通过访问多个Redis节点来实现更高的可用性和一致性。RedLock的工作原理是在加锁时向多个Redis节点发送请求只有当所有节点都成功返回时才认为加锁成功。