司机开始接单,乘客填写出发地——目标地,开始下单
service-order模块
- @Operation(summary="司机抢单")
- @GetMapping("/robNewOrder/{driverId}/{orderId}")
- public Result<Boolean> robNewOrder(@PathVariable Long driverId,@PathVariable Long orderId){
- return Result.ok(orderInfoServcie.robNewOrder(driverId,orderId));
- }
复制代码- @Override
- public Boolean robNewOrder(Long driverId,Long orderId){
-
- /**
- 为了防止数据库压力过大,在saveOrderInfo添加订单的时候
- 需要向redis添加,redisTemplate.opsForValue().set(RedisConstant.ORDER_ACCEPT_MARK,
- "0",RedisConstant.ORDER_ACCEPT_MARK_EXPIRES_TIME,
- TimeUnit.MINUTES);
- */
- if(redisTemplate.hasKey(RedisConstant.ORDER_ACCEPT_MARK)){
- throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
- }
- //如果订单存在Redis,则修改订单状态"已经接单"
- //修改条件:根据订单id+司机id
- LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(orderInfo::getId,orderId);
- OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);
-
- orderInfo.setStatus(OrderStatus.ACCEPTED.getStats());
- orderInfo.setDriverId(driverId);
- orderInfo.setAccpetTime(new Date());
-
- int rows = orderInfoMapper.updateById(orderInfo);
- if(rows != 1){
- throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
- }
- redisTemplate.delete(RedisConstant.ORDER_ACCEPT_MARK);
- return true;
- }
复制代码 server-order-client远程界说模块
- @GetMapping("/order/info/robNewOrder/{driverId}/{orderId}")
- Result<Boolean> robNewOrder(@PathVariable("driverId") Long driverId,@PathVariable("orderId") Long orderId);
复制代码 web调用模块
- @Operation(summary = "司机抢单")
- @GuiguLogin()
- @GetMapping("/robNewOrder/{orderId}")
- public Result<Boolean> robNewOrder(@PathVariable Long orderId){
-
- Long driverId = AuthContextHolder.getUserId();
- return Result.ok(orderService.robNewOrder(driverId,orderId));
- }
复制代码- @Override
- public Boolean robNewOrder(Long driverId,Long orderId){
- return orderInfoFeignClient.robNewOrder(driverId,orderId).getData();
- }
复制代码 当地锁VS分布式锁
乐观锁进行对抢单功能的优化
司机抢单 update order_info set status=2,driver_id=?,accept_time=> where id=? and status= 1
其中将status订单状态(等待接单)作为版本号来判断
- @Override
- public Boolean robNewOrder(Long driverId,Long orderId){
-
- /**
- 为了防止数据库压力过大,在saveOrderInfo添加订单的时候
- 需要向redis添加,redisTemplate.opsForValue().set(RedisConstant.ORDER_ACCEPT_MARK,
- "0",RedisConstant.ORDER_ACCEPT_MARK_EXPIRES_TIME,
- TimeUnit.MINUTES);
- */
- if(redisTemplate.hasKey(RedisConstant.ORDER_ACCEPT_MARK)){
- throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
- }
- //司机抢单 update order_info set status=2,driver_id=?,accept_time=> where id=? and status= 1
- //其中将status订单状态(等待接单)作为版本号来判断
- LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(OrderInfo::getId,orderId);
- wrapper.eq(OrderInfo::getStatus,OrderStatus.WAITING_ACCEPT.getStatus());//以1作为条件
- OrderInfo orderInfo = new OrderInfo();
- orderInfo.setStatus(OrderStatus.ACCEPTED.getStatus());//改成2
- orderInfo.setDriverId(driverId);
- orderInfo.setAcceptTime(new Date());
-
- int rows = orderInfoMapper.updateById(orderInfo,wrapper);
- if(rows != 1){
- throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
- }
- redisTemplate.delete(RedisConstant.ORDER_ACCEPT_MARK);
- return true;
- }
复制代码 分布式锁Redisson进行对抢单功能的优化
当地锁的范围
- @GetMapping("testLock")
- public Result testLock(){
-
- testService.testLock();
- return Return.ok();
- }
复制代码 使用synchronized解决并发问题,通过jmeter工具实现模仿测试
- @Override
- public synchronized void testLock(){
-
- String value = redisTemplate.opsForValue().get("num");
- if(StringUtils.isBlank(value)){
- return;
- }
- int num = Integer.parseInt(value);
- retisTemplate.opsForValue().set("num",String.valueOf(++num));
- }
复制代码 通过idea进行对服务的复制,模仿集群场景
通过网关服务进行转发,jmeter进行车市
Redis实现分布式锁
- @Override
- public void testLock(){
-
- //获取锁
- Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent("lock","lock");
-
- //如果为true,表示获取锁成功
- if(ifAbsent){
- String value = redisTemplate.opsForValue().get("num");
- if(StringUtils.isBlank(value)){
- return;
- }
- //释放锁
- redisTemplate.delete("lock");
- }else {
- try{
- Thread.sleep(100);
- this.testLock();
- }catch(InterrutedException e){
- e.printStackTrace();
- }
- }
-
-
- }
复制代码 如果开释锁之前,出现了非常,导致无法开释锁
解决方案:以下两种
- 将redisTemplate.delete(“lock”)开释锁放到finally中
- 为锁设置过期时间 redisTemplate.opsForValue().setIfAbsent(“lock”,“lock”,3,Time.SECONDS);
如果开释的是别人的锁
解决方案:
- @Override
- public void testLock(){
-
- String uuid = UUID.randomUUID().toString();
- //key value 过期时间 过期时间的单位
- Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent("lock",uuid,3,TimeUnit.SECONDS);
- if(ifAbsent){
- String value = redisTemplate.opsForValue().get("num");
- if(StringUtils.isBlank(value)){
- return;
- }
- int num = Integer.parseInt(value);
- redisTemplate.opsForValue().set("num",String.valueOf(++num));
-
- //释放锁
- String redisUUID = redisTemplate.opsForValue().get("lock");
- if(uuid.equals(redisUUID)){
- redisTemplate.delete("lock");
- }
-
- }else{
- try{
- Thread.sleep(100);
- this.testLock();
- }catch(InterruptedException e){
- e.printStackTrace();
- }
- }
- }
复制代码 判断开释锁后,锁过期自动开释
当2获取锁,1直接开释2的锁了,不能包管原子性

