zoukankan      html  css  js  c++  java
  • 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这个路径,页面信息见下图: 

      于是把源码下来看看 (apache-tomcat-6.0.39-src),细看tomcat下webapps/manager/WEB-INF/web.xml文件配置,发现原来tomcat是通过org.apache.catalina.manager.ManagerServlet这个类来提供以上服务的,跟踪此类doGet方法代码 

     1 // --------------------------------------------------------- Public Methods
     2 
     3     /**
     4      * Process a GET request for the specified resource.
     5      * 
     6      * @param request
     7      *            The servlet request we are processing
     8      * @param response
     9      *            The servlet response we are creating
    10      * 
    11      * @exception IOException
    12      *                if an input/output error occurs
    13      * @exception ServletException
    14      *                if a servlet-specified error occurs
    15      */
    16     public void doGet(HttpServletRequest request, HttpServletResponse response)
    17             throws IOException, ServletException {
    18 
    19         // Identify the request parameters that we need
    20         // 取得访问路径,
    21         String command = request.getPathInfo();
    22 
    23         String path = request.getParameter("path");
    24         String deployPath = request.getParameter("deployPath");
    25         String deployConfig = request.getParameter("deployConfig");
    26         String deployWar = request.getParameter("deployWar");
    27 
    28         // Prepare our output writer to generate the response message
    29         response.setContentType("text/html; charset=" + Constants.CHARSET);
    30 
    31         String message = "";
    32         // Process the requested command
    33         if (command == null || command.equals("/")) {
    34         } else if (command.equals("/deploy")) {
    35             message = deployInternal(deployConfig, deployPath, deployWar);
    36             // 找到了,就是这个路径,往下看list方法
    37         } else if (command.equals("/list")) {
    38         } else if (command.equals("/reload")) {
    39             message = reload(path);
    40         } else if (command.equals("/undeploy")) {
    41             message = undeploy(path);
    42         } else if (command.equals("/expire")) {
    43             message = expireSessions(path, request);
    44         } else if (command.equals("/sessions")) {
    45             try {
    46                 doSessions(path, request, response);
    47                 return;
    48             } catch (Exception e) {
    49                 log("HTMLManagerServlet.sessions[" + path + "]", e);
    50                 message = sm
    51                         .getString("managerServlet.exception", e.toString());
    52             }
    53         } else if (command.equals("/start")) {
    54             message = start(path);
    55         } else if (command.equals("/stop")) {
    56             message = stop(path);
    57         } else if (command.equals("/findleaks")) {
    58             message = findleaks();
    59         } else {
    60             message = sm.getString("managerServlet.unknownCommand", command);
    61         }
    62         // 就是这个方法生成上面的那个页面
    63         list(request, response, message);
    64     }

    list

      1 /**
      2      * Render a HTML list of the currently active Contexts in our virtual host,
      3      * and memory and server status information.
      4      * 
      5      * @param request
      6      *            The request
      7      * @param response
      8      *            The response
      9      * @param message
     10      *            a message to display
     11      */
     12     public void list(HttpServletRequest request, HttpServletResponse response,
     13             String message) throws IOException {
     14 
     15         if (debug >= 1)
     16             log("list: Listing contexts for virtual host '" + host.getName()
     17                     + "'");
     18 
     19         PrintWriter writer = response.getWriter();
     20 
     21         // HTML Header Section
     22         writer.print(Constants.HTML_HEADER_SECTION);
     23 
     24         // Body Header Section
     25         Object[] args = new Object[2];
     26         args[0] = request.getContextPath();
     27         args[1] = sm.getString("htmlManagerServlet.title");
     28         writer.print(MessageFormat.format(Constants.BODY_HEADER_SECTION, args));
     29 
     30         // Message Section
     31         args = new Object[3];
     32         args[0] = sm.getString("htmlManagerServlet.messageLabel");
     33         if (message == null || message.length() == 0) {
     34             args[1] = "OK";
     35         } else {
     36             args[1] = RequestUtil.filter(message);
     37         }
     38         writer.print(MessageFormat.format(Constants.MESSAGE_SECTION, args));
     39 
     40         // Manager Section
     41         args = new Object[9];
     42         args[0] = sm.getString("htmlManagerServlet.manager");
     43         args[1] = response.encodeURL(request.getContextPath() + "/html/list");
     44         args[2] = sm.getString("htmlManagerServlet.list");
     45         args[3] = response.encodeURL(request.getContextPath() + "/"
     46                 + sm.getString("htmlManagerServlet.helpHtmlManagerFile"));
     47         args[4] = sm.getString("htmlManagerServlet.helpHtmlManager");
     48         args[5] = response.encodeURL(request.getContextPath() + "/"
     49                 + sm.getString("htmlManagerServlet.helpManagerFile"));
     50         args[6] = sm.getString("htmlManagerServlet.helpManager");
     51         args[7] = response.encodeURL(request.getContextPath() + "/status");
     52         args[8] = sm.getString("statusServlet.title");
     53         writer.print(MessageFormat.format(Constants.MANAGER_SECTION, args));
     54 
     55         // Apps Header Section
     56         args = new Object[6];
     57         args[0] = sm.getString("htmlManagerServlet.appsTitle");
     58         args[1] = sm.getString("htmlManagerServlet.appsPath");
     59         args[2] = sm.getString("htmlManagerServlet.appsName");
     60         args[3] = sm.getString("htmlManagerServlet.appsAvailable");
     61         args[4] = sm.getString("htmlManagerServlet.appsSessions");
     62         args[5] = sm.getString("htmlManagerServlet.appsTasks");
     63         writer.print(MessageFormat.format(APPS_HEADER_SECTION, args));
     64 
     65         // Apps Row Section
     66         // Create sorted map of deployed applications context paths.
     67 
     68         // host就当是当前的tomcat吧,那么contexts就此tomcat下的所有应用
     69         Container children[] = host.findChildren();
     70         String contextPaths[] = new String[children.length];
     71         // 循环每个应用
     72         for (int i = 0; i < children.length; i++)
     73             // 应用名称
     74             contextPaths[i] = children[i].getName();
     75 
     76         TreeMap sortedContextPathsMap = new TreeMap();
     77 
     78         for (int i = 0; i < contextPaths.length; i++) {
     79             // 应用部署路径
     80             String displayPath = contextPaths[i];
     81             sortedContextPathsMap.put(displayPath, contextPaths[i]);
     82         }
     83 
     84         String appsStart = sm.getString("htmlManagerServlet.appsStart");
     85         String appsStop = sm.getString("htmlManagerServlet.appsStop");
     86         String appsReload = sm.getString("htmlManagerServlet.appsReload");
     87         String appsUndeploy = sm.getString("htmlManagerServlet.appsUndeploy");
     88         String appsExpire = sm.getString("htmlManagerServlet.appsExpire");
     89 
     90         Iterator iterator = sortedContextPathsMap.entrySet().iterator();
     91         boolean isHighlighted = true;
     92         boolean isDeployed = true;
     93         String highlightColor = null;
     94 
     95         while (iterator.hasNext()) {
     96             // Bugzilla 34818, alternating row colors
     97             isHighlighted = !isHighlighted;
     98             if (isHighlighted) {
     99                 highlightColor = "#C3F3C3";
    100             } else {
    101                 highlightColor = "#FFFFFF";
    102             }
    103 
    104             Map.Entry entry = (Map.Entry) iterator.next();
    105             String displayPath = (String) entry.getKey();
    106             String contextPath = (String) entry.getValue();
    107             Context context = (Context) host.findChild(contextPath);
    108             if (displayPath.equals("")) {
    109                 displayPath = "/";
    110             }
    111 
    112             if (context != null) {
    113                 try {
    114                     isDeployed = isDeployed(contextPath);
    115                 } catch (Exception e) {
    116                     // Assume false on failure for safety
    117                     isDeployed = false;
    118                 }
    119 
    120                 args = new Object[7];
    121                 args[0] = URL_ENCODER.encode(contextPath + "/");
    122                 args[1] = RequestUtil.filter(displayPath);
    123                 if (context.getDisplayName() == null) {
    124                     args[2] = "&nbsp;";
    125                 } else {
    126                     args[2] = RequestUtil.filter(context.getDisplayName());
    127                 }
    128                 managerServlet
    129                 // 应用是否已启动
    130                 args[3] = new Boolean(context.getAvailable());
    131                 args[4] = response.encodeURL(request.getContextPath()
    132                         + "/html/sessions?path="
    133                         + URL_ENCODER.encode(displayPath));
    134                 if (context.getManager() != null) {
    135                     args[5] = new Integer(context.getManager()
    136                             .getActiveSessions());
    137                 } else {
    138                     args[5] = new Integer(0);
    139                 }
    140 
    141                 args[6] = highlightColor;
    142                 //打印出一行关于此应用的信息,应用的URL,当前状态,session数等,具体见上图  
    143                 writer.print(MessageFormat.format(APPS_ROW_DETAILS_SECTION,
    144                         args));
    145 
    146                 args = new Object[14];
    147                 args[0] = response
    148                         .encodeURL(request.getContextPath()
    149                                 + "/html/start?path="
    150                                 + URL_ENCODER.encode(displayPath));
    151                 args[1] = appsStart;
    152                 args[2] = response.encodeURL(request.getContextPath()
    153                         + "/html/stop?path=" + URL_ENCODER.encode(displayPath));
    154                 args[3] = appsStop;
    155                 args[4] = response.encodeURL(request.getContextPath()
    156                         + "/html/reload?path="
    157                         + URL_ENCODER.encode(displayPath));
    158                 args[5] = appsReload;
    159                 args[6] = response.encodeURL(request.getContextPath()
    160                         + "/html/undeploy?path="
    161                         + URL_ENCODER.encode(displayPath));
    162                 args[7] = appsUndeploy;
    163 
    164                 args[8] = response.encodeURL(request.getContextPath()
    165                         + "/html/expire?path="
    166                         + URL_ENCODER.encode(displayPath));
    167                 args[9] = appsExpire;
    168                 args[10] = sm.getString("htmlManagerServlet.expire.explain");
    169                 Manager manager = context.getManager();
    170                 if (manager == null) {
    171                     args[11] = sm.getString("htmlManagerServlet.noManager");
    172                 } else {
    173                     args[11] = new Integer(context.getManager()
    174                             .getMaxInactiveInterval() / 60);
    175                 }
    176                 args[12] = sm.getString("htmlManagerServlet.expire.unit");
    177 
    178                 args[13] = highlightColor;
    179 
    180                 if (context.getPath().equals(this.context.getPath())) {
    181                     writer.print(MessageFormat.format(
    182                             MANAGER_APP_ROW_BUTTON_SECTION, args));
    183                 } else if (context.getAvailable() && isDeployed) {
    184                     writer.print(MessageFormat.format(
    185                             STARTED_DEPLOYED_APPS_ROW_BUTTON_SECTION, args));
    186                 } else if (context.getAvailable() && !isDeployed) {
    187                     writer.print(MessageFormat.format(
    188                             STARTED_NONDEPLOYED_APPS_ROW_BUTTON_SECTION, args));
    189                 } else if (!context.getAvailable() && isDeployed) {
    190                     writer.print(MessageFormat.format(
    191                             STOPPED_DEPLOYED_APPS_ROW_BUTTON_SECTION, args));
    192                 } else {
    193                     writer.print(MessageFormat.format(
    194                             STOPPED_NONDEPLOYED_APPS_ROW_BUTTON_SECTION, args));
    195                 }
    196 
    197             }
    198         }
    199 
    200         // Deploy Section
    201         args = new Object[7];
    202         args[0] = sm.getString("htmlManagerServlet.deployTitle");
    203         args[1] = sm.getString("htmlManagerServlet.deployServer");
    204         args[2] = response.encodeURL(request.getContextPath() + "/html/deploy");
    205         args[3] = sm.getString("htmlManagerServlet.deployPath");
    206         args[4] = sm.getString("htmlManagerServlet.deployConfig");
    207         args[5] = sm.getString("htmlManagerServlet.deployWar");
    208         args[6] = sm.getString("htmlManagerServlet.deployButton");
    209         writer.print(MessageFormat.format(DEPLOY_SECTION, args));
    210 
    211         args = new Object[4];
    212         args[0] = sm.getString("htmlManagerServlet.deployUpload");
    213         args[1] = response.encodeURL(request.getContextPath() + "/html/upload");
    214         args[2] = sm.getString("htmlManagerServlet.deployUploadFile");
    215         args[3] = sm.getString("htmlManagerServlet.deployButton");
    216         writer.print(MessageFormat.format(UPLOAD_SECTION, args));
    217 
    218         // Diagnostics section
    219         args = new Object[5];
    220         args[0] = sm.getString("htmlManagerServlet.diagnosticsTitle");
    221         args[1] = sm.getString("htmlManagerServlet.diagnosticsLeak");
    222         args[2] = response.encodeURL(request.getContextPath()
    223                 + "/html/findleaks");
    224         args[3] = sm.getString("htmlManagerServlet.diagnosticsLeakWarning");
    225         args[4] = sm.getString("htmlManagerServlet.diagnosticsLeakButton");
    226         writer.print(MessageFormat.format(DIAGNOSTICS_SECTION, args));
    227 
    228         // Server Header Section
    229         args = new Object[7];
    230         args[0] = sm.getString("htmlManagerServlet.serverTitle");
    231         args[1] = sm.getString("htmlManagerServlet.serverVersion");
    232         args[2] = sm.getString("htmlManagerServlet.serverJVMVersion");
    233         args[3] = sm.getString("htmlManagerServlet.serverJVMVendor");
    234         args[4] = sm.getString("htmlManagerServlet.serverOSName");
    235         args[5] = sm.getString("htmlManagerServlet.serverOSVersion");
    236         args[6] = sm.getString("htmlManagerServlet.serverOSArch");
    237         writer.print(MessageFormat
    238                 .format(Constants.SERVER_HEADER_SECTION, args));
    239 
    240         // Server Row Section
    241         args = new Object[6];
    242         args[0] = ServerInfo.getServerInfo();
    243         args[1] = System.getProperty("java.runtime.version");
    244         args[2] = System.getProperty("java.vm.vendor");
    245         args[3] = System.getProperty("os.name");
    246         args[4] = System.getProperty("os.version");
    247         args[5] = System.getProperty("os.arch");
    248         writer.print(MessageFormat.format(Constants.SERVER_ROW_SECTION, args));
    249 
    250         // HTML Tail Section
    251         writer.print(Constants.HTML_TAIL_SECTION);
    252 
    253         // Finish up the response
    254         writer.flush();
    255         writer.close();
    256     }

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

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

    public class HostManagerServlet
    extends HttpServlet implements ContainerServlet

     1   /**
     2      * Set the Wrapper with which we are associated.
     3      *
     4      * @param wrapper The new wrapper
     5      */
     6     public void setWrapper(Wrapper wrapper) {
     7 
     8         //这里所有需要的对象都有了,其实下面我们需要拿到wrapper就够了
     9         this.wrapper = wrapper;
    10         if (wrapper == null) {
    11             context = null;
    12             host = null;
    13             engine = null;
    14         } else {
    15             context = (Context) wrapper.getParent();
    16             host = (Host) context.getParent();
    17             engine = (Engine) host.getParent();
    18         }
    19 
    20         // Retrieve the MBean server
    21         mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
    22         
    23     }

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

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

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

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

     

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

      Xml代码

    <Context antiResourceLocking="false" privileged="true" />  

    HTTP Status 500 - Error allocating a servlet instance


    type Exception report

    message Error allocating a servlet instance

    description The server encountered an internal error that prevented it from fulfilling this request.

    exception

    javax.servlet.ServletException: Error allocating a servlet instance
    	org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    	org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
    	org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:879)
    	org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:617)
    	org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1760)
    	java.lang.Thread.run(Thread.java:722)
    

    root cause

    java.lang.SecurityException: Servlet of class manager.session.http.servlet.TomcatWrapperServlet is privileged and cannot be loaded by this web application
    	org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    	org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
    	org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:879)
    	org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:617)
    	org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1760)
    	java.lang.Thread.run(Thread.java:722)
    

    note The full stack trace of the root cause is available in the Apache Tomcat/6.0.37 logs.


    Apache Tomcat/6.0.37

    来自:http://www.jspspace.com/ResearchTopics/Art-1757-17.html

  • 相关阅读:
    51nod1459 迷宫游戏
    51nod2006 飞行员配对(二分图最大匹配)
    51nod2006 飞行员配对(二分图最大匹配)
    GIT学习之路第四天 远程仓库
    GIT学习之路第四天 远程仓库
    搞懂树状数组
    搞懂树状数组
    线段树基础详解
    线段树基础详解
    折半枚举(双向搜索)poj27854 Values whose Sum is 0
  • 原文地址:https://www.cnblogs.com/mjorcen/p/3620586.html
Copyright © 2011-2022 走看看