分布式锁(多服务共享锁) 在分布式的部署环境下,通过锁机制来让多客户端互斥的对共享资源进行访问
控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。
当在打车软件中,乘客下了订单。多个司机抢单,此时因为单子只有一个,多个司机对此共享资源进行抢,此处应该使用分布式锁;
@GetMapping("/do/{orderId}")
public String grab(@PathVariable("orderId") int orderId, int driverId){
System.out.println("order:"+orderId+",driverId:"+driverId);
//此处调用锁控制层代码
grabService.grabOrder(orderId,driverId);
return "";
}
使用synchronized 不能保证多台服务器只有一个抢成功;因为synchronized 只能锁本服务的资源;多台服务的资源是锁不住的;
@Autowired
OrderService orderService;
@Override
public String 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;
}
这一层就是写的伪代码,后续并不关注他
1:在数据库中创建一个抢占锁表,假如只有两个字段,id,value
2:当多个司机来抢单时,每一个试图抢单的进程都会忘数据库表中增加一行记录,记录id就是本订单id
3:设计抢占锁表的id主键不可重复,那么谁在数据库插入成功了,就是抢占锁成功
4:其他因为主键约束插入失败的,视为抢占锁失败
5:抢锁成功的,执行完业务后调用释放锁即删除哪行记录;
1:在此实现lock接口,开发自己的mysqllock
2:lock方法尝试加锁,加上了返回true,失败则递归或者循环调用锁
3:抢锁成功的,执行完业务后调用unlock释放锁;(删除哪行记录)
package com.online.taxi.order.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import com.online.taxi.order.dao.TblOrderLockDao;
import com.online.taxi.order.entity.TblOrderLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.Data;
@Service
@Data
public class MysqlLock implements Lock {
@Autowired
private TblOrderLockDao mapper;
private ThreadLocal<TblOrderLock> orderLockThreadLocal ;
@Override
public void lock() {
// 1、尝试加锁
if(tryLock()) {
System.out.println("尝试加锁");
return;
}
// 2.休眠,等待再次抢占锁
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 3.递归再次调用
lock();
}
/**
* 非阻塞式加锁,成功,就成功,失败就失败。直接返回
*/
@Override
public boolean tryLock() {
try {
TblOrderLock tblOrderLock = orderLockThreadLocal.get();
mapper.insertSelective(tblOrderLock);
System.out.println("加锁对象:"+orderLockThreadLocal.get());
return true;
}catch (Exception e) {
return false;
}
}
@Override
public void unlock() {
mapper.deleteByPrimaryKey(orderLockThreadLocal.get().getOrderId());
System.out.println("解锁对象:"+orderLockThreadLocal.get());
orderLockThreadLocal.remove();
}
@Override
public void lockInterruptibly() throws InterruptedException {
// TODO Auto-generated method stub
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
// TODO Auto-generated method stub
return false;
}
@Override
public Condition newCondition() {
// TODO Auto-generated method stub
return null;
}
}
package com.online.taxi.order.service.impl;
import com.online.taxi.order.entity.TblOrderLock;
import com.online.taxi.order.lock.MysqlLock;
import com.online.taxi.order.service.GrabService;
import com.online.taxi.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("grabMysqlLockService")
public class GrabMysqlLockServiceImpl implements GrabService {
@Autowired
private MysqlLock lock;
@Autowired
OrderService orderService;
ThreadLocal<TblOrderLock> orderLock = new ThreadLocal<>();
@Override
public String grabOrder(int orderId, int driverId) {
// 生成 锁
//生成key
TblOrderLock ol = new TblOrderLock();
ol.setOrderId(orderId);
ol.setDriverId(driverId);
orderLock.set(ol);
lock.setOrderLockThreadLocal(orderLock);
// lock
lock.lock();
// 执行业务
try {
System.out.println("司机:"+driverId+" 执行抢单逻辑");
boolean b = orderService.grab(orderId, driverId);
if(b) {
System.out.println("司机:"+driverId+" 抢单成功");
}else {
System.out.println("司机:"+driverId+" 抢单失败");
}
}finally {
// 释放锁
lock.unlock();
}
// 执行业务
return null;
}
}
1:优点:开发量小,而且不依赖其他框架,当公司业务量小,并且不具备相关分布式锁框架技术储备时可使用
2:缺点:只适用于业务量小的情况下,如果并发量增大,对数据库io压力是很大的负担