2012年4月25日水曜日

web server benchmark : apache lighttpd nodejs warp yesod tomcat jetty

結論

QCon 2012 で Warp+Yesod がいいと言っていたので、ベンチをとってみました。いいっぽいです

サーバーサイドのチューニングはデフォなので、スレッド数位は合わせてベンチマークをとりたいと思って下ります。 なんかサーセン

環境

Server

Ubuntu 11.10

Intel(R) Xeon(R) CPU 5160 @ 3.00GHz x 2

Memory 2 GiB

|

Switching Hub

corega Fast SW-8D

|

Client

Mac OS X 10.7.3

2.53 GHz Intel Core i5

Memory 8 GB 1067 MHz DDR3

計測の仕方

$ ab -n request_count -c 128 -r -g gnuplot_file_name   http://ubuntu.server:port/path   >& result.txt

This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

計測したもの

Apache Lighttpd

Server Software:        Apache/2.2.20
Server Hostname:        xxx.xxx.xxx.xxx
Server Port:            80

Document Path:          /index.html
Document Length:        85 bytes

Server Software:        lighttpd/1.4.30
Server Hostname:        xxx.xxx.xxx.xxx
Server Port:            8123

Document Path:          /index.html
Document Length:        85 bytes

Node.js Warp + Yesod

version: 0.6.15


Server Software:        
Server Hostname:        xxx.xxx.xxx.xxx
Server Port:            8124

Document Path:          /
Document Length:        80 bytes

ghc: 7.0.3

warp: 1.2.0.1

yesod: 1.0.0.2


Server Software:        Warp/1.2.0.1
Server Hostname:        xxx.xxx.x.160
Server Port:            8123

Document Path:          /index
Document Length:        99 bytes
Tomcat Jetty

Server Software:        Apache-Coyote/1.1
Server Hostname:        xxx.xxx.xxx.xxx
Server Port:            8080

Document Path:          /sample/index.html
Document Length:        85 bytes


Server Software:        Jetty(8.1.2.v20120308)
Server Hostname:        xxx.xxx.xxx.xxx
Server Port:            8080

Document Path:          /sample/index.html
Document Length:        85 bytes

計測結果

wait time 分布図

30000 request

10000 request

wait time について => Between writing request and reading response

Source らしい(何となく怪しい) から一部抜粋

struct data {
#ifdef USE_SSL
    /* XXX insert timings for ssl */
#endif
    int read;                   /* number of bytes read */
    long starttime;             /* start time of connection in seconds since
                                 * Jan. 1, 1970 */
    long waittime;              /* Between writing request and reading response */
    long ctime;                 /* time in ms to connect */
    long time;                  /* time in ms for connection */
};

テスト時間 (sec)

apache, lighttpd はリクエスト数が多いとテストに時間がかかります。

result/apache-time.dat
request  1       2       3       4
  200    0.028   0.035   0.030   0.031
  500    0.081   0.087   0.078   0.069
 1500    0.187   0.172   0.187   0.185
10000   90.143 109.438 107.131 104.303
30000   18.995   3.584  32.986 327.660

result/lighttpd-time.dat
request  1       2       3       4
  200    0.025   0.025   0.027   0.026
  500    0.063   0.069   0.055   0.053
 1500    2.139   0.561   0.168   0.169
10000  50.097   52.646   2.285  34.739
30000 142.147    5.152 165.114 162.209

result/nodejs-time.dat
request  1      2        3       4
  200    0.034  0.039    0.034   0.032
  500    0.097  0.074    0.073   0.072
 1500    0.238  0.235    0.245   0.211
10000    1.560  1.610    5.101   1.505
30000    8.593 24.368   16.838  31.659

result/warp-time.dat
request  1      2        3       4
  200    0.071  0.093    0.074   0.070
  500    0.160  0.309    0.229   0.176
 1500    1.264  1.057    1.326   1.048
10000    3.115 10.991   11.347   5.078
30000   33.264 34.866   33.098  34.690

result/tomcat-time.dat
request  1      2        3       4
  200    0.021  0.024    0.025   0.080
  500    0.052  0.050    0.049   0.043
 1500    0.151  0.167    0.166   0.153
10000    9.062 10.112    6.957   0.965
30000   21.004 12.324   21.602  18.784

result/jetty-time.dat
request  1      2        3       4
  200    0.025  0.025    0.026   0.028
  500    0.087  0.104   14.926   0.059
 1500    1.127 22.815    1.244  14.990
10000   11.779 11.658   11.648  11.617
30000   36.141 36.806   36.773  36.225

2012年4月18日水曜日

組込み Tomcat と Jetty : Embedded in java

TomcatとJettyの組込みです バージョンは下記です
  • Tomcat 7.0.26
  • Jetty 8.1.12

Tomcat 参考サイト

embedding-tomcat

コマンド

