제목 : [질문]2번이상 호출된 servlet 누가 좀 말려줘요....
글쓴이: 박상현(linuzer) 2001/03/09 09:48:04 조회수:536 줄수:32
JDF에서 제시하는 데로 Entity bean만들고 DBWrapper만들고 ConnectionResource만들어서
주소검색하는 servlet을 만들어 봤는데요..
(물론 JDF에서 제공하는 servlet을 일부수정한 수준임)
주소를 넣고 submit을 한번만 누르면 정상적으로 실행이 됩니다.
그런데 검색이 끝나기도 전에 submit을 2번이상 누르게 되면 무지막지한 
Connection by reset by peer, IO Exception을 내면서
log파일에는 수백MBytes의 에러로그가 쌓이고 급기야 out of memory가 되면서 전체 
시스템의 수행속도가 급격하게 떨어집니다.
그리고는 제어가 넘어오지 않으므로 해서 다른 기능은 사용할 수 없게 됩니다.
그래서 제가 이것을 해결하려고 아래에 적어둔 일을 해보았는데 전부 실패 했습니다.
아무래도 servlet에 한번 진입하면 통제가 불가능한것 같아요....

1. .jsp파일에서 javascript를 이용하여 플래그 셋팅후 넘어가지 못하게 함 
2. servlet파일을 SingleThreadModel로 implements
3. 해당 method에서 synchronized를 걸어줌
4. service() method를 overriding해서 class 변수를 선언후 셋팅하여 thread확인후 
   이후에 들어오는 thread를 stop시킴
5. printJspPage()에 Exception체크
6. 기타 등등
을 해보았는데 도무지 어떻게 해야 할지 막막하네요..
제가 궁금한것은 고의적이던, 실수든 submit버튼을 2번이상 눌러서 같은 servlet에 
계속진입함으로서 서버가 IO error를 무한적으로
내면서 급기야 out of memory내면서 죽어버리는 문제를 해결하고 싶습니다.
일단은 이러한 문제가 발생하지 않도록 하는 방법과 최소한 한번만 에러를 발생시키고
전체적으로 영향을 주지 않게 하는 방법을 찾고 있습니다.
귀찮게 하고 싶지 않아서 초보 허접이가 근 1주일을 갖는방법(?)을 동원하여 해결해보려
했는데 더이상은 제가 해볼 수 있는 방법이 없습니다.
그리고 이렇게 되면 2인 이상이 동시에 이것을 사용할 수 없는건가요?
(2인 이상이 거의 유사한 시간에 servlet에 진입할 수 있잖아요....그럼 위와 같은 
에러가 발생하면서 서버가 죽게 되는것은 아닌가요?)
감사합니다.
제목 : Re: [질문]2번이상 호출된 servlet 누가 좀 말려줘요....
글쓴이: 신석영(dogfly) 2001/03/10 19:50:19 조회수:586 줄수:31
글쎄요 저도 jdf를 사용하여 여러프로그램들을 만들어봤지만 submit 버튼을 연속해서
눌렀다고해서 그런상황이 발생된 적은 없었습니다.
servlet을 일부 수정하였다고 하셨고..
"log파일에는 수백MBytes의 에러로그가 쌓이고 급기야 out of memory.."

위의 상황으로 봐서는 submit 버튼이 두번 눌리웠을때(Exception 상황에)
 로직이 무한루프를 타지 않나 하는 생각이 듭니다.

소스를 올려놓지 않으셨으니 정확한 답변을 드릴순 없지만.
그냥 내 맘대로 상상해 보건데 


 Exception 시에 다음과 같이 코딩하셨겠죠?
catch(Exception e)
{
   Message msg = new MessageStore("V007");
   printMessagePage(req, res, msg, e);
   return;  <<--- 이부분은 꼭 들어가야 합니다.
}

혹시 printMessagePage 메소드가 또다른 메소드를 호출하고..
하는 식으로 되어서 무한루프가 도는것은 아닌지..

소스코드를 꼭 첨부해 주세요... 그래야 정확한 답변을 드리지요..
(ConnectionResource 를 포함한 모든 소스코드를 서블릿 코드는 물론..
서블릿이 상속받는 슈퍼클래스 까지 몽땅.. )

from 신석영
dogfly@hanmail.net
dogfly@uno-systems.com
Mobile:017-763-6500
제목 : [질문 추가] 소스는 요..
글쓴이: 박상현(linuzer) 2001/03/11 12:15:10 조회수:344 줄수:24
답변 감사합니다.
소스는 javaservice.net/~java/jdf/pbf/src/gov/mpb/sm/ZipCodeServlet.java을 
이용했습니다.(거의 바뀐것 없습니다.)
entity bean은 같은 디렉토리의 smt_200.java이고,
Wrapper는 smt_200DbWrap.java을 사용했습니다.
그래서 소스를 올리지 않았습니다.(거의 변한게 없으니 깐요...죄송)

석영님이 예상하신 그런 문제가 발생합니다.

버튼이 두번이상 눌리게 되면 두번째 이상의 것은 service()에서 체크해서 
return을 시킬려고 했거든요(맞는 생각인지 모름)...
그런데 첫번째 것이 실행되고 printJspPage()에서 와서 문제가 생기는것 같아요
(실행도중 어디 선가 return시키면 HttpServletResponse가 깨져서 제어가 
넘어가지 않고 무한 루프에 빠지는것 같습니다). 
log파일을 봐도 query문장까지 찍히는데 JSP Call error(?)가 찍히고 
printMessagePage()에서 반복적으로 메세지가 발생합니다
(요놈이 log파일을 거의 점유하지요)

소스를 보니 지적하신 부분에 return이 없네요....
이게 왜 무한 루프에 빠지게 하는건가요?
(저는 이원영씨 작품이라 소스에는 문제가 없을것이라 생각했습니다)
그럼..
미리 답변 감사합니다.
제목 : Re: "return"은 꼭 들어가야 합니다.
글쓴이: 이원영(javaservice) 2001/03/11 21:29:12 조회수:806 줄수:23
엔지니어는 프로그래밍 언어로 대화합니다. 관련 에러로그 전부와 관련 소스 전부를
올리세요...
뭐든 가급적 추측하지 마시고, 있는 그대로 문제현상을 설명해 보세요...

PS:신석영님이 지적한 바와 같이 catch(Exception e){} 절에서 "retrun" 문은 반드시
  들어가야 합니다. ZipCodeServlet.java 에서와 같이 말입니다.
  만약  이 부분이 들어가질 않으면, printMessagePage()나 printPopupMessagePage()
  에서 PrintWriter out = res.getWriter() 가 불려져서 output stream 에 내용이
  buffering 됩니다. 만약, return 문이 기술되어 있지 않으면, 로직이 밑으로 흘러
  printJspPage()를 만나 내부적으로 JSP로의 forward  가 일어나게 되고, 십중 팔구는
  IllegalStateException 을 발생하게 될 겁니다. 이때 이 Error 를 처리하기
  위해 다시 printErrorPage()를 부르게되고 내부적으로 IllegalStateException 을
  또다시 발생하게 되니 결국 recurrsive looping 을 무한하게 돌게 될 겁니다.
  IBM WebSphere 의 경우는 이같은 recurrsive logic 를 잡아내어 recurrsive error 
  라고 보여주지만, 다른 서블렛엔진의 경우는 무한루프를 돌면서 Stackover flow 를
  내거나 OutOfMemoryError 를 낼 수도 있습니다.

================================================
  자바서비스넷 이원영
  E-mail: javaservice@hanmail.net
  PCS:019-310-7324
================================================
제목 : [재 질문]다시한번 봐주세요..(소스첨부)
글쓴이: 박상현(linuzer) 2001/03/12 10:26:13 조회수:712 줄수:130
자꾸 귀찮게 해서 정말 죄송합니다.
소스 화일도 첨부하니 다시한번 봐주세요...
return을 넣어도 계속 에러가 뜹니다.
log파일에 있는 몇가지는 제가 확인하기 위해서 집어 넣은것 입니다.
환경은 win98+tomcat3.2+oracle8i입니다.
그리고 ZipCodeServlet.java중 searchByKeyword()만 테스트 중입니다.
감사합니다.....

=====java console에 뜨는 내용(첫줄 이외는 반복적으로 발생하는 부분임)========
flag(2) : running so I'll go back
2001-03-12 11:28:39 - Ctx( /psh ): IOException in: R( /psh + /kict/address.jsp
 null) Connection reset by peer: socket write error
2001-03-12 11:28:39 - Ctx( /psh ): IOException in: R( /psh + /kict/message.jsp
 null) Connection reset by peer: socket write error
2001-03-12 11:28:39 - Ctx( /psh ): IOException in: R( /psh + /kict/message.jsp
 null) Connection reset by peer: socket write error
2001-03-12 11:28:39 - Ctx( /psh ): IOException in: R( /psh + /kict/message.jsp
 null) Connection reset by peer: socket write error

