背景
为了保证数据的一致性,在一些业务处理中都会选择加锁来保证数据的一致性。在单机模式下我们通常选择使用synchronized等这种JAVA提供好的jvm锁来实现,但是在集群和分布式情况下,这种jvm级别的锁式无法满足我们的需求,因为一个服务部署在多台服务器上,这些服务器上的jvm是无法通讯的,所以我们需要一种方案来解决分布式情况下数据一致性。
在互联网公司,基本上企业内部都会有自己的一套分布式锁开发框架
前言
分布式锁一般有三种实现方式:
- 数据库乐观锁
- 基于redis实现分布式锁
- 基于zooKeeper实习哪分布式锁
本次讲着重介绍zooKeeper实现分布式锁,和与数据库和redis实习分布式锁的对比。
核心
zookeeper的分布式锁主要是依据临时节点和事件监听机制来完成的
curator框架实现zookeeper分布式锁
前提:需要搭建起zookeeper基础架构,并且使用gateway网关做负载均衡
1 2 3 4 5
| <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>4.0.1</version> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Configuration public class CuratorConfig {
@Value("${spring.cloud.zookeeper.connect-string}") private String zookeeperConnectionString;
@Bean(initMethod = "start") public CuratorFramework curatorFramework(){ RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); return CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @Autowired private StringRedisTemplate stringRedisTemplate;
@Autowired private CuratorFramework curatorFramework;
private static AtomicInteger count = new AtomicInteger(0);
@Override public String curatorDecuctStock() { String returnResult = "";
InterProcessMutex interProcessMutex = new InterProcessMutex(curatorFramework, "/stockLock"); try { interProcessMutex.acquire();
int stock = Integer.parseInt(Objects.requireNonNull(stringRedisTemplate.opsForValue().get("stock"))); if (stock > 0) { int realStock = stock - 1; stringRedisTemplate.opsForValue().set("stock", realStock + ""); System.out.println("扣减库存成功,剩余库存:" + realStock + " 卖出数量为:" + count.addAndGet(1)); returnResult = "扣减库存成功"; } else { System.out.println("扣减库存失败,库存不足"); returnResult = "扣减库存失败,库存不足"; } } catch (Exception e) { e.printStackTrace(); }finally { try { interProcessMutex.release(); } catch (Exception e) { e.printStackTrace(); } }
return returnResult; }
|
集群压测
- jmeter压测
- 总库存为200
- redis单机本地部署
- 三个服务集群
- geteway(本地部署)轮询负载均衡
- zookeeper做分布式锁(本地单机)
1 2 3 4 5 6 7 8 9 10
| 扣减库存成功,剩余库存:1 卖出数量为:67 扣减库存失败,库存不足
扣减库存成功,剩余库存:0 卖出数量为:67 扣减库存失败,库存不足
扣减库存成功,剩余库存:2 卖出数量为:66 扣减库存失败,库存不足
|
redisson(redis)分布式锁和curator(zookeeper)分布式锁对比
通过jmeter压测发现,在同样的并发请求,redis本地单机,zookeeper本地单机的情况下,redis的300线程的200库存量用时平均在3秒左右,而zookeeper的300线程的200库存量用时平均在28秒左右。
可以明显看的出来,针对本次的300线程200库存,redis单机和zookeeper单机
对比 |
zookeeper |
redis |
时间 |
28秒 |
3秒 |
CAP |
CP |
AP |
性能 |
较低 |
高 |
可靠 |
高 |
较低 |
参考:
https://blog.csdn.net/java_66666/article/details/81015302