2011-10-31 3 views
11

Servlet 3.0을 사용한 간단한 채팅과 비동기 지원을 기반으로 한 Comet 패턴을 구현하려고합니다.Servlet 3 Tomcat 7의 비동기 작업

이 글에서 영감을 해요 : http://www.javaworld.com/javaworld/jw-02-2009/jw-02-servlet3.html?page=3

내 서블릿은 다음과 같습니다.

@WebServlet(name="chatServlet", urlPatterns={"/ChatServlet"}, asyncSupported=true) 
public class ChatServlet extends HttpServlet { 

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
     AsyncContext aCtx = request.startAsync(request, response); 
     ServletContext appScope = request.getServletContext();  
     List<AsyncContext> watchers = (List<AsyncContext>) appScope.getAttribute("watchers"); 
     watchers.add(aCtx); //register the watcher 
    } 

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
      AsyncContext aCtx = request.startAsync(request, response); 
      ServletContext appScope = request.getServletContext(); 
      Queue<String> messages = (Queue<String>)appScope.getAttribute("messages"); 
      messages.add(someMessage); 
    } 
} 

지금 내 리스너는 다음과 같습니다 : 나는 용기 개시 동안 내 그것의 동결을 시작하고 때

@WebListener 
public class ChatPushService implements ServletContextListener { 

     @Override 
     public void contextInitialized(ServletContextEvent sce) { 
       final List<AsyncContext> watchers = new ArrayList<AsyncContext>(); 
      sce.getServletContext().setAttribute("watchers", watchers); 
       // store new messages not published yet 
      Queue<String> messages = new ConcurrentLinkedQueue<String>(); 
      sce.getServletContext().setAttribute("messages", messages); 
      Executor messageExecutor = Executors.newCachedThreadPool(); 
      final Executor watcherExecutor = Executors.newCachedThreadPool(); 
      while(true) 
       {  

       if(!messages.isEmpty()) 
       { 
        System.out.println("notEmpty"); 
        String message = messages.poll(); 
        messageExecutor.execute(new Runnable(){ 

         @Override 
         public void run() { 
          for(final AsyncContext aCtx : watchers){ 
           watcherExecutor.execute(new Runnable(){ 

            @Override 
             public void run() { 
              try { 
              aCtx.getResponse().getWriter().print("brrrrr"); 
             } catch (IOException e) { 
              // TODO Auto-generated catch block 
              e.printStackTrace(); 
             } 
            } 
           }); 
          } 
         } 
       }); 
       } 
     } 

    } 
    } 

. public void contextInitialized 기능은 비동기 적 배경에 실행되지 않고 더 용기 초기화를 차단처럼

Nov 1, 2011 1:12:09 AM org.apache.catalina.core.AprLifecycleListener init 
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/lib/jvm/java-6-openjdk/jre/lib/amd64/server:/usr/lib/jvm/java-6-openjdk/jre/lib/amd64:/usr/lib/jvm/java-6-openjdk/jre/../lib/amd64:/usr/java/packages/lib/amd64:/usr/lib/jni:/lib:/usr/lib 
Nov 1, 2011 1:12:09 AM org.apache.tomcat.util.digester.SetPropertiesRule begin 
WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.jee.server:Servlet3Comet' did not find a matching property. 
Nov 1, 2011 1:12:09 AM org.apache.coyote.AbstractProtocol init 
INFO: Initializing ProtocolHandler ["http-bio-8080"] 
Nov 1, 2011 1:12:09 AM org.apache.coyote.AbstractProtocol init 
INFO: Initializing ProtocolHandler ["ajp-bio-8009"] 
Nov 1, 2011 1:12:09 AM org.apache.catalina.startup.Catalina load 
INFO: Initialization processed in 624 ms 
Nov 1, 2011 1:12:09 AM org.apache.catalina.core.StandardService startInternal 
INFO: Starting service Catalina 
Nov 1, 2011 1:12:09 AM org.apache.catalina.core.StandardEngine startInternal 
INFO: Starting Servlet Engine: Apache Tomcat/7.0.22 

는 것 같습니다.

왜?

누구든지이 문제에 대해 도움을 줄 수 있습니까?

답변

8

while 루프가 실행 중입니다. contextInitialized() 잘못된 방법입니다. contextInitialized()은 응용 프로그램 시작의 일부로 Servlet Container에 의해 호출되며 while 루프를 사용하면 응용 프로그램 시작이 차단됩니다.

는 전문가에 메시지를 게시하는 ContextListener 하나 개의 데몬 스레드를 시작합니다 같은 코드를 수정

@WebListener 
public class ChatPushService implements ServletContextListener { 

    @Override 
    public void contextInitialized(ServletContextEvent sce) { 
      final List<AsyncContext> watchers = new ArrayList<AsyncContext>(); 
     sce.getServletContext().setAttribute("watchers", watchers); 
      // store new messages not published yet 
     Queue<String> messages = new ConcurrentLinkedQueue<String>(); 
     sce.getServletContext().setAttribute("messages", messages); 
     new chatManager(sce.getServletContext()).start(); //START DAEMON 

     } 
} 
public class ChatManager implements Runnable 
{ 
ServletContext servletCtx; 
public ChatManager(ServletContext ctx) 
{ 
    this.servletCtx = ctx; 
} 
public void run() 
{ 
     List<AsyncContext> watchers = (List<AsyncContext>) servletCtx.getAttribute("watchers"); 
    Queue<String> messages = (Queue<String>)appScope.getAttribute("messages"); 
    Executor messageExecutor = Executors.newCachedThreadPool(); 
     final Executor watcherExecutor = Executors.newCachedThreadPool(); 
     while(true) 
      {  

      if(!messages.isEmpty()) 
      { 
       System.out.println("notEmpty"); 
       String message = messages.poll(); 
       messageExecutor.execute(new Runnable(){ 

        @Override 
        public void run() { 
         for(final AsyncContext aCtx : watchers){ 
          watcherExecutor.execute(new Runnable(){ 

           @Override 
            public void run() { 
             try { 
             aCtx.getResponse().getWriter().print("brrrrr"); 
            } catch (IOException e) { 
             // TODO Auto-generated catch block 
             e.printStackTrace(); 
            } 
           } 
          }); 
         } 
        } 
      }); 
      } 
    } 

} 

} 
나는 라 메쉬의 코드에 대해 언급 할 수없는, 그래서 때문에 ... 여기 을 배치해야
+0

그 부분도 나를 놀라게했지만 그 코드는 OP가 복사 한 Javaworld의 기사에서 제시되었으므로 OP는 여기에서 책임을지지 않습니다. – BalusC

+0

POST로 제출 된 각 메시지가 구독 된 모든 클라이언트에게 전달되는 채팅을 만들려고합니다. 따라서 대기열이 생성되고 while (true) 블록은이 새로운 메시지가 이미 대기열에 있는지를 모니터링합니다. –

0

ChatManager runnable 둘레에 스레드가 래핑되지 않습니다. run()을 호출하고 start()를 호출하면 안됩니다. 또한, 꽤 분명하지만, 그것은 새로운 ChatManager() .. 새 chatManager() ... 자바의 계정은 대소 문자를 구분해야합니다.