==========log파일 내용(submit버튼을 연속 2번 누른경우) ===========
D111121 thread name(1) is  : Thread-10
D111121 flag(1) before service : sleeping
D111121 flag(1) after service : running
D111121 thread name(2) is  : Thread-9
D111121 flag(2) before service : running
D111121 flag(2) in if_check(I'll go back)  : running
D111121 new line==========================================
D111121 dead thread name(2) is  : Thread-9
S111130 [ZipCodeServlet:1122806] /psh/servlet/ZipCodeServlet:127.0.0.1(127.0.0.1):calling
D111130 flag(2) at performTask() : running
D111130 flag(2) before searchByKeyword() : running
Q111136 [ZipCodeDbWrap:5841268] SELECT distinct addr1 FROM ZipCode :elapsed=3630
Q111136 [ZipCodeDbWrap:5841268] SELECT zipcd, addr1, addr2, addr3 FROM ZipCode WHERE addr1 like '부평4동%' or addr2 like '부평4동%' or addr3 like '부평4동%':elapsed=0
D111136 flag(2) after searchByKeyword() : sleeping
S111136 JSP Call Error: ZipCodeServlet
Request URI: /psh/kict/address.jsp
User Location  : 127.0.0.1(127.0.0.1)
java.net.SocketException: Connection reset by peer: socket write error
	at java.net.SocketOutputStream.socketWrite(Native Method)
	at java.net.SocketOutputStream.write(SocketOutputStream.java:83)
	at org.apache.tomcat.service.http.HttpResponseAdapter.endHeaders(HttpResponseAdapter.java:124)
	at org.apache.tomcat.core.BufferedServletOutputStream.sendHeaders(BufferedServletOutputStream.java:126)
	at org.apache.tomcat.core.BufferedServletOutputStream.reallyFlush(BufferedServletOutputStream.java:236)
	at org.apache.tomcat.core.ResponseImpl.finish(ResponseImpl.java:206)
	at org.apache.tomcat.facade.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:215)
	at com.lgeds.jdf.servlet.JDFBaseServlet.printJspPage(JDFBaseServlet.java:236)
	at ZipCodeServlet.selectByKeyword(ZipCodeServlet.java:163)
	at ZipCodeServlet.performTask(ZipCodeServlet.java:103)
	at PBaseServlet.performPreTask(PBaseServlet.java:45)
	at com.lgeds.jdf.servlet.JDFBaseServlet.performBasePreTask(JDFBaseServlet.java:170)
	at com.lgeds.jdf.servlet.JDFBaseServlet.doPost(JDFBaseServlet.java:140)
	at ZipCodeServlet.service(ZipCodeServlet.java:91)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
	at org.apache.tomcat.core.ServletWrapper.doService(ServletWrapper.java:404)
	at org.apache.tomcat.core.Handler.service(Handler.java:286)
	at org.apache.tomcat.core.ServletWrapper.service(ServletWrapper.java:372)
	at org.apache.tomcat.core.ContextManager.internalService(ContextManager.java:797)
	at org.apache.tomcat.core.ContextManager.service(ContextManager.java:743)
	at org.apache.tomcat.service.http.HttpConnectionHandler.processConnection(HttpConnectionHandler.java:210)
	at org.apache.tomcat.service.TcpWorkerThread.runIt(PoolTcpEndpoint.java:416)
	at org.apache.tomcat.util.ThreadPool$ControlRunnable.run(ThreadPool.java:498)
	at java.lang.Thread.run(Thread.java:484)

S111137 JSP Call Error: ZipCodeServlet
Request URI: /psh/kict/message.jsp
User Location  : 127.0.0.1(127.0.0.1)
java.net.SocketException: Connection reset by peer: socket write error
	at java.net.SocketOutputStream.socketWrite(Native Method)
	at java.net.SocketOutputStream.write(SocketOutputStream.java:83)
	at org.apache.tomcat.service.http.HttpResponseAdapter.endHeaders(HttpResponseAdapter.java:124)
	at org.apache.tomcat.core.BufferedServletOutputStream.sendHeaders(BufferedServletOutputStream.java:126)
	at org.apache.tomcat.core.BufferedServletOutputStream.reallyFlush(BufferedServletOutputStream.java:236)
	at org.apache.tomcat.core.ResponseImpl.finish(ResponseImpl.java:206)
	at org.apache.tomcat.facade.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:215)
	at com.lgeds.jdf.servlet.JDFBaseServlet.printJspPage(JDFBaseServlet.java:236)
	at PBaseServlet.printMessagePage(PBaseServlet.java:91)
	at com.lgeds.jdf.servlet.JDFBaseServlet.printMessagePage(JDFBaseServlet.java:319)
	at com.lgeds.jdf.servlet.JDFBaseServlet.printJspPage(JDFBaseServlet.java:269)
	at ZipCodeServlet.selectByKeyword(ZipCodeServlet.java:163)
	at ZipCodeServlet.performTask(ZipCodeServlet.java:103)
	at PBaseServlet.performPreTask(PBaseServlet.java:45)
	at com.lgeds.jdf.servlet.JDFBaseServlet.performBasePreTask(JDFBaseServlet.java:170)
	at com.lgeds.jdf.servlet.JDFBaseServlet.doPost(JDFBaseServlet.java:140)
	at ZipCodeServlet.service(ZipCodeServlet.java:91)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
	at org.apache.tomcat.core.ServletWrapper.doService(ServletWrapper.java:404)
	at org.apache.tomcat.core.Handler.service(Handler.java:286)
	at org.apache.tomcat.core.ServletWrapper.service(ServletWrapper.java:372)
	at org.apache.tomcat.core.ContextManager.internalService(ContextManager.java:797)
	at org.apache.tomcat.core.ContextManager.service(ContextManager.java:743)
	at org.apache.tomcat.service.http.HttpConnectionHandler.processConnection(HttpConnectionHandler.java:210)
	at org.apache.tomcat.service.TcpWorkerThread.runIt(PoolTcpEndpoint.java:416)
	at org.apache.tomcat.util.ThreadPool$ControlRunnable.run(ThreadPool.java:498)
	at java.lang.Thread.run(Thread.java:484)

S111137 JSP Call Error: ZipCodeServlet <======이하 반복적으로 발생하는 에러메세지................
Request URI: /psh/kict/message.jsp
User Location  : 127.0.0.1(127.0.0.1)
java.net.SocketException: Connection reset by peer: socket write error
	at java.net.SocketOutputStream.socketWrite(Native Method)
	at java.net.SocketOutputStream.write(SocketOutputStream.java:83)
	at org.apache.tomcat.service.http.HttpResponseAdapter.endHeaders(HttpResponseAdapter.java:124)
	at org.apache.tomcat.core.BufferedServletOutputStream.sendHeaders(BufferedServletOutputStream.java:126)
	at org.apache.tomcat.core.BufferedServletOutputStream.reallyFlush(BufferedServletOutputStream.java:236)
	at org.apache.tomcat.core.ResponseImpl.finish(ResponseImpl.java:206)
	at org.apache.tomcat.facade.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:215)
	at com.lgeds.jdf.servlet.JDFBaseServlet.printJspPage(JDFBaseServlet.java:236)
	at PBaseServlet.printMessagePage(PBaseServlet.java:91)
	at com.lgeds.jdf.servlet.JDFBaseServlet.printMessagePage(JDFBaseServlet.java:319)
	at com.lgeds.jdf.servlet.JDFBaseServlet.printJspPage(JDFBaseServlet.java:269)
	at PBaseServlet.printMessagePage(PBaseServlet.java:91)
	at com.lgeds.jdf.servlet.JDFBaseServlet.printMessagePage(JDFBaseServlet.java:319)
	at com.lgeds.jdf.servlet.JDFBaseServlet.printJspPage(JDFBaseServlet.java:269)
	at ZipCodeServlet.selectByKeyword(ZipCodeServlet.java:163)
	at ZipCodeServlet.performTask(ZipCodeServlet.java:103)
	at PBaseServlet.performPreTask(PBaseServlet.java:45)
	at com.lgeds.jdf.servlet.JDFBaseServlet.performBasePreTask(JDFBaseServlet.java:170)
	at com.lgeds.jdf.servlet.JDFBaseServlet.doPost(JDFBaseServlet.java:140)
	at ZipCodeServlet.service(ZipCodeServlet.java:91)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
	at org.apache.tomcat.core.ServletWrapper.doService(ServletWrapper.java:404)
	at org.apache.tomcat.core.Handler.service(Handler.java:286)
	at org.apache.tomcat.core.ServletWrapper.service(ServletWrapper.java:372)
	at org.apache.tomcat.core.ContextManager.internalService(ContextManager.java:797)
	at org.apache.tomcat.core.ContextManager.service(ContextManager.java:743)
	at org.apache.tomcat.service.http.HttpConnectionHandler.processConnection(HttpConnectionHandler.java:210)
	at org.apache.tomcat.service.TcpWorkerThread.runIt(PoolTcpEndpoint.java:416)
	at org.apache.tomcat.util.ThreadPool$ControlRunnable.run(ThreadPool.java:498)
	at java.lang.Thread.run(Thread.java:484)

