技术开发 频道

Servlet 3.0实战:异步处理特性应用

  这个类是 Web 日志实现的关键类之一,它继承至 Writer,实际上是一组 Writer 的集合,其中包含至少一个默认 Writer 将数据输出至控制台,另包含零至若干个由 Queue 所决定的 Response Writer 将数据输出至客户端。输出过程中,控制台的 Writer 是同步的直接输出,输出至 http 客户端的则由线程 notifierRunnable 进行异步输出。具体实现方式是信息放置在阻塞队列 MESSAGE_QUEUE 中,子线程循环时使用到这个队列的 take() 方法,当队列没有数据这个方法将会阻塞线程直到等到新数据放入队列为止。

  我们在 Log4j.xml 中修改一下配置,将 Appender 切换为 WebLogAppender,那对 Log4j 本身的扩展就算完成了:

  清单 3:Log4j.xml 配置

<appender name="CONSOLE" class="org.fenixsoft.log.WebLogAppender">
      
<param name="Threshold" value="DEBUG"/>
      
<layout class="org.apache.log4j.PatternLayout">
        
<!-- The default pattern: Date Priority [Category] Message\n -->
        
<param name="ConversionPattern" value="%d %p [%c] %m%n"/>
      
</layout>
  
</appender>

  接着,建立一个支持异步的 Servlet,目的是每个访问这个 Servlet 的客户端,都在 ASYNC_CONTEXT_QUEUE 中注册一个异步上下文对象,这样当有 Logger 信息发生时,就会输出到这些客户端。同时,将建立一个针对这个异步上下文对象的监听器,当产生超时、错误等事件时,将此上下文从队列中移除。

  清单 4:Web 日志注册 Servlet

/**
* Servlet implementation class WebLogServlet
*/
@WebServlet(urlPatterns
= { "/WebLogServlet" }, asyncSupported = true)
public class WebLogServlet extends HttpServlet {

    
/**
    
* serialVersionUID
    
*/
    
private static final long serialVersionUID = -260157400324419618L;

    
/**
    
* 将客户端注册到监听 Logger 的消息队列中
    
*/
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {
        res.setContentType(
"text/html;charset=UTF-8");
        res.setHeader(
"Cache-Control", "private");
        res.setHeader(
"Pragma", "no-cache");
        req.setCharacterEncoding(
"UTF-8");
        PrintWriter writer
= res.getWriter();
        
// for IE
        writer.println(
"<!-- Comet is a programming technique that enables web
        servers to send data to the client without having any need for the client
        
to request it. -->\n");
        writer.flush();

        final AsyncContext ac
= req.startAsync();
        ac.setTimeout(
10 * 60 * 1000);
        ac.addListener(
new AsyncListener() {
            
public void onComplete(AsyncEvent event) throws IOException {
                WebLogAppender.ASYNC_CONTEXT_QUEUE.remove(ac);
            }

            
public void onTimeout(AsyncEvent event) throws IOException {
                WebLogAppender.ASYNC_CONTEXT_QUEUE.remove(ac);
            }

            
public void onError(AsyncEvent event) throws IOException {
                WebLogAppender.ASYNC_CONTEXT_QUEUE.remove(ac);
            }

            
public void onStartAsync(AsyncEvent event) throws IOException {
            }
        });
        WebLogAppender.ASYNC_CONTEXT_QUEUE.add(ac);
    }
}


  服务端处理到此为止差不多就结束了,我们再看看客户端的实现。其实客户端我们直接访问这个 Servlet 就可以看到浏览器不断的有日志输出,并且这个页面的滚动条会一直持续,显示 http 连接并没有关闭。为了显示,我们还是对客户端进行了包装,通过一个隐藏的 frame 去读取 WebLogServlet 发出的信息,既 Comet 流方式实现。

0
相关文章