博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
tomcat集群时统计session与在线人数
阅读量:7066 次
发布时间:2019-06-28

本文共 20997 字,大约阅读时间需要 69 分钟。

  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 creating10      * 11      * @exception IOException12      *                if an input/output error occurs13      * @exception ServletException14      *                if a servlet-specified error occurs15      */16     public void doGet(HttpServletRequest request, HttpServletResponse response)17             throws IOException, ServletException {18 19         // Identify the request parameters that we need20         // 取得访问路径,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 message29         response.setContentType("text/html; charset=" + Constants.CHARSET);30 31         String message = "";32         // Process the requested command33         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 = sm51                         .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 safety117                     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] = " ";125                 } else {126                     args[2] = RequestUtil.filter(context.getDisplayName());127                 }128                 managerServlet129                 // 应用是否已启动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] = response148                         .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 Section201         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 section219         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 Section229         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(MessageFormat238                 .format(Constants.SERVER_HEADER_SECTION, args));239 240         // Server Row Section241         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 Section251         writer.print(Constants.HTML_TAIL_SECTION);252 253         // Finish up the response254         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 server21         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 implements19         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     @Override35     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     // 初始化后可通过此静态方法取得所有session43     public static Map
fillSessions() {44 if (wrapper == null) {
// 没有初始化45 throw new RuntimeException(46 "本servlet未被初始化,您必须先通过URL访问本servlet后,才可以调用这个方法");47 }48 Map
sessions = new LinkedHashMap
();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
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代码

 


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

 

 

转载地址:http://xetll.baihongyu.com/

你可能感兴趣的文章
Android 解决ScrollView嵌入ListView | GridView | ScrollView显示问题
查看>>
[Android Pro] AndroidX重构和映射
查看>>
栈练习1,2,3
查看>>
WebLoad 解析服务器返回的XML格式内容
查看>>
生产者与消费者问题 之锁的顺序
查看>>
mvvm
查看>>
树状数组小结
查看>>
走进windows编程的世界-----消息处理函数(3)
查看>>
UIPickerView的使用
查看>>
hdu 5348 MZL&#39;s endless loop
查看>>
MAC 通过brew安装软件
查看>>
非对称加密(RSA、DH密钥交换算法、数字签名)
查看>>
vue的路由传值query方法
查看>>
tornado学习笔记(一)
查看>>
win7开WIFI热点, xrdp远程, zsh 主题乱码, watchdog启用, 在线Linux内核, WebRTC
查看>>
EZOJ #257
查看>>
浅谈MySQL中的查询优化
查看>>
PYTHON RE正则表达式
查看>>
QT中定时器的使用方法
查看>>
Prototype 原型模式
查看>>