아래 설정의 scheme과 proxyPort 를 이용해서 Tomcat 서버의 보안을 높일 수 있다.

 

<Connector maxPostSize="80000" enableLookups="false" port="10000" protocol="AJP/1.3" redirectPort="8443" URIEncoding="UTF-8" scheme="https" proxyPort="443"/>

 

 

* 참고자료

 

http://tomcat.apache.org/tomcat-5.5-doc/config/http.html

 

http://pwu-developer.blogspot.com/2011/04/securing-tomcat-with-apache-web-server.html

 

 

Posted by '김용환'
,

 

3.5년전 DDOS 공격을 잘 방어할 수 있는 방법을 찾고자 redirection 기능만 넣고 테스트한 적이 있다.

Nginx라는 녀석이 좋은 대안이 될 수도 있다는 생각이 들었다.

TC1

TC2

TC3

TC4

TC4(nolog)

Nginx

Base

Apache 2.2

Apache 2.2

Apache 2.2

Tux

Tux

Nginx

1분 부하값
(load avg)

220

22

220

0.3

0.3

0.5

테스트

Java script redirect

Apache redirect(302)

Java script

redirect

Java script redirect

Java script redirect

Redirect

(302)

MAX TPS

4358.56

5120.5

4523.25

10838.5

10903.1

10790.2

 

테스트 결과는 kernel 기반의 Tux 웹 서버와 Nginx의 성능은 비슷했다.

Tux의 장점은 Static content serving(DMA directly from page cache) 와 user/kernel간의 context switching도 적고  system call이 적어 cpu를 더 효율적으로 쓸 수 있다. 그러나 단점으로는 kernel기반이다 보니 쉽게 운영하기 어려운 단점이 있다.

Ingo Molnár(http://en.wikipedia.org/wiki/Ingo_Moln%C3%A1r ) 라는 사람이 버려졌던 Tux를 리눅스 커널 2.6.0에 올렸다. 

 

Tux는 왜 점점 버려지고 있었을까?

1. 운영 측면에서 불편하다.

운영이 편리한(디버깅, 개발, 플러그인 추가) 어플을 사용하는 것이 편하다.

커널단을 건드려서 복잡한 단계를 진행하는 것보다 어플단에서 진행하는 게 백 번 낫다. 안정성 측면에서도 커널영역이 아닌 유저레벨에서 처리가 가능하다.  데몬 실행 할때만 root 권한이 필요하니. apache httpd나 nginx가 편리하다.

 

2. 메모리 사용 (sendfile)

어쩌면, sendfile이 대중화되면서 Tux의 장점이 사라졌던 것 같다. Apache http 서버는 sendfile directive가 추가되고 sendfile을 통해서 user space buffer copy 부분이 사라졌다. (zerocopy) Apache http 서버는 sendfile 기능을 넣어서 약 10%정도의 성능 효과를 발휘할 수 있다. (물론 nginx도.. ) 웬만한 웹서버는 sendfile 기능은 off이다.

f = open(file)

sendfile (socket, f, 0, fiesize)

굳이 커널레벨을 쓸 필요가 없다.

 

3. 성능

커널 단이라서 빠를 수 있지만, 오히려 처리 아키텍쳐가 더 큰 영향을 미친다.

 

4. 플러그인 개발

커널 모듈을 올렸나 내리는 작업이 결코 개발자에게는 좋지 않다.  바로 dynamic linking 하는 아파치 모듈이 더 편리하다. (현재까지는 nginx는 아직 dynamic linking을 지원하지 않는다. )

 

마치며..

과거에 Tux를 이용해서 먼가 해보려는 나의 시도는 굉장히 위험했던 것 같다. 언제나 생각해봐도 어플단에서 할 수 있는 것들을 최대한 살려서 쓰는 게 좋을 것 같다.

참고로. Tux 와 비슷하지만 성공했던 모델은 MS의 IIS (kernel + user) 이다. IIS 업데이트할 때마다 리스타트하는 상황을 보면. 참 거시기 하다. 여전히 어떻게 생존해 나갈지도 매우 궁금하기도 하구..

Posted by '김용환'
,
Posted by '김용환'
,

 

Spring Batch의 CommandLineJobRunner를 이용한 jar 를 실행하기 위한 스크립트를 공유한다.

http://static.springsource.org/spring-batch/reference/html/configureJob.html

 

Hudson 의 Execution Shell에 java command 쓰다가 하루 시간이 다 가므로.. 스크립트를 만들어서 쓰면 많이 편리하다. serivce project 이름, xml 파일 이름, define한 job 이름, 저장할 table의 prefix, 기타 덧붙일 스트링이 있으면 사용하기 편할 수 있다.

 

실제 사용 예)

