HanG321 Blog

AsyncWeb & load test

There is insufficient information for AsyncWeb (subproject of Mina) on internet,
what I need is simulating a web services that reply in various seconds for load testing.

the following code may not be correct, some wiered debug message…
and I just depressed it by log4j.properties

Mostly follow the lightweight example of AsyncWeb,
the eclipse project zip can be obtained here


public class Main {

  private static final int PORT = 80;

  /**
   * Main
   *
   * @param args
   * @throws IOException
   */
  public static void main(String[] args) throws IOException {
    SocketAcceptor acceptor = new NioSocketAcceptor();

    acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new HttpCodecFactory()));
    acceptor.getFilterChain().addLast("executor", new ExecutorFilter(new OrderedThreadPoolExecutor(32)));

    //Allow the port to be reused even if the socket is in TIME_WAIT state
    acceptor.setReuseAddress(true);

    acceptor.getSessionConfig().setReuseAddress(true);
    acceptor.getSessionConfig().setReceiveBufferSize(1024);
    acceptor.getSessionConfig().setSendBufferSize(1024);
    // No Nagle's algorithm
    acceptor.getSessionConfig().setTcpNoDelay(true);
    acceptor.getSessionConfig().setSoLinger(-1);
    acceptor.setBacklog(10240);

    acceptor.setHandler(new CustomHttpIoHandler());  // customer handler
    acceptor.bind(new InetSocketAddress(PORT));

  }

}

public class CustomHttpIoHandler implements IoHandler {

  private final Logger logger = LoggerFactory.getLogger(getClass());

  private final Timer timer;

  private Random random = new Random(System.currentTimeMillis());

  private final String SOAPENV_HEAD = "";

  public CustomHttpIoHandler(){
    timer = new Timer(true);
  }

  public void exceptionCaught(IoSession session, Throwable cause)
  throws Exception {
    if (!(cause instanceof IOException)) {
      cause.printStackTrace();
    }
    session.close(true);
  }

  public void messageSent(IoSession session, Object message) throws Exception {
  }

  public void sessionClosed(IoSession session) throws Exception {
  }

  public void sessionCreated(IoSession session) throws Exception {
  }

  public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
    session.close(true);
  }

  public void sessionOpened(IoSession session) throws Exception {
    session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30);
  }

  public void messageReceived(IoSession session, Object message) throws Exception {

    HttpRequest req = (HttpRequest) message;
    doAsynchronousDelayedResponse(session, req);

  }

  /**
   * business logic for various percentage and behaviour
   *
   * @param session mina session
   * @param req Http request
   * @throws CharacterCodingException
   */
  private void doAsynchronousDelayedResponse(final IoSession session, final HttpRequest req) throws CharacterCodingException {

    int delay = 0;
    Behaviour behaviour;

    int value = nextRandom(1, 100);
    if(value < = 50){
      // 50% of responses return valid result within 0-3 secs (good response time & good result)
      delay = nextRandom(10, 3000);
      behaviour = Behaviour.GOOD_TIME_GOOD_RESULT;

    }else if(value > 50 && value < =60){
      //10% of responses return expected error within 0-3 secs (good response time & expected error result)
      delay = nextRandom(10, 3000);
      behaviour = Behaviour.GOOD_TIME_ERROR_RESULT;

    }else if(value > 60 && value < = 90){
      //30% of responses return valid result within 3-15 secs (fair response time & good result)
      delay = nextRandom(3000, 15000);
      behaviour = Behaviour.FAIR_TIME_GOOD_RESULT;

    }else if(value > 90 && value < =95){
      //5% of responses return valid result within 15-190 secs (poor response time & good result)
      delay = nextRandom(15000, 190000);
      behaviour = Behaviour.POOR_TIME_GOOD_RESULT;

    }else if(value > 95 && value < =97){
      //2.5% of responses return valid result after 190 seconds (bad response time & good result)
      delay = nextRandom(15000, 190000);
      behaviour = Behaviour.BAD_TIME_GOOD_RESULT;

    }else{
      //2.5% of responses return 'no content' after 190 seconds (bad response time & bad result)
      delay = nextRandom(15000, 190000);
      behaviour = Behaviour.BAD_TIME_BAD_RESULT;
    }

    final MutableHttpResponse res = new DefaultHttpResponse();
    res.setStatus(HttpResponseStatus.OK);
    res.setContentType("application/soap+xml; charset=utf-8");

    // get corresponding response from behaviour
    StringBuffer sb = getResponseXML(behaviour);

    IoBuffer bb = IoBuffer.allocate(1024);
    bb.setAutoExpand(true);
    bb.putString(sb.toString(), Charset.forName("UTF-8").newEncoder());
    bb.flip();
    res.setContent(bb);

    logger.info("delay:" + delay + " stub:" + behaviour);

    timer.schedule(new TimerTask() {
      @Override
      public void run() {
        writeResponse(session, req, res);
      }
    }, delay);
  }

  /**
   * Write the http response
   *
   * @param session mina session
   * @param req HTTP Request
   * @param res HTTP Response
   */
  private void writeResponse(IoSession session, HttpRequest req, MutableHttpResponse res) {

    res.normalize(req);
    WriteFuture future = session.write(res);
    if (!HttpHeaderConstants.VALUE_KEEP_ALIVE.equalsIgnoreCase(
        res.getHeader( HttpHeaderConstants.KEY_CONNECTION))) {
      future.addListener(IoFutureListener.CLOSE);
    }
  }

  /**
   * Generate next random number from min(inclusive) to max(inclusive)
   * @param min minimum number
   * @param max maximum number
   * @return random integer
   */
  private int nextRandom(int min, int max){

    return random.nextInt(max-min+1)+min;
  }

  /**
   * Indicate what kind of behaviour
   *
   * @author SteveChan
   */
  public enum Behaviour{
    GOOD_TIME_GOOD_RESULT,  //return valid result within 0-3 secs
    GOOD_TIME_ERROR_RESULT, //return expected error within 0-3 secs
    FAIR_TIME_GOOD_RESULT,  //return valid result within 3-15 secs
    POOR_TIME_GOOD_RESULT,  //return valid result within 15-190 secs (
    BAD_TIME_GOOD_RESULT,   //return valid result after 190 seconds
    BAD_TIME_BAD_RESULT     //return 'no content' after 190 seconds
  }

  /**
   *
   * @param stub indicate what Behaviour
   * @return StringBuffer with return xml content
   */
  private StringBuffer getResponseXML(Behaviour stub){

    StringBuffer sb = new StringBuffer();

    if(stub.equals(Behaviour.GOOD_TIME_GOOD_RESULT) ||
        stub.equals(Behaviour.FAIR_TIME_GOOD_RESULT) ||
        stub.equals(Behaviour.POOR_TIME_GOOD_RESULT) ||
        stub.equals(Behaviour.BAD_TIME_GOOD_RESULT)){

      sb.append(SOAPENV_HEAD)
      .append("")
      // some content here
      .append("")
      .append("");

    }else if(stub.equals(Behaviour.GOOD_TIME_ERROR_RESULT)){

      sb.append(SOAPENV_HEAD)
      .append("")
      // some content here
      .append("")
      .append("");

    }else{
      //BAD_TIME_BAD_RESULT
      sb.append(SOAPENV_HEAD)
      .append("")
      // some content here
      .append("")
      .append("");
    }

    if(logger.isDebugEnabled()){
      logger.debug("return stardard soap response xml by " + stub );
      logger.debug(sb.toString());
    }
    return sb;

  }

}
Exit mobile version