Download address.jsp (6873 Bytes) address.jsp (6873 Bytes)
Download ZipCode.java (783 Bytes) ZipCode.java (783 Bytes)
Download ZipCodeServlet.java (9124 Bytes) ZipCodeServlet.java (9124 Bytes)
Download ZipCodeDbWrap.java (12105 Bytes) ZipCodeDbWrap.java (12105 Bytes)
제목 : Re: JDF의 버그 인정합니다
글쓴이: 이원영(javaservice) 2001/03/12 13:00:38 조회수:7291 줄수:199
Tomcat 에서 "java.net.SocketException: Connection reset by peer: socket write error" 는
사실 정상적인 상황의 에러입니다. 사용자가 F5 키를 여러번 누르거나, 응답이 오기전에 다른
사이트로 이동하거나 할 경우, 서버측에서는 결과물인 HTML을 브라우져로 내려보내려 할 때,
이미 client 의 socket 이 닫혔으므로 위와 같은 IOException 을 발생하게 됩니다.

문제는 JDF에서 이 부분을 어떻게 처리 해야 겠느냐의 문제로 귀착되는데, 현재는 아래처럼
코딩이 되어 있습니다.

http://javaservice.net/~java/jdf/src/com/lgeds/jdf/servlet/JDFBaseServlet.java

    .....
    protected void printJspPage (HttpServletRequest req, HttpServletResponse res,  String jspfile) 
    {
        try {

            // IBM WebSphere 2.0.x
            //((com.sun.server.http.HttpServiceResponse) res).callPage(jspfile, req);

            // Servlet 2.1    
            RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(jspfile);
            dispatcher.forward(req, res);
        }
        catch (IllegalStateException e) {
            try{
                java.io.PrintWriter out = res.getWriter();
                java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
                java.io.PrintWriter writer = new java.io.PrintWriter(bos);
                writer.println("JSP Call Error: " + this.getClass().getName() );
                writer.println("Request URI: " + req.getRequestURI() );
                String user = req.getRemoteUser();
                if ( user != null ) writer.println("User : " + user );
                writer.println("User Location  : " + req.getRemoteHost() + 
                    "(" + req.getRemoteAddr() + ")");
                e.printStackTrace(writer);
                writer.flush();
                res.setContentType("text/html;charset=euc-kr");
                out.println("<html><head><title>Errort</title></head><body bgcolor=white><xmp>");
                out.println(bos.toString());
                out.println("</xmp></body></html>");
                out.close();
            }
            catch(Exception ex){}
        }
        catch (Exception e) {
            java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
            java.io.PrintWriter writer = new java.io.PrintWriter(bos);
            writer.println("JSP Call Error: " + this.getClass().getName() );
            writer.println("Request URI: " + req.getRequestURI() );
            String user = req.getRemoteUser();
            if ( user != null ) writer.println("User : " + user );
            writer.println("User Location  : " + req.getRemoteHost() + 
                "(" + req.getRemoteAddr() + ")");
            e.printStackTrace(writer);
            writer.flush();
            Logger.sys.println(bos.toString());
            printMessagePage(req,res, bos.toString());
        }
    }

즉 현재, dispatcher.forward(req, res); 시에 IOException 이 발생할 경우, 두번째 
catch(Exception e){} block 에서 님이 보고 계시는 에러를 Loggrer.sys.println(...)을
통해 로깅하게 됩니다.

그런뒤 다시 printMessagePage(....) 가 불려지게 되고, 님께서는 PBaseServlet.java 를
만들어 사용하셨으나, 그 역할을 원래 하도록 한 PBF(Project Base Framework)의
PublicBaseServlet.java 의 printMessagePage(...)는 아래와 같이 코딩이 되어 있습니다.

http://javaservice.net/~java/jdf/pbf/src/gov/mpb/pbf/PublicBaseServlet.java


	protected void printMessagePage(HttpServletRequest req, HttpServletResponse res, 
		String user_msg, String debug_msg) 
	{
		if ( user_msg == null ) user_msg = "message is null";
		req.setAttribute("jdf_user_msg", user_msg);

		if ( debug_msg == null ) debug_msg = "";
		else {
			try {
				com.lgeds.jdf.Config conf = new com.lgeds.jdf.Configuration();
				boolean mode =  conf.getBoolean("gov.mpb.pbf.jspDebugMessageMode");
				if ( mode ) {
					Logger.debug.println(debug_msg);
				}
			}
			catch(Exception e){}
		}
		req.setAttribute("jdf_debug_msg", debug_msg);
		printJspPage(req, res, "/pbf/message.jsp");
	}

사실 이 부분의 저의 실수이긴 한데, 여기서 다시 printJspPage() 가 불려지고, 이는
JDBBaseServlet.java 의 printJspPage()에서 다시 IOException 이 발생하여 
recurrsive 하게 계속 무한 루프를 돌게 됩니다.

인정합니다. 잘못됐군요....

이 현상이 IBM WebSphere 의 경우는 recurrsive error 를 smart 하게 잡아내어
계속적인 recurrsive loop 를 타지 않지만, tomcat 의 경우는 끝까지 돌게 되는
군요....

해결책은 JDFBaseServlet 의 printJspPage(...)에서 IOException 을 가로채어
무시케 해야 할 듯 합니다.
catch(IOException e){....} 절을 아래처럼 추가 해야 겠어요....



[com/lgeds/jdf/servlet/JDFBaseServlet.java]
    .....
    protected void printJspPage (HttpServletRequest req, HttpServletResponse res,  String jspfile) 
    {
        try {

            // IBM WebSphere 2.0.x
            //((com.sun.server.http.HttpServiceResponse) res).callPage(jspfile, req);

            // Servlet 2.1    
            RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(jspfile);
            dispatcher.forward(req, res);
        }
        catch (IllegalStateException e) {
            try{
                java.io.PrintWriter out = res.getWriter();
                java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
                java.io.PrintWriter writer = new java.io.PrintWriter(bos);
                writer.println("JSP Call Error: " + this.getClass().getName() );
                writer.println("Request URI: " + req.getRequestURI() );
                String user = req.getRemoteUser();
                if ( user != null ) writer.println("User : " + user );
                writer.println("User Location  : " + req.getRemoteHost() + 
                    "(" + req.getRemoteAddr() + ")");
                e.printStackTrace(writer);
                writer.flush();
                res.setContentType("text/html;charset=euc-kr");
                out.println("<html><head><title>Errort</title></head><body bgcolor=white><xmp>");
                out.println(bos.toString());
                out.println("</xmp></body></html>");
                out.close();
            }
            catch(Exception ex){}
        }
        catch (IOException e) {
            try{
                java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
                java.io.PrintWriter writer = new java.io.PrintWriter(bos);
                writer.println("JSP Call Error: " + this.getClass().getName() );
                writer.println("Request URI: " + req.getRequestURI() );
                String user = req.getRemoteUser();
                if ( user != null ) writer.println("User : " + user );
                writer.println("User Location  : " + req.getRemoteHost() + 
                    "(" + req.getRemoteAddr() + ")");
                //e.printStackTrace(writer);
                writer.println(e.toString() + 
                  ":(client may have terminated connection before server completed response)" );
                writer.flush();
                Logger.debug.println(bos.toString());
            }
            catch(Exception ex){}
        }
        catch (Exception e) {
            java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
            java.io.PrintWriter writer = new java.io.PrintWriter(bos);
            writer.println("JSP Call Error: " + this.getClass().getName() );
            writer.println("Request URI: " + req.getRequestURI() );
            String user = req.getRemoteUser();
            if ( user != null ) writer.println("User : " + user );
            writer.println("User Location  : " + req.getRemoteHost() + 
                "(" + req.getRemoteAddr() + ")");
            e.printStackTrace(writer);
            writer.flush();
            Logger.sys.println(bos.toString());
            printMessagePage(req,res, bos.toString());
        }
    }


Logger.debug.pritln() 으로 찍히니 DEBUG 모드일 경우에만 로그에 남도록 했습니다.

PS: 
 > 그리고 이렇게 되면 2인 이상이 동시에 이것을 사용할 수 없는건가요?
 > (2인 이상이 거의 유사한 시간에 servlet에 진입할 수 있잖아요....그럼 위와 같은 
 > 에러가 발생하면서 서버가 죽게 되는것은 아닌가요?)

 그렇지는 않습니다. 서로다른 사용자는 서로다른 Thread에 의해 동작하여 서로다른
 client socket 을 갖고 있으므로 위에서 설명한 현상은 일어나지 않습니다.
 브라우져에서 응답이 오기전에 브라우져를 닫거나 다른 곳으로 가버린 상황에서만
 나타납니다.



NOTE: 좋은 지적 감사합니다. 차기 버전에 반영토록 하겠습니다. 또한 해당 문제의
      현상을 정확하게 파악하고 있는 신석영님에게도 감사합니다.


================================================
  자바서비스넷 이원영
  E-mail: javaservice@hanmail.net
  PCS:019-310-7324
================================================
제목 : [감사]답변 감사합니다.
글쓴이: 박상현(linuzer) 2001/03/12 15:53:29 조회수:316 줄수:5
답변 감사합니다.

역쉬......

고수님들은 허접초보의 영원한 빽입니다요...(딸랑딸랑)
Posted by 큰바우
:
>
> {IOException: Socket Closed 에 대한 질문}
> {Young Kim(ykim), ykim@fasttide.com}
>
> WebLogic 4.5.1 사용중 error 가 한번 나면 계속 다음과 같이 Logging 을 계속하더군요.
> 상황은 Browser 에서 첫번째 Request Processing 하고, response 가 오기전에, 
> 또다른 Request 를 하면 아래와 같은 Exception 이 발생하던군요.
> Code 는 JSP 를 이용한 Servlet,EJB, Oracle Access application 입니다.
> 자료가 많아서 적당한 Q&A 를 찾기가 어렵군요. 조언 부탁합니다.
> ------------------
> Sat Mar 17 19:25:04 GMT 2001:<E> <ServletContext-Servlets> exception raised on '/jsp/shared/ErrorPage.jsp'
> Sat Mar 17 19:25:05 GMT 2001:<E> <ServletContext-Servlets> java.io.IOException: Socket Closed
> 	at weblogic.security.SSL.SSLSocket.sendRecord(SSLSocket.java, Compiled Code)
> 	at java.lang.Exception.<init>(Exception.java, Compiled Code)
> 	at java.io.IOException.<init>(IOException.java, Compiled Code)
> 	at weblogic.security.SSL.SSLSocket.sendRecord(SSLSocket.java, Compiled Code)
> 	at weblogic.security.SSL.SSLSocket.sendData(SSLSocket.java, Compiled Code)
> 	at weblogic.security.SSL.SSLSocketOutputStream.write(SSLSocketOutputStream.java, Compiled Code)
> 	at weblogic.servlet.internal.ServletOutputStreamImpl.flush(ServletOutputStreamImpl.java, Compiled Code)
> 	at weblogic.servlet.internal.ServletOutputStreamImpl.flushWithCheck(ServletOutputStreamImpl.java, Compiled Code)
> 	at weblogic.servlet.internal.ServletOutputStreamImpl.print(ServletOutputStreamImpl.java, Compiled Code)
> 	at javax.servlet.ServletOutputStream.println(ServletOutputStream.java, Compiled Code)
> 	at weblogic.servlet.jsp.JspWriterImpl.println(JspWriterImpl.java, Compiled Code)
> 	at com.fasttide.utils.servlet.MenuOptions.formatLevel1MenuItem(MenuOptions.java, Compiled Code)
> 	at com.fasttide.utils.servlet.MenuOptions.buildMainMenuItems(MenuOptions.java, Compiled Code)
> 	at com.fasttide.utils.servlet.MenuOptions.displayMenuOptions(MenuOptions.java, Compiled Code)
> 	at com._jsp._shared._ErrorPage._jspService(_ErrorPage.java, Compiled Code)
> ----

처리한 결과를 브라우져로 보낼 때 해당 OutputStream.flush() 시에 발생하는
IOException 은 대부분의 "어플리케이션서버"는 그 상황을 로깅합니다.
이러한 현상은 브라우져에서 호출한 후 서버측에서 그 결과를 내려보내주기 전에
"stop/forward/back"등의 버튼을 누른다거나, 혹은 응답시간이 늦어지니 다른 사이트로
가버렸거나, 브라우져를 닫아 버렸을 때 발생합니다.

따라서, "Warning 성 에러"로 취급하시면 됩니다.

이런 현상은 비단 BEA WebLogic 에서만 나타나는 현상은 아닙니다. Tomcat 에서도
나타나며, IBM Websphere 에서도 나타납니다.

Servlet 으로 개발할 경우, 대부분 이러한 현상에 대해 handling 이 가능합니다.
out.println()/out.flush()/out.close() 시에 발생하게 되니까 개발자의 코드에서 
catch 해 내어 "무시"케 하거나 혹은 "로깅"을 할 수 있는 거지요..
또한, RequestDispatcher 의 forward() 할 때도 발생할 수 있습니다.
마찬가지로 개발자의 어플리케이션 코드에서 catch 해 낼 수가 있고, 이 상황을
handling 할 수가 있습니다.

그러나, JSP의 경우는 얘기가 다릅니다.
JSP 코드는 자동으로 Servlet 으로 변경되어 엔진이 만든 Java JspServlet 코드로 
생산되고, 이 때, 위와 같은 상황이 발생했을 때, 그 자동으로 만들어진 코드에서 "무시"케
할까? 혹은 "에러"로 로깅을 할까?를 엔진마다 "선택"할 수 밖에 없는데, 만약 당신이 
엔진을 개발하는 사람이라면 어느 쪽을 택하겠습니까? 당연히 그 상황을 로그로 남기도록
했겠죠..

그래서, 유독 JSP에서만 그와 같은 에러가 로그에 남게 됩니다.

IBM WebSphere 의 경우는 "IOException: native write error: return code : -1",
Tomcat 의 경우는 "IOException: Connection reset by peer" 등과 유사한 에러가
남게 됩니다.


질문중에 하나를 확인할 것이 있습니다. "계속 다음과 같이 Logging 을 계속하더군요"
라는 것은 한번만 위와 같은 상황을 만들어 주면, 동일한 에러로그가 recursive 하게
무한 Loop 을 돌며 계속적으로 남게 되나요? 
이 경우라면, IT-PLUS 나 한국BEA 에게 그 상황을 설명하여 버그를 fix 하거나
해결법을 찾아 보세요....(그렇지 않을 것으로 추측합니다.)

통상 이런 버그는 "일단 에러가 생기면, 그 내용을 브라우져에게 알려주려고 하고, 
이는 브라우져가 닫혔으니 다시 동일한 에러를 내고, 다시 또 브라우져에게 알려주려고
하고.... " 이렇게 반복적인 루틴을 타기 때문에 발생합니다.

PS: "그렇기 때문에, Servlet으로 개발하시는 개발자는 이러한 부분에 대해 명시적인 처리를
    반드시 하셔야 합니다." 그렇지 않으면, JDF 에서 제가 실수한 것과 같은 동일한
    문제에 직면하실 수 있습니다.

    "Re: JDF의 버그 인정합니다"
     http://www.javaservice.net/~java/bbs/read.cgi?m=jdf&b=fw_qna&c=r_p&n=984369638


-------------------------------------------------------  
  본 문서는 자유롭게 배포/복사 할 수 있으나 반드시
  이 문서의 저자에 대한 언급을 삭제하시면 안됩니다
================================================
  자바서비스넷 이원영
  E-mail: javaservice@hanmail.net
  PCS:019-310-7324
================================================
Posted by 큰바우
:

Jeus 시작하기

Programming/Java 2007. 3. 21. 11:41 |

- JEUS 시작하기
 

JEUS (Java Enterprise User Solution) 인터넷으로 각광 받고 있는 Java 기반으로 솔루션으로, 환경에서 어플리케이션을 운용하는 필요한 각종 서비스들을 제공해 주는 어플리케이션 서버이다.

JEUS 어플리케이션을 개발하고 실행할 있는 플랫폼 역할을 하면서, 트랜잭션 관리, 세션 유지, 부하 분산 다양한 기능을 제공할 뿐만 아니라, 계층화된 구조로 성과 기능 확장성이 우수해 비즈니스 로직을 손쉽고 효과적으로 구현할 있게 한다.

 

 

2.   JEUS 디렉토리 구조

2.1 전체 디렉토리 구조

JEUS 최초 설치하면 설치 지정된 디렉토리(JEUS_HOME) 다음과 같은 디렉토리가 생긴다.

 

 

각각의 디렉토리에 대한 설명은 다음에 계속한다.

 

2.2 bin 디렉토리

JEUS 실행, 모니터링 JEUS 운영에 관련된 스크립트 파일이 존재한다. Windows 경우에는 batch파일로 있으며, UNIX 경우에는 shell script파일로 되어 있다. 여기 있는 파일들 jeus.properties.bat(UNIX 경우: jeus.properties) 있는데 파일에는 JEUS실행에 필요한 각종 환경변수들이 설정되어 있다. 이를 제외한 나머지 실행파일들은 실행전에 항상 파일을 호출하여 환경변수를 적용하게 되어 있으므로 환경 변수의 수정이 필요한 경우에는 파일의 내용을 수정하면 된다.

 

2.3 client 디렉토리

EJB 등을 사용할 경우 client에서 사용하게 client모듈 등이 들어가게 되는 디렉토리이다. 일반적인 경우 사용하지 않는다.

 

2.4 config 디렉토리