/home/www/script/batch_start.pl -p batch_bot –j bot_collector

 

스크립트

#!/usr/bin/perl

use strict;
use Getopt::Std;

my %OPTS;
getopt('pxjtma', \%OPTS);

my $WWW_HOME = "$ENV{'WWW_HOME'}";

main();

sub main {

        if(scalar(%OPTS) < 2) {
        print STDERR "Usage : batch_start.pl  -p [project-name] -x [xmlfile] -j [jobname] -t [table_prefix]\n";
        print STDERR "ex) batch_start.pl -p batch_template -x batch_template_job.xml -j job1\n\n";
        print STDERR "Usage : batch_start.pl  -p [project-name] -j [jobname] -t [table_prefix] -a [append string]\n";
        print STDERR "ex) batch_start.pl -p batch_template -j job1 -a \"jobparam1=p1\"\n";

        exit 1;
    }

    my $project_name =  $OPTS{"p"};
    my $xml_name =      $OPTS{"x"};
    my $job_name =      $OPTS{"j"};
    my $prefix_name =   $OPTS{"t"};
    my $module_name =   $OPTS{"m"};
    my $appendstr =     $OPTS{"a"};

    # define 해야 할 곳~

    my $project_path =  "…"; 
    my $jar_name = `ls $project_path| grep $project_name`;
   
        if ($module_name) {
            $jar_name = $module_name.".jar";       
        }

        if($xml_name) {
                $xml_name = "$xml_name";
        } else {
                $xml_name = "$job_name";
        }

        chomp($jar_name);

        my @libList = `ls $project_path/lib`;
        my $classpath_prefix = "$project_path/lib";

        print "## project_name = $project_name"."\n";
        print "## xml_file = $xml_name"."\n";
        print "## job_name = $job_name"."\n";
        print "## jar_name = $jar_name"."\n";
        print "## prefix_name = $prefix_name"."\n";
        print "## appendstr = $appendstr"."\n";

        my $classpath = "./";

        foreach my $lib_jar (@libList) {
                chomp($lib_jar);
                $classpath .= ":$classpath_prefix/$lib_jar";
        }

        chomp($appendstr);

        print "java -jar $project_path/$jar_name $xml_name $job_name $prefix_name $appendstr -classpath$..\n";
        !system("java -jar $project_path/$jar_name $xml_name $job_name $prefix_name $appendstr -classpath$classpath") or die "failure";
}

__END__

 

<java 쪽 코드>

pom.xml에 추가할 코드- jar 파일을 바로 실행하게 한다.  

<build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
     <archive>
      <index>true</index>
      <manifest>
       <mainClass>com.google.batch.ParameterJobLuancher</mainClass>
       <addClasspath>true</addClasspath>
       <classpathPrefix>lib/</classpathPrefix>
      </manifest>
     </archive>
    </configuration>
   </plugin>

...
</build>

Spring Batch의 CommandLineJobRunner 처럼 클래스를 하나 만듬

