之前文章实现了zookeeper集群采用curator客户端实现分布式锁,最近一直在做实验,希望使用redis的客户端reddisson实现分布锁,在大型电商项目或者银行项目,扣减库存,扣减余额面临的是高并发的环境,保证数据的一致性非常重要,不管是zookeeper,还是redis实现分布式锁,我理解其实都是将多线程变成了单线程去获取锁,每次只能一个线程能获取到锁,没有获取到的,自旋去等待。等上一个线程释放了,再获取。每次只有一个线程去操作库存或者余额当然就不会出现同时扣减的情况。好,下面给出redisson实现分布式锁的关键步骤,关于redisson的更多介绍可以看官网介绍https://redisson.org/
本文实验是在springboot项目实现的。
1.pom.xml添加依赖
org.springframework.boot
spring-boot-starter-data-redis
2.1.7.RELEASE
org.redisson
redisson-spring-boot-starter
3.11.2
2.编写Redisson配置类
RedissionConfiguration.java
/**
*
*/
package com.figo.springboottest.utils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author figo
*
*/
@Configuration
public class RedissionConfiguration {
@Bean
public RedissonClient getRedissionClient(){
Config config=new Config();
//集群模式,集群节点的地址须使用“redis://”前缀,否则将会报错。
//此例集群为3节点,各节点1主1从
config.useClusterServers().addNodeAddress("redis://131.252.98.100:6379","redis://131.252.98.100:6380",
"redis://131.252.98.101:6379","131.252.98.101:6380","redis://131.252.98.102:6379","redis://131.252.98.102:6380").setPassword("123456");//如果没有密码不要setPassword
return Redisson.create(config);
}
}
3.编写库存扣减服务
StockService.java
该service模拟高并发的情况,实际应该是通过controller页面请求过来的
package com.figo.springboottest.service;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import com.figo.springboottest.mapper.tariff.TStockMapper;
import com.figo.springboottest.model.TStock;
@Service("stockService")
public class StockService {
/**
* 库存表mapper .
*/
@Autowired
TStockMapper stockmapper;
/**
* 1000个线程并发执行 .
*/
int threadNum=1000;
/**
* 锁名称 .
*/
String lockname = "stock";
/**
* 计算器 .
*/
CountDownLatch cdl = new CountDownLatch(1);
/**
* 假设购买的商品是苹果 ,1000个人在线同时购买苹果,库存是1000
* 实验证明如果不使用分布锁,库存每次都会减不完,使用分布锁后会
* 1000人会将1000个苹果库存扣减为0.
*/
public void buyApple()
{
for (int a = 0; a < threadNum; a++) {
Thread thread = new Thread(new MyRunable());
thread.start();
}
//计算器置为0,所有线程开始执行 .
cdl.countDown();
}
@Qualifier("getRedissionClient") // 指定从本地自写的class中取得实例
@Autowired
private RedissonClient redissonClient;
//定义runnable
public class MyRunable implements Runnable {
@Override
public void run() {
try {
// 等待在这里,直到计数器数值为0开始执行
System.out.println("threadName=" + Thread.currentThread().getName());
cdl.await();
// 从RedissonClient取得Rlock实例
RLock rlock = redissonClient.getLock(lockname);
// 尝试取锁,有效期为20s,到期后自动释放。如果取得锁继续执行。取锁失败则自旋。
// 亦可使用rlock.tryLock()方法,此方法也为尝试取锁,并返回boolean结果
rlock.lock(20, TimeUnit.SECONDS);
//获取当前库存
TStock stock=stockmapper.selectByPrimaryKey("0000000001");
if(stock.getStock()==0)
{
System.out.println("库存已经扣减完!");
return;
}
//扣减库存
stock.setStock(stock.getStock()-1);
stockmapper.updateByPrimaryKey(stock);
// 打印当前线程名称,库存剩余多少
System.out.println("threadName=" + Thread.currentThread().getName() + ",current stock=" + stock.getStock());
// 业务完成,释放锁
rlock.unlock();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
System.out.println(e.getStackTrace());
}
}
}
}
上面是3步关键步骤,具体的代码就不贴了,大家可以试验一下。