1.zookeeper方式实现分布式锁
- 方案一:Curator框架来实现ZooKeeper分布式锁
注意: 下面教程为 模拟高并发情况下生成订单,springboot+mybatis+通用mapper+zookeeper下单并入库
项目github地址 的zhou-cloud-springboot/ zhou-springboot-lock 模块
(1)Curator相关依赖包
<!--zookeeper分布式锁-->
<!--注意 zookeeper版本问题,zookeeper为3.4.6-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
(2)Curator配置类
@Configuration
@ConfigurationProperties(prefix = "curator")
@Data
public class CuratorConfig {
private int retryCount;
private int elapsedTimeMs;
private String connectString;
private int sessionTimeoutMs;
private int connectionTimeoutMs;
@Bean(initMethod = "start")
public CuratorFramework curatorFramework() {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(elapsedTimeMs, retryCount);
CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
.connectString(connectString)
.sessionTimeoutMs(sessionTimeoutMs)
.retryPolicy(retryPolicy)
.build();
return curatorFramework;
}
}
(3)对应springboot配置文件applciation.yml
#zookeeper分布式锁curator配置
curator:
connectionTimeoutMs: 5000 # 连接超时时间
elapsedTimeMs: 5000 #重试间隔时间
retryCount: 3 #重試次數
sessionTimeoutMs: 60000 # session超时时间
connectString: 192.168.91.139:2181,192.168.91.140:2181,192.168.91.140:2181 # zookeeper 地址 如果是单机,只写一台
(4)具体生成订单的实现类
@Slf4j
@Service
public class CuratorDisLockOrderServiceImpl implements OrderService {
//订单号生成类,时间戳+计数
private static OrderNumGenerator codeGenerator = new OrderNumGenerator();
private static String LOCK_PATH = "/distribute-lock";
@Autowired
private CuratorFramework curatorFramework;
@Autowired
private OrderDao orderDao;
@Override
public String createOrder() {
String orderCode = "";
InterProcessMutex lock = new InterProcessMutex(curatorFramework, LOCK_PATH);
try {
lock.acquire();
//生成订单编号
orderCode = codeGenerator.getOrderNumber();
log.info(Thread.currentThread().getName() + "-->获取锁成功-->生成订单编号:{}", orderCode);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
lock.release();
log.info(Thread.currentThread().getName() + "-->释放锁成功。");
} catch (Exception e) {
e.printStackTrace();
}
}
return orderCode;
}
}
(4)访问测试controller
@RestController
@Slf4j
@RequestMapping("/zookeeper")
public class OrderController {
@Autowired
private OrderService orderService;
/**
* 模拟高并发场景,多线程,并发下单
* (模拟多个用户同时访问)
*
* @return
*/
@RequestMapping("/orderThread")
public String createOrdertTest() {
//并发线程数
int count = 20;
//循环屏障
CyclicBarrier cb = new CyclicBarrier(count);
//模拟高并发场景,多线程,创建订单
for (int i = 0; i < count; i++) {
new Thread(new Runnable() {
@Override
public void run() {
log.info(Thread.currentThread().getName() + "--我已经准备好了");
try {
//等待所有线程启动准备好,才一起往下执行
cb.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
//创建订单
orderService.createOrder();
}
}).start();
}
return "ok";
}
}
这样就可以实现订单号的唯一生成,运用Curator是不是很简单,具体实现 项目github地址 的zhou-cloud-springboot/ zhou-springboot-lock 模块
- 方案二:原生方案来实现ZooKeeper分布式锁
持续更新中。。。。
2.redis方式实现分布式锁
-
方案一:Redisson来实现redis分布式锁
(1)尝试获取锁和释放锁
``` /** * 尝试获取锁 * * @param lockKey * @param waitTime 最多等待时间 * @param leaseTime 上锁后自动释放锁时间 * @return */ public static boolean tryLock(String lockKey, int waitTime, int leaseTime) { RLock lock = redissonClient.getLock(lockKey); try { return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS); } catch (InterruptedException e) { return false; } } ```/** * 释放锁 * * @param lockKey */ public static void unlock(String lockKey) { RLock lock = redissonClient.getLock(lockKey); lock.unlock(); }(2)模拟高并发下用户下单
@Slf4j @Service public class RedisOrderServiceImpl implements RedisOrderService { private static OrderNumGenerator codeGenerator = new OrderNumGenerator(); @Autowired private OrderDao orderDao; @Override public String createOrder() { try { //最多等待3秒,20秒后自动解锁 RedissonUtil.tryLock(RedisConstant.OrderLockConstant.ORDER_LOCK_KEY , TimeUnit.SECONDS, 3, 20); String orderNumber = codeGenerator.getOrderNumber(); //重要的是订单号,最好异步去生成订单详情 mq发消息 , Order order = new Order(); order.setId(orderNumber); order.setPrice(new BigDecimal(19)); order.setCreateTime(new Date()); orderDao.insert(order); } catch (Exception e) { e.printStackTrace(); } finally { RedissonUtil.unlock(RedisConstant.OrderLockConstant.ORDER_LOCK_KEY); } return "ok"; } }(3)测试controller ,多线程模拟用户下单
@RestController @Slf4j @RequestMapping("/redis") public class RedisOrderController { @Autowired private RedisOrderService orderService; /** * 模拟高并发场景,多线程,并发下单 * * @return */ @RequestMapping("/orderThread") public String createOrdertTest() { //并发线程数 int count = 20; //循环屏障 CyclicBarrier cb = new CyclicBarrier(count); //模拟高并发场景,多线程,创建订单 for (int i = 0; i < count; i++) { new Thread(new Runnable() { @Override public void run() { log.info(Thread.currentThread().getName() + "--我已经准备好了"); try { //等待所有线程启动准备好,才一起往下执行 cb.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } //创建订单 orderService.createOrder(); } }).start(); } return "ok"; } }经测试,发现20个订单号唯一
项目github地址 的zhou-cloud-springboot/ zhou-springboot-lock 模块
- 方案二:setnx 方法来实现redis分布式锁
持续更新中。。。。
3.数据库方式实现分布式锁
持续更新中。。。。
总结
- 数据库锁
优点:直接使用数据库,操作简单。
缺点:分布式系统大多数瓶颈都在数据库,使用数据库锁会增加数据库负担。
- 缓存锁
优点:性能高,实现起来比较方便,在允许偶发的锁失效情况,不影响系统正常使用,建议使用缓存锁。
缺点:通过锁超时机制不是十分可靠,当线程获取锁之后,处理时间过长会导致锁超时,这样就失去了锁的作用。
- Zookeeper锁
优点:不依靠超时时间来释放锁,可靠性高,系统要求可靠性高,建议采用zookeeper锁。
缺点:性能比不上缓存锁,因为要频繁的创建节点和删除节点。