Spring Poller多服务器部署下文件重复处理及文件锁实现咨询
兄弟,这个分布式文件处理的锁问题我之前也踩过坑,给你几个在Spring生态里能直接落地的解决方案,都是经过验证的:
方案一:基于Spring Integration的分布式FileLocker
Spring Integration自带的DefaultFileLocker只能在单JVM里生效,完全不适合你这种多服务器共享文件夹的场景。我们得自己实现分布式版本的FileLocker,常用的依赖是Redis或者数据库:
- Redis实现:借助Redis的
SETNX命令(或者用Redisson的分布式锁),在尝试处理文件前,先给文件路径加个分布式锁。只有拿到锁的节点才能操作文件,处理完再释放锁。 - 数据库实现:建一张锁表,用文件绝对路径做唯一约束,操作前先插入锁记录,成功插入的节点就能处理文件,同时要设置锁的过期时间,防止节点宕机导致锁一直占用。
给你贴个Redis版的简易实现代码:
public class RedisFileLocker implements FileLocker { private final StringRedisTemplate redisTemplate; private static final String LOCK_KEY_PREFIX = "file-processing-lock:"; private static final long LOCK_EXPIRE_MS = 30000; // 30秒自动过期 public RedisFileLocker(StringRedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } @Override public boolean lock(File file) { String lockKey = LOCK_KEY_PREFIX + file.getAbsolutePath(); // 原子性设置锁,只有不存在时才成功 Boolean lockAcquired = redisTemplate.opsForValue() .setIfAbsent(lockKey, "locked", LOCK_EXPIRE_MS, TimeUnit.MILLISECONDS); return Boolean.TRUE.equals(lockAcquired); } @Override public void unlock(File file) { String lockKey = LOCK_KEY_PREFIX + file.getAbsolutePath(); redisTemplate.delete(lockKey); } // 实现FileLocker的其他必要方法(比如isLocked等) }
然后把这个locker配置到你的文件入站适配器里:
@Bean public FileInboundChannelAdapter fileInboundAdapter() { FileInboundChannelAdapter adapter = new FileInboundChannelAdapter(new SimpleFileListFilter()); adapter.setDirectory(new File("/your/shared/inbound/folder")); adapter.setFileLocker(new RedisFileLocker(stringRedisTemplate())); adapter.setAutoCreateDirectory(true); return adapter; }
方案二:利用文件系统的原子重命名操作
这个方案不需要额外中间件,完全依赖文件系统的原子重命名特性(大部分主流文件系统都支持):
- 当Poller扫到文件时,先尝试把文件重命名成一个唯一的临时名称(比如
原文件名.随机UUID.processing) - 如果重命名成功,说明当前节点抢到了处理权,接着把文件移到处理中文件夹开始处理
- 如果重命名失败,直接跳过这个文件,等下一轮扫描再处理其他文件
核心代码大概是这样:
private boolean tryAcquireFile(File file) { String tempFilePath = file.getAbsolutePath() + "." + UUID.randomUUID() + ".processing"; File tempFile = new File(tempFilePath); // 重命名操作是原子的,同一时间只有一个节点能成功 return file.renameTo(tempFile); }
处理逻辑里先调用这个方法,返回true再继续执行后续的文件处理和移动操作。
方案三:用分布式任务调度统一管控
如果你的业务场景比较复杂,还可以用分布式任务调度框架从根源上避免多节点抢文件:
- 比如用Spring Cloud Task或者带分布式锁的Quartz,配置成只有一个节点会触发文件扫描任务
- 扫描到文件后,把处理任务通过消息队列(比如RabbitMQ、Kafka)分发给各个节点处理
- 这样所有节点不会直接去扫共享文件夹,自然不会出现抢文件的问题
额外注意事项
- 不管用哪种方案,一定要处理锁过期的情况,防止节点宕机导致文件一直被锁死
- 处理完成后必须释放锁或者清理临时文件,避免残留垃圾
- 最好保证文件处理逻辑的幂等性,极端情况下就算出现重复处理也不会出问题
内容的提问来源于stack exchange,提问作者Krishna Chaitanya
相关产品推荐
相关产品推荐