解决方案:
- @Override
- public void testLock(){
-
- String uuid = UUID.randomUUID().toString();
- //key value 过期时间 过期时间的单位
- Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent("lock",uuid,3,TimeUnit.SECONDS);
- if(ifAbsent){
- String value = redisTemplate.opsForValue().get("num");
- if(StringUtils.isBlank(value)){
- return;
- }
- int num = Integer.parseInt(value);
- redisTemplate.opsForValue().set("num",String.valueOf(++num));
-
- //释放锁(通过lua脚本)
- DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
- String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
- redisScript.setScriptText(script);
- redisScript.setResultType(Long.class);
- stringRedisTemplate.execute(redisScript,Arrays.asList("lock"),uuid);
-
- }else{
- try{
- Thread.sleep(100);
- this.testLock();
- }catch(InterruptedException e){
- e.printStackTrace();
- }
- }
- }
复制代码 Redisson实现分布式锁
- 获取锁 redissonClient.getLock(“lock”)
- 阻塞一直等待直到获取锁,获取锁之后默认过期时间30s lock.lock()
- 获取到锁,锁过期时间10s lock.lock(10,TimeUnit.SECONDS)
- 第一个参数获取锁等待时间,第二个参数获取锁过期时间 lock.tryLock(30,10,TimeUnit.SECONDS)
- 开释锁 lock.unlock()
①、引入依靠redisson
②、设置类
- @Data
- @Configuration
- @ConfigurationProperties(prefix = "spring.data.redis")
- public class RedissonConfig{
-
- private String host;
- private String password;
- private String port;
- private int timeout = 3000;
- private static String ADDRESS_PREFIX = "redis://";
- @Bean
- RedissonClient redissonSingle(){
-
- Config config = new Config();
- if(!StringUtils.hasText(host)){
- throw new RuntimeException("host is empty");
- }
- SingleServerConfig serverConfig = config.useSingleServer()
- .setAddress(ADDRESS_PREFIX + THIS.HOST + ":" + port)
- .setTimeout(this.timeout);
- if(StringUtils.hasText(this.password)){
- serverConfig.setPassword(this.password);
- }
- return Redisson.create(config);
- }
- }
复制代码 ③、添加Redisson分布式锁到司机抢单功能
OrderInfoServiceImpl添加分布式锁
- @Autowired
- private RedissonClient redissonClient;
- @Override
- public Boolean robNewOrder(Long driverId,Long orderId){
-
- if(!redisTemplate.hasKey(RedisConstant.ORDER_ACCEPT_MARK)){
- throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
- }
- //创建锁
- RLock lock = redissonClient.getLock(RedisConstant.ROB_NEW_ORDER_LOCK + orderId);
- try{
- //获取锁 等待时间 过期时间 时间单位
- boolean flag = lock.tryLock(10,5,TimeUnit.SECONDS);
-
- if(flag){//获取锁成功
- LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(OrderInfo::getId,orderId);
- OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);
- orderInfo.setStatus(OrderStatus.ACCEPTED.getStatus());
- orderInfo.setDriverId(driverId);
- orderInfo.setAccpetTime(new Date());
-
- int rows = orderInfoMapper.updateById(orderInfo);
- if(rows!=1){
- throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
- }
-
- redisTemplate.delete(RedisConstant.ORDER_ACCEPT_MARK);
- }
-
- }catch(Exception e){
- throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
- }finally{
- //释放锁
- if(lock.isLocked()){
- lock.unlock();
- }
- }
-
- return true;
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |