刚接触分布式时就听说过分布式和单机的种种不同,于是今天就来准备来了解了解分布式锁。
在谈分布式锁之前先来看看不使用锁时会出现什么问题?
场景1: 假设现在有一个订单系统和一个库存系统,具体业务流程为:用户发起请求>>判断库存是否充足>>生成订单>>执行出库。
实现如下:
订单系统
@RestController
public class hello {
public static int aaa=0;//统计生成的订单数
@RequestMapping("/order_form")
public String order_form() throws InterruptedException {
aaa++;
Thread.sleep(30);
return "----------成功生成订单---累计生成订单数"+aaa;
}
}
库存系统
//库存系统
@RestController
public class test {
public static Integer repositoryNum=100;//初始库存
public static Integer shellNum=0;//剩余库存数
//检查库存是否充足
@RequestMapping("/check_num")
public boolean check_num(){
return repositoryNum>=5;
}
//出库
@RequestMapping("/reduce")
public Integer reduce(int num){
public Integer sell(int num) {
try {
Thread.sleep(50);//线程阻塞50毫秒模拟业务执行过程
} catch (InterruptedException e) {
e.printStackTrace();
}
test.repositoryNum-=num;
test.shellNum+=num;
System.out.println("成功卖出="+num+"共卖出"+test.shellNum+",剩余库存="+test.repositoryNum);
return test.repositoryNum;
}
}
}
feign接口
@RestController
public class controller {
@Autowired
OrderFeign order;//订单系统
@Autowired
RepositoryFeign repository;//库存系统
//销售业务
@RequestMapping("/shell")
public synchronized String shell() {
if(repository.check()){
//产生订单
String result=order.order_form();
//开始出库
Integer num=repository.reduce(5);
return result+"剩余:"+num;
}else{
System.out.println("剩余库存不足,出库失败");
return "剩余库存不足,出库失败";
}
}
}
Eureka:
测试结果:
通过9091端口和9090端口都可以实现销售的业务。这样看来似乎是没什么问题的。并且我们在feign的业务方法中加上了synchronized关键字,保证了多线程环境下的数据一致性。
于是我再次通过zuul网关进行访问,zuul网关会将请求依次发送到9090或9091端口进行执行。
看起来依然没有毛病,可以正常的实现销售业务。于是我又通过jmeter模拟了100个用户并发访问。这时候问题出现了
可以看到当库存为0时依然卖出了5件商品。这是因为当库存剩余5个时,9090端口先请求库存系统发现库存充足,9091端口又请求了库存系统发现库存充足。于是9090和9091端口都执行了出库操作。这样看来我们即使加上synchronized关键字后依然不能保证分布式中的数据一致性问题。
总结: 分布式环境中,服务通常是部署在多个服务器中,多台服务器无法使用同一把锁,所以引入了分布式锁这一概念。
感谢大佬文章:https://mp.weixin.qq.com/s/RLeujAj5rwZGNYMD0uLbrg