redis实现
在Spring Boot中使用Redis实现外卖配送单发布并保证只有一个骑手接单,可以通过以下步骤实现:
-
确保你的Spring Boot项目已经集成了Redis,并正确配置了Redis连接信息。
-
在发布外卖配送单时,生成一个唯一的标识符(比如订单ID或随机UUID),作为这个配送单的唯一标识。
-
在Redis中设置一个键,用来表示当前已经被接单的配送单。这个键可以是一个字符串类型的键,例如:“delivery_order_accepted”。
-
当骑手想要接单时,首先通过Redis的分布式锁机制尝试获取锁。只有一个骑手能够成功获取到锁,表示该骑手接到了单子。
-
如果使用Jedis客户端,可以使用
SETNX
命令实现分布式锁。例如,使用setnx("delivery_order_accepted", orderId)
来尝试获取锁,返回值为1表示成功获取到锁,0表示锁已经被其他骑手获取。 -
如果使用Lettuce客户端,可以使用
StatefulRedisConnection
的sync()
方法,然后使用setnx
命令实现分布式锁。
-
-
如果骑手成功获取到锁,即成功接到单子,将配送单的信息存储在Redis中,例如使用Hash结构保存配送单的详细信息。
-
如果骑手没有成功获取到锁,表示已经有其他骑手接到了单子,可以给骑手返回一个提示或者重新获取其他的配送单。
需要注意的是,为了防止锁过期或异常情况下的数据不一致,可以设置合适的过期时间,并在骑手接单后更新或删除Redis中的配送单信息。
这样,通过使用Redis的分布式锁机制,你可以保证只有一个骑手能够接到配送单,避免多个骑手同时接单的情况发生。
以下是一个示例代码,演示如何使用Redis实现在Spring Boot中发布外卖配送单并保证只有一个骑手接单:
package com.tan.springboot2.p2;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Slf4j
@RestController
@RequestMapping("/delivery")
public class DeliveryController {
private static final String DELIVERY_ORDER_ACCEPTED_KEY_PREFIX = "delivery_order_accepted:";
private static final long LOCK_EXPIRATION_SECONDS = 300;
private static final long LOCK_RENEWAL_SECONDS = 10;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@GetMapping("/publish")
public String publishDeliveryOrder(String orderId) {
String key = DELIVERY_ORDER_ACCEPTED_KEY_PREFIX + orderId;
ValueOperations<String, String> ops = redisTemplate.opsForValue();
String value = UUID.randomUUID().toString();
boolean lockAcquired=false;
try {
// 尝试获取锁
lockAcquired = ops.setIfAbsent(key, value, LOCK_EXPIRATION_SECONDS, TimeUnit.SECONDS);
if (lockAcquired) {
// 成功获取到锁,表示骑手接到了单子
// 这里可以保存配送单的详细信息到Redis中,使用Hash结构等
log.info("cg");
Thread.sleep(280*1000L);
// 返回接单成功的消息
return "骑手已接到单子:" + orderId;
} else {
log.info("sb");
// 返回接单失败的消息
return "该单子已被其他骑手接走" + orderId;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (lockAcquired) {
releaseLock(key, value);
}
}
return key;
}
private void releaseLock(String key, String value) {
RedisScript<Long> script = new DefaultRedisScript<>(
"if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end",
Long.class
);
redisTemplate.execute(script, Collections.singletonList(key), value);
}
}
boolean expired = redisTemplate.getExpire(key, TimeUnit.SECONDS) == -2;
在Redis中,使用getExpire
方法可以获取指定键的剩余过期时间(TTL,Time to Live)。该方法返回一个长整型数值,表示键的剩余过期时间,单位为秒。有以下几种情况:
- 如果键存在并且具有设置的过期时间,
getExpire
返回键的剩余过期时间的秒数。 - 如果键存在但没有设置过期时间,
getExpire
返回-1。 - 如果键不存在,或者已经过期,
getExpire
返回-2。
在示例代码中,我们通过redisTemplate.getExpire(key, TimeUnit.SECONDS)
来获取指定键的剩余过期时间,然后与-2进行比较。如果返回值等于-2,说明键不存在或已经过期,表示锁已过期。在这种情况下,我们可以尝试重新获取锁,并进行续期操作。
需要注意的是,getExpire
方法返回的剩余过期时间单位为秒,与我们在setIfAbsent
方法中设置的过期时间单位保持一致。
请求测试 http://localhost:8081/delivery/publish?orderId=1
https://github.com/tanwu001/springboot2.1
代码在p2包中
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/161150.html