面试题答案
一键面试1. 使用Zookeeper实现分布式调度
- 技术原理:Zookeeper是一个分布式协调服务,利用其临时有序节点和watch机制来实现分布式锁。
- 设计思路:
- 每个应用实例启动时,尝试在Zookeeper指定路径下创建一个临时有序节点。
- 拥有最小序号的节点获得执行定时任务的权利。
- 其他节点通过watch机制监听序号比自己小一位的节点,当该节点消失(任务执行完毕或节点故障)时,重新竞争执行权。
- 关键步骤:
- 引入依赖:在Spring Boot项目的
pom.xml
中添加Zookeeper客户端依赖,如Curator。
<dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>4.2.0</version> </dependency>
- 创建Zookeeper客户端:在Spring配置类中创建Zookeeper客户端实例。
@Configuration public class ZookeeperConfig { @Value("${zookeeper.connectString}") private String connectString; @Value("${zookeeper.sessionTimeoutMs}") private int sessionTimeoutMs; @Value("${zookeeper.connectionTimeoutMs}") private int connectionTimeoutMs; @Bean public CuratorFramework curatorFramework() { return CuratorFrameworkFactory.builder() .connectString(connectString) .sessionTimeoutMs(sessionTimeoutMs) .connectionTimeoutMs(connectionTimeoutMs) .build(); } }
- 实现分布式锁和任务调度:在定时任务类中,利用Curator的
InterProcessMutex
实现分布式锁。
import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.locks.InterProcessMutex; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class DistributedTask { private static final String LOCK_PATH = "/distributed-lock/task"; @Autowired private CuratorFramework curatorFramework; @Scheduled(cron = "0 0 0 * * *") // 每天0点执行 public void executeTask() throws Exception { InterProcessMutex mutex = new InterProcessMutex(curatorFramework, LOCK_PATH); try { if (mutex.acquire(10, TimeUnit.SECONDS)) { try { // 执行任务逻辑 System.out.println("执行定时任务..."); } finally { mutex.release(); } } } catch (Exception e) { e.printStackTrace(); } } }
- 引入依赖:在Spring Boot项目的
2. 使用Redis实现分布式调度
- 技术原理:Redis的
SETNX
(SET if Not eXists)命令可以实现原子性的设置操作,利用此特性来实现分布式锁。 - 设计思路:
- 每个应用实例在执行定时任务前,尝试使用
SETNX
命令在Redis中设置一个特定的键值对,键为任务的唯一标识,值可以是任意内容。 - 如果设置成功,说明该实例获得了执行任务的权利;如果设置失败,说明已有其他实例在执行该任务。
- 为防止死锁,需要给锁设置一个过期时间。
- 每个应用实例在执行定时任务前,尝试使用
- 关键步骤:
- 引入依赖:在
pom.xml
中添加Spring Data Redis依赖。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
- 配置Redis:在
application.properties
中配置Redis连接信息。
spring.redis.host=127.0.0.1 spring.redis.port=6379
- 实现分布式锁和任务调度:在定时任务类中,使用
RedisTemplate
实现分布式锁。
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class DistributedTask { private static final String LOCK_KEY = "distributed:task:lock"; private static final String LOCK_VALUE = "task-lock-value"; private static final long EXPIRE_TIME = 10 * 1000; // 10秒过期时间 @Autowired private RedisTemplate<String, String> redisTemplate; @Scheduled(cron = "0 0 0 * * *") public void executeTask() { Boolean result = redisTemplate.opsForValue().setIfAbsent(LOCK_KEY, LOCK_VALUE, EXPIRE_TIME); if (result != null && result) { try { // 执行任务逻辑 System.out.println("执行定时任务..."); } finally { redisTemplate.delete(LOCK_KEY); } } } }
- 引入依赖:在
分布式调度算法思路总结
无论是使用Zookeeper还是Redis,核心思路都是通过一种分布式协调机制(锁)来保证同一时刻只有一个实例能够执行定时任务。在设计分布式调度算法时,需要考虑锁的获取与释放机制,避免死锁和重复执行的情况,同时要处理好节点故障、网络波动等异常情况,确保系统的稳定性和可靠性。