운영시 사용하게 되는 모든 설정 파일들이 들어있는 디렉토리이다. 하위 디렉토리로 dtds디렉토리는 JEUS설정 파일의 모든 xml 대한 구조(dtd) 가지고 있는 디렉토리이고, hostname으로 되어 있는 디렉토리에 현재 운영과 관련된 모든 설정을 가지고 있다.

 

2.5 lib 디렉토리

디렉토리는 운영과 관련된 library들이 들어 있다. 아래에는 다음과 같이 3개의 디렉토리가 있다.

-          application: 개발시 필요한 library들을 넣어두는 디렉토리이다. Jar/zip형태의 파일을 넣어도 되고, package형태로 풀어놓아도 된다.

-          datasource: 디렉토리는 JDBC 혹은 각종 connector 위한 library 넣어두는 디렉토리이다. Jar/zip형태의 파일만 인식한다.

-          system: 디렉토리는 JEUS 엔진과 관련된 library들이 들어 있다.

위의 디렉토리들 개발 혹은 운영 필요한 라이브러리들은 application/datasource 넣어두면 된다. System 경우는 JEUS에서 사용하는 곳이므로 되도록이면 다른 파일들을 넣지 않는 것이 좋다.

 

2.6 license 디렉토리

라이센스가 들어가는 디렉토리이다. 최초 설치시(JEUS 4.2부터) 3개월간 사용할 있는 데모라이센스가 들어가 있다. 라이센스 파일은 license이다.

 

2.7 logs 디렉토리

JEUS 운영 남게 되는 각종 로그 파일이 쌓이게 되는 디렉토리이다. JEUS운영환경의 설정에 따라 내부에는 디렉토리나 파일들이 naming rule 따라 만들어지게 된다.

 

2.8 samples 디렉토리

각종 예제 프로그램들이 목록에 따라 들어가 있는 디렉토리이다.

 

2.9 webhome 디렉토리

최초 설치시 각종 샘플과 같은 각종 application들이 들어가 있는 디렉토리이다. 하위 디렉토리는 다음과 같은 것이 이다.

-          admin_home: browser 이용해 JEUS 제어하고자 필요한 프로그램들이 들어 있다. 이는 JEUS에서 사용하는 디렉토리이므로 건드리지 않는 것이 좋다.

-          ejb_home: EJB 필요한 jar파일이나 class 들어가는 디렉토리이다.

-          servlet_home: WEB application 들어가는 디렉토리이다.

-          ear_home: EAR application들이 들어가는 디렉토리이다.

이러한 디렉토리들은 유저의 필요에 따라(admin_home제외) 변경을 있다. 변경을 하고자 한다면 $JEUS_HOME/bin/jeus.properties.bat파일을 열어 관련된 환경변수를 수정하면 된다.

 

2.10 webserver 디렉토리

JEUS 기본적으로 포함되어 있는 Web Server(WebtoB light) 들어 있는 디렉토리이다.

 

2.11 workspace 디렉토리

JEUS 운영 내부적으로 사용하는 임시 디렉토리이다.

 

 

3.   JEUS 실행 구조

JEUS 크게 가지 형태의 process 운영된다고 있다.

하나는 manager process이고 나머지 하나는 container process이다.

첫째로 manager process container process 관리하는 역할과 clustering환경 구성 JEUS 상호간에 필요한 정보들을 주고 받는 process 개발 만들어진 각종 application과는 직접적인 관이 없다.

번째로 container프로세스는 개발자에 의해 개발된 application 실행시켜 주는 process이다.

이러한 구조를 그림으로 나타내면 다음과 같다.

 

 

그림에서처럼 manager process 자신에게 포함된 container들을 관리하는 역할과 물리적으로 떨어져 있는 서버의 다른 manager process 통신을 하면서 clustering환경을 유지하는 역할을 하게 된다.

Container process 하나의 서버에 여러 개가 존재할 있다. 또한 container process 여러 개의 engine 포함하게 된다. 이러한 engine 개발된 application 자신에게 해당하는 application 실행시켜주게 된다. 예를 든다면 EJB engine EJB프로그램을 Servlet engine WEB application 실행하게 되는 것이다. 이러한 container process 하나의 독립적인 process 동작하게 된다.

 

3.2 설정 파일

앞에서 JEUS 모든 설정 파일은 $JEUS_HOME/config 디렉토리에 존재한다고 했었다. Config 디렉토리의 구조를 살펴보면 다음과 같다. 아래 그림은 최초 설치 만들어져 있는 디렉토리이다.

 


앞에서도 간단히 설명을 했듯이 dtds 디렉토리는 xml 관련된 dtd파일이 들어 있는 곳이고, security 디렉토리는 JEUS운영 사용하게 되는 policy파일이 들어 있는 디렉토리이다.

나머지 hostname(그림에서는 ybsong_note)으로 되어 있는 디렉토리인데, 현재 JEUS 설치된 서버의 운영과 관련된 모든 설정들은 디렉토리에 들어 있다.

가장 먼저 살펴볼 파일은 hostname 디렉토리에 들어 있는 JEUSMain.xml파일이다.

참고로 운영을 위해 각종 설정 파일을 생성할 먼저 JEUSMain.xml파일을 만들게 되고, 파일의 구성에 따라 부수적으로 필요한 설정 파일을 만들어 설정을 하게 된다.

 

JEUS에서 가장 중요한 설정 파일로 가장 기본이 되는 설정으로 manager프로세스에 대한 설정이라고 보면 된다. 여기에는 어떠한 container프로세스가 있는지 container에는 어떠한 engine 들어 있는지에 대한 설정이 포함되어 있다. JEUS구동 시에도 가장 먼저 파일을 읽어 들인 설정에 따라 필요한 container process 실행시킨다고 생각하면 쉽게 이해될 것이다.

아래는 최초 설정 들어 있는 JEUSMain.xml 파일이다.

<?xml version="1.0"?>

<!DOCTYPE jeus-system PUBLIC "-//Tmax Soft, Inc.//DTD JEUS Main Config 4.0//EN"

                             "http://www.tmaxsoft.com/jeus/dtd/4.0/jeus-main-config.dtd">

<jeus-system>

    <node>

        <name>ybsong_note</name>

        <class-ftp>true</class-ftp>

        <sequential-start>true</sequential-start>

        <enable-webadmin>true</enable-webadmin>

        <system-log>

            <level>debug</level>

        </system-log>

        <engine-container>

            <name>container1</name>

            <engine-command>

                <type>servlet</type>

                <name>engine1</name>

                <startup-mode>xml</startup-mode>

            </engine-command>

            <engine-command>

                <type>ejb</type>

                <name>engine1</name>

                <startup-mode>xml</startup-mode>

                <deploy-mode>xml</deploy-mode>

            </engine-command>

            <tm-config>

                <pooling>

                    <min>10</min>

                    <max>20</max>

                    <step>2</step>

                    <period>600000</period>

                </pooling>

                <active-timeout>300000</active-timeout>

                <commit-timeout>120000</commit-timeout>

                <capacity>20000</capacity>

            </tm-config>

        </engine-container>

    </node>

</jeus-system>

아래에 중요한 개의 tag 대해 설명하였다.

-          node: 하나의 서버를 의미한다. 물리적인 하나의 서버를 의미한다고 봐도 무방하다. 내부에 engine-container tag 포함하고 있다.

-          engine-container: 앞에서 설명한 container process 의미한다. 여러 개를 가질 있으며 이는 여러 개의 container프로세스가 운영될 있음을 의미한다.

-          engine-command: 이는 앞에서 언급된 engine 의미한다. Type 따른 하는 운영되는 application 형태가 달라지게 된다.

위의 tag engine-container engine-command 적절히 조합하여 필요한 만큼의 container process engine 생성하면 된다. JEUSMain.xml 설정이 끝났다면 이젠 여기서 설정된 engine들에 대한 설정이 필요하게 된다.

참고로 Container process 대한 설정은 필요하지 않다. 이유는 Container process application 운영하는 기본 단위가 아니고 engine 기본 단위이므로 이들에 대한 설정만을 필요로 한다. 다시 말하면, container process 그대로 하나의 process 단위이며 engine 묶어주는 그룹의 개념만을 지니고 있으므로 JEUSMain.xml 내용만으로 이상의 설정은 필요 없는 것이다.

JEUSMain.xml 대한 자세한 설정은 JEUS Server 매뉴얼을 참조하면 된다.

 

3.4 engine 설정 파일

Engine 설정 파일은 engine 이름으로 디렉토리 아래에 존재하게 된다. Engine 이름을 붙이는 방법은 JEUS 내부의 naming rule 다음과 같이 JEUSMain.xml파일의 내용을 가지고 만들어지게 된다.

[hostname]_[engine tag type]_[engine tag name]

앞에서 보여진 JEUSMain.xml파일을 참고하면 다음과 같이 개의 engine이름이 존재하게 되는 것이다.

-          ybsong_note_ejb_engine1

-          ybsong_note_servlet_engine1

