我已经通过使用标准 API 和规范的通用方法实现了此功能,没有第三方框架或库。该解决方案已广泛用于部署在 glassfish 应用程序服务器和 jboss 中的许多企业级系统。它也已成功地与 weblogic (12c) 一起使用。但是,该方法应该适用于任何支持标准 JMX 规范的应用程序服务器或 servlet 容器。
tldr ; 这个版本是创建两个 JMX bean 接口和一个 http 会话侦听器。其中一个 JMX bean 接口为每个受监视的应用程序创建一个实例,并负责跟踪来自每个受监视应用程序的所有会话,它基本上提供每个应用程序的所有会话的统计信息。另一个 JMX bean 接口为每个受监视应用程序中创建的每个会话创建一个实例。http 会话监听器监视每个应用程序的会话并做两件事。通知与此应用程序对应的第一个 JMX bean 有关创建/销毁的会话的信息,以便更新统计信息。从 JMX 服务注册或取消注册与会话对应的 JMX 实例。
一切设置好后,就可以从 jdk 附带的 jconsole 和 visualvm 等 JMX 客户端使用它。从 jmx 客户端可以查看 JMX bean 的所有属性并调用它们的任何方法。
以下屏幕截图来自使用 jconsole 的测试应用程序。
这些是来自 JMX bean 实例的属性,对应于每个受监视的应用程序。
这些是可以在选定的特定会话上执行的操作。
如果监控多个应用程序,则每个 jmx bean 接口下将出现更多具有自己结构的应用程序上下文,即 /TestApplication 、 /Application2 等。
如何
最初需要创建两个 JMX bean 接口(简单教程),然后是一个 HttpSessionListener(网上有很多教程)。
1.第一个 JMX bean 接口对于每个受监控的应用程序只有一个实例,并将存储与从任何受监控的应用程序创建的会话相关的所有信息。它基本上用于持久性。我只将数据保存在内存中,这意味着如果服务器宕机,数据将会丢失,但通常只要服务器启动,只需检查统计信息。如果您想将数据保存到日志或数据库中以便始终拥有这些信息,您当然可以在接口的实现中做到这一点。
所以这可能如下,
public interface SessionsMXBean {
/**
* Get Indicates whether the data should be persisted in memory.
*/
public boolean getPersistData();
/**
* Set Indicates whether the data should be persisted in memory.
*/
public void setPersistData(boolean value);
/**
* Get All active sessions that have been persisted.
*/
public String getActiveSessions();
/**
* Get All dates of each active session that has been persisted.
*/
public String getDatesOfSessions();
/**
* Get The threshold for the number of session, after which persistence will
* take place. If -1 all are persisted.
*/
public int getSessionsThreshold();
/**
* Set The threshold for the number of session, after which persistence will
* take place. If -1 all are persisted.
*/
public void setSessionsThreshold(int value);
/**
* Set The limit of size to be persisted in KB. If -1 then no size limit.
*/
public void setPersistenceSize(long value);
/**
* Clears all persisted data.
*/
public void clearData();
/**
* Unregisters this instance
*/
public void unregisterThis();
}
然后你必须创建这个接口的实现,最终将保存这种数据。
public class SessionsImpl implements SessionsMXBean {
/*
here you need to implement the interface and have all kind of objects you require
*/
public synchronized void incrementSessions() {
....
}
public synchronized void decrementSessions() {
.....
}
2.第二个 JMX bean 接口将为每个受监控应用程序中创建的每个会话创建一个实例。此接口将存储会话对象,并且还将具有可以从 jmx 客户端调用的方法,以使这些会话无效。这可以如下,
public interface HttpSessionMXBean {
/**
* Get HTTP Session id
*/
public String getSessionId();
/**
* Get the date created
*/
public String getDateCreated();
/**
* Get the date created in milliseconds
*/
public long getMillisCreated();
/**
* Get attributes from http session
*
* @param attrName Attribute Name
* @return java.lang.String
*/
public String getAttribute(String attrName);
/**
* Invalidate this session
*/
public void invalidate();
/**
* Unregisters this instance
*/
public void unregisterThis();
}
再次需要一个实现,
public class HttpSessionMXBeanImpl implements HttpSessionMXBean {
....
3.然后创建 HttpSessionListener,它将创建/删除第二个 bean 接口的实例,并从服务器的 JMX 服务中注册/注销它们。这将在会话创建和失效/过期时发生。因此,每个应用程序都有一个侦听器,并在其 web.xml 中定义了它。
HttpSessionListener
....
public class MyJMXHTTPSessionListener implements HttpSessionListener {
....
private SessionsImpl sesssionsImpl;
private Map<String, HttpSessionMXBeanImpl> httpSessionMXBeans
@Override
public void sessionCreated(HttpSessionEvent se) {
//requires synchronized block here with this i.e.
synchronized (this) {
/*check if a jmx bean instance of the 1st interface exists otherwise create one*/
if(sessionsImpl==null){
sesssionsImpl= new SesssionsImpl();
/* take care here to create a nice and unique path per instance
of the application in order to be nicely presented on the JMX tree of the JMX clients */
String id = ("services.jmx.beans:type=Sessions,"+ "realm=" + se.getSession().getServletContext().getContextPath());
sessionManagerMXBean.setId(id);
ObjectName objectName = new ObjectName(id);
if (ManagementFactory.getPlatformMBeanServer().isRegistered(objectName)) {
ManagementFactory.getPlatformMBeanServer().
unregisterMBean(objectName);
}
ManagementFactory.getPlatformMBeanServer().
registerMBean(sesssionsImpl,
objectName);
}
sesssionsImpl.inrementSessions();
/*
create a jmx bean instance of the 2nd interface
and register it to the jmx service as already shown using the unique session id
and a nice path indicating the 2nd interface jmx beans.
*/
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
//requires synchronized block here with this i.e.
synchronized (this) {
/*unregister the jmx bean instance of the 2nd interface,
remove it from the list
and call decrementSessions() on the jmx bean instance corresponding to this app*/
}
}
}
如果您通过添加以下几行在 web.xml 文件中定义 HttpSessionListener,则可以随时为任何 Web 应用程序轻松激活此功能,
web.xml
<listener>
<listener-class>
myservices.myhttpsessionlisteners.MyJMXHTTPSessionListener
</listener-class>
</listener>