海西数据

韩国服务器,美国服务器,香港服务器,台湾服务器,日本服务器,美国空间,马来西亚,新加坡服务器,海外服务器租用

« 服务器应用自动重新启动IIS批处理Tomcat在Linux操作系统下安装的方法 »

tomcat集群时统计session与在线人数

tomcat集群时,原来通过HttpSessionListener实现类监听session的创建和销毁来统计在线人数的方法不再有效,因为不是每个人登陆都会在同一个tomcat服务器上,而在另一台tomcat上登陆的人的session是通过session复制创建的,而复制过程不会调用HttpSessionListener接口的方法,也一直没找着如何监听session复制的方法,所以就没法统计在线人了。

     今天突然回想起tomcat下的manager应用上面就能看到session数和session的内容,于是本文的实现原理就是,做一个类似这样的servlet,此servlet把tomcat上负责管理应用的对象保存下来,供我任意使用。在tomcat上看应用的信息时,使用的是http://localhost:8080/manager/html/list这个路径,页面信息见下图:



    于是把源码下来看看(最新版原码下载地址http://apache.etoak.com/tomcat/tomcat-6/v6.0.26/src/apache-tomcat-6.0.26-src.zip),细看tomcat下webapps/manager/WEB-INF/web.xml文件配置,发现原来tomcat是通过org.apache.catalina.manager.ManagerServlet这个类来提供以上服务的,跟踪此类doGet方法代码

 

Java代码 复制代码
  1. public void doGet(HttpServletRequest request,   
  2.                      HttpServletResponse response)   
  3.        throws IOException, ServletException {   
  4.   
  5.        ......   
  6.   
  7.        //取得访问路径,   
  8.        String command = request.getPathInfo();   
  9.        if (command == null)   
  10.            command = request.getServletPath();   
  11.   
  12.        ......   
  13.        ......   
  14.   
  15.        if (command == null) {   
  16.            writer.println(sm.getString("managerServlet.noCommand"));   
  17.        } else if (command.equals("/deploy")) {   
  18.            if (war != null || config != null) {   
  19.                deploy(writer, config, path, war, update);   
  20.            } else {   
  21.                deploy(writer, path, tag);   
  22.            }   
  23.        } else if (command.equals("/install")) {   
  24.            // Deprecated   
  25.            deploy(writer, config, path, war, false);   
  26.        } else if (command.equals("/list")) {//找到了,就是这个路径,往下看list方法   
  27.            list(writer);   
  28.        } else if ......   
  29.                  ......   
  30.   
  31.        } else if (command.equals("/findleaks")) {   
  32.            findleaks(writer);   
  33.        } else {   
  34.            writer.println(sm.getString("managerServlet.unknownCommand",   
  35.                                        command));   
  36.        }   
  37.   
  38.        // Finish up the response   
  39.        writer.flush();   
  40.        writer.close();   
  41.   
  42.    }   
  43.   
  44.   
  45.    //就是这个方法生成上面的那个页面   
  46.    protected void list(PrintWriter writer) {   
  47.   
  48.        ......   
  49.   
  50.      //host就当是当前的tomcat吧,那么contexts就此tomcat下的所有应用   
  51.        Container[] contexts = host.findChildren();   
  52.        for (int i = 0; i < contexts.length; i++) {//循环每个应用   
  53.            Context context = (Context) contexts[i];   
  54.          //应用路径   
  55.            String displayPath = context.getPath();   
  56.            if( displayPath.equals("") )   
  57.                displayPath = "/";   
  58.            if (context != null ) {   
  59.                if (context.getAvailable()) {//如果应用已启动   
  60.                   /*打印出一行关于此应用的信息,应用的URL,当前状态,session数等,具体见上图 */  
  61.                    writer.println(sm.getString("managerServlet.listitem",   
  62.                                                displayPath,   
  63.                                                "running",   
  64.                                      "" + context.getManager().findSessions().length,   
  65.                                                context.getDocBase()));   
  66.                } else {   
  67.                    writer.println(sm.getString("managerServlet.listitem",   
  68.                                                displayPath,   
  69.                                                "stopped",   
  70.                                                "0",   
  71.                                                context.getDocBase()));   
  72.                }   
  73.            }   
  74.        }   
  75.    }  



     注意:context.getManager().findSessions()可以取得所有session,但这是个org.apache.catalina.Session[]数组,不是HttpSession[]数组,但这个Session接口里面有个getSession方法,返回结果正是HttpSession类型,没错,就是循环这个数组并调用其getSession方法就可以取得所有在线用户了

     上面的Session[]数组是从context对象里面来的,而context是从host对象来的,host是个初始值为NULL的成员变量,是什么时候赋上值的?是在init方法执行前,setWrapper方法执行时赋的值,请看setWrapper方法代码

 

