Hot deploy for enterprise Java architecture without vendor specialized technology

posted Mar 27, 2011, 5:28 AM by Kuwon Kang   [ updated May 24, 2011, 10:48 PM ]

Main-Frame시대에서 hot deployment는 당연한 기술적 접근이었다. 

업무 수행중 버그가 발견되고 긴급히 patch해야하는 경우 cobol 소스를 바로 수정하여 운영계에 배포하면 운영중지 없지
즉시 반영되어 업무를 수행할 수 있었다.

그러나 Java에서는 이 Hot Deployment는 해결되어지지 않는 영원한 숙제처럼 여겨진다.

Web Application Server Vendor들이 나름대로의 기술적 안을 가지고 Hot Deployment를 지원한다고 하지만 실제 Enterprise에서 이를 사용하는 경우는 거의 존재하지 않는다.

설령 된다고 할 지라도 여러 기술적 한계와 다른 특성들과 결합될 때 운영상에 많은 문제점들이 발견되고 또한 Vendor자체가 제한하는 기술적 요소 발생한다.

따라서 hot deployment는 Java영역에서는 풀리지 않는 신비? 또는 넘을 수 없는 장벽? 된다고 정말 되는거야? 라고 의아해 하는 기술이 되어 버렸다.

그러나 방법이 없는 것은 아니다. 
운영중 무정지 상태로 긴급 반영 되어야 하는 업무 application을 JVM restart또는 Web Application Context Reload없이 지원할 수 있는 기술적 대안이 있다.

Hot deployment의 대안이 될 수 있는 이 기술에 대한 방법과 기술적 근거, 아키텍처를 살펴 보도록 하겠다.

발표자료는 아래 첨부 파일을 참조하세요.

About hot deployment
  • Hot deployment 정의
  • Hot deployment 란 무엇인가?
  • 사용자들은 왜 Hot deployment를 믿지 못하는가?
  • 일반적인 Hot deployment 문제들

Idea, idea, idea
  • 새로운 Hot deployment의 발상.
  • 전통적인 Hot deployment 기술구조
  • Idea 구현을 위한 Hot deployment 조건
  • Idea design concept
  • Bean Life Cycle (Bean factory)
  • Rich Internet Application
  • Abstract Value Object
Technical Architecture
  • Session Facade Pattern
  • Adapter
  • Service Broker
  • Class loader
  • J2ee class loader
  • Hot class loader
  • Hot context
  • Context version
  • Hot deploy manager
  • Version life cycle
  • Context remover
  • Object creation
  • JVM에서 일어나는 일들.
  • JVM Heap
  • JVM Runtime Data Area
  • Method area
  • Context in JVM
Benefit and limitations of the idea
  • Benefits
  • Limit


CORBA Technology and the Java Platform

posted Aug 3, 2010, 5:15 PM by Kuwon Kang


CORBA Technology and the Java Platform


CORBA (Common Object Request Broker Architecture) technology is the open standard for heterogeneous computing. CORBA complements the Java platform by providing a distributed objects framework, services to support that framework, and interoperability with other languages. The Java platform complements CORBA by providing "Write Once, Run AnywhereTM" portability, a highly productive implementation environment, and a very robust platform. By combining the Java platform with CORBA and other key enterprise technologies, Java 2 Platform, Enterprise Edition creates the ultimate platform for enterprise solutions.

CORBA standards provide the proven, interoperable infrastructure to Java 2 Platform, Enterprise Edition. CORBA technology is an integral part of the Java 2 platform through Enterprise JavaBeans, RMI over IIOP, Java IDL, and Java Transaction Service.

Enterprise JavaBeans technology (EJB)

Enterprise JavaBeans is part of the Java 2 Platform, Enterprise Edition. The EJB server-side component model simplifies development of middleware components that are transactional, scalable, and portable. EJB servers reduce the complexity of developing middleware by providing automatic support for middleware services such as transactions, security, database connectivity, and more.

EJBs use the RMI/IDL CORBA subset for their distributed object model, and use the Java Transaction Service (JTS) for their distributed transaction model. When Enterprise JavaBeans are implemented using the RMI-IIOP protocol for EJB interoperability in heterogeneous server environments, the standard mapping of the EJB architecture to CORBA enables the following interoperability:

  • A client using an ORB from one vendor can access enterprise beans residing on an EJB server provided by another vendor.
  • Enterprise beans in one EJB server can access enterprise beans in another EJB server.
  • A non-Java platform CORBA client can access any enterprise bean object.


RMI over IIOP is part of the Java 2 Platform, Enterprise Edition and Java 2 Platform, Standard Edition. RMI over IIOP provides the ability to write CORBA applications for the Java platform without learning CORBA Interface Definition Language (IDL). To work with CORBA applications in other languages, IDL can be generated from Java programming language interfaces. RMI over IIOP includes the full functionality of a CORBA Object Request Broker.

Java IDL

Java IDL is part of the Java 2 Platform, Standard Edition. Java IDL includes a CORBA Object Request Broker (ORB) that is available in every deployment of the Java 2 Platform, Standard Edition. To develop applications with Java IDL, define the interfaces in IDL (Interface Definition Language), and use the IDL-to-Java compiler, idlj to generate the CORBA stubs, skeletons, and ties.

Java Transaction Service

Java Transaction Service (JTS) is based on the CORBA Object Transaction Service (OTS). JTS is part of the Java 2 Platform, Enterprise Edition and provides an implementation of the Java Transaction API (JTA).

Tackling Misconceptions About Spring

posted Jun 20, 2010, 9:17 PM by Kuwon Kang

Posted by Scott Delap on Aug 08, 2006 08:36 AM


Spring has transitioned from a bleeding edge project to widely used component of enterprise applications written in Java today.  As with any popular project misconceptions start to arise.  Steve Anglin recently blogged on oreillynet.com about 10 common misconceptions developers have about Spring, summarized here:

    1. Spring is not lightweight. It aims to do everything and is becoming fat and bloated.
    ... the Spring Framework has been designed from day one to be non-invasive and modular. You can and should choose only what is needed for your particular project, but as requirements evolve, you can incrementally add (or remove) modules. The Spring team emphasizes keeping things as simple as possible ...2. Spring is overkill for simple applications.
    As mentioned in the previous point, the Spring Framework can help you to keep things as simple as possible. You can use only what you need initially yet adapt and extend your application as requirements become more clear....

    3. Spring does not scale to handle very large applications
    Spring is used for very large applications by hundreds of the biggest corporations in the world. The Spring Framework is well-established in the enterprise development space where it is meeting the demands of applications for banking, government, and the airline industry ...

    4. Spring forces you to use Aspect-Oriented Programming (AOP) which is experimental.
    Some of Spring’s powerful capabilities leverage a technique called Aspect-Oriented Programming or AOP (see chapters 3 and 4), but as always you can choose whether or not to use those particular features ...

    5. Spring replaces Java Enterprise Edition (JEE).
    This is one of the most common misconceptions about Spring and yet one of the most misguided. In fact, Spring is largely about Java Enterprise Edition (JEE) integration. Spring offers a compelling alternative to the traditional, heavyweight Enterprise JavaBean-based component model while still leveraging the full power of the other JEE standards ...

    6. Spring and EJB are mutually exclusive.
    In the previous point, we described Spring as “a compelling alternative to” EJB. However, Spring promotes choice - including the choice of using EJB. 7. Spring cannot take advantage of Java 5 annotations like EJB3 does.
    Pitchfork , an add-on for the Spring Framework, supports JSR-250 and a subset of the JSR-220 (EJB3) annotations. Pitchfork is a result of collaboration between Interface21 and BEA and is published under the Apache 2.0 license ...

    8. For a large application, Spring’s XML can become a maintenance nightmare.
    If not managed properly, Spring XML files can indeed become too hard to edit and too easy to break. Here we point out how using the right tools and the right techniques can address some of the specific issues you may encounter ...

    9. Spring does everything with reflection, so it is slow.
    Spring does not do everything with reflection, but it does make extensive use of reflection. Therefore, we want to clarify the role of reflection in Spring and discuss the performance characteristics of reflection in general ...

    10. Spring MVC is complex and not as simple as the rest of Spring.
    Spring MVC is highly-configurable and highly-extensible due to its interface-based design. It uses well-established design patterns such as the strategy (e.g. ViewResolver) and the template method (e.g. SimpleFormController) ...

Spring WS에서 Why Contract fist?

posted Jun 20, 2010, 9:16 PM by Kuwon Kang

