本文中案例都会在上传到git上,请放心浏览
git地址:https://github.com/muxiaonong/Spring-Cloud/tree/master/order-lock
本文会使用到 三台 redis 独立服务器,可以自行提前搭建好
前言
在Java中,我们对于锁会比较熟悉,常用的有 synchronized、Lock锁,在java并发编程中,我们通过锁,来实现当多个线程竞争同一个共享资源或者变量而造成的数据不一致的问题,但是JVM锁只能针对于单个应用服务,随着我们业务的发展需要,单体单机部署的系统早已演化成分布式系统,由于分布式系统的多线程、多进程而且分布在不同的机器上,这个时候JVM锁的并发控制就没有效果了,为了解决跨JVM锁并且能够控制共享资源的访问,于是有了分布式锁的诞生。
什么是分布式锁
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁
为什么JVM锁在分布式下不可以呢?
我们通过代码来看一下就知道,为什么集群下jvm锁是不可靠的呢?我们模拟一下商品抢购的场景,A服务有十个用户去抢购这个商品,B服务有十个用户去抢购这个商品,当有其中一个用户抢购成功后,其他用户不可以在对这个商品进行下单操作,那么到底是A服务会抢到还是B服务会抢到这个商品呢,我们来看一下
当其中有一个用户抢购成功后,status会变成1
GrabService:
public interface GrabService { /** * 商品抢单 * @param orderId * @param driverId * @return */ public ResponseResult grabOrder(int orderId, int driverId); }
GrabJvmLockServiceImpl:
@Service("grabJvmLockService") public class GrabJvmLockServiceImpl implements GrabService { @Autowired OrderService orderService; @Override public ResponseResult grabOrder(int orderId, int driverId) { String lock = (orderId+""); synchronized (lock.intern()) { try { System.out.println("用户:"+driverId+" 执行下单逻辑"); boolean b = orderService.grab(orderId, driverId); if(b) { System.out.println("用户:"+driverId+" 下单成功"); }else { System.out.println("用户:"+driverId+" 下单失败"); } } finally { } } return null; } }
OrderService :
public interface OrderService { public boolean grab(int orderId, int driverId); }
OrderServiceImpl :
@Service public class OrderServiceImpl implements OrderService { @Autowired private OrderMapper mapper; public boolean grab(int orderId, int driverId) { Order order = mapper.selectByPrimaryKey(orderId); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if(order.getStatus().intValue() == 0) { order.setStatus(1); mapper.updateByPrimaryKeySelective(order); return true; } return false; } }
这里我们模拟集群环境,启动两个端口,8004和8005进行访问
这里我们用jmeter进行测试
如果不会jmeter的可以看我之前对tomcat进行压测的文章:tomcat优化
项目启动顺序:先启动 Server-eureka注册中心、在启动 8004和8005端口
测试结果:
这里我们可以看到 8004 服务和 8005 服务 同时都有一个用户去下单成功这个商品,但是这个商品只能有一个用户能够去抢到,因此jvm锁如果是在集群或分布式下,是无法保证访问共享变量的数据同时只有一个线程访问的,无法解决分布式,集群环境的问题。所以需要使用到分布锁。
分布式锁三种实现方式
分布式锁的实现方式总共有三种:
基于数据库实现分布式锁
基于缓存(Redis)实现分布式锁
基于Zookeeper实现分布式锁