起動は下の感じで、止めるのは Ctrl-C でお願いします
# tomcat
$ java -cp srv.jar:lib/* web.server.WebServer

# jetty
$ java -cp srv.jar:lib/* web.server.WebServer -jetty

# set port
$ java -cp srv.jar:lib/* web.server.WebServer server.port 8123

他のパラメータはソースを見てください
動かした感じ, jetty の後 tomcat 動かしてます










ソース

//
// Embbed Web Server
//
// Property
// ----------
//
// server.port port number
// server.basedir base dir path.
// server.webapps webapp dir name.
// server.contextpath context path of web app
// server.maxthreads
// server.minthreads
//
//
// server.secure true/false
// server.secure.key.alias alias
// server.secure.key.password keystore pasword
// server.secure.key.file keystore file path
// server.secure.ssl.protocol protocol
//
//
// Dir
// ----------
//
// home.dir/
// server.jar jar file of this class
// lib/ tomcat libs & jetty
// webapps/
// sample/
// WEB-INF/
// lib/webservlet.jar or your servlet.jar
// indx.html
//
//
// @author wooyoowaan@gmail.com
//
package web.server;
import java.io.File;
import java.io.FilenameFilter;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.servlet.ServletException;
// TOMCAT
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
//import org.apache.catalina.connector.Connector;
// JETTY
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
abstract public class WebServer {
// server configuration
public static final String PROPS_PORT = "server.port";
public static final String PROPS_BASEDIR = "server.basedir";
public static final String PROPS_WEBAPPS = "server.webapps";
public static final String PROPS_CONTEXTPATH = "server.contextpath";
public static final String PROPS_RESOURCE_BASE= "server.resourcebase";
public static final String PROPS_WAR = "server.war";
public static final String PROPS_EXTRA_CLASSPATH = "server.extr.classpath";
public static final String PROPS_MAX_THREADS = "server.maxthreads";
public static final String PROPS_MIN_THREADS = "server.minthreads";
// scurity properties
public static final String PROPS_SECURE = "server.secure";
public static final String PROPS_KEY_ALIAS = "server.secure.key.alias";
public static final String PROPS_KEY_PASSWORD = "server.secure.key.password";
public static final String PROPS_KEY_FILE = "server.secure.key.file";
public static final String PROPS_SSL_PROTOCOL = "server.secure.ssl.protocol";
public static final String PROPS_CLIENT_AUTH = "server.secure.client.auth";
protected Properties _properties;
/**
* start web server
*/
public void start() {
doStart();
}
/**
* stop web server
*/
public void stop() {
doStop();
}
abstract protected void doStart();
abstract protected void doStop();
protected void check(StateChecker checker) throws Exception {
Thread thread = new Thread(checker);
thread.start();
thread.join();
}
/**
*
*/
protected int getInt(String name, int defaultValue) {
String s = _properties.getProperty(name);
return s == null ? defaultValue : Integer.parseInt(s) ;
}
/**
*
*/
protected String getProperty(String name, String defaultValue) {
return _properties.getProperty(name, defaultValue);
}
/**
*
*/
protected boolean getBool(String name, boolean defaultFlag) {
String s = _properties.getProperty(name, null);
return s == null ? defaultFlag : Boolean.parseBoolean(s);
}
public static void main(String[] args) {
Properties props = new Properties();
boolean useTomcat = true;
for (int i = 0; i < args.length ; i++) {
String arg = args[i];
if (arg.equals("-jetty")) {
useTomcat = false;
} else {
String key = arg;
i++;
String value = args[i];
props.setProperty(key, value);
}
}
final WebServer server = useTomcat ? new TomcatServer(props) : new JettyServer(props) ;
try {
server.start();
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
server.stop();
}
});
while (true) {}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Jetty Server
*/
class JettyServer extends WebServer {
private Server _server;
/**
*
*/
public JettyServer(Properties props) {
_properties = props;
}
/**
*
*/
protected void doStart() {
_server = new Server();
int maxThreads = getInt(PROPS_MAX_THREADS, 200);
int minThreads = getInt(PROPS_MIN_THREADS, 8);
QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setMinThreads(minThreads);
threadPool.setMaxThreads(maxThreads);
this._server.setThreadPool(threadPool);
Connector connector = null;
if (getBool(PROPS_SECURE, false)) {
SslSelectChannelConnector sslConnector = new SslSelectChannelConnector();
sslConnector.setKeystore(getProperty(PROPS_KEY_FILE, "keystore/sha1.keystore"));
sslConnector.setKeyPassword(getProperty(PROPS_KEY_PASSWORD, "password"));
sslConnector.setProtocol(getProperty(PROPS_SSL_PROTOCOL, "TLS"));
//sslConnector.setPassword();
// trust ...
connector = sslConnector;
connector.setPort(getInt(PROPS_PORT, 8443));
} else {
connector = new SelectChannelConnector();
connector.setPort(getInt(PROPS_PORT, 8080));
}
// set connectors
this._server.setConnectors(new Connector[] { connector });
//
// setup
//
WebAppContext ctx = new WebAppContext(getProperty(PROPS_WEBAPPS,"webapps"),
getProperty(PROPS_CONTEXTPATH, "/sample"));
ctx.setExtractWAR(true);
ctx.setResourceBase(getProperty(PROPS_RESOURCE_BASE, "webapps/sample"));
ctx.setWar(getProperty(PROPS_WAR, "/sample"));
String extraClasspath = getProperty(PROPS_EXTRA_CLASSPATH, null);
if (extraClasspath != null) {
ctx.setExtraClasspath(getClassPath(extraClasspath));
}
ctx.setServer(_server);
_server.setHandler(ctx);
//
// start
//
try {
_server.start();
check(new StateChecker() {
public boolean isOK() {
return _server.isRunning() || _server.isFailed();
}
});
} catch (Exception e) {
throw new IllegalStateException("Jetty Boot Failed", e);
}
if (_server.isFailed()) {
throw new IllegalStateException("Jetty Boot Failed");
}
}
/**
*
*/
protected void doStop() {
try {
_server.stop();
if (!_server.isStopped()) {
check(new StateChecker() {
public boolean isOK() {
return _server.isStopped() || _server.isFailed();
}
});
}
} catch (Exception e) {
throw new IllegalStateException("jetty stop failed");
}
if (_server.isFailed()) {
throw new IllegalStateException("jetty stop failed");
}
_server = null;
}
private String getClassPath(String extraPath) {
try {
StringBuilder classPath = new StringBuilder();
StringTokenizer st = new StringTokenizer(extraPath, ",;");
while (st.hasMoreTokens()) {
File dir = new File(st.nextToken());
File[] flst = dir.listFiles(new FilenameFilter() {
public boolean accept(File file, String name) {
return name.endsWith(".jar") || name.endsWith(".zip") || name.endsWith(".class");
}
});
for (File f : flst) {
if (classPath.length() > 0) {
classPath.append(',');
}
classPath.append(f.toURI().toURL());
}
}
return classPath.toString();
} catch (Exception e) {
// ignore
}
return extraPath;
}
}
/**
* Tomcat
*/
class TomcatServer extends WebServer {
private Tomcat _server;
/**
*
*/
public TomcatServer(Properties props) {
_properties = props;
}
/**
*
*/
protected void doStart() {
_server = new Tomcat();
File baseDir = new File(getProperty(PROPS_BASEDIR, "."));
_server.setBaseDir(baseDir.getAbsolutePath());
// https
if (getBool(PROPS_SECURE, false)) {
org.apache.catalina.connector.Connector connector = new org.apache.catalina.connector.Connector("HTTP/1.1");
int port = getInt(PROPS_PORT, 8443);
connector.setPort(port);
connector.setSecure(true);
File keyStoreFile = new File(baseDir, getProperty(PROPS_KEY_FILE, "keystore/sha1.keystore"));
connector.setScheme("https");
connector.setAttribute("keyAlias", getProperty(PROPS_KEY_ALIAS, "tomcat"));
connector.setAttribute("keystoreFile", keyStoreFile.getAbsolutePath());
connector.setAttribute("keystorePass", getProperty(PROPS_KEY_PASSWORD, "password"));
connector.setAttribute("clientAuth", getProperty(PROPS_CLIENT_AUTH, "false"));
connector.setAttribute("sslProtocol", getProperty(PROPS_SSL_PROTOCOL, "TLS"));
connector.setAttribute("SSLEnabled", true);
//_server.setConnector(connector);
_server.getService().addConnector(connector);
/*
Connector defaultConnector = _server.getConnector();
defaultConnector.setRedirectPort(port);
System.out.println("Con [" + );
for (Connector con : _server.getService().findConnectors()) {
System.out.println(con);
if (con != connector) {
con.setRedirectPort(port);
}
}
*/
} else {
_server.setPort(getInt(PROPS_PORT, 8080));
}
File webApps = new File(baseDir, getProperty(PROPS_WEBAPPS, "webapps"));
_server.getHost().setAppBase(webApps.getAbsolutePath());
try {
String contextPath = getProperty(PROPS_CONTEXTPATH, "/sample");
_server.addWebapp(contextPath, new File(webApps, contextPath).getAbsolutePath());
_server.start();
} catch (ServletException e) {
throw new IllegalStateException("tomcat boot failed!!", e);
} catch (LifecycleException e) {
throw new IllegalStateException("tomcat boot failed!!", e);
}
}
/**
*
*/
protected void doStop() {
try {
_server.stop();
} catch (LifecycleException e) {
throw new IllegalStateException("tomcat stop failed!!", e);
}
}
}
class StateChecker implements Runnable {
public boolean isOK() {return true;}
public void run() {
while (!isOK()) {
try {
Thread.currentThread().sleep(1000);
} catch (Exception e) {
break;
}
}
}
}
view raw WebServer.java hosted with ❤ by GitHub

2012年4月11日水曜日

タネマキは使える

週末にフラッとタネマキに行ってきました。

混む時もあるそうですが、おおよそ使えるのでは無いでしょうか?

ここです!!タネマキ