Hello World

Java: Schedule a job to run on a time interval 본문

카테고리 없음

Java: Schedule a job to run on a time interval

EnterKey 2016. 1. 27. 10:28
반응형

Recently I’ve spent some time building a set of tests around rolling upgrades between Neo4j versions and as part of that I wanted to log the state of the cluster as the upgrade was happening.

The main thread of the test blocks waiting until the upgrade is done so I wanted to log on another thread every few seconds. Alistair pointed me at the ScheduledExecutorService which worked quite nicely.

I ended up with a test which looked roughly like this:
 
 

01public class MyUpgradeTest {
02    @Test
03    public void shouldUpgradeFromOneVersionToAnother() throws InterruptedException
04    {
05        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
06        scheduledExecutorService.scheduleAtFixedRate( new LogAllTheThings(), 01, TimeUnit.SECONDS );
07 
08        Thread.sleep(10000);
09        // do upgrade of cluster
10        scheduledExecutorService.shutdown();
11    }
12 
13    static class LogAllTheThings implements Runnable
14    {
15        @Override
16        public void run()
17        {
18            Date time = new Date( System.currentTimeMillis() );
19 
20            try
21            {
22                Map<String, Object> masterProperties = selectedProperties( client(), URI.create( "http://localhost:7474/" ) );
23                System.out.println( String.format( "%s: %s", time, masterProperties ) );
24            }
25            catch ( Exception ignored )
26            {
27                ignored.printStackTrace();
28            }
29        }
30 
31        private static Client client()
32        {
33            DefaultClientConfig defaultClientConfig = new DefaultClientConfig();
34            defaultClientConfig.getClasses().add( JacksonJsonProvider.class );
35            return Client.create( defaultClientConfig );
36        }
37 
38        public static Map<String, Object> selectedProperties( Client client, URI uri )
39        {
40            Map<String, Object> jmxProperties = new HashMap<String, Object>();
41 
42            ArrayNode transactionsProperties = jmxBean( client, uri,"org.neo4j/instance%3Dkernel%230%2Cname%3DTransactions" );
43            addProperty( jmxProperties, transactionsProperties, "LastCommittedTxId" );
44 
45            ArrayNode kernelProperties = jmxBean( client, uri,"org.neo4j/instance%3Dkernel%230%2Cname%3DKernel" );
46            addProperty( jmxProperties, kernelProperties, "KernelVersion" );
47 
48            ArrayNode haProperties = jmxBean( client, uri,"org.neo4j/instance%3Dkernel%230%2Cname%3DHigh+Availability" );
49            addProperty( jmxProperties, haProperties, "Role" );
50            addProperty( jmxProperties, haProperties, "InstanceId" );
51 
52            return jmxProperties;
53        }
54 
55        private static void addProperty( Map<String, Object> jmxProperties, ArrayNode properties, String propertyName )
56        {
57            jmxProperties.put( propertyName, getProperty( properties, propertyName ) );
58        }
59 
60        private static String getProperty( ArrayNode properties, String propertyName )
61        {
62            for ( JsonNode property : properties )
63            {
64                if ( property.get( "name" ).asText().equals( propertyName ) )
65                {
66                    return property.get( "value" ).asText();
67                }
68            }
69 
70            throw new RuntimeException( "Could not find requested property: " + propertyName );
71        }
72 
73        private static ArrayNode jmxBean( Client client, URI uri, String beanExtension )
74        {
75            ClientResponse clientResponse = client
76                    .resource( uri + "db/manage/server/jmx/domain/" + beanExtension )
77                    .accept( MediaType.APPLICATION_JSON )
78                    .get( ClientResponse.class );
79 
80            JsonNode transactionsBean = clientResponse.getEntity( JsonNode.class );
81            return (ArrayNode) transactionsBean.get( 0 ).get( "attributes" );
82        }
83    }
84}

LogAllTheThings gets called once every second and it logs the KernelVersion, InstanceId, LastCommittedTxId and Role which Neo4j server exposes as JMX properties.

If we run that against a local Neo4j cluster we’d see something like the following:

1Sun Nov 17 22:31:55 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master}
2Sun Nov 17 22:31:56 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master}
3Sun Nov 17 22:31:57 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master}
4Sun Nov 17 22:31:58 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master}
5Sun Nov 17 22:31:59 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master}
6...
7removed for brevity

The next step was to get the properties of all the members of the cluster at the same time and to do that we can introduce another ExecutorService which has a thread pool of 3 so that it will evaluate each machine (at least close to) simultaneously:

01static class LogAllTheThings implements Runnable
02    {
03        private ExecutorService executorService = Executors.newFixedThreadPool( 3 );
04 
05        @Override
06        public void run()
07        {
08            List<URI> machines = new ArrayList<>(  );
09            machines.add(URI.create( "http://localhost:7474/" ));
10            machines.add(URI.create( "http://localhost:7484/" ));
11            machines.add(URI.create( "http://localhost:7494/" ));
12 
13            Map<URI, Future<Map<String, Object>>> futureJmxProperties = new HashMap<>(  );
14            for final URI machine : machines )
15            {
16                Future<Map<String, Object>> futureProperties = executorService.submit( newCallable<Map<String, Object>>()
17                {
18                    @Override
19                    public Map<String, Object> call() throws Exception
20                    {
21                        try
22                        {
23                            return selectedProperties( client(), machine );
24                        }
25                        catch ( Exception ignored )
26                        {
27                            ignored.printStackTrace();
28                            return new HashMap<>();
29                        }
30                    }
31                } );
32 
33                futureJmxProperties.put( machine, futureProperties );
34            }
35 
36            Date time = new Date( System.currentTimeMillis() );
37            System.out.println( time );
38            for ( Map.Entry<URI, Future<Map<String, Object>>> uriFutureEntry : futureJmxProperties.entrySet() )
39            {
40                try
41                {
42                    System.out.println( "==> " + uriFutureEntry.getValue().get() );
43                }
44                catch ( Exception ignored )
45                {
46 
47                }
48            }
49        }
50 
51        // other methods the same as above
52    }

We submit each job to the ExecutorService and receive back a Future which we store in a map before retrieving its result later on. If we run that we’ll see the following output:

01Sun Nov 17 22:49:58 GMT 2013
02==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master}
03==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=2, LastCommittedTxId=18, Role=slave}
04==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=3, LastCommittedTxId=18, Role=slave}
05Sun Nov 17 22:49:59 GMT 2013
06==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master}
07==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=2, LastCommittedTxId=18, Role=slave}
08==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=3, LastCommittedTxId=18, Role=slave}
09Sun Nov 17 22:50:00 GMT 2013
10==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master}
11==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=2, LastCommittedTxId=18, Role=slave}
12==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=3, LastCommittedTxId=18, Role=slave}
13 
14...
15removed for brevity

Overall the approach works quite well although I’m always open to learning of a better way if there is one!
 

Reference: Java: Schedule a job to run on a time interval from our JCG partner Mark Needham at the Mark Needham Blogblog.


출처: http://www.javacodegeeks.com/2013/11/java-schedule-a-job-to-run-on-a-time-interval.html?utm_content=buffer7724f&utm_medium=social&utm_source=facebook.com&utm_campaign=buffer

반응형
Comments