View Javadoc

1   /*
2    * $Id: ReuseExperimentMule2067TestCase.java 22387 2011-07-12 03:53:36Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.transport.tcp.issues;
12  
13  import org.mule.tck.junit4.AbstractMuleTestCase;
14  
15  import java.io.IOException;
16  import java.net.BindException;
17  import java.net.InetSocketAddress;
18  import java.net.ServerSocket;
19  import java.net.Socket;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.junit.Test;
24  
25  /**
26   * Can we avoid the "address already in use" errors by using SO_REUSEADDR?
27   *
28   * Typical results are
29  <pre>
30   [07-24 19:32:49] INFO  ReuseExperimentMule2067TestCase [main]: Measuring average run length for 100 repeats without reuse and a pause of 100 ms
31   [07-24 19:33:49] INFO  ReuseExperimentMule2067TestCase [main]: Average run length: 57.3 +/- 33.15131973240282
32   [07-24 19:33:49] INFO  ReuseExperimentMule2067TestCase [main]: Measuring average run length for 100 repeats with reuse and a pause of 100 ms
33   [07-24 19:35:32] INFO  ReuseExperimentMule2067TestCase [main]: Average run length: 100.0 +/- 0.0
34   [07-24 19:35:32] INFO  ReuseExperimentMule2067TestCase [main]: Measuring average run length for 100 repeats without reuse and a pause of 10 ms
35   [07-24 19:35:48] INFO  ReuseExperimentMule2067TestCase [main]: Average run length: 96.8 +/- 7.332121111929359
36   [07-24 19:35:48] INFO  ReuseExperimentMule2067TestCase [main]: Measuring average run length for 100 repeats with reuse and a pause of 10 ms
37   [07-24 19:36:04] INFO  ReuseExperimentMule2067TestCase [main]: Average run length: 100.0 +/- 0.0
38   [07-24 19:36:04] INFO  ReuseExperimentMule2067TestCase [main]: Measuring average run length for 100 repeats without reuse and a pause of 1 ms
39   [07-24 19:36:10] INFO  ReuseExperimentMule2067TestCase [main]: Average run length: 75.8 +/- 37.690317058894586
40   [07-24 19:36:10] INFO  ReuseExperimentMule2067TestCase [main]: Measuring average run length for 100 repeats with reuse and a pause of 1 ms
41   [07-24 19:36:18] INFO  ReuseExperimentMule2067TestCase [main]: Average run length: 100.0 +/- 0.0
42  </pre>
43   * which suggest that enabling address re-use could help with the issue.
44   *
45   * Note that if a single socket (ie a single port number) is reused for all tests we often
46   * zeroes eveywhere (even with waits of 2sec and similar between iterations/tests).  This
47   * suggests that once the error occurs, the socket enters a long-lived "broken" state.
48   *
49   * All this is by AC on linux, dual CPU, Java 1.4 - I suspect results will vary like crazy
50   * in different contexts.
51   */
52  public class ReuseExperimentMule2067TestCase extends AbstractMuleTestCase
53  {
54  
55      private static final int NO_WAIT = -1;
56      private static final int PORT = 65432;
57      private static boolean NO_REUSE = false;
58      private static boolean REUSE = true;
59  
60      private Log logger = LogFactory.getLog(getClass());
61  
62      @Test
63      public void testReuse() throws IOException
64      {
65          repeatOpenCloseClientServer(1000, 10, PORT, 1, REUSE, false); // fails, but less often?
66          repeatOpenCloseClientServer(100, 10, PORT, 1, NO_REUSE, false); // intermittent
67      }
68  
69      @Test
70      public void testMeasureImprovement() throws IOException
71      {
72          measureMeanRunLength(10, 100, 10, PORT, 100, NO_REUSE);
73          measureMeanRunLength(10, 100, 10, PORT+10, 100, REUSE);
74          measureMeanRunLength(10, 100, 10, PORT+20, 10, NO_REUSE);
75          measureMeanRunLength(10, 100, 10, PORT+30, 10, REUSE);
76          measureMeanRunLength(10, 100, 10, PORT+40, 1, NO_REUSE);
77          measureMeanRunLength(10, 100, 10, PORT+50, 1, REUSE);
78      }
79  
80      protected void measureMeanRunLength(int sampleSize, int numberOfRepeats, int numberOfConnections,
81                                          int port, long pause,  boolean reuse)
82              throws IOException
83      {
84          logger.info("Measuring average run length for " + numberOfRepeats + " repeats " +
85                  (reuse ? "with" : "without") + " reuse and a pause of " + pause + " ms");
86          int totalLength = 0;
87          long totalLengthSquared = 0;
88          for (int i = 0; i < sampleSize; ++i)
89          {
90              int length = repeatOpenCloseClientServer(numberOfRepeats, numberOfConnections, port+i, pause, reuse, true);
91              totalLength += length;
92              totalLengthSquared += length * length;
93          }
94          double mean = totalLength / (double) sampleSize;
95          double sd = Math.sqrt(totalLengthSquared / (double) sampleSize - mean * mean);
96          logger.info("Average run length: " + mean + " +/- " + sd);
97      }
98  
99      protected int repeatOpenCloseClientServer(int numberOfRepeats, int numberOfConnections, int port,
100                                               long pause, boolean reuse, boolean noFail)
101             throws IOException
102     {
103         String message = "Repeating openCloseClientServer with pauses of " + pause + " ms "
104                     + (reuse ? "with" : "without") + " reuse";
105         if (noFail)
106         {
107             logger.debug(message);
108         }
109         else
110         {
111             logger.info(message);
112         }
113         for (int i = 0; i < numberOfRepeats; i++)
114         {
115             if (0 != i)
116             {
117                 pause(pause);
118             }
119             try
120             {
121                 openCloseClientServer(numberOfConnections, port, reuse);
122             }
123             catch (BindException e)
124             {
125                 if (noFail && e.getMessage().indexOf("Address already in use") > -1)
126                 {
127                     return i;
128                 }
129                 throw e;
130             }
131         }
132         return numberOfRepeats;
133     }
134 
135     protected void openCloseClientServer(int numberOfConnections, int port, boolean reuse)
136             throws IOException
137     {
138         Server server = new Server(port, reuse);
139         try {
140             new Thread(server).start();
141             for (int i = 0; i < numberOfConnections; i++)
142             {
143                 logger.debug("opening socket " + i);
144                 Socket client = new Socket("localhost", port);
145                 client.close();
146             }
147         }
148         finally
149         {
150             server.close();
151         }
152     }
153 
154     protected void pause(long pause)
155     {
156         if (pause != NO_WAIT)
157         {
158             try
159             {
160                 synchronized(this)
161                 {
162                     if (pause > 0)
163                     {
164                         this.wait(pause);
165                     }
166                 }
167             }
168             catch (InterruptedException e)
169             {
170                 // ignore
171             }
172         }
173     }
174 
175     protected static class Server implements Runnable
176     {
177 
178         private Log logger = LogFactory.getLog(getClass());
179         private ServerSocket server;
180 
181         public Server(int port, boolean reuse) throws IOException
182         {
183             server = new ServerSocket();
184             server.setReuseAddress(reuse);
185             server.bind(new InetSocketAddress("localhost", port));
186         }
187 
188         public void run()
189         {
190             try
191             {
192                 while (true)
193                 {
194                     Socket socket = server.accept();
195                     socket.close();
196                 }
197             }
198             catch (Exception e)
199             {
200                 logger.debug("Expected - dirty closedown: " + e);
201             }
202         }
203 
204         public void close() throws IOException
205         {
206             server.close();
207             server = null;
208         }
209     }
210 
211 }