이렇게 engine 이름을 만들었다면 이러한 엔진을 위한 설정파일이 들어가게 되는 디렉토리를 만들어야 한다. 디렉토리는 바로 JEUSMain.xml 있는 디렉토리에 engine이름으로 디렉토리를 만들면 된다(앞에서 보인 config디렉토리의 구조에 관한 그림 참조).

engine 대한 설정 파일들은 모두 자신의 이름으로 디렉토리 안에 존재하게 되는 것이다. 또한 engine마다 필요한 설정 파일은 engine type 따라 달라지게 된다. Type 가지가 있다. 아래에 type 따른 설정 파일의 이름을 기술해 놓았다.

-          servlet type: WEBMain.xml

-          ejb type: EJBMain.xml

-          jms type: JMSMain.xml

이후에는 이들 가장 많이 사용하는 servlet ejb type 설정 파일에 대해 살펴본다.

 

3.5 servlet engine 설정 파일

앞에서 봤듯이 servlet type engine 설정 파일은 WEBMain.xml이다.

최초 설치 아래와 같은 설정 파일이 [hostname]_servlet_engine1 디렉토리에 들어 있다.

<?xml version="1.0"?>

<!DOCTYPE web-container PUBLIC "-//Tmax Soft., Inc.//DTD WEB Main Config 4.0//EN"

                               "http://www.tmaxsoft.com/jeus/dtd/4.0/web-main-config.dtd">

<web-container>

    <context-group>

        <group-name>MyGroup</group-name>

        <group-docbase>webapps</group-docbase>

        <session-config>

            <shared>true</shared>

            <timeout>20</timeout>

        </session-config>

        <logging>

            <error-log>

                <target>stdout</target>

                <level>information</level>

                <buffer-size>0</buffer-size>

            </error-log>

            <user-log>

                <target>file</target>

                <buffer-size>0</buffer-size>

            </user-log>

            <access-log>

                <target>file</target>

                <buffer-size>0</buffer-size>

                <valid-day>1</valid-day>

                <log-format>

                    <time-format>default</time-format>

                </log-format>

            </access-log>

        </logging>

        <context>

            <context-name>examples</context-name>

            <context-path>/examples</context-path>

        </context>

        <webserver-connection>

            <http-listener>

                <port>8088</port>

                <listener-id>http1</listener-id>

                <output-buffer-size>8192</output-buffer-size>

                <thread-pool>

                    <min>25</min>

                    <max>30</max>

                    <step>2</step>

                    <max-idle-time>1000</max-idle-time>

                </thread-pool>

            </http-listener>

        </webserver-connection>

    </context-group>

</web-container>

위의 파일 내용 개의 tag 아래에서 설명한다.

-          context-group: tag context 묶음을 의미한다. J2EE spec에서는 context 대해서만 기술되어 있다. JEUS에서 context group 개념을 사용하는 것은 세션 정보의 공유 등을 위해서 사용한다. 그대로 group 개념이므로 내부에 context tag 포함하고 있음을 있다. 참고로 context WEB application 단위로 생각하면 쉽게 이해할 있다. group-docbase group 물리적으로 존재하는 디스크 위치(디렉토리) 의미한다.

-          context: WEB application 단위이다. Context 설정에 따라 이에 따른 deployment descriptor(DD)파일이 하나씩 존재하게 된다. 파일의 위치는 WEBMain.xml 같은 위치이며 이름은 jeus-web-dd_[context_name].xml파일이다.

-          webserver-connection: servlet type engine WEB application 처리하는 engine이므로 web server와의 결을 담당하는 부분이 필요하게 된다. 이에 대한 설정을 하는 tag이다. 위에서는 http-listener 되어 있지만, 외에도 webtob-listener/apache-listener 등이 있다.
http-listener
다른 web server없이 JEUS자체적으로 HTTP 받아들이고자 경우 사용하고, webtob-listener 자체 web server WebtoB, apache-listener Apache와의 동을 위해 사용하면 된다.

아래에는 context마다 하나씩 존재하게 되는 DD파일을 표시하였다.

<?xml version="1.0"?>

<!DOCTYPE jeus-web-dd PUBLIC "-//Tmax Soft., Inc.//DTD JEUS WEB Deployment Info 4.0//EN"

                             "http://www.tmaxsoft.com/jeus/dtd/4.0/jeus-web-dd.dtd">

<jeus-web-dd>

    <context>

        <context-name>examples</context-name>

        <docbase>examples</docbase>

        <auto-reload>

            <enable-reload>true</enable-reload>

            <check-on-demand>true</check-on-demand>

        </auto-reload>

    </context>

</jeus-web-dd>

DD파일은 각각의 context 대한 설정을 나타낸다. Context-name WEBMain.xml 설정된 context-name 의미하고, docbase 디스크 상에서의 물리적인 위치다. Auto-reload 운영 프로그램이 수정될 경우 이를 바로 반영할지에 대한 여부를 나타낸다.

자세한 내용들은 JEUS Web Container매뉴얼을 참조하면 된다.

 

3.6 EJB engine 설정 파일

EJB type engine 설정 파일은 EJBMain.xml 파일이다.

최초 설치 포함된 EJBMain.xml파일은 다음과 같다.

<?xml version="1.0"?>

<!DOCTYPE ejb-engine PUBLIC "-//Tmax Soft, Inc.//DTD EJB Main Config 4.0//EN"

                            "http://www.tmaxsoft.com/jeus/dtd/4.0/ejb-main-config.dtd">

<ejb-engine>

    <enable-user-notify>false</enable-user-notify>

    <enable-interop>false</enable-interop>

    <system-log>

        <target>stdout</target>

        <level>debug</level>

    </system-log>

    <module-list>samples</module-list>

</ejb-engine>

위의 tag module-list engine 시작될 가지고 올라가야 EJB 모듈을 나열하는 tag이다.

EJB engine 경우 위와 같이 간단하다. 이는 EJB 경우 engine 설정보다는 EJB 구성하는데 필요한 ejb-jar.xml파일과 DD.xml 많은 내용들이 기술되어 있기 때문이다. 이들에 대한 내용은 개발과 관련된 부분이므로 JEUS EJB 매뉴얼 혹은 개발자 교재를 참조하면 된다.

 

 

4.   JEUS 구동 종료

4.1 JEUS console tool 이용한 구동 종료

JEUS 구동은 크게 가지 작업으로 나누어진다.

먼저 JEUS manager process(jeus명령어) 구동한 jeusadmin tool 이용해 manager 접속한 boot명령을 이용해 부팅을 하게 된다.

Manager 구동하면 다음과 같이 보여진다.

[c:\dmscm]#jeus

[ErrorMsgManager] Message Manager is initialized

[2004.04.29 14:28:08][0] [JeusServer] JEUSMain.xml is loaded

[2004.04.29 14:28:08][3] [LocalSecurityRealm] Loading File Realm from C:\Jeus42\config\ybsong_note\file-realm.xml

[2004.04.29 14:28:09][0] [JeusServer] JEUSMain.xml is loaded

[2004.04.29 14:28:09][0] [JeusServer] JMX Manager started

[2004.04.29 14:28:10][0] [JRSMAcceptThread] Exported Remote Security Provider at

 port 9743

[2004.04.29 14:28:10][0] [JeusServer] JeusServer is Ready

위와 같이 Ready 메시지가 나오면 manager 구동된 것이다. 다른 command창을 열러 jeusadmin [hostname] 명령을 주어 manager 아래와 같이 로그인 boot명령을 보내면 된다. ID/password 물어보게 되는데, 최초 설치 ID administrator이며 password 설치 입력한 값을 넣으면 된다.

[c:\dmscm]#jeusadmin ybsong_note

[ErrorMsgManager] Message Manager is initialized

[JeusCommander] Login

>administrator

[JeusCommander] Password

> 

[JeusCommander] JEUS 4.2 Jeus Manager Controller

ybsong_note>boot

[JeusCommander] ybsong_note boot done

ybsong_note_container1

ybsong_note>down

Do you really want to down whole JEUS? (y : n):y

[JeusCommander] ybsong_note down successful

ybsong_note>jeusexit

[JeusCommander] ybsong_note jeusexit successful

ybsong_note>exit

 

[c:\dmscm]#

위의 과정은 manager 로그인 boot명령을 통해 JEUSMain.xml 설정에 따라 container 올리는 작업과 down명령을 통해 내리는 작업을 순차적으로 나열한 것이다.

jeusexit명령어는 앞에서 띄워놓은 manager 종료시키는 작업을 수행한다. exit명령어는 jeusadmin tool 빠져나가는 명령어이다.

이와 같은 과정을 거쳐 JEUS 기동하고 내리는 작업을 완료하였다. 하지만 때에 따라서는 이러한 작업이 복잡하게 느껴질 있다. 편의를 위해 JEUS에서는 one-step으로 JEUS 구동하고 종료할 있는 방법을 제공한다. 아래와 같은 방법을 통하면 그러한 작업을 쉽게 있다.

구동 : jeus –xml –U[ID] –P[password]

종료 : jeusadmin [hostname] jeusexit –U[ID] –P[password]

 