Java代码 复制代码
  1. public void setWrapper(Wrapper wrapper) {   
  2.   
  3.     this.wrapper = wrapper;   
  4.     if (wrapper == null) {   
  5.         context = null;   
  6.         host = null;   
  7.         oname = null;   
  8.     } else {   
  9.         //这里所有需要的对象都有了,其实下面我们需要拿到wrapper就够了   
  10.         context = (Context) wrapper.getParent();   
  11.         host = (Host) context.getParent();   
  12.         Engine engine = (Engine) host.getParent();   
  13.         try {   
  14.             oname = new ObjectName(engine.getName()    
  15.                     + ":type=Deployer,host=" + host.getName());   
  16.         } catch (Exception e) {   
  17.             // ?   
  18.         }   
  19.     }   
  20.   
  21.     // Retrieve the MBean server   
  22.     mBeanServer = Registry.getRegistry(nullnull).getMBeanServer();   
  23.        
  24. }  



     setWrapper会在初始化时被调用,怎么实现的,首先看web.xml中对此servlet的配置,没什么特别,我们可以发散一下思维,struts2里面action如何能自动注入request对象?Spring如何让service监听事件?答案是一样的,那就是让你的类实现某个接口,你要的东西就给你了,对的,这里也一样,此servlet实现了ContainerServlet接口,初始的时候setWrapper方法才会被调用。

      是JAVA新手的看这里,我提出上面这些问题,不是想卖什么关子,只是想启发JAVA初学者们,当某天你们做设计时,可以参考这种方法,一句概括就是:只要你实现我的接口,我就可以让你做某事,而不需要任何额外的配置。当然这种设计的缺点就是入侵、偶合。举个简单的应用场景:每天晚上,我用一个定时器通过Spring搜索所有实现了“GarbageCleaner”接口的service bean,并调用其clean方法清理对应模块的垃圾数据,那么任何模块的service只要实现了此接口,就会被调用。

      回到正题,我也自已写个servlet并且实ContainerServlet接口吧,使用静态方法取得所有的session,具体代码如下:

 

Java代码 复制代码
  1.   
  2. import java.io.IOException;   
  3. import java.util.LinkedHashMap;   
  4. import java.util.Map;   
  5.   
  6. import javax.servlet.ServletException;   
  7. import javax.servlet.http.HttpServlet;   
  8. import javax.servlet.http.HttpServletRequest;   
  9. import javax.servlet.http.HttpServletResponse;   
  10. import javax.servlet.http.HttpSession;   
  11.   
  12. import org.apache.catalina.ContainerServlet;   
  13. import org.apache.catalina.Context;   
  14. import org.apache.catalina.Session;   
  15. import org.apache.catalina.Wrapper;   
  16.   
  17. import com.esc.common.exception.BusinessException;   
  18.   
  19. public class TomcatWrapperServlet   
  20.     extends HttpServlet implements ContainerServlet {   
  21.     private static final long serialVersionUID = 1L;   
  22.        
  23.     //弄个静态变量,初始化后就记下来,以备随时使用   
  24.     private static Wrapper wrapper = null;   
  25.     public Wrapper getWrapper() {   
  26.         return wrapper;   
  27.     }   
  28.     public void setWrapper(Wrapper wrapper) {   
  29.         TomcatWrapperServlet.wrapper = wrapper;   
  30.     }   
  31.        
  32.     //doGet不做任何事情,只需要接收第一次请求,触发初始动作就完成它的使命了   
  33.     @Override  
  34.     protected void doGet(HttpServletRequest req, HttpServletResponse resp)   
  35.             throws ServletException, IOException {   
  36.         resp.getWriter().println("Hello world!");   
  37.         resp.getWriter().flush();   
  38.         resp.getWriter().close();   
  39.     }   
  40.        
  41.     //初始化后可通过此静态方法取得所有session   
  42.     public static Map<String,HttpSession> fillSessions() {   
  43.         if(wrapper==null){//没有初始化   
  44.             throw new BusinessException("本servlet未被初始化,您必须先通过URL访问本servlet后,才可以调用这个方法");   
  45.         }   
  46.         Map<String,HttpSession> sessions = new LinkedHashMap<String, HttpSession>();   
  47.            
  48.         //取得本应用   
  49.         Context context = (Context) wrapper.getParent();   
  50.         //取得Session[]数组   
  51.         Session[] temps = context.getManager().findSessions();   
  52.         if(temps!=null && temps.length>0){   
  53.             for (int j = 0; j < temps.length; j++) {   
  54.                 //Map<sessionId,session>   
  55.                 sessions.put(temps[j].getSession().getId(), temps[j].getSession());   
  56.             }   
  57.         }   
  58.         return sessions;   
  59.     }   
  60.   
  61. }  



      在web.xml配置一下,然后启动应用,访问之,结果出现异常,是一个安全异常:TomcatWrapperServlet is privileged and cannot be loaded by this web application,说我的类是个特权类,不能被普通的web应用加载,为何manager这个应用又可以呢?把manager/META-INF/context.xml复制到我的应用,再加载,再访问,一切搞定,此文件内容只有一句
 

Xml代码 复制代码
  1. <Context antiResourceLocking="false" privileged
  • 相关文章:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

日历

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

Copyright 海西数据-万纵科技 xmwzidc.cn. ALL Rights Reserved