Недавно я потратил некоторое время на создание набора тестов для обновления обновлений между версиями Neo4j, и как часть этого я хотел записать состояние кластера в процессе обновления.
Основной поток тестовых блоков ожидает завершения обновления, поэтому я хотел входить в другой поток каждые несколько секунд. Алистер указал мне на ScheduledExecutorService, который работал довольно хорошо.
Я закончил тестом, который выглядел примерно так:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
public class MyUpgradeTest { @Test public void shouldUpgradeFromOneVersionToAnother() throws InterruptedException { ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); scheduledExecutorService.scheduleAtFixedRate( new LogAllTheThings(), 0 , 1 , TimeUnit.SECONDS ); Thread.sleep( 10000 ); // do upgrade of cluster scheduledExecutorService.shutdown(); } static class LogAllTheThings implements Runnable { @Override public void run() { Date time = new Date( System.currentTimeMillis() ); try { Map<String, Object> masterProperties = selectedProperties( client(), URI.create( "http://localhost:7474/" ) ); System.out.println( String.format( "%s: %s" , time, masterProperties ) ); } catch ( Exception ignored ) { ignored.printStackTrace(); } } private static Client client() { DefaultClientConfig defaultClientConfig = new DefaultClientConfig(); defaultClientConfig.getClasses().add( JacksonJsonProvider. class ); return Client.create( defaultClientConfig ); } public static Map<String, Object> selectedProperties( Client client, URI uri ) { Map<String, Object> jmxProperties = new HashMap<String, Object>(); ArrayNode transactionsProperties = jmxBean( client, uri, "org.neo4j/instance%3Dkernel%230%2Cname%3DTransactions" ); addProperty( jmxProperties, transactionsProperties, "LastCommittedTxId" ); ArrayNode kernelProperties = jmxBean( client, uri, "org.neo4j/instance%3Dkernel%230%2Cname%3DKernel" ); addProperty( jmxProperties, kernelProperties, "KernelVersion" ); ArrayNode haProperties = jmxBean( client, uri, "org.neo4j/instance%3Dkernel%230%2Cname%3DHigh+Availability" ); addProperty( jmxProperties, haProperties, "Role" ); addProperty( jmxProperties, haProperties, "InstanceId" ); return jmxProperties; } private static void addProperty( Map<String, Object> jmxProperties, ArrayNode properties, String propertyName ) { jmxProperties.put( propertyName, getProperty( properties, propertyName ) ); } private static String getProperty( ArrayNode properties, String propertyName ) { for ( JsonNode property : properties ) { if ( property.get( "name" ).asText().equals( propertyName ) ) { return property.get( "value" ).asText(); } } throw new RuntimeException( "Could not find requested property: " + propertyName ); } private static ArrayNode jmxBean( Client client, URI uri, String beanExtension ) { ClientResponse clientResponse = client .resource( uri + "db/manage/server/jmx/domain/" + beanExtension ) .accept( MediaType.APPLICATION_JSON ) .get( ClientResponse. class ); JsonNode transactionsBean = clientResponse.getEntity( JsonNode. class ); return (ArrayNode) transactionsBean.get( 0 ).get( "attributes" ); } } } |
LogAllTheThings вызывается раз в секунду и регистрирует KernelVersion, InstanceId, LastCommittedTxId и Role, которые сервер Neo4j предоставляет в качестве свойств JMX.
Если мы запустим это с локальным кластером Neo4j, мы увидим что-то вроде следующего:
1
2
3
4
5
6
7
|
Sun Nov 17 22:31:55 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master} Sun Nov 17 22:31:56 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master} Sun Nov 17 22:31:57 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master} Sun Nov 17 22:31:58 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master} Sun Nov 17 22:31:59 GMT 2013: {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master} ... removed for brevity |
Следующим шагом было получить свойства всех членов кластера одновременно и сделать это, мы можем представить еще один ExecutorService, который имеет пул потоков, равный 3, чтобы он мог оценивать каждую машину (по крайней мере, близко) одновременно:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
static class LogAllTheThings implements Runnable { private ExecutorService executorService = Executors.newFixedThreadPool( 3 ); @Override public void run() { List<URI> machines = new ArrayList<>( ); Map<URI, Future<Map<String, Object>>> futureJmxProperties = new HashMap<>( ); for ( final URI machine : machines ) { Future<Map<String, Object>> futureProperties = executorService.submit( new Callable<Map<String, Object>>() { @Override public Map<String, Object> call() throws Exception { try { return selectedProperties( client(), machine ); } catch ( Exception ignored ) { ignored.printStackTrace(); return new HashMap<>(); } } } ); futureJmxProperties.put( machine, futureProperties ); } Date time = new Date( System.currentTimeMillis() ); System.out.println( time ); for ( Map.Entry<URI, Future<Map<String, Object>>> uriFutureEntry : futureJmxProperties.entrySet() ) { try { System.out.println( "==> " + uriFutureEntry.getValue().get() ); } catch ( Exception ignored ) { } } } // other methods the same as above } |
Мы отправляем каждую работу в ExecutorService и получаем обратно Будущее, которое мы сохраняем на карте, прежде чем получить его результат позже. Если мы запустим это, мы увидим следующий вывод:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
Sun Nov 17 22:49:58 GMT 2013 ==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master} ==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=2, LastCommittedTxId=18, Role=slave} ==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=3, LastCommittedTxId=18, Role=slave} Sun Nov 17 22:49:59 GMT 2013 ==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master} ==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=2, LastCommittedTxId=18, Role=slave} ==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=3, LastCommittedTxId=18, Role=slave} Sun Nov 17 22:50:00 GMT 2013 ==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=1, LastCommittedTxId=18, Role=master} ==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=2, LastCommittedTxId=18, Role=slave} ==> {KernelVersion=Neo4j - Graph Database Kernel 2.0.0-M06, InstanceId=3, LastCommittedTxId=18, Role=slave} ... removed for brevity |
В целом, подход работает довольно хорошо, хотя я всегда открыт для изучения лучшего пути, если он есть!