场景:
单体集群,使用同步锁synchronized可以解决高并发问题。
当启用集群模式时,例如:多个乘客在同一个地方下单,刚好搜索周边车辆时,他们访问到了不同的服务器,然而,每个服务器都使用的同步锁。他们搜索到了同一个车辆跟司机,造成司机同时接到多单的问题。
使用redisson分布式锁,相当于一个中间件,采用redis的setNx方法。在获取周边车辆和司机的时候先经过redis上锁,使他们访问到的是同一把锁。
driverId是Long类型,采用(driverId+"").intern() 上锁,使每个线程获取的是String常量池
同一把锁。
依赖:
<!-- redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.1</version>
</dependency>
application.yml
spring:
redis:
host: localhost
port: 6379
database: 0
配置类:
/**
* RedissonClient
*/
@Component
public class RedissonConfig {
private String potocol = "redis://";
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.port}")
private String redisPort;
@Value("${spring.redis.database}")
private Integer redisDatabase;
@Bean
public RedissonClient redissonClient(){
Config config = new Config();
config.useSingleServer().setAddress(potocol+redisHost+":"+redisPort).setDatabase(redisDatabase);
return Redisson.create();
}
}
使用:
/**
* 搜索周边终端
*/
public OrderDTO aroundTerminal(String center,Integer radius,OrderDTO orderDTO){
ApiResult<List<TerminalResponse>> listApiResult = serviceMapFeignClient.aroundSearch(center, radius);
List<TerminalResponse> list = listApiResult.getData();
if (list.size() == 0){
// 未搜索到终端,扩大范围
log.info(radius+"米内,未找到车辆");
if (radius == 5000){
log.info("此轮派单没有找到车");
return null;
}
radius = radius + 1000;
aroundTerminal(center,radius,orderDTO);
}
// 搜索到终端后
// 解析终端
for (TerminalResponse terminalResponse : list) {
Long carId = terminalResponse.getCarId();
// 司机是否是出车状态,是否有正在进行的订单。
log.info("车辆id:"+carId+"是否可出车...");
ApiResult<DriverOrderResponse> availableDriver = serviceDriverUserFeignClient.getAvailableDriverByCarId(carId);
if (availableDriver.getCode() == CommonStatusEnum.CAR_NOT_BIND_DRICER.getCode() ||
availableDriver.getCode() == CommonStatusEnum.AVAILABLE_DRIVER_EMPTY.getCode() ){
// 没有可用的司机
log.info("车辆id:"+carId+"车辆没有对应的司机");
continue;
}else {
log.info("车辆id:"+carId+"找到了对应的司机");
DriverOrderResponse driverData = availableDriver.getData();
// 并发问题处理,多个订单匹配到了同一个司机
// 使用sync在单机情况下可以,如果集群,多服务时还是会多个订单匹配到了同一个司机
// 基于redisson实现分布式锁 redis方法setnx不存在设置成功
// 锁司机id小技巧,字符串.intern(),原理最终指向都变成了字符串常量
// 同步锁 synchronized ((driverData.getDriverId()+"").intern()){
// redisson分布式锁
String lockKey = (driverData.getDriverId()+"").intern();
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
// 查询司机是否有正在进行的订单
if (isOrderGoingonByDriverUser(driverData.getDriverId()) > 0){
// 有正在进行的订单
log.info(driverData.getDriverId()+"司机的订单正在进行中...");
lock.unlock();
continue;
}
log.info("找到了可接单的司机......");
// 订单匹配司机 司机id,司机手机号,车辆id,接单时车辆经纬度,接单时间,驾驶证号,车牌号,订单状态
orderDTO.setDriverId(driverData.getDriverId());
orderDTO.setDriverPhone(driverData.getDriverPhone());
orderDTO.setCarId(carId);
orderDTO.setLicenseId(driverData.getLicenseId());
orderDTO.setVehicleNo(driverData.getVehicleNo());
orderDTO.setReceiveOrderTime(LocalDateTime.now());
orderDTO.setOrderStatus(OrderConstant.ORDER_STATUS_DRIVER_ACCEPT);
String longitude = terminalResponse.getLongitude();
String latitude = terminalResponse.getLatitude();
orderDTO.setReceiveOrderCarLongitude(longitude);
orderDTO.setReceiveOrderCarLatitude(latitude);
orderMapper.updateById(orderDTO);
// 释放锁
lock.unlock();
break;
// 同步锁结尾 }
}
}
return orderDTO;
}