(http://www.docjar.com/html/api/org/springframework/batch/core/launch/support/CommandLineJobRunner.java.html)

 

 

 

Posted by '김용환'
,

 

tomcat 6 에서 동작하던 웹 어플리케이션을 tomcat 7 (7.0.25) 으로 올려보았다.  tomcat 7이 servlet 3.0의 comet을 지원하는 것외에 특별히 고쳤을까 싶었는데..

tomcat6의 catalina.sh를 그대로 사용할 때와 tag library쪽에 이슈가 있었다.

 

## catalina.sh start 되게

설정 파일 conf/server.xml과 bin/catalina.sh 는 tomcat 6에 있는 것으로 사용하려고 하다가 다음과 같은 에러를 만났다. (역시 tomcat 7의 catalina.sh을 사용하는 것이 맞을 듯.)

java.lang.NoClassDefFoundError: org/apache/juli/logging/LogFactory

 

tomcat7의 catalina.sh 에 classpath에 bin/tomcat-juli.jar 부분이 변경된 것 같다.

# Add tomcat-juli.jar to classpath
# tomcat-juli.jar can be over-ridden per instance
if [ -r "$CATALINA_BASE/bin/tomcat-juli.jar" ] ; then
  CLASSPATH=$CLASSPATH:$CATALINA_BASE/bin/tomcat-juli.jar
else
  CLASSPATH=$CLASSPATH:$CATALINA_HOME/bin/tomcat-juli.jar
fi

 

또한 입맛에 좀 맞게 catalina.sh를 변경해야 한다.  (CATALINA_JAVAOPTS, $CATALINA_LOGDIR, CATALINA_LOGFILE, CATALINA_HOME)

conf/server.xml은 그대로 수정해서 사용하니 잘 동작된다.

 

## catalina.sh  stop되게

stop 시에 대한 설정을 일부 수정해야 한다.

stop을 하면, sleep 5초를 한다. (디폴트) kill 하고, 안죽으면 force하게 죽는 작업이 되어 있다.

이 부분을 입맛에 맞게 변경

 

## catalina.sh configtest

아파치의 문법 체크(httpd -t )처럼 설정 문법을 테스트하는게 생겼다.

 

소스는 다음과 같다.

elif [ "$1" = "configtest" ] ; then
   
    eval \"$_RUNJAVA\" $JAVA_OPTS \
      -Djava.endorsed.dirs=\"$JAVA_ENDORSED_DIRS\" -classpath \"$CLASSPATH\" \
      -Dcatalina.base=\"$CATALINA_BASE\" \
      -Dcatalina.home=\"$CATALINA_HOME\" \
      -Djava.io.tmpdir=\"$CATALINA_TMPDIR\" \
      org.apache.catalina.startup.Bootstrap configtest
    result=$?
    if [ $result -ne 0 ]; then
        echo "Configuration error detected!"
    fi   
    exit $result
fi

 

 

## Tomcat 이슈

tomcat6에서 잘 동작하는 소스를 tomcat 7에서 돌릴 때는 어떠할까. 태그 라이브러리쪽에 이슈가 있었다.

org.apache.jasper.JasperException: The absolute uri: http://taglib.google.com/hiu cannot be resolved in either web.xml or the jar files deployed with this application

at org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:56)
at org.apache.jasper.compiler.ErrorDispatcher.dispatch(ErrorDispatcher.java:410)
at org.apache.jasper.compiler.ErrorDispatcher.jspError(ErrorDispatcher.java:117)
at org.apache.jasper.compiler.TagLibraryInfoImpl.generateTLDLocation(TagLibraryInfoImpl.java:311)
at org.apache.jasper.compiler.TagLibraryInfoImpl.<init>(TagLibraryInfoImpl.java:152)
at org.apache.jasper.compiler.Parser.parseTaglibDirective(Parser.java:410)
at org.apache.jasper.compiler.Parser.parseDirective(Parser.java:475)
at org.apache.jasper.compiler.Parser.parseElements(Parser.java:1427)
at org.apache.jasper.compiler.Parser.parse(Parser.java:138)
at org.apache.jasper.compiler.ParserController.doParse(ParserController.java:242)
at org.apache.jasper.compiler.ParserController.parse(ParserController.java:102)
at org.apache.jasper.compiler.Compiler.generateJava(Compiler.java:198)
at org.apache.jasper.compiler.Compiler.compile(Compiler.java:373)
at org.apache.jasper.compiler.Compiler.compile(Compiler.java:353)
at org.apache.jasper.compiler.Compiler.compile(Compiler.java:340)
at org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:646)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:357)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)

 

tomcat 6에서는 기존에 tag library 파일들을 META-INF 디렉토리안에 넣으면 자동으로 인식하는 기능이 있었는데, 이 부분이 tomcat 7부터는 web.xml에 명시적으로 관련 정보를 넣는 구조로 바뀌었다.

 

