View Javadoc

1   package org.paneris.bibliomania.loadtest;
2   
3   import java.io.BufferedReader;
4   import java.io.FileInputStream;
5   import java.io.FileReader;
6   import java.io.InputStream;
7   import java.io.ObjectInputStream;
8   import java.net.URL;
9   import java.util.Enumeration;
10  import java.util.Random;
11  import java.util.Vector;
12  
13  import org.melati.util.UnexpectedExceptionException;
14  
15  class HitStats {
16    public long totalToOpen, totalToFinish;
17    public int successes;
18    public int failures;
19    // public int errors;
20  
21    public void notifyTransaction(long started, long opened, long finished
22                                  // , boolean error
23                                  ) {
24      totalToOpen += opened - started;
25      totalToFinish += finished - started;
26      // if (error) ++errors else
27      ++successes;
28    }
29  
30    public void notifyFailure() {
31      ++failures;
32    }
33  
34    public long meanToOpen() {
35      return successes == 0 ? 0 : totalToOpen / successes;
36    }
37  
38    public long meanToFinish() {
39      return successes == 0 ? 0 : totalToFinish / successes;
40    }
41  }
42  
43  class ExponentialDistribution {
44    private double minusMean;
45  
46    public ExponentialDistribution(double mean) {
47      minusMean = -mean;
48    }
49  
50    public double sample() {
51      for (;;) {
52        float x = LoadTest.random.nextFloat();
53        if (x != 0.)
54          return minusMean * Math.log(x);
55      }
56    }
57  }
58  
59  class BitBucket extends Thread {
60    private String url;
61    private HitStats stats;
62  
63    public BitBucket(String url, HitStats stats) {
64      this.url = url;
65      this.stats = stats;
66    }
67  
68    private static final byte[] b = new byte[4096];
69  
70    public void run() {
71      System.err.println(url);
72      try {
73        long started = System.currentTimeMillis();
74        InputStream i = new URL(url).openStream();
75        long opened = System.currentTimeMillis();
76        while (i.read(b) != -1);
77        long finished = System.currentTimeMillis();
78  
79        stats.notifyTransaction(started, opened, finished);
80      }
81      catch (Exception e) {
82        //System.err.println(e);
83        stats.notifyFailure();
84      }
85    }
86  }
87  
88  abstract class Level {
89    abstract String urlFromSection(BookDesc book);
90    String url(BookDesc book) {
91      return book.sectiongroup + "/" + urlFromSection(book);
92    }
93  }
94  
95  class SectionLevel extends Level {
96    String urlFromSection(BookDesc book) {
97      return "" + book.section;
98    }
99  
100   public String toString() {
101     return "sec";
102   }
103 }
104 
105 class AuthorLevel extends Level {
106   String urlFromSection(BookDesc book) {
107     return book.section + "/" + book.author;
108   }
109 
110   public String toString() {
111     return "aut";
112   }
113 }
114 
115 class BookLevel extends Level {
116   String urlFromSection(BookDesc book) {
117     return book.section + "/" + book.author + "/" + book.book;
118   }
119 
120   public String toString() {
121     return "bok";
122   }
123 }
124 
125 class ChapterLevel extends Level {
126   String urlFromSection(BookDesc book) {
127     ChapterDesc c =
128         book.chapters[LoadTest.random.nextInt(book.chapters.length)];
129     return book.section + "/" + book.author + "/" + book.book + "/" +
130            c.chapter + "/" + (LoadTest.random.nextInt(c.pages) + 1) + ".html";
131   }
132 
133   public String toString() {
134     return "cha";
135   }
136 }
137 
138 abstract class Urler {
139   abstract String urlPath(Level level, BookDesc book);
140 }
141 
142 class ReadUrler extends Urler {
143   String urlPath(Level level, BookDesc book) {
144     return level.url(book);
145   }
146 
147   public String toString() {
148     return "read";
149   }
150 }
151 
152 class SearchUrler extends Urler {
153 
154   static String[] words;
155 
156   static {
157     try {
158       Vector w = new Vector();
159       BufferedReader r = new BufferedReader(new FileReader("/usr/share/dict/words"));
160       for (;;) {
161         String s = r.readLine();
162         if (s == null)
163           break;
164         w.addElement(s);
165       }
166       words = new String[w.size()];
167       w.copyInto(words);
168     }
169     catch (Exception e) {
170       e.printStackTrace();
171       System.exit(1);
172     }
173   }
174 
175   static String randomWord() {
176     return words[LoadTest.random.nextInt(words.length)];
177   }
178 
179   int searchgroup;
180   int numwords;
181 
182   SearchUrler(int searchgroup, int numwords) {
183     this.searchgroup = searchgroup;
184     this.numwords = numwords;
185   }
186 
187   String urlPath(Level level, BookDesc book) {
188     StringBuffer qt = new StringBuffer();
189 
190     qt.append(randomWord());
191 
192     for (int i = 1; i < numwords; ++i) {
193       qt.append("+");
194       qt.append(randomWord());
195     }
196 
197     return
198         "b/org.paneris.bibliomania.Search/" + searchgroup + "/" +
199         level.urlFromSection(book) + "?" + "queryText=" + qt;
200   }
201 
202   public String toString() {
203     return "search(" + numwords + ")";
204   }
205 }
206 
207 class ReqType {
208   HitStats stats = new HitStats();
209   Urler urler;
210   Level level;
211 
212   ReqType(Urler urler, Level level) {
213     this.urler = urler;
214     this.level = level;
215   }
216 
217   void go(BookDesc book) {
218     new BitBucket(LoadTest.urlPrefix + urler.urlPath(level, book), stats).
219         start();
220   }
221 
222   public String toString() {
223     return urler + "/" + level;
224   }
225 }
226 
227 public class LoadTest {
228 
229   static Random random = new Random();
230   static String urlPrefix;
231 
232   private long runtimeMillis;
233   private boolean keepGoing;
234   private ExponentialDistribution wait;
235   private DiscreteDistribution types = new DiscreteDistribution();
236   private int searchsectiongroup;
237 
238   public LoadTest(long runtimeMillis, int hitsPerSec, int kSearch) {
239     this.runtimeMillis = runtimeMillis;
240     wait = new ExponentialDistribution(1000 / hitsPerSec);
241 
242     types = new DiscreteDistribution();
243 
244     Urler
245         read = new ReadUrler(),
246         search1 = new SearchUrler(searchsectiongroup, 1),
247         search2 = new SearchUrler(searchsectiongroup, 2);
248         //search3 = new SearchUrler(searchsectiongroup, 3);
249 
250     Level
251         sec = new SectionLevel(),
252         aut = new AuthorLevel(),
253         bok = new BookLevel(),
254         cha = new ChapterLevel();
255     
256     types.add(new ReqType(read, sec), 6);
257     types.add(new ReqType(read, aut), 12);
258     types.add(new ReqType(read, bok), 18);
259     types.add(new ReqType(read, cha), 37);
260     /*
261     types.add(new ReqType(search1, sec), 2 * kSearch);
262     types.add(new ReqType(search1, aut), 2 * kSearch);
263     types.add(new ReqType(search1, bok), 2 * kSearch);
264     types.add(new ReqType(search2, sec), 1 * kSearch);
265     types.add(new ReqType(search2, aut), 1 * kSearch);
266     types.add(new ReqType(search2, bok), 1 * kSearch);
267     */
268   }
269 
270   private class Stopper extends Thread {
271     public void run() {
272       try {
273         Thread.sleep(runtimeMillis);
274       }
275       catch (Exception e) {
276         throw new UnexpectedExceptionException(e);
277       }
278 
279       keepGoing = false;
280     }
281   }
282 
283   public static String pad(String it, int w) {
284     StringBuffer s = new StringBuffer(w);
285     while (s.length() + it.length() < w)
286       s.append(' ');
287     s.append(it);
288     return s.toString();
289   }
290 
291   public static String pad(long l, int w) {
292     return pad("" + l, w);
293   }
294 
295   public void run() throws Exception {
296     keepGoing = true;
297     new Stopper().start();
298 
299     while (keepGoing) {
300       Thread.sleep((long)wait.sample());
301       ((ReqType)types.sample()).go((BookDesc)extractTree.books.sample());
302     }
303 
304     System.err.println("stopping");
305     Thread.sleep(5);
306 
307     System.out.println();
308     System.out.println("        win open end fail");
309     for (Enumeration h = types.elements(); h.hasMoreElements();) {
310       ReqType hit = (ReqType)h.nextElement();
311       System.out.print(hit.toString());
312       System.out.println(
313           pad(hit.stats.successes, 9) + " " +
314           pad(hit.stats.meanToOpen(), 5) + " " +
315           pad(hit.stats.meanToFinish(), 5) + " " +
316           pad(hit.stats.failures, 5) + " ");
317     }
318   }
319 
320   static ExtractTree extractTree;
321 
322   public static void main(String[] args) throws Exception {
323     ObjectInputStream i =
324         new ObjectInputStream(new FileInputStream("books.ser"));
325 
326     extractTree = (ExtractTree)i.readObject();
327 
328     i.close();
329 
330     /*
331     urlPrefix = "http://" + args[0] + "/";
332     new LoadTest(Integer.parseInt(args[1]) * 1000L,
333         Integer.parseInt(args[2]),
334         Integer.parseInt(args[3])).run();
335         */
336     urlPrefix = "http://bibliomania.com/";
337     new LoadTest(new Integer(40) * 1000L,
338         new Integer(200),
339         new Integer(32)).run();
340   }
341 }