4.2 JEUS GUI tool JManager 이용한 구동 종료

JManager GUI 이용해 JEUS 제어할 있도록 하는 tool이다. 실행하는 방법은 command창에서 jmanager라고 입력을 하면 된다. tool 이용해 부팅을 하기 위해서는 console 이용할 때와 마찬가지로 jeus명령어를 이용해 manager process 구동되어 있어야 한다.

JManager 구동하면 가장 먼저 다음과 같이 ID/password 물어보는 창이 보여진다.

 


로그인을 하면 다음과 같이 화면이 보여지게 된다.

 

 

위와 같이 화면이 나타나면 왼쪽 tree에서 hostname부분을 마우스 오른쪽 버튼을 눌러 나타나는 팝업 메뉴에서 boot 선택하면 된다. 종료를 때에는 down 선택하면 된다.

JManager 대한 자세한 내용은 JEUS GUI 매뉴얼을 참조하면 된다.

 

4.3 JEUS WEB tool 이용한 구동 종료

WEB tool browser 이용한 방법을 의미한다.

WEB tool 사용하기 위해서는 먼저 JEUSMain.xml 다음과 같이 설정이 되어 있어야 사용할 있다. JEUS 4.2 경우 설치 기본적으로 설정되어 있으므로 따로 설정할 필요는 없다.

<jeus-system>

    <node>

        <name>ybsong_note</name>

        <class-ftp>true</class-ftp>

        <sequential-start>true</sequential-start>

        <enable-webadmin>true</enable-webadmin>

        <system-log>

            <level>debug</level>

        </system-log>

        <engine-container>

Console tool 혹은 JManager 이용할 경우에는 jeus명령어를 통해 manager process 기동되어 있으면 사용할 있었으나, WEB tool 경우 boot명령까지 완료되어 있는 상태에서 사용할 있다. 이는 WEB tool 내부적으로 servlet engine 사용하기 때문이다. One-step 부팅을 이용하면 쉽게 사용할 있다.

먼저 부팅이 되어 있는 상태에서 다음과 같이 접속을 하면 ID/password 물어보는데, 이후부터는 JManager 비슷한 방법으로 사용을 하면 된다.

접속 URL: http://ip:9744/webadmin

IP JEUS 설치된 서버의 ip 말하며 9744 default port이다. 값은 환경변수에 따라 달라지는데, 기본적으로 JEUS_BASEPORT + 8번의 값을 사용한다. JEUS_BASEPORT $JEUS_HOME/bin/jeus.properties.bat파일에 설정되어 있다.

최초 브라우저를 이용해 접속을 하면 다음과 같은 화면이 보여지게 된다.

 

 

ID/password 입력한 로그인을 하면 다음과 같은 화면이 보여지게 된다.

 

 

 

이후부터는 직접 부팅과 다운 작업 설정의 변경, 모니터링과 같은 작업을 수행할 있다.

 

 

5.   container 추가

5.1 설정 파일을 직접 편집하는 방법

이는 3장에서 설명한 방법으로 직접 설정 파일을 편집하면 된다.

가장 먼저 편집해야 파일은 JEUSMain.xml 파일이다. 파일을 열어 engine-container tag 하나 만들면 된다. 쉽게 작업을 하려면 기존에 있는 engine-container tag copy & paste 필요한 부분들만 수정하면 된다. 수정이 필요한 부분은 engine-container/name tag name engine-command/name부분이다. 이를 수정하는 이유는 container 이름과 engine 이름이 중복되어 사용할 없기 때문이다.

JEUSMain.xml파일의 수정이 완료되었다면 그에 따라 engine 설정 파일을 만들어 주어야 한다. 역시 3장에서 설명한 것처럼 engine 이름에 따라 디렉토리를 만든 engine type 따른 설정 파일(WEBMain.xml, EJBMain.xml 혹은 JMSMain.xml) 만들어 준다. 역시 작업을 쉽게 하려면 기존에 있는 디렉토리 전체를 복사한 쉽게 설정 파일 필요한 부분만 수정을 하면 된다(ex: http-listener부분의 port 같은 부분).

 

5.2 WEB tool 이용하는 방법

여기서는 WEB tool 이용하는 방법에 대해서 설명한다. JManager 이용하는 방법에 대해서는 JEUS GUI tool 매뉴얼을 참조하면 된다.

 

 

위의 그림에서처럼 tree에서 hostname으로 아이콘을 마우스 오른쪽 버튼으로 클릭하면 팝업 메뉴가 나타나는데 여기서 Add container 선택하여 container 추가할 있다.

하지만 이렇게 Add container 경우 앞에서 설정 파일을 직접 편집할 때처럼 직접 많은 설정들을 해주어야 하므로 조금은 불편할 있다. 직접 편집을 복사해서 사용한 것처럼 WEB tool에서는 container 복제해주는 작업을 손쉽게 있는 기능이 있다. 이는 Clone container메뉴를 선택해서 사용하면 된다. 위의 그림에서 clone container 선택하면 아래와 같은 화면이 보여지게 된다.

 

 

위의 그림처럼 오른쪽 프레임에 가지 설정을 있는 화면이 보여진다. Source Engine Container 복제할 container 의미하며, Target Engine Container 만들어질 container 이름을 의미한다. 그리고 Target Engine engine 이름으로 사용할 이름을 말한다(여기서는 dmscm이라고 했으므로, ybsong_note_servlet_dmscm ybsong_note_ejb_dmscm 이라는 engine 만들어질 것이다).

또한 Instant Start 만들어진 container 바로 부팅할 것인지의 여부를 설정하는 것이다. 바로 부팅을 경우 포트의 충돌 등으로 인해 정상 동작하지 않을 있으므로 여기서는 선택을 하지 않도록 한다.

Clone버튼을 누르면 container 복사하는 작업이 수행된다.

 

 

위와 같이 성공했음을 보여주는 메시지가 나오고 왼쪽 tree hostname부분을 다시 클릭해보면, 아래 그림과 같이 개의 container 있음을 있다. 컬러로 보여지는 것은 현재 부팅되어 운영되고 있음을 나타내고, 흑백으로 보여지는 것은 설정은 되어 있으나 운영은 되고 있지 않음을 나타낸다.

이제 가지 수정 작업을 해야 하므로 수정이 필요한 container tree에서 선택한 보여지는 오른쪽 프레임의 상단 메뉴 Configuration 선택해 필요한 작업을 하도록 한다.

마우스로 필요한 곳들을 클릭하면서 필요한 설정을 변경하는 작업을 하면 된다.

 

 

6.   기타 console tool

6.1 jeusadmin 사용하기

jeusadmin tool manager 접속한 manager 제어하기 위한 tool이다. Container 부팅/다운, engine 부팅/다운 작업 등을 수행할 있다.

- jeusadmin <HostName>

/was>jeusadmin ybsong_note

[ErrorMsgManager] Message Manager is initialized

[JeusCommander] Login

>administrator

[JeusCommander] Password

> 

[JeusCommander] JEUS 4.0.4.9 Jeus Manager Controller

ybsong_note>

1) 현재 떠있는 엔진 목록보기

서버별로 설정에 따라 Engine Container 1 혹은 이상 구동 중이며, JVM 개의 Engine Container에는 servlet engine 혹은 EJB Engnine 구동 되고있다.

ybsong_note >allenglist

ybsong_note _servlet_engine1

ybsong_note _servlet_engine1

위와 같이 수행을 하게 되면 현재 구동중인 엔진의 목록을 있다. 또한 위의 엔진들을 booting / down 수도 있다. (help 명령어 참조)

방법 : starteng / downeng <engineName>

) starteng ybsong_note_servlet_engine1

   downeng ybsong_note_servlet_engine1

 

2) 현재 있는 Engine Container PID 목록보기

Engine Container 각각의 엔진을 담는 그릇과 같은 것으로 JVM 1개를 의미한다.

 

ybsong_note>allenglist

ybsong_note_servlet_engine1

ybsong_note_servlet_engine1

ybsong_note>pidlist

ybsong_note_container2 : 120782

ybsong_note_container1 : 58660

 

현재 운영중인 JVM 대한 PID할당 정보를 얻을 있으며, 또한 위의 Engine Container booting / down 수도 있다. (help 명령어 참조)

방법 : startcon / downcon <containerName>

) startcon ybsong_note_container1

   downcon ybsong_note_container1

 

6.2 webadmin 사용하기

webadmin tool servlet type 엔진을 관리하기 위한 console tool이다. 현재 servlet engine 모니터링 동적인 설정의 변경과 같은 작업을 수행할 있다.

- webadmin <engineName>

/was>webadmin ybsong_note_servlet_engine1

$ username : administrator

$ password : [ErrorMsgManager] Message Manager is initialized

-- Welcome to JEUS Web Container(v4.0.6.1) Admin --

 

$$1 [ybsong_note:engine1]

 

1) servlet engine 상태정보 보기(st명령)

 

$$1 [ybsong_note:engine1] st

[2003.11.14 14:52:55][0] [TMLinkManager] accept thread is started