즉 기존의 파일은 다음과 같이 사용했다.


src/main/java/META-INF/taglib/hiu-taglib.tld

<?xml version="1.0" encoding="UTF-8"?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">

    <description>HIU Custom Tags</description>
    <tlib-version>1.2</tlib-version>
    <short-name>hiu</short-name>
    <uri>http://taglib.google.com/hiu</uri>


<tag>

….

</tag>

…..

 

web.xml에 taglibarary에 대한 url와 location에 대해서 명확히 지정해야 한다.

(tld 파일을 META-INF가 아닌 WEB-INF 로 이동해야 잘 인식해서 WEB-INF 로 이동하니 잘된다.
/META-INF/taglib.tld 파일로 수정해도 인식 못한다. )

* web.xml에 추가할 내용

<jsp-config>
    <taglib>
        <taglib-uri>http://taglib.google.com/hiu</taglib-uri>
        <taglib-location>/WEB-INF/hiu-taglib.tld</taglib-location>
    </taglib>
</jsp-config>

WEB-INF 디렉토리 밑에 tag lib를 두면 자동으로 인식하기 때문에 꼭 저렇게 사용하지는 않아도 된다.

정확한 지식을 위해서 jsp 2.2 스펙을 참조한다.

 

 

* Java Server Page 2.2 specification (tomcat 7이 적용, jsp 2.2) 
http://jcp.org/aboutJava/communityprocess/mrel/jsr245/index.html

스펙을 참조하니. 이해가 되었다. 역시 스펙의 힘이란…

 

예제가 설명이 되어 있다.

 

순서에 대한 정보도 있다.

자세한 내용은 아래를 참조하면 된다.

JSP.7.3.2 TLD resource path
JSP.7.3.3 Taglib Map in web.xml
JSP.7.3.4 Implicit Map Entries from TLDs
JSP.7.3.5 Implicit Map Entries from the Container
JSP.7.3.6 Determining the TLD Resource Path

Posted by '김용환'
,

node.js

Web service 2012. 2. 9. 16:31

 

node.js 사용만 하다가. node.js 내부에 대한 궁금증이 생겼다..

V8 + thread pool + event loop 가 핵심이다.

 

node.js 의 소스디렉토리 : https://github.com/joyent/node/

 

https://developer.palm.com/index.php?id=2109&option=com_content&view=article 에 따르면, Hp webOS 2.1에 탑재(built-in)되어 있다.

image

아키텍처는 다음과 같다. (http://blog.zenika.com/index.php?post/2011/04/10/NodeJS)

image

 

중요 라이브러리는 2가지가 있다.

1. C event loop 라이브러리 (libev) : libev - a high performance full-featured event loop written in C
(http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod)

* 이 라이브러리의 주요 특징은 Timer(ev_timer), FD(ev_io), 시그널(ev_signal), 상태(ev_stat)이 있다.

// a single header file is required
  #include <ev.h>

  #include <stdio.h> // for puts

  // every watcher type has its own typedef'd struct
  // with the name ev_TYPE
  ev_io stdin_watcher;
  ev_timer timeout_watcher;

  // all watcher callbacks have a similar signature
  // this callback is called when data is readable on stdin
  static void
  stdin_cb (EV_P_ ev_io *w, int revents)
  {
    puts ("stdin ready");
    // for one-shot events, one must manually stop the watcher
    // with its corresponding stop function.
    ev_io_stop (EV_A_ w);

    // this causes all nested ev_run's to stop iterating
    ev_break (EV_A_ EVBREAK_ALL);
  }

  // another callback, this time for a time-out
  static void
  timeout_cb (EV_P_ ev_timer *w, int revents)
  {
    puts ("timeout");
    // this causes the innermost ev_run to stop iterating
    ev_break (EV_A_ EVBREAK_ONE);
  }

  int
  main (void)
  {
    // use the default event loop unless you have special needs
    struct ev_loop *loop = EV_DEFAULT;

    // initialise an io watcher, then start it
    // this one will watch for stdin to become readable
    ev_io_init (&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ 0, EV_READ);
    ev_io_start (loop, &stdin_watcher);

    // initialise a timer watcher, then start it
    // simple non-repeating 5.5 second timeout
    ev_timer_init (&timeout_watcher, timeout_cb, 5.5, 0.);
    ev_timer_start (loop, &timeout_watcher);

    // now wait for events to arrive
    ev_run (loop, 0);

    // break was called, so exit
    return 0;
  }

 

소개 자료는 다음과 같다.

Libev is an event loop: you register interest in certain events (such as a file descriptor being readable or a timeout occurring), and it will manage these event sources and provide your program with events.

To do this, it must take more or less complete control over your process (or thread) by executing the event loop handler, and will then communicate events via a callback mechanism.

You register interest in certain events by registering so-called event watchers, which are relatively small C structures you initialise with the details of the event, and then hand it over to libev by starting the watcher.

 

Libev supports select, poll, the Linux-specific epoll, the BSD-specific kqueue and the Solaris-specific event port mechanisms for file descriptor events (ev_io), the Linux inotify interface (for ev_stat), Linux eventfd/signalfd (for faster and cleaner inter-thread wakeup (ev_async)/signal handling (ev_signal)) relative timers (ev_timer), absolute timers with customised rescheduling (ev_periodic), synchronous signals (ev_signal), process status change events (ev_child), and event watchers dealing with the event loop mechanism itself (ev_idle, ev_embed, ev_prepare and ev_check watchers) as well as file watchers (ev_stat) and even limited support for fork events (ev_fork).

It also is quite fast (see this benchmark comparing it to libevent for example).


2. c thread pool 라이브러리 (liibeio)

http://pod.tst.eu/http://cvs.schmorp.de/libeio/eio.pod

* async 기능 제공

예제 파일

static struct ev_loop *loop;
static ev_idle repeat_watcher;
static ev_async ready_watcher;

/* idle watcher callback, only used when eio_poll */
/* didn't handle all results in one call */
static void
repeat (EV_P_ ev_idle *w, int revents)
{
  if (eio_poll () != -1)
    ev_idle_stop (EV_A_ w);
}

/* eio has some results, process them */
static void
ready (EV_P_ ev_async *w, int revents)
{
  if (eio_poll () == -1)
    ev_idle_start (EV_A_ &repeat_watcher);
}

/* wake up the event loop */
static void
want_poll (void)
{
  ev_async_send (loop, &ready_watcher)
}

void
my_init_eio ()
{
  loop = EV_DEFAULT;

  ev_idle_init (&repeat_watcher, repeat);
  ev_async_init (&ready_watcher, ready);
  ev_async_start (loop &watcher);

  eio_init (want_poll, 0);
}

특징

This library provides fully asynchronous versions of most POSIX functions dealing with I/O. Unlike most asynchronous libraries, this not only includes read and write, but also open, stat, unlink and similar functions, as well as less rarely ones such as mknod, futime or readlink.

It also offers wrappers around sendfile (Solaris, Linux, HP-UX and FreeBSD, with emulation on other platforms) and readahead (Linux, with emulation elsewhere>).

The goal is to enable you to write fully non-blocking programs. For example, in a game server, you would not want to freeze for a few seconds just because the server is running a backup and you happen to call readdir.

 

 

3. V8 (구글 작품)

http://code.google.com/p/v8/

http://code.google.com/intl/ko-KR/apis/v8/embed.html

Posted by '김용환'
,

 

Webwork를 MVC Framework로, BO, DAO를 Spring으로 쓰다가 Spring 2.X대의 MVC 의 xml bean 설정 때문에 귀찮아했는데.. 그러나 Spring 3.0부터는 annotation을 추가하면서 좋아졌다. (반면 점점 복잡해질 웹 프로젝트의경우는 xml 설정으로 가는 게 더 나을 수 있다. annotation을 쓰면 클래스 이름을 잘 맞춰야 할 것 같다. )

아래 링크를 토대로 Spring 3 MVC Annoataion 기초 내용을 바탕으로 한 자료를 가지고 아주 간단한 maven 프로젝트를 만들었다.

http://www.raistudies.com/spring-mvc-tutorial/
http://www.mkyong.com/spring-mvc/spring-mvc-form-handling-annotation-example/
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html
http://blog.springsource.org/2010/07/22/spring-mvc-3-showcase/
http://www.mkyong.com/spring-mvc/spring-mvc-handler-interceptors-example/
http://blog.springsource.org/2009/12/21/mvc-simplifications-in-spring-3-0/

 

> > Spring MVC 3 맛보기 maven 프로젝트 다운

 

 

war 파일 없이 간단하게 mvn clean jetty:run을 이용해서 쉽게 테스트가 가능하다.

Posted by '김용환'
,

 

Jetty는 Websocket를 지원하고 있는데, 톰캣은 지원하지 않고 있다. 그러나 톰캣도 조만간에 web socket을 지원할 것 같다. 아직 공지를 하지 않았지만, 리더인 Mark Tomas는 이메일을 통해서 속도보다는 기능 측면에서 구현한 코드를 내어놓았다. 구조를 크게 바꾸지 않은 상태로 진행하려는 Mark Tomas의 노력이 있어보인다. 

http://people.apache.org/~markt/patches/draft/2012-01-24-websocket.patch

코드를 완벽히 구현한 것은 아니고 간단하게 틀만 제공한 것으로서 톰캣 내부 개발자들이 잘 만들리라 생각된다. (참고로. 톰캣과 아파치 쪽은 상당히 보수적으로 운영되고 있어서 왠만하면 잘 구현 안해준다. 커미터들도 상당히 제한된 사람들로 이루어진 부분이 있다. 개인적으로 친해진다면 잘 구현해줄지도.. )

 

소스 내용을 잠깐 소개하면 다음과 같다. (내용은 언제든지 바뀔 수 있을 것이다.)

1. org.apache.catalina.websocket 패키지를 추가

2. 소켓에 Upgrade 개념을 추가함

SocketState 에 Upgrade를 추가

public abstract class AbstractEndpoint {

         public enum SocketState {
             // TODO Add a new state to the AsyncStateMachine and remove
             //      ASYNC_END (if possible)
-            OPEN, CLOSED, LONG, ASYNC_END, SENDFILE
+            OPEN, CLOSED, LONG, ASYNC_END, SENDFILE, UPGRADE
         }

 

3. 클래스 구조

image

 

 

image

 

image

 

image

 

4. 예제

클라이언트

+<script type="text/javascript">
+function echo() {
+  if ("WebSocket" in window) {
+    alert("WebSocket is supported by this browser.");
+
+    // TODO: Can we use relative URLs?
+    var ws = new WebSocket("ws://localhost:8080/examples/websocket/echo");
+    ws.onopen = function() {
+      ws.send("Connection opened");
+      alert("WebSocket connection opened.");
+    }
+    ws.onmessage = function(event) {
+      alert("Received: " + event.data);
+    }
+    ws.onclose = function() {
+      alert("WebSocket connection closed.");
+    }
+    // TODO: Extend with a text box for users to enter data
+  } else {
+    alert("WebSocket is not supported by this browser.");
+  }
+}
+</script>

 

자바 서블릿 코드

+public class Echo extends WebSocketServlet {
+
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    protected StreamListener createWebSocketConnection() {
+        return new EchoListener();
+    }
+
+    private static final class EchoListener extends StreamListener {
+
+        @Override
+        protected void onBinaryData(InputStream is) throws IOException {
+            // Simply echo the data to stdout
+            int i = is.read();
+            while (i != -1) {
+                System.out.write(i);
+                i = is.read();
+            }
+        }
+
+        @Override
+        protected void onTextData(Reader r) throws IOException {
+            char[] c = new char[1];
+            int i = r.read(c);
+            while (i != -1) {
+                System.out.println(c);
+                i = r.read(c);
+            }
+        }
+    }
+}

Posted by '김용환'
,

 

톰캣 정보를 공유하는 tomcatexpert.com에서 2011년에 팁&테크로 올린 내용 중 인기있는 내용 10개를 추려 놓았다.  이 중 괜찮은 내용만 뽑아본다.

http://www.tomcatexpert.com/blog/2012/01/04/year-review-2011

 

1. Cross-site Scripting (XSS) Prevention in Apache Tomcat 7

http://www.tomcatexpert.com/blog/2011/01/26/cross-site-scripting-xss-prevention-tomcat-7

Setting useHttpOnly to true in the $CATALINA_BASE/conf/context.xml file will turn on cross-site script protection for all webapps.

 

2. Performance Tuning the JVM for Running Apache Tomcat

http://www.tomcatexpert.com/blog/2011/11/22/performance-tuning-jvm-running-tomcat

-server, –Xss, -XX:PermSize and -XX:MaxPermSize, -Xms and -Xmx

-XX:+UseParallelGC, -XX:+UseConcMarkSweepGC, -XX:+UseParallelOldGC, -XX:+CMSIncrementalMode

 

3. Setting Up Measurement of Garbage Collection in Apache Tomcat

http://www.tomcatexpert.com/blog/2011/11/16/setting-measurement-garbage-collection-apache-tomcat

  • -Xloggc:$CATALINA_HOME/logs/gc.log or   Xloggc:%CATALINA_HOME%/logs/gc.log
  • -XX:+PrintHeapAtGC
  • -XX:+PrintGCDetails
  • -XX:+PrintGCTimeStamps
  • -XX:-HeapDumpOnOutOfMemoryError

 

4. A Finer Point of Apache Tomcat Valves

http://www.tomcatexpert.com/blog/2011/11/11/finer-point-apache-tomcat-valves

Posted by '김용환'
,

 

Tomcat 클래스 로더에 대한 패치가 6.0.28에 이루어졌다. 클래스 로딩이 갑자기 많아져서, 중복된 클래스를 찾는 부분이 일어나 링크에러가 나는 경우이다. 이것 때문에 deadlock이 발생하는 케이스이다. (동기화 문제는 늘 어려운 문제이다. )

이 문제는 클래스로딩이 많은 웹 서비스에서 어쩌다 한번 나타날 수 있으며 그 파장은 deadlock이다. WebappClassLoader 클래스의 loadClass 메서드와 findClass 안에서 로딩한 클래스가 두 개가 되면서 나타나는 현상이다.

이에 대한 톰캣 패치 내역과 소스를 살펴본다.

http://tomcat.apache.org/tomcat-6.0-doc/changelog.html

44041: Fix threading issue in WebappClassLoader that can lead to duplicate class definition under high load. (markt/fhanik)

 

https://issues.apache.org/bugzilla/show_bug.cgi?id=44041

Class clazz = Thread.currentThread().getContextClassLoader().loadClass("<Class Name>"); java.lang.LinkageError: duplicate class definition: com/ubikingenierie/bug/ClassLoadedDynamically at java.lang.ClassLoader.defineClass1(Native Method)

 

 

http://svn.apache.org/viewvc?view=revision&revision=941868

기존 코드 상으로는 클래스 로딩시 클래스의 이름을 가지고 했다. 이는 쓰레드 경쟁 관계에서 똑같은 클래스를 읽으므로서,

 

1. jsp 클래스 로딩시 synchronized가 새로 추가되었다.

/tomcat/tc6.0.x/trunk/java/org/apache/jasper/servlet/JasperLoader.java

+ public synchronized Class loadClass(final String name, boolean resolve) throws ClassNotFoundException {

 

2.  컴파일러가 로드된 클래스를 최적화되지 않도록 한다.

tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/ResourceEntry.java

+ public volatile Class loadedClass = null;

 

3. 톰캣 디폴트 클래스 로더인 WebappClassLoader 클래스에서 클래스 이름에 대한 synchronized가 아닌 메소드 단위의 synchronized로 처리함으로서, 속도보다는 안정성을 더 중점을 둘 수 있다.

 

tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java

public Class loadClass(String name, boolean resolve)  {

  synchronized (name.intern()) {

…..

}

 

=>

public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {

}

 

 

protected Class findClassInternal(String name) {

    synchronized (name.intern()) {

       clazz = entry.loadedClass;

        if (clazz != null)

                return clazz;

…..

}

 

protected Class findClassInternal(String name) {

    synchronized (this) {

       clazz = entry.loadedClass;

        if (clazz != null)

                return clazz;

…..

}

Posted by '김용환'
,