Spring Webservice  를 공부하다  Object-Relational Impedance Mismatch 내용을 보게 되었다.

즉 RDBMS의 Entity가 Object화 될 때 생기는 문제가 반드시 존재하게 되고 이를 해결하기 위해서는

데이터 베이스 디자인과 개발자 사이에 매우 긴밀하고 완전한 기술적인 이해와 상호 협력이 필요하는 것이다.

 따라서 이는 현실적으로 어려우므로 RDBMS를 객체지향중심으로 표현하기 위해서는 결국

임피던스 불일치가 있을 수 밖에 없다.

따라서 스프링은 이 문제와 더불어 구조적 언어인 xml, xsd와 같은곳에서 표현하게 되는 restrction과 같은 표현은 사실상 object에서는 구현이 불가하게 된다.

이 문제를 해결하기 위해서는 결국 WSDL contract를 먼저 수행하고 java를 implement하게 된다.

따라서 Spring이 구현한 것은 바로 Contract First를 사용한다고 하니 참 재미난 내용이다.


Tips for improving Spring’s start up performance

posted Jun 20, 2010, 9:10 PM by Kuwon Kang

Someone is blathering on again about how Spring sucks, and so startup performance came up in the conversation. CXF uses Spring under the covers by default (it’s optional, don’t worry). Initially there was some major slowness, but I’ve spent a lot of time profiling/improving CXF’s startup time. I thought I’d share what I learned so you can improve your startup time as well:

  1. Use the latest version of Spring (2.0.5+). The latest version of Spring contains may performance improvements (including one I found while profiling CXF).
  2. Reduce the number of configuration files. More configuration == more XML to parse.
  3. Reduce the number of <bean>s in your configuration. Contrary to popular belief, not every friggin class needs to be a <bean>. Startup time is pretty proportional to the number of beans (if you follow the suggestion in #4).
  4. Don’t use classpath*:*.xml type wildcards. These significantly slow down the starup process as Spring needs to search through all the jars. In some cases it needs to expand them (I think on certain appservers). Doing a classpath:/foo/*.xml search is much better as that can delegate to the JDK. Although further performance gains can be made by removing wildcards together I think.
  5. Reduce your number of beans - just because you can make it a <bean> doesn’t mean you should.
  6. lazy-init=”true” is your friend. Don’t load a <bean> until you need to.
  7. Turn off schema validation (Alternately: anyone want to rewrite Xerces?) - this adds about 20% more time to startup in the CXF case.

If all goes well, you should be able to get the Spring startup time pretty low. At this point its probably other things, like your ORM layer (*cough* Hibernate *cough*), that are slowing things down.

Random tidbit: I was profiling the CXF startup the other day. About 33% of the time is in Introspector.getBeanInfo(), so its really not all Spring’s fault IMO :-). Maybe someone can hack the JDK to be faster there?

Scaling Your Java EE Applications

posted Jun 20, 2010, 7:54 PM by Kuwon Kang

Discuss this Article


If an application is useful, then the network of users will grow crazily fast at some point. As more and more mission-critical applications are now running on Java EE, many Java developers are caring about scalability issues. However, most of popular Web 2.0 sites are built with script languages, and there are a lot of voices to doubt the scalability of Java Applications. In this article, Wang Yu takes real world cases as examples to explain ways on how to scale Java applications based on his experiences on the laboratory projects, and at the same time, bring together practice, science, algorithms, frameworks, and experience on failed projects, to help readers on building high scalable Java applications.

J2EE Clustering

posted Jun 20, 2010, 7:52 PM by Kuwon Kang

Under the Hood of J2EE Clustering August 2005 

Discuss this Article


More and more mission-critical and large scale applications are now running on Java 2, Enterprise Edition (J2EE). Those mission-critical applications such as banking and billing ask for more high availability (HA), while those large scale systems such as Google and Yahoo ask for more scalability. The importance of high availability and scalability in today's increasingly inter-connected world can be proved by a well known incident: a 22-hour service outage of eBay in June 1999, caused an interruption of around 2.3 million auctions, and made a 9.2 percent drop in eBay's stock value.

J2EE clustering is a popular technology to provide high available and scalable services with fault tolerance. But due to the lack of support from the J2EE specification, J2EE vendors implement clustering differently, which causes a lot of trouble for J2EE architects and developers. Following questions are common:

  • Why are the commercial J2EE Server products with Clustering capabilities so expensive? (10 times compared with no clustering capabilities)
  • Why does my application built on stand-alone J2EE server not run in a cluster?
  • Why does my application run very slowly in a cluster while much faster in non-clustered environment?
  • Why does my cluster application fail to port to other vendors’ server?

The best way to understand the limitations and considerations is to study their implementations and uncover the hood of J2EE clustering.

Basic Terminology

It makes sense to understand the different concepts and issues that underlie clustering technology before we discuss the different implementations. I hope this will not only give you the foundation necessary to understand various design issues and concepts in J2EE clustering products, but will also frame the various issues that differentiate clustering implementations and make them easier to understand as well.

ScalabilityIn some large-scale systems, it is hard to predict the number and behavior of end users. Scalability refers to a system’s ability to support fast increasing numbers of users. The intuitive way to scale up the number of concurrent sessions handled by a server is to add resources (memory, CPU or hard disk) to it. Clustering is an alternative way to resolve the scalability issue. It allows a group of servers to share the heavy tasks, and operate as a single server logically.

High AvailabilityThe single-server’s solution (add memory and CPU) to scalability is not a robust one because of its single point of failure. Those mission-critical applications such as banking and billing cannot tolerate service outage even for one single minute. It is required that those services are accessible with reasonable/predictable response times at any time. Clustering is a solution to achieve this kind of high availability by providing redundant servers in the cluster in case one server fails to provide service.

Load balancingLoad balancing is one of the key technologies behind clustering, which is a way to obtain high availability and better performance by dispatching incoming requests to different servers. A load balancer can be anything from a simple Servlet or Plug-in (a Linux box using ipchains to do the work, for example), to expensive hardware with an SSL accelerator embedded in it. In addition to dispatching requests, a load balancer should perform some other important tasks such as “session stickiness” to have a user session live entirely on one server and “health check” (or “heartbeat”) to prevent dispatching requests to a failing server. Sometimes the load balancer will participant in the “Failover” process, which will be mentioned later.

Fault ToleranceHighly available data is not necessarily strictly correct data. In a J2EE cluster, when a server instance fails, the service is still available, because new requests can be handled by other redundant server instances in the cluster. But the requests which are in processing in the failed server when the server is failing may not get the correct data, whereas a fault tolerant service always guarantees strictly correct behavior despite a certain number of faults.

FailoverFailover is another key technology behind clustering to achieve fault tolerance. By choosing another node in the cluster, the process will continue when the original node fails. Failing over to another node can be coded explicitly or performed automatically by the underlying platform which transparently reroutes communication to another server.

Idempotent methodsPronounced “i-dim-po-tent”, these are methods that can be called repeatedly with the same arguments and achieve the same results. These methods shouldn’t impact the state of the system and can be called repeatedly without worry of altering the system. For example, “getUsername()” method is an idempotent one, while “deleteFile()” method isn’t. Idempotency is an important concept when discussing HTTP Session failover and EJB failov

Powerful EJB3.1

posted Jun 20, 2010, 7:52 PM by Kuwon Kang

Guide to the EJB 3.1 Public Draft

Now that the EJB 3.1 Public Draft is available, here's a list of its main ease-of-use improvements and features, along with their corresponding sections in the spec. This should make it easier to quickly find some of the most relevant material. While it's true that the spec is a bit, shall we say, on the "large" side, I think you'll find that most of the new content is concentrated in a relatively small number of pages. Happy reviewing :-)

  • The "no interface" Local view (3.4.4 , 4.9.8)
  • Portable global JNDI names (4.4)
  • Asynchronous session bean invocations (4.5)
  • Singleton session beans (4.8)
  • Startup / Shutdown callbacks (4.8.1, 4.8.2)
  • Calendar-based timer expressions (18.2.1)
  • Automatic timer creation (18.2.2)
  • Non-persistent Timers (18.2.3)
  • Deployment of EJB components directly in a .war without an ejb-jar (20.4)
  • EJB "Lite" -- a standard lightweight subset of the EJB API (21.1)
  • Embeddable EJB -- an API for executing EJB components within Java SE (Ch. 22)

How to find bottleneck in J2EE application

posted Jun 20, 2010, 7:45 PM by Kuwon Kang   [ updated Jun 20, 2010, 7:51 PM ]

How to find bottleneck in J2EE application

자바스터디 네트워크 [www.javastudy.co.kr]

조대협 [bcho_N_O_SPAM@j2eestudy.co.kr]

J2ee application을 운영하다보면, 시스템이 극도로 느려지거나, 멈춰버리는 현상이 생기고는 한데, 분명히 개발하면서 테스트할때는 문제가 없었는데, 왜 이런일이 생기고, 어떻게 대처해야하는지에 대해서 알아보도록 하자.

일반적으로 J2ee application을 서비스 하기 위해서는 아래와 같은 구조를 가지게 된다.

<그림 1. 일반적인 J2ee application의 구조>
J2ee application의 동작에 필요한 구성 요소를 나눠보면 위와 같이 Network, User Application (이하 User AP), WAS 또는 Servlet Engine(이하 통칭해서 WAS),JVM과 RDBMS 이렇게 크게 다섯가지 조각으로 나뉘어 진다. 물론 JCA를 이용해서 Legacy와 연결할 수 도 있고, RMI/CORBA를 이용하여 다른 Architecture를 구현할 수 는 있으나, 이 강좌는 어디까지나 일반론을 설명하고자 하는것임으로 범위에서는 제외하겠다. 

1. Hang up과 slow down현상의 정의

먼저 용어를 정의하도록 하자.. 시스템이 느려지거나 멈추는 현상에 대해서 아래와 같이 용어를 정의하자

    - Hang up : Server Instance는 실행되고 있느나, 아무런 응답이 없는 상황 (멈춤 상태)
    - Slowdown : Server Instance의 response time이 아주 급격히 떨어지는 상태 (느려짐)

이 Hangup과 slowdown현상은, 대부분이 그림 1에서 설명한 다섯가지 요소중 하나 이상의 병목으로 인해서 발생한다. 즉, 이 병목 구간을 발견하고, 그 구간을 제거하면 정상적으로 시스템을 운영할 수 있게 되는것이다.

2. Slow down analysis in WAS & User AP

2-1. WAS의 기본 구조

이 병목 구간을 발견하는 방법에 앞서서, 먼저 WAS 시스템의 기본적인 내부 구조를 이해할 필요가 있다. 

<그림 2. WAS 시스템의 구조>
<그림 2>는 일반적인 WAS의 구조이다.
WAS는 Client로 부터 request를 받아서, 그 Request의 내용을 분석한다 JMS인지, HTTP , RMI request인지를 분석한후 (Dispatcher) 그 내용을 Queue에 저장한다.

Queue에 저장된 내용은 WAS에서 Request를 처리할 수 있는 Working Thread들이 있을때 각각의 Thread들이 Queue에서 Request를 하나씩 꺼내서 각각의 Thread들이 그 기능을 수행한다.

여기서 우리가 주의깊게 봐야하는것은 Thread pooling이라는 것인데.
Thread를 미리 만들어놓고, Pool에 저장한체로 필요할때 그 Thread 를 꺼내서 사용하는 방식이다. (Connection Pooling과 같은 원리이다.)

WAS에서는 일반적으로 업무의 성격마다 이 Thread Pool을 나누어서 만들어놓고 사용을 한다. 즉 예를 들어서 금융 시스템의 예금 시스템과 보험 시스템이 하나의 WAS에서 동작할때, 각각의 업무를 Thread Pool을 나누어서 분리하는 방식이다. 이 방식을 사용하면 업무의 부하에 따라서 각 Thread Pool의 Thread수를 조정할 수 있으며, 만약에 Thread가 모자르거나 deadlock등의 장애사항이 생기더라도 그것은 하나의 Thread Pool에만 국한되는 이야기이기 때문에, 다른 업무에 영향을 거의 주지 않는다.

2-2. Thread Dump를 통한 WAS의 병목 구간 분석

위에서 살펴봤듯이, WAS는 기본적으로 Thread 기반으로 작동하는 Application이다. 우리가 hang up이나 slow down은 대부분의 경우가 WAS에서 현상이 보이는 경우가 많다. (WAS에 원인이 있다는 소리가 아니라.. 다른 요인에 의해서라도 WAS의 처리 속도가 느려질 수 있다는 이야기다.)

먼저 이 WAS가 내부적으로 어떤 Application을 진행하고 있는지를 알아내면 병목 구간에 한층 쉽게 접근할 수가 있다.

1) What is thread dump?

이를 위해서 JVM에서는 Java Thread Dump라는 것을 제공한다.
Thread Dump란 Java Application이 작동하는 순간의 X-Ray 사진, snapshot을 의미한다.
즉 이 Thread dump를 연속해서 수번을 추출한다면, 그 당시에 Application이 어떻게 동작하고 진행되고 있는가를 살펴볼 수 있다.

Thread dump를 추출하기 위해서는
    - Unix 에서는 kill -3 pid
    - Windows 계열에서는 Ctrl + break
를 누르면 stdout으로 thread dump가 추출 된다.

Thread dump는 Application을 구성하고 있는 현재 구동중인 모든 Thread들의 각각의 상태를 출력해준다.

<그림 3-2. Thread dump>
그림 3-2는 전체 Thread dump중에서 하나의 Thread를 나타내는 그림이다.
Thread Dump에서 각각의 Thread는 Thread의 이름과, Thread의 ID, 그리고 Thread 의 상태와, 현재 이 Thread가 실행하고 있는 Prorgam의 스택을 보여준다.

- Thread name
각 쓰레드의 이름을 나타낸다.
※ WAS나 Servlet Engine에 따라서는 이 이름에 Thread Queue이름등을 배정하는 경우가 있다.

- Thread ID
쓰레드의 System ID를 나타낸다. 이 강좌 나중에 이 ID를 이용해서 각 Thread별 CPU 사용률을 추적할 수 있다.

- Thread Status
매우 중요한 값중의 하나로 각 Thread의 현재 상태를 나타낸다. 일반적으로 Thread가 사용되고 있지 않을때는 wait를 , 그리고 사용중일때는 runnable 을 나타내는게 일반적이다.
그외에, IO Wait나 synchronized등에 걸려 있을때 Wait for monitor entry, MW (OS별JVM별로 틀림) 등의 상태로 나타난다.

- Program stack of thread
현재 해당 Thread가 어느 Class의 어느 Method를 수행하고 있는지를 나타낸다. 이 정보를 통해서 현재 WAS가 어떤 작업을 하고 있는지를 유추할 수 있다.

2) How to analysis thread dump

앞에서 Thread dump가 무엇이고, 어떤 정보를 가지고 있는지에 대해서 알아봤다. 그러면 이 Thread dump를 어떻게 분석을 하고, System의 Bottle neck을 찾아내는데 이용할지를 살펴보기로 하자.

System이 Hangup이나 slowdown에 걸렸을때, 먼저 Thread dump를 추출해야 한다. 이때 한개가 아니라 3~5초 간격으로 5개 정도의 dump를 추출한다. 추출한 여러개의 dump를 연결하면 각 Thread가 시간별로 어떻게 변하고 있는지를 판별할 수 있다.

먼저 각각의 Thread 덤프를 비교해서 보면, 각각의 Thread는 적어도 1~2개의 덤프내에서 연속된 모습을 보여서는 안되는게 정상이다. 일반적으로Application은 내부적으로 매우 고속으로 처리되기 때문에, 하나의 Method에 1초이상 머물러 있는다는것은 거의 있을 수 없는 일이다.
Thread Dump의 분석은 각각의Thread가 시간이 지남에 따라서 진행되지 않고 멈춰 있는 Thread를 찾는데서 부터 시작된다.

예를 들어서 설명해보자 . 아래 <그림 3-3>의 프로그램을 보면 MY_THREAD_RUN()이라는 메소드에서 부터 MethodA()aMethodB()aMethodC() 를 차례로 호출하는 형태이다.

<그림 3-3. sample code>
이 프로그램을 수행하는 중에 처음부터 Thread Dump를 추출 하면 대강 다음과 같은 형태일것임을 예상할 수 있다. 함수 호출에 따라서 Stack이 출력되다가 ◇의 단계 즉 MethodC에서 무한루프에 빠지게 되면 더이상 프로그램이 진행이 없기 때문에 똑같은 덤프를 유지하게 된다.

우리는 이 덤프만을 보고 methodC에서 무엇인가 잘못되었음을 유추할 수 있고, methodC의 소스코드를 분석함으로써 문제를 해결할 수 있다.

그렇다면 이제부터 Slow down과 Hang up현상을 유발하는 일반적인 유형과 그 Thread Dump의 모양에 대해서 알아보도록 하자.

[CASE 1] lock contention

잘 알다싶이 Java 는 Multi Threading을 지원한다. 이 경우 공유영역을 보호하기 위해서 Synchronized method를 이용하는데, 우리가 흔히들 말하는 Locking이 이것이다. 

예를 들어 Thread1이 Synchronized된 Method A의 Lock을 잡고 있는 경우, 다른 쓰레드들은 그 Method를 수행하기 위해서, 앞에서 Lock을 잡은 쓰레드가 그 Lock을 반환하기를 기다려야한다. <그림 3-4>

<그림 3-4. Thread 간에 Lock을 기다리는 형태>
만약에 이 Thread 1의 MethodA의 수행시간이 아주 길다면 Thread 2,3,4는 마냥 이 수행을 아주 오랜 시간 기다려야하고, 다음 2번이 Lock을 잡는다고 해도 3,4번 Thread들은 1번과 2번 쓰레드가 끝난 시간 만큼의 시간을 누적해서 기다려야 하기때문에, 수행시간이 매우 느려지는 현상을 겪게 된다.

이처럼 여러개의 Thread가 하나의 Lock을 동시에 획득하려고 하는 상황을 Lock Contention 이라고 한다.

<그림 3-5. Lock Contention 상황에서 Thread의 시간에 따른 진행 상태>
이런 상황에서 Thread Dump를 추출해보면 <그림 3-6> 과 같은 형태를 띠게 된다.

<그림 3-6. Lock Contention에서 Lock을 기다리고 있는 상황의 Thread Dump>
그림 3-6의 덤프를 보면 12번 Thread가 org.apache.axis.utils.XMLUtils.getSAXParser에서 Lock을 잡고 있는것을 볼 수 있고, 36,15,14번 쓰레드들이 이 Lock을 기다리고 있는것을 볼 수 있다.

[CASE 1] 해결방안

이런 Lock Contention 상황은 Multi Threading환경에서는 어쩔 수 없이 발생하는 상황이기는 하지만, 이것이 문제가 되는 경우는 Synchronized Method의 실행 시간이 불필요하게 길거나, 불필요한 Synchronized문을 사용했을 때 발생한다.

그래서 이 문제를 해결하기 위해 불필요한 Sychronized Method의 사용을 자제하고, Synchronized block 안에서의 최적화된 Algorithm을 사용하는것이 필요하다. 

[CASE 2] dead lock

이 Locking으로 인해서 발생할 수 있는 또 다른 문제는 dead Lock 현상이다. 두개 이상의 쓰레드가 서로 Lock을 잡고 기다리는 “환형대기조건” 이 성립되었을때, 서로 Lock이 풀리지 않고 무한정 대기하는 현상을 이야기 한다.

<그림 3-7>을 보면 Thread1은 MethodA를 수행하고 있는데, sychronized methodB를 호출하기전에 Thread2가 methodB가 끝나기를 기다리고 있다. 마찬가지로 Therad2는 Thread3가 수행하고 있는 methodC가 끝나기를 기다리고 있고, methodC에서는 Thread1에서 수행하고 있는 methodA가 끝나기를 기다리고 있다.
즉 각각의 메소드들이 서로 끝나기를 기다리고 있는 “환형 대기조건” 이기 때문에 이 Application의 3개의 쓰레드들은 프로그램이 진행이 되지 않게 된다.

<그림 3-7. 환형 대기조건에 의한 deadlock>
이러한 “환형대기조건”에 의한 deadlock은 Thread Dump를 추출해보면 <그림 3-8> 과 같은 패턴을 띠우고 있으며 시간이 지나도 풀리지 않는다.

<그림 3-8. Deadlock이 걸렸을때 시간 진행에 따른 Thread의 상태>

<그림 3-9. Deadlock이 걸린 IBM AIX Thread Dump>
DeadLock의 검출은 Locking Condition을 비교함으로써 검출할 수 있으며, 최신 JVM (Sun 1.4이상등)에서는 Thread Dump 추출시 만약 Deadlock이 있다면 해당 Deadlock을 찾아주는 기능을 가지고 있다.

<그림 3-9>를 보자. IBM AIX의 Thread 덤프이다. 1)항목을 보면 현재 8번 쓰레드가 잡고 있는 Lock은 10번과 6번 쓰레드가 기다리고 있음을 알 수 있으며. Lock은 OracleConnection에 의해서 잡혀있음을 확인할 수 있다. (아래)

<그림 3-9>의 2)번 항목을 보면 이 6번쓰레드는 OracleStatement에서 Lock을 잡고 있는데, 이 Lock을 8번 쓰레드가 기다리고 있다. (아래)

결과적으로 6번과 8번은 서로 Lock을 기다리고 있는 상황이 되어 Lock이 풀리지 않는 Dead Lock상황이 된다.

[CASE 2] 해결 방안

Dead Lock의 해결 방안은 서로 Lock을 보고 있는것을 다른 Locking Object를 사용하거나 Lock의 방향 (Synchronized call의 방향)을 바꿔줌으로써 해결할 수 있다.

User Application에서 발생한 경우에는 sychronized method의 호출 순서를 “환형대기조건”이 생기지 않도록 바꾸도록 하고.
위와 같이 Vendor들에서 제공되는 코드에서 문제가 생긴경우에는 Vendor에 패치를 요청하도록 하여 해결한다.

[CASE 3] wait for IO response

다음으로 많이 볼 수 있는 패턴은 각각의 Thread들이 IO Response를 기다리는데… IO작업에서 response가 느리게 와서 시스템 처리속도가 느려지는 경우가 있다.

<그림 3-10. Thread들이 IO Wait를 할때 시간에 따른 Thread Dump 상황>

<그림 3-11. Thread가 DB Query Wait에 걸려 있는 stack>
<그림 3-10> 상황은 DB에 Query를 보내고 response를 Thread들이 기다리고 있는 상황이다. AP에서 JDBC를 통해서 Query를 보냈는데, Response가 오지 않으면 계속 기다리게 있게 되고, 다른 Thread가 같은 DB로 보냈는데. Response가 오지 않고, 이런것들이 중복되서 결국은 사용할 수 없는 Thread가 없게 되서 새로운 request를 처리하지 못하게 되고, 기존에 Query를 보낸 내용도 응답을 받지 못하는 상황이다.

<그림 3-11>은 각각의 Thread의 stack dump인데, 그 내용을 보면 ExecuteQuery를 수행한후에, Oracle로 부터 데이타를 read하기 위해 대기 하는것을 확인할 수 있다.

이런 현상은 주로 DB 사용시 대용량 Query(시간이 많이 걸리는)를 보냈거나, DB에 lock들에 의해서 발생하는 경우가 많으며, 그외에도 Socket이나 File을 사용하는 AP의 경우 IO 문제로 인해서 발생하는 경우가 많다.

[CASE 3] 해결 방안

이 문제의 해결 방안은 Thread Dump에서 문제가 되는 부분을 발견한후에, 해당 소스코드나 시스템등에 접근하여 문제를 해결해야한다.

[CASE 4] high CPU usage

시스템이 느려지거나 거의 멈추었을때, 우리가 초기에 해볼 수 있는 조치가 무엇인가 하면, 현재 시스템의 CPU 사용률을 체크해볼 필요가 있다.

해당 시스템의 CPU 사용률이 높은 경우, 문제가 되는경우가 종종 있는데. 이런 문제에서는 CPU를 많이 사용하는 모듈을 찾아내는것이 관건이다.

이를 위해서 먼저 top 이나 glance(HP UX)를 통해서 CPU를 많이 점유하고 있는 Process를 판독한다. 만약 WAS 이외의 다른 process가 CPU를 많이 사용하고 있다면, 그 Process의 CPU 과사용 원인을 해결해야 한다. CPU 사용률이외에도, Disk IO양이 많지는 않은지.. WAS의 JVM Process가 Swap out (DISK로 SWAP되는 현상) 이 없는지 살펴보도록 한다. JVM Process가 Swapping이 되면 실행속도가 엄청나게 느려지기 때문에, JVM Process는 Swap out 되어버리면 안된다.

일단 CPU를 과 사용하는 원인이 WAS Process임을 발견했으면 프로그램상에 어떤 Logic이 CPU를 과점유하는지를 찾아내야한다.

방식을 정리해보면 다음과 같다.
WAS Process의 Thread별 CPU 사용률을 체크한다. ← 1)
Thread Dump를 추출한다. ← 2)

1)과, 2)에서 추출한 정보를 Mapping하여, 2)의 Thread Dump상에서 CPU를 과점유하는 Thread를 찾아내어 해당 Thread의 Stack을 분석하여 CPU 과점유하는 원인을 찾아낸다.

대부분 이런 요인을 분석해보면 다음과 같은 원인이 많다.

과도한 String 연산으로 인해서 CPU 사용률이 높아지는 경우

잘 알고 있다시피 String 연산은 CPU 를 아주 많이 사용한다. String과 Loop문(for,while등)의 사용은 CPU부하를 유발하는 경우가 많기 때문에 가급적이면 String Buffer를 사용하도록 하자. 

과도한 RMI Cal
RMI호출은 Java Object를 Serialize하고 Deserialize하는 과정을 수반하는데, 이는 CPU를 많이 사용하는 작업이기 때문에 사용에 주의를 요한다. 특별히 RMI를 따로 코딩하지 않더라도 EJB를 호출하는것이 Remote Call일때는 기본적으로 RMI호출을 사용하게 되고, 부하량이 많을때 이 부분이 주로 병목의 원인이 되곤한다. 특히 JSP/ServletaEJB 호출되는것이 같은 System의 같은 JVM Process안이라도 WAS별로 별도의 설정을 해주지 않으면 RMI Call을 이용하는 형태로 구성이 되기 때문에, 이에 대한 배려가 필요하다.

※ WebLogic에서 Call By Reference를 위한 호출 방법 정의

참고로 WebLogic의 경우에는 Servlet/JSPaEJB를 호출하는 방식을 Local Call을 이용하기 위해서는 같은 ear 파일내에 패키징해야하고, EJB의 weblogic-ejb-jar.xml에 enable-call-by-reference를 true로 설정해줘야한다. (8.1이상)

자세한 내용은
http://www.j2eestudy.co.kr/lecture/lecture_read.jsp?table=j2ee&db=lecture0201_1&id=1&searchBy=subject&searchKey=deploy&block=0&page=0를 참고하시 바란다.

JNDI lookup

JNDI lookup은 Server의 JNDI에 Binding된 Object를 읽어오는 과정이다. 이 과정은 위에서 설명한 RMI call로 수행이되는데. 특히 EJB를 호출하기 위해서 Home과 Remote Interface를 lookup하는 과장에서 종종 CPU를 과점유하는 형태를 관찰 할 수 있다. 

그래서 JNDI lookup의 경우에는 EJB Home Interface를 Caller Side(JSP/Servlet 또는 poor Java client)등에서 Caching해놓고 사용하는 것을 권장한다. 단. 이경우에는 EJB의 redeploy기능을 제약받을 수 있다.

다음은 각 OS별로 CPU 사용률이 높은 Thread를 검출해내는 방법이다.

■ Solaris에서 CPU 사용률이 높은 Thread를 검출하는 방법

1.prstat 명령을 이용해서 java process의 LWP(Light Weight process) CPU 사용률을 구한다.

% prstat -L -p [WeblogicPID] 1 1

<그림 3-6. Lock Contention에서 Lock을 기다리고 있는 상황의 Thread Dump>
2. pstack 명령어를 이용해서 native thread와 LWP 간의 id mapping을 알아낸다.
(※ 전에 먼저 java process가 lwp로 돌아야되는데, startWebLogic.sh에 LD_LIBRARY_PATH에 /usr/lib/lwp 가 포함되어야 한다.)

% pstack [WebLogicPID]

<그림 3-6. Lock Contention에서 Lock을 기다리고 있는 상황의 Thread Dump>
3. 1에서 얻은 LWP ID를 pstack log를 통해서 분석해보면 어느 Thread에 mapping되는지를 확인할 수 있다.
여기서는 LWP 8이 Thread 24과 mapping이 되고 있음을 볼 수 있다. 

kill -3 [WebLogicPID]를 해서 ThreadDump를 얻어낸다.

<그림 3-6. Lock Contention에서 Lock을 기다리고 있는 상황의 Thread Dump>
Thread dump에서 nid라는 것이 있는데, 2에서 얻어낸 thread id를 16진수로 바꾸면 이값이 nid값과 같다. 즉 2에서 얻어낸 thread 24는 16진수로 0x18이기 때문에, thread dump에서 nid가 0x18인 쓰레드를 찾아서 어떤 작업을하고 있는지를 찾아내면 된다.

■ AIX Unix에서 CPU 사용률이 높은 Thread 검출해 내기

1. ps 명령을 이용하여, WebLogic Process의 각 시스템thread의사용률을 구한다.

% ps -mp [WeblogicPID] -0 THREAD

여기서 CP가 가장 높은 부분을 찾는다. 이 시스템 쓰레드가 CPU를 가장 많이 점유하고 있는 시스템 쓰레드이다. (여기서는 66723 이다.)

2. dbx 명령을 이용해서 1.에서 찾은 시스템 쓰레드의 Java Thread ID를 얻어온다.

1) % dbx -a [WebLogicPID]
2) dbx에서 “thread” 명령을 치면 Thread ID 를 Listing할 수 있다.

k-tid 항목에서 1에서 찾은 Thread ID 를 찾고, 그 k-tid에 해당하는 thread id를 찾는다. (여기서는 $t17이 된다.)

3) dbx에서 $t17 번 쓰레드의 Java Thread ID를 얻는다.
dbx에서 “th info 17” 이라고 치면 $t17번 쓰레드의 정보를 얻어온다.

pthread_t 항목에서 Java Thread ID를 얻는다. 여기서는 1011이된다.

3. Java Thread Dump에서 2에서 얻어온 Java Thread ID를 이용해서 해당 Java Thread를 찾아서 Java Stack을 보고 CPU를 많이 사용하는 원인을 찾아낸다.

1) kill -3 [WebLogicPID]
2) Thread dump를 보면 native ID 라는 항목이 있는데, 2.에서 찾은 Java Thread ID와 이 항목이 일치하는 Execute Thread를 찾으면 된다.

■ HP Unix에서 CPU 사용률이 높은 Thread 검출해내기

1. 먼저 JVM이 Hotspot mode로 작동하고 있어야 한다. (classic 모드가 아니어야 한다.) 옵션을 주지 않았으면 Hotspot 모드가 default이다.

2. glance를 실행해서 ‘G’를 누르고 WAS의 PID를 입력한다.
각 Thread의 CPU 사용률이 실시간으로 모니터링이 되는데. 

여기서 CPU 사용률이 높은 Thread의 TID를 구한다.

3. kill -3 을 이용해서 Thread dump를 추출해서. 2에서 구한 TID와 Thread Dump상의 lwp_id가 일치하는 Thread를 찾으면 된다.

지금까지 Thread Dump를 이용하는 방법을 간단하게 살펴보았다. 이 방법을 이용하면 WAS와 그 위에서 작동하는 Appllication의 Slow down 이나 hangup의 원인을 대부분 분석해낼 수 있으나, Thread Dump는 어디까지나 분석을 위한 단순한 정보이다. Thread Dump의 내용이 Slow down이나 hang up의 원인이 될수도 있으나, 반대로 다른 원인이 존재하여 그 결과로 Thread Dump와 같은 Stack이 나올 수 있기 때문에, 여러 원인을 동시에 살펴보면서 분석할 수 있는 능력이 필요하다.

3. Slow down in JVM

WAS의 성능에 큰 영향을 주는것중의 하나가 JVM이다.
JVM의 튜닝 여부에 따라서 WAS상에서 작동하는 Ap의 성능을 크게는 20~30% 까지 향상시킬 수 있는데, 우리가 지금 살펴보고 있는 slow down과 hangup 을 일으키는 직접적인 요인이 되는것은 JVM의 Full GC이다.

간단하게 JVM의 메모리 구조를 검토하고 넘어가보도록 하자.

<그림 4-1. JVM의 메모리 구조>
JVM은 크게 New영역과 Old영역, 그리고 Perm영역 3가지로 분류가 된다.
Perm 영역은 Class나 Method들이 로딩되는 영역이고 성능상의 영향을 거의 미치지 않는다.

우리가 주목해야할 부분은 객체의 생성과 저장에 관련되는 New와 Old 영역인데, 모든 객체는 생성이 되자 마자 New 영역에 저장되고, 시간이 지남에 따라 이 객체들은 Old 영역으로 이동이 된다.

New 영역을 Clear하는 과정을 Minor GC라하고, Old 영역을 Clear하는 과정은 Major GC또는 Full GC라 하는데, 성능상의 문제는 이 Full 영역에서 발생한다.

Minor GC의 경우는 1초 이내에 아주 고속으로 이뤄지는 작업이기 때문에, 신경을 쓸 필요가 없지만, Full GC의 경우에는 시간이 매우 오래걸린다.
또한 Full GC가 발생할 동안은 Application이 순간적으로 멈춰 버리기 때문에 시스템이 순간적으로 Hangup 으로 보이거나 또는 Full GC가 끝나면서 갑자기 request가 몰려버리는 현상 때문에 종종 System의 장애를 발생시키는 경우가 있다.

Full GC는 통상 1회에 3~5초 정도가 적절하고, 보통 하루에 JVM Instance당 5회 이내가 적절하다고 여겨진다. (절대 값은 없다.)

Full GC가 자주 일어나는것이 문제가 될경우에는 JVM의 Heap영역을 늘려주면 천천히 일어나지만 반대로 Full GC에 소요되는 시간이 증가한다.

개당 Full GC 시간이 오래걸릴 경우에는 JVM의 Heap 영역을 줄여주면 빨리 Full GC가 끝나지만 반대로 Full GC가 자주 일어난다는 단점이 있다.

그래서 이 부분에 대한 적절한 Tuning이 필요하다.

대부분의 Full GC로 인한 문제는 JVM자체나 WAS의 문제이기 보다는 그 위에서 구성된 Application이 잘못 구성되어 메모리를 과도하게 사용하거나 오래 점유하는 경우가 있다.

예를 들어 대용량 DBMS Query의 결과를 WAS상의 메모리에 보관하거나 , 또는 Session에 대량의 데이타를 넣는것들이 대표적인 예가 될 수 가 있다.

좀더 자세한 튜닝 방법에 대해서는 http://www.j2eestudy.co.kr/lecture/lecture_read.jsp?db=lecture0401_1&table=j2ee&id=1를 참고하기 바란다.

4. Slow down analysis in DBMS

Application이 느려지는 원인중의 많은 부분을 차지 하고 있는 것은 DBMS의 성능 문제가 있는 경우가 많다.

흔히들 DBMS Tuning을 받았더니 성능이 많이 향상되었다고 하는 경우가 많은데, 그건 그만큼 DB 설계를 제대로 하지 못했다는 이야기가 된다.

DBMS 자체 Tuning에 대한 것은 이 문서와는 논외기 때문에 제외하기로 하고, DBMS에 전송되는 각각의 SQL문장의 실행 시간을 Trace할 수 있는 것만으로도 많은 성능 향상을 기대할 수 있는데, 간단하게 SQL 문장을 실행시간은 아래 방법들을 이용해서 Trace할 수 있다.

※ http://eclipse.new21.org/phpBB2/viewtopic.php?printertopic=1&t=380&start=0&postdays=0&postorder=asc&vote=viewresult

※ http://www.j2eestudy.co.kr/qna/bbs_read.jsp?table=j2ee&db=qna0104&id=5&searchBy=subject&searchKey=sql&block=0&page=0

5. Slow down analysis in Webserver & network

WAS와 DBMS 앞단에는 WebServer와 Network 이 있기 때문에 이 Layer에서 문제가 되면 속도저하를 가지고 올 수 있다. 필자의 경험상 대부분의 slow down이나 hangup은 이 부분에서는 거의 일어나지 않지만 성능상에 종종 영향을 주는 Factor가 있는데,

WebServer 와Client간의 KeepAlive

특히 WebServer의 Keep Alive설정이 그것이다.
WebBrowser와 WebServer간에는 KeepAlive 설정을 하는것이 좋은데. 그 이유는 WebBrowser에서 하나의 HTML 페이지를 로딩하기 위해서는 Image와 CSS등의 여러 파일등을 로딩하는데, KeepAlive 설정이 없으면 각각의 파일을 로딩하는것에 각각의 Connection을 open,request,download,close를 한다. 잘들알고 있겠지만 Socket을 open하고 close하는데에는 많은 자원이 소요된다. 그래서 한번 연결해놓은 Connection을 계속 이용해서 HTTP data를 주고 받는 설정이 KeepAlive이다.
이 KeepAlive 설정은 웹을 이용한 서비스 제공에서 많은 성능 변화를 주기 때문에 특별한 이유가 없는한 KeepAlive 설정을 유지하기 바란다. 설정 방법은 각 WebServer의 메뉴얼을 참고하기 바란다.

※ Apache2.0의 Keep Alive 설정은
http://httpd.apache.org/docs-2.0/mod/core.html#keepalive를 참고하기 바란다. Default가 KeepAlive 가 On으로 되어 있다.

WebServer와 WAS간의 KeepAlive

WebServer와 WAS간에는 WebServer에서 받은 request를 forward하기 위해서 WebServer Side에 WAS와 통신을 하기 위한 plug-in 이라는 모듈을 설치하게 된다. 이 역시 WebServer와 Client간의 통신과의 같은 원리로 KeepAlive를 설정하게 되는데, 이 역시 성능에 영향을 줄 수 있는 부분이기 때문에 가급적이면 설정하기를 권장한다.

※ WebLogic에서 Webserver와의 KeepAlive설정은
http://e-docs.bea.com/wls/docs81/plugins/plugin_params.html#1143055을 참고하기 바란다.
Default는 KeepAlive가 True로 설정되어 있다.

OS에서 Kernel Parameter 설정

OS의 TCP/IP Parameter와, Thread와 Process등의 Kernel Parameter 설정이 운영에 있어서 영향을 미치는 경우가 있다. 이 Parameter들은 Tuning하기가 쉽지 않기 때문에, WAS또는 OS Vendor에서 제공하는 문서를 통해서 Tuning하기 바란다.

※ WebLogic의 OS별 설정 정보은
http://e-docs.bea.com/platform/suppconfigs/configs81/81_over/overview.html를 참고하기 바란다.
6. Common mistake in developing J2EE Application

지금까지 간단하게나마 J2EE Application의 병목구간을 분석하는 부분에 대해서 알아보았다. 대부분의 병목은 Application에서 발생하는 경우가 많은데, 이런 병목을 유발하는 Application에 자주 발생하는 개발상의 실수를 정리해보도록 하자.

1) Java Programming

sycnronized block
위에서도 설명 했듯이 sychronized 메소드는 lock contention과 deadlock등의 문제를 유발할 수 있다. 꼭 필요한 경우에는 사용을 해야하지만, 이점을 고려해서 Coding해야한다.

String 연산

이미 많은 개발자들이 알고 있는 내용이겠지만 String 연산 특히 String연산중 “+” 연산은 CPU를 매우 많이 소모하게 되고 종종 slow down의 직접적인 원인이 되는 경우가 매우 많다.
String 보다는 가급적 StringBuffer를 사용하기 바란다.

Socket & file handling

Socket이나File Handling은 FD (File Descriptor)를 사용하게 되는데, 이는 유한적인 자원이기 때문에 사용후에 반드시 close명령을 이용해서 반환해야한다. 종종 close를 하지 않아서, FD가 모자르게 되는 경우가 많다.

2) Servlet/JSP Programming

JSP Buffer Size

Jsp 에서는 JSP의 출력 내용을 저장하는 buffer 사이즈를 지정할 수 있다.

<% page buffer=”12kb” %>

이 buffer size는 출력 내용을 buffering했다가 출력하는데, 만약에 쓰고자하는 내용이 Buffer size 보다 클 경우에는 여러번에 걸쳐서 socket write가 일어나기 때문에 performance에 영향을 줄 수 있으므로 가능하다면 buffersize를 화면에 뿌리는 내용의 크기를 예측해서 지정해주는것이 바람직하다. 반대로 너무 큰 버퍼를 지정해버리면 메모리가 불필요하게 낭비 될 수 있기 때문에 이점을 주의하기 바란다.

참고로 jsp page buffer size는 지정해주지 않는경우 default로 8K로 지정된다.

member variable

Servlet/JSP는 기본적으로 Multi Thread로 동작하기 때문에, Servlet과 JSP 내에서 선언된 멤버 변수들은 각 Thread간에 공유가 된다.
그래서 이 변수들을 read/write할경우에는 sychronized method로 구성해야 하는데, 이 synchronized는 속도 저하를 유발할 수 있기 때문에, member 변수로는 read 만 하는 객체를 사용하는게 좋다.

특히 Servlet이나 JSP에서 Data Base Connection을 멤버 변수로 선언하여 Thread간 공유하는 예가 있는데, 이는 별로 좋지 않은 프로그래밍 방법이고, 이런 형태의 패턴은 Servlet이 단 하나만 실행되거나 하는것과 같은 제약된 조건 아래에서만 사용해야 한다.

Out of Memory in file upload

JSP에서 File upload control을 사용하는 경우가 많다. 이 control을 구현하는 과정에서 upload되는 파일 내용을 몽땅 메모리에 저장했다가 업로드가 끝나면 한꺼번에 file에 writing하는 경우가 있는데, 이는 큰 사이즈의 파일을 업로드할때, 파일 사이즈만큼의 메모리 용량을 요구하기 때문에, 자칫하면 Out Of Memory 에러를 발생 시킬 수 있다.
File upload는 buffer를 만들어서 읽고, 파일에 쓰는 작업을 병행하도록 해야한다.

3) JDBC Programming

Connection Leak

JDBC Programming에서 가장 대표적으로 발생되는 문제가 Connection Leak이다. Database Connection을 사용한후에 close않아서 생기는 문제인데,Exception이 발생하였을때도 반드시 Connection을 close하도록 해줘야한다.

<그림. Connection close의 올바른 예>
Out of memory in Big size query result

SQL문장을 Query하고 나오는 resultset을 사용할때, 모든 resultset의 결과를 Vector나 hashtable등을 이용해서 메모리에 저장해놓는 경우가 있다. 이런 경우에는 평소에는 문제가 없지만, SQL Query의 결과가 10만건이 넘는것과 같은 대용량일때 이 모든 데이타를 메모리 상에 저장할려면 Out Of Memory가 나온다.
Query의 결과값을 처리할때는 ResultSet을 직접 리턴받아서 사용하는것이 메모리 활용면에서 좀더 바람직하다.

Close stmt & resultset

JDBC에서 Resultset이나 Statement 객체는 기본적으로 Connection을 close하게 되면 자동으로 닫히게 된다. 그러나 WAS나 Servlet Container의 경우에는 성능향상을 위해서 Connection Pooling 기법을 이용해서 Connection을 관리하기 때문에 Connection Pooling에서 Connection을 close하는것은 실제로 close하는것이 아니라 Pool에 반환하는 과정이기 때문에 해당 Connection에 연계되어 사용되고 있는 Statement나 ResultSet이 닫히지 않는다.

Connection Pooling에서 Statement와 ResultSet을 사용후에 닫아주지 않으면 Oracle에서 too many open cursor와 같은 에러가 나오게된다. (Statement는 DB의 Cursor와 mapping이 된다.)

4) EJB Programming

When we use EJB?

EJB는 분명 강력하고 유용한 개발 기술임에는 틀림이 없다. 그러나 EJB의 장점과 용도를 모르고 사용하면 오히려 안쓰는것만 못한 경우가 많다.
각 EJB 모델 (Session Bean,Entity Bean)이 어떤때 유용한지를 알고 사용하고, 정확한 Transaction Model등을 결정해서 사용해야 한다.

Reduce JNDI look up

위에서도 설명했듯이 EJB의 Home Interface를 lookup 해오는 과정은 객체의 Serialization/DeSerialization을 동반하기 때문에, 시스템 성능에 영향을 줄 수 있다. EJB Home을 한번 look up한후에는 Hashtable등에 저장해서 반복해서 Remote Call(Serialization / DeSerialization)하는 것을 줄이는게 좋다.

Do not use hot deploy in production mode

WAS Vendor 마다 WAS 운영중에 EJB를 Deploy할 수 있는 HotDeploy 기능을 제공한다. 그러나 이는 J2ee spec에 있는 구현이 아니라 각 vendor마다 개발의 편의성을 위해서 제공하는 기능이다. 운영중에 EJB를 내렸다 올리는것은 위험하다. (Transaction이 수행중이기 때문에) Hot Deploy 기능은 개발중에만 사용하도록 하자.

5) JVM Memory tuning

Basic Tuning

Application을 개발해놓고, 운영환경으로 staging할때 별도의 JVM 튜닝을 하지 않는 경우가 많다. 튜닝이 아니더라도 최소한의 메모리 사이즈와 HotSpot VM 모델 (server/client)는 설정해줘야지 어느정도의 Application의 성능을 보장 받을 수 있다. 최소한 메모리 사이즈와 VM모델정도는 설정을 해주고 운영을 하도록 하자.

7. 결론

J2EE Application의 병목구간을 확인하기 위해서는 그 문제를 발견하고 툴과 경험을 이용해서 문제의 원인을 발견하고 제거해야한다.

대부분의 WAS또는 User Application의 slow down이나 hang up은 Thread dump를 통한 분석을 통해서 대부분 발견 및 해결을 할 수 있다.

그외에 부분 JVM이나 WebServer,Network 등에 대해서는 별도의 경험과 Log 분석등을 알아내야하고 DB에 의한 slow down이나 hang up현상은 DB 자체의 분석이 필요하다.

Java theory and practice: Understanding JTS -- The magic behind the scenes

posted Jun 20, 2010, 7:45 PM by Kuwon Kang

How J2EE containers hide the complexity of transaction management
Brian Goetz (brian@quiotix.com), Principal Consultant, Quiotix Corp
In Part 1 of this series, we examined transactions and explored their basic properties -- atomicity, consistency, isolation, and durability. Transactions are the basic building blocks of enterprise applications; without them, it would be nearly impossible to build fault-tolerant enterprise applications. Fortunately, the Java Transaction Service (JTS) and the J2EE container do much of the work of managing transactions for you automatically, so you don't have to integrate transaction awareness directly into your component code. The result is almost a kind of magic -- by following a few simple rules, a J2EE application can automatically gain transactional semantics with little or no additional component code. This article aims to demystify some of this magic by showing how and where the transaction management occurs. What is JTS? JTS is a component transaction monitor. What does that mean? In Part 1, we introduced the concept of a transaction processing monitor (TPM), a program that coordinates the execution of distributed transactions on behalf of an application. TPMs have been around for almost as long as databases; IBM first developed CICS, which is still used today, in the late 1960s. Classic (orprocedural) TPMs manage transactions defined procedurally as sequences of operations on transactional resources (such as databases). With the advent of distributed object protocols, such as CORBA, DCOM, and RMI, a more object-oriented view of transactions became desirable. Imparting transactional semantics to object-oriented components required an extension of the TPM model, in which transactions are instead defined in terms of invoking methods on transactional objects. JTS is just that: a component transaction monitor (sometimes called an object transaction monitor), or CTM. The design of JTS and J2EE's transaction support was heavily influenced by the CORBA Object Transaction Service (OTS). In fact, JTS implements OTS and acts as an interface between the Java Transaction API, a low-level API for defining transaction boundaries, and OTS. Using OTS instead of inventing a new object transaction protocol builds upon existing standards and opens the way for compatibility between J2EE and CORBA components. At first glance, the transition from procedural transaction monitors to CTMs seems to be only a change in terminology. However, the difference is more significant. When a transaction in a CTM commits or rolls back, all the changes made by the objects involved in the transaction are either committed or undone as a group. But how does a CTM know what the objects did during that transaction? Transactional components like EJB components don't have commit() or rollback() methods, nor do they register what they've done with the transaction monitor. So how do the actions performed by J2EE components become part of the transaction?

Back to top

Transparent resource enlistment While the application state is manipulated by components, it is still stored in transactional resource managers (for example, databases and message queue servers), which can be registered as resource managers in a distributed transaction. In Part 1, we talked about how multiple resource managers can be enlisted in a single transaction, coordinated by a transaction manager. Resource managers know how to associate changes in application state with specific transactions. But this just moves the focus of our question from the component to the resource manager -- how does the container figure out what resources are involved in the transaction so it can enlist them? Consider the following code, which might be found in a typical EJB session bean: Listing 1. Transparent resource enlistment with bean-managed transactions
  InitialContext ic = new InitialContext();
  UserTransaction ut = ejbContext.getUserTransaction();

  DataSource db1 = (DataSource) ic.lookup("java:comp/env/OrdersDB");
  DataSource db2 = (DataSource) ic.lookup("java:comp/env/InventoryDB");
  Connection con1 = db1.getConnection();
  Connection con2 = db2.getConnection();
  // perform updates to OrdersDB using connection con1
  // perform updates to InventoryDB using connection con2
Notice that there is no code in this example to enlist the JDBC connections in the current transaction -- the container does this for us. Let's look at how this happens. Three types of resource managers When an EJB component wants to access a database, a message queue server, or some other transactional resource, it acquires a connection to the resource manager (usually by using JNDI). Moreover, the J2EE specification only recognizes three types of transactional resources -- JDBC databases, JMS message queue servers, and "other transactional services accessed through JCA." Services in the latter class (such as ERP systems) must be accessed through JCA (the J2EE Connector Architecture). For each of these types of resources, either the container or the provider helps to enlist the resource into the transaction. In Listing 1, con1 and con2 appear to be ordinary JDBC connections such as those that would be returned fromDriverManager.getConnection(). We get these connections from a JDBC DataSource, which was obtained by looking up the name of the data source in JNDI. The name used in our EJB component to find the data source (java:comp/env/OrdersDB) is specific to the component; the resource-ref section of the component's deployment descriptor maps it to the JNDI name of some application-wide DataSource managed by the container. The hidden JDBC driver Every J2EE container can create transaction-aware pooled DataSource objects, but the J2EE specification doesn't show you how, because it's outside the spec. If you browse the J2EE documentation, you won't find anything on how to create JDBC data sources. You'll have to look in the documentation for your container instead. Depending on your container, creating a data source might involve adding a data source definition to a property or configuration file, or might be done through a GUI administration tool. Each container (or connection pool manager, like PoolMan) provides its own mechanism for creating a DataSource, and it is in this mechanism that the JTA magic is hidden. The connection pool manager obtains a Connection from the specified JDBC driver, but before returning it to the application, wraps it with a facade that also implements Connection, interposing itself between the application and the underlying connection. When the connection is created or a JDBC operation is performed, the wrapper asks the transaction manager if the current thread is executing in the context of a transaction, and automatically enlists the Connection in the transaction if one exists. The other types of transactional resources, JMS message queues and JCA connectors, rely on a similar mechanism to hide resource enlistment from the user. When you make a JMS queue available to a J2EE application at deployment time, you again use a provider-specific mechanism to create the managed JMS objects (queue connection factories and destinations), which you then publish in a JNDI namespace. The managed objects created by the provider contain similar auto-enlistment code as the JDBC wrapper added by the container-supplied connection pool manager.

Back to top

Transparent transaction control The two types of J2EE transactions -- container-managed and bean-managed -- differ in how they start and end a transaction. Where a transaction starts and ends is referred to as transaction demarcation. The example code in Listing 1 demonstrates a bean-managed transaction (sometimes called a programmatic transaction.) Bean-managed transactions are started and ended explicitly by a component using the UserTransaction class. UserTransaction is made available to EJB components through theejbContext and to other J2EE components through JNDI. Container-managed transactions (or declarative transactions) are started and ended transparently on behalf of the application by the container, based on transaction attributes in the component's deployment descriptor. You indicate whether an EJB component uses bean-managed or container-managed transactional support by setting the transaction-type attribute to eitherContainer or Bean. With container-managed transactions, you can assign transactional attributes at either the EJB class or method levels; you can specify a default set of transactional attributes for the EJB class, and you can also specify attributes for each method if different methods are to have different transactional semantics. These transactional attributes are specified in the container-transactionsection of the assembly descriptor. An example assembly descriptor is shown in Listing 2. The supported values for the trans-attribute are:
  • Supports
  • Required
  • RequiresNew
  • Mandatory
  • NotSupported
  • Never
The trans-attribute determines if the method supports execution within a transaction, what action the container should take when the method is called within a transaction, and what action the container should take if it is called outside of a transaction. The most common container-managed transaction attribute is Required. When Required is set, a transaction in process will enlist your bean in that transaction, but if no transaction is running, the container will start one for you. We will investigate the differences between the various transaction attributes, and when you might want to use each, in Part 3 of this series. Listing 2. Sample EJB assembly descriptor
Powerful, but dangerous Unlike the example in Listing 1, with declarative transaction demarcation there is no transaction management code in the component methods. Not only does this make the resulting component code easier to read (because it is not cluttered with transaction management code), but it has another, more significant advantage -- the transactional semantics of the component can be changed at application assembly time, without modifying or even accessing the source code for the component. While being able to specify transaction demarcation separate from the code is a very powerful feature, making poor decisions at assembly time can render your application unstable or seriously impair its performance. The responsibility for correctly demarcating container-managed transactions is shared between the component developer and the application assembler. The component developer needs to provide sufficient documentation as to what the component does, so that the application deployer can make intelligent decisions on how to structure the application's transactions. The application assembler needs to understand how the components in the application interact, so that transactions can be demarcated in a way that enforces application consistency and doesn't impair performance. We'll discuss these issues in Part 3 of this series.

Back to top

Transparent transaction propagation In either type of transaction, resource enlistment is transparent; the container automatically enlists any transactional resources used during the course of the transaction into the current transaction. This process extends not only to resources used by the transactional method, such as the database connections acquired in Listing 1, but also by methods it calls -- even remote methods. Let's take a look at how this happens. The container associates transactions with threads Let's say that methodA() of object A starts a transaction, and then calls methodB() of object B, which acquires a JDBC connection and updates the database. The connection acquired by B will be automatically enlisted in the transaction created by A. How did the container know to do this? When a transaction is initiated, the transaction context is associated with the executing thread. When A creates the transaction, the thread in which A is executing is associated with that transaction. Because local method invocations execute in the same thread as the caller, any methods called by A will also be in the context of that transaction. Skeletons in the closet What if object B is really a stub to an EJB component executing in another thread or even another JVM? Amazingly, resources accessed by remote object B will still be enlisted in the current transaction. The EJB object stub (the part that executes in the context of the caller), the EJB protocol (RMI over IIOP), and the skeleton object on the remote end all conspire to make this happen transparently. The stub determines if the caller is executing a transaction. If so, the transaction ID, or Xid, is propagated to the remote object as part of the IIOP call along with the method parameters. (IIOP is the CORBA remote-invocation protocol, which provides for propagating various elements of execution context, such as transaction context and security context; seeResources for more information on RMI over IIOP.) If the call is part of a transaction, the skeleton object on the remote system automatically sets the remote thread's transaction context, so that when the actual remote method is invoked, it is already part of the transaction. (The stub and skeleton objects also take care of beginning and committing container-managed transactions.) Transactions can be initiated by any J2EE component -- an EJB component, a servlet, or a JSP page (or an application client, if the container supports it). This means that your application can start a transaction in a servlet or JSP page when a request arrives, do some processing within the servlet or JSP page, access entity beans and session beans on multiple servers as part of the page's logic, and have all of this work be part of one transaction, transparently. Figure 1 demonstrates how the transaction context follows the path of execution from servlet to EJB to EJB. Figure 1. Multiple components in a single transaction Multiple components in a single transaction

Back to top

Optimizations Having transactions be managed by the container allows the container to make certain optimization decisions for us. In Figure 1, we see a servlet and multiple EJB components access a database within the context of a single transaction. Each obtains aConnection to the database; it may well be the case that each is accessing the exact same database. JTS can detect whether multiple resources are involved in the transaction or not, even if multiple connections are made to the same resource from different components, and optimize the execution of the transaction. As you'll recall from Part 1, involving multiple resources managers in a single transaction requires the use of the two-phase commit protocol, which is more expensive than the single-phase commit used by a single resource manager. JTS is able to determine if only a single resource manager is enlisted in a transaction. If it detects that all the resources involved in the transaction are the same, it can skip the two-phase commit and let the resource manager handle the transaction by itself.

Back to top

Conclusion The magic that allows for transparent transaction control, resource enlistment, and transaction propagation is not a part of JTS, but instead a part of how J2EE containers use JTA and JTS services behind the scenes on behalf of J2EE applications. There are many entities that conspire behind the scenes to make this magic happen transparently; the EJB stubs and skeletons, the JDBC driver wrappers provided by the container vendor, the JDBC drivers provided by the database vendor, the JMS providers, and the JCA connectors. All of these entities interact with the transaction manager so that your application code doesn't have to. In Part 3, we'll look at some of the practical issues associated with managing transactions in a J2EE context -- transaction demarcation and isolation -- and their effects on application consistency, stability, and performance. Resources

1-10 of 22