Apache Ignite。如何创建计时器任务(基于cron)而不在每个节点上执行该任务?

时间:2018-12-14 13:43:17

标签: java cron cluster-computing ignite

如何创建计时器任务(基于cron)而不在每个节点上执行该任务,即使用Apache Ingite在时间表中的每个时间点执行一次?

我有一个群集,该群集由2个节点和带有计时器任务的应用程序(战争)组成。在非集群模式下,应用程序运行良好。但是它具有内部计时器任务(即,每5分钟启动一次),可以处理共享资源。

我尝试这样做。但是,如果两个应用程序实例都已启动(每个应用程序实例都尝试启动相同的计时器任务),则IngiteScheduler#scheduleLocal会在每个节点上部署并运行任务。

我认为ignite具有用于 具有id 的部署任务的机制……

谢谢。


2018年12月18日更新:

(感谢@alamar提出想法)

下面我显示源代码并测试成功的解决方案:

IgniteTimerTest.java:

package com.stackoverflow.question53780890.test;

import com.stackoverflow.question53780890.JobRunner;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteServices;
import org.apache.ignite.resources.SpringResource;
import org.apache.ignite.services.Service;
import org.apache.ignite.services.ServiceContext;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;

import java.io.Serializable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class IgniteTimerTest {
    private static final ExecutorService ES = Executors.newFixedThreadPool(5);

    @Test
    public void test() throws Exception {


        Future<ConfigurableApplicationContext> applicationContextFutureOne = ES.submit(() -> create(ConfigOne.class));
        Future<ConfigurableApplicationContext> applicationContextFutureTwo = ES.submit(() -> create(ConfigTwo.class));

        try (ConfigurableApplicationContext applicationContextOne = applicationContextFutureOne.get();
             ConfigurableApplicationContext applicationContextTwo = applicationContextFutureTwo.get();) {
            Ignite igniteOne = applicationContextOne.getBean(Ignite.class);
            Ignite igniteTwo = applicationContextTwo.getBean(Ignite.class);
            IgniteServices servicesOne = igniteOne.services();
            IgniteServices servicesTwo = igniteTwo.services();
            servicesOne.deployClusterSingleton(TestTimerRunner.class.getName(), new TestTimerRunner());
            Thread.sleep(JobRunner.PERIOD * 3);
            servicesTwo.deployClusterSingleton(TestTimerRunner.class.getName(), new TestTimerRunner());
            Thread.sleep(JobRunner.PERIOD * 3);
            applicationContextOne.close();
            Thread.sleep(JobRunner.PERIOD * 3);
            int countValue = JobRunner.getConterValue();
            Assertions.assertTrue(9 <= countValue && countValue <= 11);


        }
    }

    private ConfigurableApplicationContext create(Class mainClass) {
        ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(mainClass);
        return context;

    }

    class TestTimerRunner implements Serializable, Service {

        @SpringResource(resourceClass = JobRunner.class)
        private transient JobRunner jobRunner;

        public TestTimerRunner() {
        }
        @Override
        public void cancel(ServiceContext ctx) {
            jobRunner.stop();
        }

        @Override
        public void init(ServiceContext ctx) throws Exception {

        }

        @Override
        public void execute(ServiceContext ctx) throws Exception {
            jobRunner.start();
        }

    }

    @Configuration
    @ImportResource("classpath:common.xml")
    public static class ConfigOne {
        @Bean
        public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
            PropertySourcesPlaceholderConfigurer holder = new PropertySourcesPlaceholderConfigurer();
            holder.setLocation(new ClassPathResource("one.properties"));
            return holder;

        }
    }

    @Configuration
    @ImportResource("classpath:common.xml")
    public static class ConfigTwo {
        @Bean
        public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
            PropertySourcesPlaceholderConfigurer holder = new PropertySourcesPlaceholderConfigurer();
            holder.setLocation(new ClassPathResource("two.properties"));
            return holder;

        }
    }
}

JobRunner.java:

package com.stackoverflow.question53780890;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;


import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;


public class JobRunner implements Runnable {

    public static final long PERIOD = 5_000;
    private static AtomicInteger counter = new AtomicInteger();
    @Autowired
    private TaskScheduler taskScheduler;
    private ScheduledFuture<?> job;

    public static int getConterValue() {
        return counter.get();
    }

    public void start() {
        job = taskScheduler.scheduleAtFixedRate(this, PERIOD);
    }

    public void stop() {
        job.cancel(true);
    }

    @Override
    public void run() {
        counter.incrementAndGet();
    }
}

common.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

    <task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
    <task:executor id="myExecutor" pool-size="5"/>
    <task:scheduler id="myScheduler" pool-size="5"/>
    <bean id="jobRunner" class="com.stackoverflow.question53780890.JobRunner"/>
    <bean id="ignite" class="org.apache.ignite.IgniteSpringBean">
        <property name="configuration" ref="igniteConfiguration"/>
    </bean>
    <bean id="igniteConfiguration" class="org.apache.ignite.configuration.IgniteConfiguration">
        <property name="igniteInstanceName" value="${igniteInstanceName}"/>
         <property name="discoverySpi">
            <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
                <property name="ipFinder">
                    <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder"
                          p:addresses-ref="igniteNodesAddresses"/>
                </property>
                <property name="joinTimeout" value="20000"/>
                <property name="localPort" value="${igniteLocalPort}"/>
                <property name="localPortRange" value="1"/>

            </bean>
        </property>
        <property name="communicationSpi">
            <bean class="org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi">
                <property name="localPort" value="${communicationSpiPort}"/>
            </bean>
        </property>
    </bean>
    <bean id="igniteNodesAddresses" class="org.springframework.util.StringUtils"
          factory-method="commaDelimitedListToStringArray">
        <constructor-arg type="java.lang.String" value="${igniteNodes}"/>
    </bean>

</beans>

one.properties:

igniteLocalPort=47501
communicationSpiPort=47101
igniteNodes=localhost:47501,localhost:47502
igniteInstanceName=one

two.properties:

igniteLocalPort=47502
communicationSpiPort=47102
igniteNodes=localhost:47501,localhost:47502
igniteInstanceName=two

1 个答案:

答案 0 :(得分:3)

我认为您可以部署一个singleton service,它将在其long中进行scheduleLocal并在其execute()方法中进行调度。这样,它将故障转移到下一个节点,并在当前节点离开时正确地重新安排您的任务。