[2003.11.14 14:52:55][0] [TMClient] TMClient initailized

[2003.11.14 14:52:55][1] [JNSLocal_] Try to connect to 192.168.21.5:9738

[2003.11.14 14:52:55][1] [JNSLocal_] Connected to JNSServer 192.168.21.5:9738

[2003.11.14 14:52:55][1] [JNSLocal_] Successfully started. (ID 192.168.21.5:37035)

        >>>>>>>> ybsong_note_servlet_engine1 <<<<<<<<

-- WebContainer [ybsong_note_servlet_engine1] --

< memory information >

VM Total Memory    = 134478336 Bytes

VM Free Memory     = 73117120 Bytes

 

< session information >

session clustering version : 2

number of local sessions : 11

number of remote sessions : 16 (active = 16, passivated = 0)

- primary session server name      : session1

- backup session server name       : null

- current session server name      : session1

- current session server is alive  : true

- current session server connections (total, used)  : 10, 0

 

< DBConnectionPool information >

[jdbc/dass] COM.ibm.db2.jdbc.app.DB2Driver

Total DB connections = 0, Free DB connections = 0

Init DB conns = 2, Max DB conns = 4, Increment rate = 1, Max idle time = -1

 

< WebtConnectionPool information >

-- ContextGroup [MyGroup] --

< thread information >

WebtobListener[webtob1] Current thread = 20, Wait Queue Count = 4, Max thread = 25

    webtob1-hth0(localhost:9900) [port: 9900] live connections = 20

HttpListener[WebListener1] Current thread = 10, Wait Queue Count = 0, Max thread = 20

 

< request information >

dass : request = 375, avgTime = 359 ms

 

Command is successfully achieved...

 

st 명령을 수행하게 되면 다음의 목록을 보여준다.

1)     해당 jvm 메모리 사용현황 : st –m

2)     DBConnectionPool 실시간 사용현황 : st –d

3)     Web Server와의 정보 : st –t

4)     설정한 Context 들어온 요청 count 평균처리시간 : st –r

5)     유지하고 있는 Session 객체의 개수 : st –s

위에서 주의 깊게 보아야 것은 다음과 같다.

1)     jvm 메모리 사용현황을 통하여 애플리케이션상의 Memory leak 현상이 없는지 파악하여야 한다. jvm 메모리 사용이 계속 증가하면 이를 의심해 보아야 한다.

2)     Session Information 통해서는 현재 login 통하여 들어와 있는 유저수를 있다.

* 보기 : st –i 1 –k 100  (1 간격으로 100 반복하여 수행하기)

 

2) 호출된 애플리케이션 정보보기

 

$$1 [ybsong_note:engine1] info

[2003.11.14 14:55:26][0] [TMLinkManager] accept thread is started

[2003.11.14 14:55:27][0] [TMClient] TMClient initailized

[2003.11.14 14:55:27][1] [JNSLocal_] Try to connect to 192.168.21.5:9738

[2003.11.14 14:55:27][1] [JNSLocal_] Connected to JNSServer 192.168.21.5:9738

[2003.11.14 14:55:27][1] [JNSLocal_] Successfully started. (ID 192.168.21.5:37115)

        >>>>>>>> ybsong_note_servlet_engine1 <<<<<<<<

<<< WebContainer Name: ybsong_note_servlet_engine1 >>>

#### ContextGroup : MyGroup

  #### Context : dass

    0:[/dass/jsp/KR/stock/9065input.jsp] <Ready> class: jeus_jspwork._dass._jsp._KR._stock._403_9065input, total_reqs: 2

    1:[/dass/jsp/KR/stock/0311input.jsp] <Ready> class: jeus_jspwork._dass._jsp._KR._stock._403_0311input, total_reqs: 5

    2:[/dass/jsp/KR/banking/0758output.jsp] <Ready> class: jeus_jspwork._dass._jsp._KR._banking._403_0758output, total_reqs: 4

    3:[/dass/jsp/KR/signon_output.jsp] <Ready> class: jeus_jspwork._dass._jsp._KR._403_signon_5foutput, total_reqs: 15

 

…(중략)

 

    52:[/dass/jsp/dassadmin/session/UserCount.jsp] <Ready> class: jeus_jspwork._dass._jsp._dassadmin._session._403_UserCount, total_reqs: 91

 

 

Command is successfully achieved...

 

ContextGroup안에 있는 Context 호출된 Application 호출 빈도수를 알아 있다. 이를 통해서 많이 호출되고 있는 Application 추출할 수가 있다.

* 보기 : info –i 1 –k 100  (1 간격으로 100 반복하여 수행하기)

 

3) application 수행 thread 상태보기

 

$$1 [ybsong_note:engine1] ti

[2003.11.14 14:57:56][0] [TMLinkManager] accept thread is started

[2003.11.14 14:57:56][0] [TMClient] TMClient initailized

[2003.11.14 14:57:56][1] [JNSLocal_] Try to connect to 192.168.21.5:9738

[2003.11.14 14:57:56][1] [JNSLocal_] Connected to JNSServer 192.168.21.5:9738

[2003.11.14 14:57:56][1] [JNSLocal_] Successfully started. (ID 192.168.21.5:37195)

        >>>>>>>> ybsong_note_servlet_engine1 <<<<<<<<

-- Thread State [webtob1-hth0(localhost:9900)] --

[webtob1-hth0(localhost:9900)-w0][waiting, wt=497100 ms]

[webtob1-hth0(localhost:9900)-w1][waiting, wt=554373 ms]

[webtob1-hth0(localhost:9900)-w2][waiting, wt=1446008 ms]

[webtob1-hth0(localhost:9900)-w3][waiting, wt=498939 ms]

[webtob1-hth0(localhost:9900)-w4][waiting, wt=1481884 ms]

…(중략)

[webtob1-hth0(localhost:9900)-w19][waiting, wt=499114 ms]

[total : 20    active : 0    idle : 20    blocked : 0    reconnecting : 0]

 

-- Thread State [WebListener1] --

[WebListener1-w0][waiting, wt=130521 ms]

[WebListener1-w1][waiting, wt=132761 ms]

[WebListener1-w2][waiting, wt=127836 ms]

…(중략)

[WebListener1-w9][waiting, wt=125673 ms]

[total : 10    active : 0    idle : 10    blocked : 0    reconnecting : 0]

 

Command is successfully achieved...

 

위의 정보를 통하여 현재 실시간으로 들어오고 있는 uri 정보를 수가 있다. 특히  rt(=run time)라는 수행시간정보를 통하여 해당 호출된 서비스가 waiting 빠졌는지, 처리가 얼마나 걸리고 있는지를 실시간으로 모니터링 있다. 만일 rt 시간이 비정상적으로 길어지거나, 모든 Thread 차서 여유 공간이 없다면, waiting 또는 hanging 현상을 의심해보고 Thread Dump 등을 통하여 원인을 찾도록 한다.

 

* 보기 : ti –i 1 –k 100  (1 간격으로 100 반복하여 수행하기)

 

$$1 [DS-CBS4:engine1] ti -a

[2003.11.14 15:03:46][0] [TMLinkManager] accept thread is started

[2003.11.14 15:03:46][0] [TMClient] TMClient initailized

[2003.11.14 15:03:46][1] [JNSLocal_] Try to connect to 192.168.21.5:9738

[2003.11.14 15:03:46][1] [JNSLocal_] Connected to JNSServer 192.168.21.5:9738

[2003.11.14 15:03:46][1] [JNSLocal_] Successfully started. (ID 192.168.21.5:37401)

        >>>>>>>> ybsong_note_servlet_engine1 <<<<<<<<

-- Thread State [webtob1-hth0(localhost:9900)] --

[webtob1-hth0(localhost:9900)-w0][waiting, 2003.11.14 14:49:39, wt=846868 ms][req=11, avgTime=488 ms, total=11][alive=true]

…(중략)

[webtob1-hth0(localhost:9900)-w19][waiting, 2003.11.14 14:49:37, wt=848882 ms][req=10, avgTime=715 ms, total=10][alive=true]

[total : 20    active : 0    idle : 20    blocked : 0    reconnecting : 0]

 

-- Thread State [WebListener1] --

[WebListener1-w0][waiting, 2003.11.14 15:02:41, wt=64660 ms][req=21, avgTime=318 ms, total=21][alive=true]

…(중략)

[WebListener1-w9][waiting, 2003.11.14 15:02:41, wt=64682 ms][req=21, avgTime=18 ms, total=21][alive=true]

[total : 10    active : 0    idle : 10    blocked : 0    reconnecting : 0]

 

Command is successfully achieved...

 

* ti –a 이용하여 요청을 처리한 건수 처리시간 평균을 보여준다.

 



출처 : http://technet.tmaxsoft.com/kr/document/tutorial/readBoardForm.do?fc=doc&seqNo=9977&bbsCode=tutorial&sc=doc_tutorial&tmaxsso_nsso=yes

Posted by 큰바우
: