在我工作的公司,我们使用
Quartz进行作业调度。大部分时间Quartz都在为我们提供所需的一切:
- 通过cron表达式进行调度。
- 监控和启动已启动的工作......
无论如何,有时在超高峰期间,Quartz表会遇到数据库锁定问题。所以我在想什么是Quartz替代品。以下项目前景看好:
- Cron4J
- Apache Ignite(哦,是的!)
但是,让我们尝试一些轻松简单的方法,如何使用
Spring Scheduling实现任务启动,以及使用
Hazelcast数据网格实现集群编排?
Spring Scheduling和Hazelcast一起运行
任务目标:假设我想在两个微服务上每2秒启动一次特定任务,我想确保群集中始终只有一个任务在运行!
这是Hazelcast派上用场的时刻。我们将为它测试两种Hazelcast解决方案:
- ILock - Hazelcast分布式锁,用于访问关键部分。只允许一个线程位于关键部分。
- ISemaphore - 用于关键部分编排的集群范围计数信号量。允许多个线程位于关键部分。取决于设置。
在ILock和ISemaphore这两个分布式对象之间的重要区别在于ILock需要由请求锁定的同一线程释放。另一方面,ISemaphore可以通过完全另一个线程释放。
- ISemaphore - 如果你想异步解锁关键部分,那么ISemaphore就是你的方式。
- ILock - 访问需要同步应答的一些共享资源是ILock的一种方式。
演示
让我们有两个微服务在工作后每两秒启动一次:
private void doJob(final String microServiceName)抛出InterruptedException {
System.out.println(microServiceName +“in critical section ...”);
for(int i = 0; i <4; i ++){
Thread.sleep(1000);
System.out.println(“在做什么”+ microServiceName);
}
}
|
用ILock演示
如上所述,我们希望确保只有线程处于关键部分:(ILock解决方案)
public void performJobWithLock(final String microServiceName) {
final ILock lock = hazelcastConfiguration.getJobLock();
if (lock.tryLock()) {
try {
doJob(microServiceName);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
@Scheduled(fixedRate = 2000)
public void performJob() {
jobServices.performJobWithLock("service1");
}
|
请注意,在try / finally块之前获取锁定(tryLock方法)。我使用的tryLock方法在获取锁定方面更安全,因为跳过了try / finally块。但是当使用ILock.lock()方法时,您需要知道未授予锁定的可能性,然后ILock.unlock()方法引发IllegalStateException。
测试
- 打开两个终端窗口。
- git clone https://bitbucket.org/tomask79/spring-integration-hazelcast.git
- 在pom.xml的顶级目录中运行“mvn clean install”
- 第一个终端中的java -jar spring-microservice-service1 / target / service1-0.0.1-SNAPSHOT.war。
- java -jar spring-microservice-service2 / target / service2-0.0.1-SNAPSHOT.war在第二个终端。
- 在你启动两个微服务之后,输出将出现在两个窗口中,但绝不会同时出现!
第一个终端:
service1 in critical section...
Doing something in service1
Doing something in service1
Doing something in service1
Doing something in service1
|
第二个终端:
service2 in critical section...
Doing something in service2
Doing something in service2
Doing something in service2
Doing something in service2
|
使用ISemaphore进行演示
与ILock相同,但这次ISemaphore是从另一个线程异步释放的:
public void performJobWithSemaphore(final String microServiceName) {
final ISemaphore semaphore = hazelcastConfiguration.getJobSemaphore();
try {
semaphore.acquire();
Thread thread = new Thread() {
public void run() {
try {
try {
doJob(microServiceName);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
semaphore.release();
System.out.println("Thread released semaphore..."+microServiceName);
}
}
};
thread.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
|
不要忘记将ISemaphore permits 的数量设置为1.我们希望ISemaphore表现为ILock,但我们需要能够从另一个线程中释放临界区。
@Bean
public ISemaphore getJobSemaphore() {
ISemaphore semaphore = hazelcastInstance().getSemaphore("criticalJobSemaphore");
semaphore.init(1);
return semaphore;
}
|
测试
以与ILock相同的方式启动两个微服务。以下输出将在两个终端窗口中显示,但同样,永远不会同时出现!
第一个终端
Doing something in service1
Doing something in service1
Doing something in service1
Doing something in service1
Thread released semaphore...service1
|
第二终端
Doing something in service2
Doing something in service2
Doing something in service2
Doing something in service2
Thread released semaphore...service2
|