使用Hazelcast调度Spring tasks


在我工作的公司,我们使用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 ...”); 
        forint 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