2222import java .net .URISyntaxException ;
2323import java .security .KeyManagementException ;
2424import java .security .NoSuchAlgorithmException ;
25+ import java .util .ArrayList ;
26+ import java .util .Collection ;
2527import java .util .List ;
2628import java .util .Random ;
27- import java .util .UUID ;
29+ import java .util .concurrent .ExecutionException ;
30+ import java .util .concurrent .ExecutorService ;
31+ import java .util .concurrent .Executors ;
32+ import java .util .concurrent .Future ;
33+ import java .util .concurrent .ScheduledExecutorService ;
34+ import java .util .concurrent .SynchronousQueue ;
35+ import java .util .concurrent .ThreadPoolExecutor ;
36+ import java .util .concurrent .TimeUnit ;
2837import java .util .concurrent .TimeoutException ;
38+ import java .util .function .Supplier ;
39+
40+ import static java .lang .Math .min ;
41+ import static java .lang .String .format ;
2942
3043public class MulticastSet {
3144
@@ -37,7 +50,7 @@ public class MulticastSet {
3750
3851 private final Random random = new Random ();
3952
40- private ThreadHandler threadHandler = new DefaultThreadHandler ();
53+ private ThreadingHandler threadingHandler = new DefaultThreadingHandler ();
4154
4255 public MulticastSet (Stats stats , ConnectionFactory factory ,
4356 MulticastParams params , List <String > uris ) {
@@ -55,73 +68,95 @@ public MulticastSet(Stats stats, ConnectionFactory factory,
5568 this .params .init ();
5669 }
5770
58- public void run () throws IOException , InterruptedException , TimeoutException , NoSuchAlgorithmException , KeyManagementException , URISyntaxException {
71+ public void run ()
72+ throws IOException , InterruptedException , TimeoutException , NoSuchAlgorithmException , KeyManagementException , URISyntaxException , ExecutionException {
5973 run (false );
6074 }
6175
6276 public void run (boolean announceStartup )
63- throws IOException , InterruptedException , TimeoutException , NoSuchAlgorithmException , KeyManagementException , URISyntaxException {
77+ throws IOException , InterruptedException , TimeoutException , NoSuchAlgorithmException , KeyManagementException , URISyntaxException , ExecutionException {
78+
79+ ScheduledExecutorService heartbeatSenderExecutorService = this .threadingHandler .scheduledExecutorService (
80+ "perf-test-heartbeat-sender-" ,
81+ this .params .getHeartbeatSenderThreads ()
82+ );
83+ factory .setHeartbeatExecutor (heartbeatSenderExecutorService );
6484
6585 setUri ();
66- Connection conn = factory .newConnection ();
86+ Connection conn = factory .newConnection ("perf-test-configuration" );
6787 params .configureAllQueues (conn );
6888 conn .close ();
6989
7090 this .params .resetTopologyHandler ();
7191
72- Thread [] consumerThreads = new Thread [params .getConsumerThreadCount ()];
92+ Runnable [] consumerRunnables = new Runnable [params .getConsumerThreadCount ()];
7393 Connection [] consumerConnections = new Connection [params .getConsumerCount ()];
7494 for (int i = 0 ; i < consumerConnections .length ; i ++) {
7595 if (announceStartup ) {
7696 System .out .println ("id: " + testID + ", starting consumer #" + i );
7797 }
7898 setUri ();
79- conn = factory .newConnection ();
99+ ExecutorService executorService = this .threadingHandler .executorService (
100+ format ("perf-test-consumer-%s-worker-" , i ),
101+ nbThreadsForConsumer (this .params )
102+ );
103+ factory .setSharedExecutor (executorService );
104+
105+ conn = factory .newConnection ("perf-test-consumer-" + i );
80106 consumerConnections [i ] = conn ;
81107 for (int j = 0 ; j < params .getConsumerChannelCount (); j ++) {
82108 if (announceStartup ) {
83109 System .out .println ("id: " + testID + ", starting consumer #" + i + ", channel #" + j );
84110 }
85- Thread t = new Thread (params .createConsumer (conn , stats ));
86- consumerThreads [(i * params .getConsumerChannelCount ()) + j ] = t ;
111+ consumerRunnables [(i * params .getConsumerChannelCount ()) + j ] = params .createConsumer (conn , stats );
87112 }
88113 }
89114
90115 this .params .resetTopologyHandler ();
91116
92- Thread [] producerThreads = new Thread [params .getProducerThreadCount ()];
117+ AgentState [] producerStates = new AgentState [params .getProducerThreadCount ()];
93118 Connection [] producerConnections = new Connection [params .getProducerCount ()];
119+ // producers don't need an executor service, as they don't have any consumers
120+ // this consumer should never be asked to create any threads
121+ ExecutorService executorServiceForProducersConsumers = this .threadingHandler .executorService (
122+ "perf-test-producers-worker-" , 0
123+ );
124+ factory .setSharedExecutor (executorServiceForProducersConsumers );
94125 for (int i = 0 ; i < producerConnections .length ; i ++) {
95126 if (announceStartup ) {
96127 System .out .println ("id: " + testID + ", starting producer #" + i );
97128 }
98129 setUri ();
99- conn = factory .newConnection ();
130+ conn = factory .newConnection ("perf-test-producer-i" );
100131 producerConnections [i ] = conn ;
101132 for (int j = 0 ; j < params .getProducerChannelCount (); j ++) {
102133 if (announceStartup ) {
103134 System .out .println ("id: " + testID + ", starting producer #" + i + ", channel #" + j );
104135 }
105- Thread t = new Thread (params .createProducer (conn , stats ));
106- producerThreads [(i * params .getProducerChannelCount ()) + j ] = t ;
136+ AgentState agentState = new AgentState ();
137+ agentState .runnable = params .createProducer (conn , stats );
138+ producerStates [(i * params .getProducerChannelCount ()) + j ] = agentState ;
107139 }
108140 }
109141
110- for (Thread consumerThread : consumerThreads ) {
111- this . threadHandler . start ( consumerThread );
142+ for (Runnable runnable : consumerRunnables ) {
143+ runnable . run ( );
112144 if (params .getConsumerSlowStart ()) {
113- System .out .println ("Delaying start by 1 second because -S/--slow-start was requested" );
114- Thread .sleep (1000 );
145+ System .out .println ("Delaying start by 1 second because -S/--slow-start was requested" );
146+ Thread .sleep (1000 );
115147 }
116148 }
117149
118- for (Thread producerThread : producerThreads ) {
119- this .threadHandler .start (producerThread );
150+ ExecutorService producersExecutorService = this .threadingHandler .executorService (
151+ "perf-test-producer-" , producerStates .length
152+ );
153+ for (AgentState producerState : producerStates ) {
154+ producerState .task = producersExecutorService .submit (producerState .runnable );
120155 }
121156
122157 int count = 1 ; // counting the threads
123- for (int i = 0 ; i < producerThreads .length ; i ++) {
124- this . threadHandler . waitForCompletion ( producerThreads [i ]);
158+ for (int i = 0 ; i < producerStates .length ; i ++) {
159+ producerStates [i ]. task . get ( );
125160 if (count % params .getProducerChannelCount () == 0 ) {
126161 // this is the end of a group of threads on the same connection,
127162 // closing the connection
@@ -130,20 +165,21 @@ public void run(boolean announceStartup)
130165 count ++;
131166 }
132167
133- count = 1 ; // counting the threads
134- for (int i = 0 ; i < consumerThreads .length ; i ++) {
135- this .threadHandler .waitForCompletion (consumerThreads [i ]);
136- if (count % params .getConsumerChannelCount () == 0 ) {
137- // this is the end of a group of threads on the same connection,
138- // closing the connection
139- consumerConnections [count / params .getConsumerChannelCount () - 1 ].close ();
140- }
141- count ++;
168+ for (Connection consumerConnection : consumerConnections ) {
169+ consumerConnection .close ();
142170 }
171+
172+ this .threadingHandler .shutdown ();
143173 }
144174
145- public void setThreadHandler (ThreadHandler threadHandler ) {
146- this .threadHandler = threadHandler ;
175+ // from Java Client ConsumerWorkService
176+ public final static int DEFAULT_CONSUMER_WORK_SERVICE_THREAD_POOL_SIZE = Runtime .getRuntime ().availableProcessors () * 2 ;
177+
178+ protected static int nbThreadsForConsumer (MulticastParams params ) {
179+ // for backward compatibility, the thread pool should large enough to dedicate
180+ // one thread for each channel when channel number is <= DEFAULT_CONSUMER_WORK_SERVICE_THREAD_POOL_SIZE
181+ // Above this number, we stick to DEFAULT_CONSUMER_WORK_SERVICE_THREAD_POOL_SIZE
182+ return min (params .getConsumerChannelCount (), DEFAULT_CONSUMER_WORK_SERVICE_THREAD_POOL_SIZE );
147183 }
148184
149185 private void setUri () throws URISyntaxException , NoSuchAlgorithmException , KeyManagementException {
@@ -157,29 +193,61 @@ private String uri() {
157193 return uri ;
158194 }
159195
196+ public void setThreadingHandler (ThreadingHandler threadingHandler ) {
197+ this .threadingHandler = threadingHandler ;
198+ }
199+
160200 /**
161201 * Abstraction for thread management.
162202 * Exists to ease testing.
163203 */
164- interface ThreadHandler {
204+ interface ThreadingHandler {
205+
206+ ExecutorService executorService (String name , int nbThreads );
165207
166- void start ( Thread thread );
208+ ScheduledExecutorService scheduledExecutorService ( String name , int nbThreads );
167209
168- void waitForCompletion ( Thread thread ) throws InterruptedException ;
210+ void shutdown () ;
169211
170212 }
171213
172- static class DefaultThreadHandler implements ThreadHandler {
214+ static class DefaultThreadingHandler implements ThreadingHandler {
215+
216+ private final Collection <ExecutorService > executorServices = new ArrayList <>();
217+
218+ @ Override
219+ public ExecutorService executorService (String name , int nbThreads ) {
220+ if (nbThreads <= 0 ) {
221+ return create (() -> Executors .newSingleThreadExecutor (new NamedThreadFactory (name )));
222+ } else {
223+ return create (() -> Executors .newFixedThreadPool (nbThreads , new NamedThreadFactory (name )));
224+ }
225+ }
173226
174227 @ Override
175- public void start (Thread thread ) {
176- thread .start ();
228+ public ScheduledExecutorService scheduledExecutorService (String name , int nbThreads ) {
229+ return (ScheduledExecutorService ) create (() -> Executors .newScheduledThreadPool (nbThreads , new NamedThreadFactory (name )));
230+ }
231+
232+ private ExecutorService create (Supplier <ExecutorService > s ) {
233+ ExecutorService executorService = s .get ();
234+ this .executorServices .add (executorService );
235+ return executorService ;
177236 }
178237
179238 @ Override
180- public void waitForCompletion (Thread thread ) throws InterruptedException {
181- thread .join ();
239+ public void shutdown () {
240+ for (ExecutorService executorService : executorServices ) {
241+ executorService .shutdown ();
242+ }
182243 }
244+ }
245+
246+ private static class AgentState {
247+
248+ private Runnable runnable ;
249+
250+ private Future <?> task ;
183251
184252 }
185253
0 commit comments