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:
01 | public class MyUpgradeTest { |
03 | public void shouldUpgradeFromOneVersionToAnother() throws InterruptedException |
05 | ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); |
06 | scheduledExecutorService.scheduleAtFixedRate( new LogAllTheThings(), 0 , 1 , TimeUnit.SECONDS ); |
10 | scheduledExecutorService.shutdown(); |
13 | static class LogAllTheThings implements Runnable |
18 | Date time = new Date( System.currentTimeMillis() ); |
22 | Map<String, Object> masterProperties = selectedProperties( client(), URI.create( "http://localhost:7474/" ) ); |
23 | System.out.println( String.format( "%s: %s" , time, masterProperties ) ); |
25 | catch ( Exception ignored ) |
27 | ignored.printStackTrace(); |
31 | private static Client client() |
33 | DefaultClientConfig defaultClientConfig = new DefaultClientConfig(); |
34 | defaultClientConfig.getClasses().add( JacksonJsonProvider. class ); |
35 | return Client.create( defaultClientConfig ); |
38 | public static Map<String, Object> selectedProperties( Client client, URI uri ) |
40 | Map<String, Object> jmxProperties = new HashMap<String, Object>(); |
42 | ArrayNode transactionsProperties = jmxBean( client, uri, "org.neo4j/instance%3Dkernel%230%2Cname%3DTransactions" ); |
43 | addProperty( jmxProperties, transactionsProperties, "LastCommittedTxId" ); |
45 | ArrayNode kernelProperties = jmxBean( client, uri, "org.neo4j/instance%3Dkernel%230%2Cname%3DKernel" ); |
46 | addProperty( jmxProperties, kernelProperties, "KernelVersion" ); |
48 | ArrayNode haProperties = jmxBean( client, uri, "org.neo4j/instance%3Dkernel%230%2Cname%3DHigh+Availability" ); |
49 | addProperty( jmxProperties, haProperties, "Role" ); |
50 | addProperty( jmxProperties, haProperties, "InstanceId" ); |
55 | private static void addProperty( Map<String, Object> jmxProperties, ArrayNode properties, String propertyName ) |
57 | jmxProperties.put( propertyName, getProperty( properties, propertyName ) ); |
60 | private static String getProperty( ArrayNode properties, String propertyName ) |
62 | for ( JsonNode property : properties ) |
64 | if ( property.get( "name" ).asText().equals( propertyName ) ) |
66 | return property.get( "value" ).asText(); |
70 | throw new RuntimeException( "Could not find requested property: " + propertyName ); |
73 | private static ArrayNode jmxBean( Client client, URI uri, String beanExtension ) |
75 | ClientResponse clientResponse = client |
76 | .resource( uri + "db/manage/server/jmx/domain/" + beanExtension ) |
77 | .accept( MediaType.APPLICATION_JSON ) |
78 | .get( ClientResponse. class ); |
80 | JsonNode transactionsBean = clientResponse.getEntity( JsonNode. class ); |
81 | return (ArrayNode) transactionsBean.get( 0 ).get( "attributes" ); |
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:
1 | Sun Nov 17 22:31:55 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master} |
2 | Sun Nov 17 22:31:56 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master} |
3 | Sun Nov 17 22:31:57 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master} |
4 | Sun Nov 17 22:31:58 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master} |
5 | Sun Nov 17 22:31:59 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master} |
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:
01 | static class LogAllTheThings implements Runnable |
03 | private ExecutorService executorService = Executors.newFixedThreadPool( 3 ); |
08 | List<URI> machines = new ArrayList<>( ); |
13 | Map<URI, Future<Map<String, Object>>> futureJmxProperties = new HashMap<>( ); |
14 | for ( final URI machine : machines ) |
16 | Future<Map<String, Object>> futureProperties = executorService.submit( new Callable<Map<String, Object>>() |
19 | public Map<String, Object> call() throws Exception |
23 | return selectedProperties( client(), machine ); |
25 | catch ( Exception ignored ) |
27 | ignored.printStackTrace(); |
28 | return new HashMap<>(); |
33 | futureJmxProperties.put( machine, futureProperties ); |
36 | Date time = new Date( System.currentTimeMillis() ); |
37 | System.out.println( time ); |
38 | for ( Map.Entry<URI, Future<Map<String, Object>>> uriFutureEntry : futureJmxProperties.entrySet() ) |
42 | System.out.println( "==> " + uriFutureEntry.getValue().get() ); |
44 | catch ( Exception ignored ) |
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:
01 | Sun 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} |
05 | Sun 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} |
09 | Sun 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} |
Overall the approach works quite well although I’m always open to learning of a better way if there is one!
출처: 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