哪个网站做签约设计师比较好,做一个付费网站多少钱,淘宝上做的网站,长沙网站备案拍照点为了防止死锁#xff0c;我们会给分布式锁加一个过期时间#xff0c;但是万一这个时间到了#xff0c;我们业务逻辑还没处理完#xff0c;怎么办#xff1f; 这是一个分布式应用里很常见到的需求#xff0c;关于这个问题#xff0c;有经验的程序员会怎么处理呢#xff… 为了防止死锁我们会给分布式锁加一个过期时间但是万一这个时间到了我们业务逻辑还没处理完怎么办 这是一个分布式应用里很常见到的需求关于这个问题有经验的程序员会怎么处理呢今天的文章V 哥来详细说一说把这个问题彻底讲清楚。开干
首先我们在设置过期时间时要结合业务场景去考虑尽量设置一个比较合理的值就是理论上正常处理的话在这个过期时间内是一定能处理完毕的。
之后我们再来考虑对这个问题进行兜底设计。
关于这个问题目前常见的解决方法有两种 守护线程“续命”额外起一个线程定期检查线程是否还持有锁如果有则延长过期时间。Redisson 里面就实现了这个方案使用“看门狗”定期检查每1/3的锁时间检查1次如果线程还持有锁则刷新过期时间。 超时回滚当我们解锁时发现锁已经被其他线程获取了说明此时我们执行的操作已经是“不安全”的了此时需要进行回滚并返回失败。
同时需要进行告警人为介入验证数据的正确性然后找出超时原因是否需要对超时时间进行优化等等。下面V哥分别用案例来介绍以上两种解决方法。对于进一步理解比较有帮助请继续往下看。
守护线程“续命”
Redisson 是一个基于 Java 的 Redis 客户端库它提供了多种分布式数据结构和服务包括实现为 Redisson 对象的分布式锁。使用 Redisson 可以简化分布式锁的实现和管理特别是它的自动续期功能可以避免锁在业务执行期间过期。
以下是使用 Redisson 库实现自动续期的 Java 案例代码以及详细流程步骤的解释
添加 Redisson 依赖
首先需要在项目的 pom.xml 文件中添加 Redisson 的依赖
dependencies!-- 其他依赖... --dependencygroupIdorg.redisson/groupIdartifactIdredisson-spring-boot-starter/artifactIdversion3.15.3/version !-- 请使用最新版本 --/dependency
/dependencies配置 Redisson
在 Spring Boot 应用中可以通过配置类来配置 Redisson
Configuration
public class RedissonConfig {Value(${spring.redis.host})private String host;Value(${spring.redis.port})private int port;Beanpublic Config redissonConfig() {Config config new Config();SingleServerConfig singleServerConfig config.useSingleServer();singleServerConfig.setAddress(String.format(%s:%d, host, port));singleServerConfig.setPassword(your-password); // 如果需要密码return config;}
}使用 RedissonLock
在业务代码中通过注入 RLock 来使用分布式锁
Service
public class SomeService {private final RLock lock;public SomeService(RLock lock) {this.lock lock;}public void someMethod() {lock.lock(); // 加锁try {// 执行业务逻辑// ...} finally {lock.unlock(); // 释放锁}}
}自动续期机制
Redisson 的 RLock 对象会自动处理锁的续期。当一个线程获取了锁Redisson 会在后台启动一个定时任务看门狗用于在锁即将过期时自动续期。 详细流程步骤 获取锁当调用 lock.lock() 时Redisson 会尝试在 Redis 中创建一个具有过期时间的锁。 锁的自动续期Redisson 会启动一个后台线程看门狗它会在锁的过期时间的一半时检查锁是否仍然被当前线程持有。 续期锁如果锁仍然被持有看门狗会延长锁的过期时间。这确保了即使业务逻辑执行时间较长锁也不会过期。 执行业务逻辑在锁的保护下执行业务逻辑。 释放锁当业务逻辑执行完毕后调用 lock.unlock() 释放锁。如果当前线程是最后一个持有锁的线程Redisson 会从 Redis 中删除锁。 异常处理如果在执行业务逻辑时发生异常finally 块中的 unlock() 调用确保了锁能够被释放防止死锁。 看门狗线程终止一旦锁被释放看门狗线程会停止续期操作并结束。
通过这种方式Redisson 提供了一个简单而强大的机制来处理分布式锁的自动续期从而减少了锁过期导致的问题。
超时回滚
使用超时回滚机制处理 Redis 分布式锁过期的情况是指当一个线程因为执行时间过长导致持有的分布式锁过期而其他线程又获取了同一把锁时原线程需要能够检测到这一情况并执行业务逻辑的回滚操作。以下是使用 Java 实现的一个业务场景案例以及详细流程步骤的解释
业务场景设定
假设我们有一个电商网站需要处理订单支付的业务。为了保证在支付过程中数据的一致性我们需要使用分布式锁来避免并发问题。
定义分布式锁
我们首先定义一个分布式锁的接口 DistributedLock然后实现这个接口
public interface DistributedLock {boolean tryLock(String key, String requestId, long timeout, TimeUnit unit);boolean releaseLock(String key, String requestId);
}public class RedisDistributedLock implements DistributedLock {private final RedisTemplateString, String redisTemplate;private static final String LOCK_SCRIPT if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end;public RedisDistributedLock(RedisTemplateString, String redisTemplate) {this.redisTemplate redisTemplate;}Overridepublic boolean tryLock(String key, String requestId, long timeout, TimeUnit unit) {long expireTime unit.toMillis(timeout);// 使用 Lua 脚本来确保原子性return redisTemplate.execute(new StringRedisSerializer(), new StringRedisSerializer(),new DefaultRedisScript(LOCK_SCRIPT, Boolean.class),Arrays.asList(key), requestId);}// 省略 releaseLock 方法的实现...
}业务逻辑实现
接下来我们实现订单支付的业务逻辑
Service
public class OrderService {private final DistributedLock distributedLock;private final OrderRepository orderRepository;public OrderService(DistributedLock distributedLock, OrderRepository orderRepository) {this.distributedLock distributedLock;this.orderRepository orderRepository;}public void processPayment(String orderId) {String lockKey order: orderId;String requestId UUID.randomUUID().toString();boolean isLocked distributedLock.tryLock(lockKey, requestId, 30, TimeUnit.SECONDS);if (!isLocked) {throw new RuntimeException(Could not acquire lock for order: orderId);}try {// 执行支付逻辑Order order orderRepository.findById(orderId).orElseThrow(() - new RuntimeException(Order not found));if (order.getStatus() OrderStatus.PENDING) {// 执行扣款等操作...order.setStatus(OrderStatus.COMPLETED);orderRepository.save(order);}} catch (Exception e) {// 回滚逻辑// 根据业务需求进行回滚例如恢复库存、撤销交易等throw e;} finally {// 释放锁distributedLock.releaseLock(lockKey, requestId);}}
}超时回滚流程步骤 尝试获取锁在执行业务逻辑之前首先尝试获取分布式锁。 执行业务逻辑如果成功获取锁则执行支付逻辑包括检查订单状态、扣款、更新订单状态等。 异常处理如果在执行过程中发生异常执行回滚逻辑撤销已经进行的操作以保证数据的一致性。 释放锁无论业务逻辑是否成功执行都需要在 finally 块中释放锁以避免死锁。 超时回滚检测如果业务逻辑执行时间过长导致锁过期其他线程可能会获取到同一把锁并执行业务逻辑。在这种情况下原线程在执行回滚逻辑时需要检测锁的状态如果发现锁已经被其他线程持有则需要根据业务需求进行相应的处理。 锁释放后的处理在释放锁之后如果业务逻辑执行失败可能需要通知用户或者记录日志以便进一步处理。
通过这种方式我们可以确保即使在分布式锁过期的情况下业务逻辑也能够通过超时回滚机制来保证数据的一致性和完整性。
搞定。关注“威哥爱编程”一起消灭项目中一个一个问题成长路上我们搀扶前行。