자바 서블릿 컨테이너의 Comet 지원 3 - Resin

이전글

Resin 은 버전 3.1에서 Comet 모델을 지원한다. Tomcat 과 마찬가지로 javax.servlet.Servlet 을 확장한 인터페이스를 제공하는 방식이다. com.caucho.servlet.comet.CometServlet 이 그것인데 이 인터페이스에는 다음과 같은 메서드들이 있다.

public interface CometServlet extends Servlet {
  boolean service(ServletRequest request, ServletResponse response,
                            CometController controller) throws ServletException, IOException;
  boolean resume(ServletRequest request, ServletResponse response,
                             CometController controller) throws ServletException, IOException;
}

Tomcat 과 마찬가지 방식으로 이 인터페이스를 구현한 서블릿은 요청을 처리할 때 기존 서블릿의 service 메서드를 호출하지 않고 이 인터페이스의 service 메서드를 호출한다. service 메서드의 호출 결과가 true 가 되면 요청 처리는 suspend 되고 스레드는 풀로 되돌아간다. CometController 의 wake 메서드를 호출하면 요청 처리는 재개되어 resume 메서드가 실행된다. resume 메서드가 true 를 반환하면 또 다시 suspend 되어 wake 를 기다리게 되고 false 를 반환하면 요청 처리가 끝나게 된다.

service, resume 메서드에 인자로 들어오는 CometController 객체는 service, resume 그리고 다른 이벤트 대기 스레드 사이에서 공유하여 신호를 보낼 수 있고(wake 메서드) 서로간 데이타 교환을 할 수도 있다(setAttribute, getAttribute).

Jetty 나 Tomcat 과는 어떻게 다른지 같은 채팅 예제로 실제 사용법을 알아보겠다. ChatServlet 과 chat.jsp 는 Jetty 의 경우와 같고 BroadcasterServlet 은 다음과 같다.

// BroadcasterServlet.java
// ...
@Override
public boolean service(ServletRequest request, ServletResponse response,
                            CometController controller) throws ServletException, IOException {
    HttpServletResponse res = (HttpServletResponse) response;
    res.setContentType("text/html; charset=utf-8");
    messageSender.addSession(controller);
    return true;
}

@Override
public boolean resume(ServletRequest request, ServletResponse response,
                             CometController controller) throws ServletException, IOException {
    messageSender.removeSession(controller);
    String message = (String) controller.getAttribute("message");
    HttpServletResponse res = (HttpServletResponse) response;
    PrintWriter out = res.getWriter();
    out.println(message);
    res.flushBuffer();
    return false;
}
// ...

service 메서드에서 최초 요청 처리를 처리하면서 인자로 넘어온 CometController 객체를 messageSender 객체에 저장하여 이벤트 처리 스레드로 넘긴다. 이후 CometController 의 wake 메서드가 호출되면 resume 이 실행되고 여기서 실제 데이타를 전송한다. 전송할 데이타는 이벤트 처리 스레드에서 CometController.setAttribute 으로 넘기고 여기서는 getAttribute 로 받는다.

다음은 MessageSender 클래스이다.

// MessageSender.java
public class MessageSender implements Runnable {
  private final BlockingQueue<String> messages =
                         new LinkedBlockingQueue<String>();
  private final Set<CometController> sessions =
                         new CopyOnWriteArraySet<CometController>();

  private volatile boolean running = true;

  public void stop() {
    this.running = false;
  }

  public void sendMessage(String message) {
    try {
      messages.put(message);
    } catch (InterruptedException ignore) {
      // ignore
    }
  }

  public void addSession(CometController controller) {
    sessions.add(controller);
  }

  public void removeSession(CometController controller) {
    sessions.remove(controller);
  }

  @Override
  public void run() {
    while (running) {
      String message = null;
      try {
        message = messages.take();
      } catch (InterruptedException ignore) {
        // ignore
      }
      for (CometController controller : controllers) {
        controller.setAttribute("message", message);
        controller.wake();
      }
    }
  }
}

MessageSender 는 Jetty 의 경우와 비슷하다. CometController 들을 sessions 에 저장해 두고 채팅 메시지가 들어오면 이를 CometController 에 저장(setAttribute) 한 후 wake 를 호출해서 요청 처리가 resume 되게 한다.

Resin 의 Comet 지원은 Jetty 와 비교하면 직관적으로 이해하기 쉽다. 또한 Tomcat 처럼 번거롭지도 않다. 세 서블릿 컨테이너의 Comet 지원 중에서 가장 사용하기가 쉬운 것 같다.

2008-08-01 09:35 | Permlink | Comments
blog comments powered by Disqus

About

Laser Keyboard

I'm Jin Kim. Live in Seoul, Korea. Software Developer. Java, Perl, Scala, Common Lisp. Mainly programming in web program and its backend data store. Interested in data processing and distributed computing.

Archive

RSS