有时候我们想获取当前运行的Tomcat的各种参数,比如maxThreads、acceptThreadCount等。例如下面两张图:
#####1、Http11Protocol
import com.google.common.collect.Maps;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import java.util.List;
import java.util.Map;
/**
* Created by wangxindong on 2017/10/31.
*/
public class TomcatContainerMonitor {
private static final Logger logger = LoggerFactory.getLogger(TomcatContainerMonitor.class);
private static final String tomcatEmbedDomain = "Tomcat";
private static final String tomcatDomain = "Catalina";
private static final String bioProtocol="org.apache.coyote.http11.Http11Protocol";
private static final String nioProtocol="org.apache.coyote.http11.Http11NioProtocol";
public Object doMonitor() {
Map<String, Object> map = Maps.newHashMap();
List<MBeanServer> mBeanServers = MBeanServerFactory.findMBeanServer(null);
if (!mBeanServers.isEmpty()) {
MBeanServer mBeanServer = mBeanServers.get(0);
String domain = getTomcatDomian(mBeanServer);
try {
ObjectName[] objNames = (ObjectName[]) mBeanServer.getAttribute(new ObjectName(domain, "type", "Service"), "connectorNames");
for (ObjectName on : objNames) {
Map<String, Object> connector = Maps.newHashMap();
Object protocol = mBeanServer.getAttribute(on, "protocol");
connector.put("protocol", protocol);
Object acceptCount = mBeanServer.getAttribute(on, "acceptCount");
connector.put("acceptCount", acceptCount);
Object connectionTimeout = mBeanServer.getAttribute(on, "connectionTimeout");
connector.put("connectionTimeout", connectionTimeout);
Object protocolHandlerClassName = mBeanServer.getAttribute(on, "protocolHandlerClassName");
connector.put("protocolHandlerClassName", protocolHandlerClassName);
Object enableLookups = mBeanServer.getAttribute(on, "enableLookups");
connector.put("enableLookups", enableLookups);
Object uriEncoding = mBeanServer.getAttribute(on, "URIEncoding");
connector.put("URIEncoding", uriEncoding);
Object useBodyEncodingForURI = mBeanServer.getAttribute(on, "useBodyEncodingForURI");
connector.put("useBodyEncodingForURI", useBodyEncodingForURI);
Object localPort = mBeanServer.getAttribute(on, "port");
map.put("connector-" + localPort, connector);
if (protocolHandlerClassName.toString().equals(nioProtocol)) {//NIO
String threadPoolONStr = domain + ":type=ThreadPool,name=\"http-nio-" + localPort + "\"";
ObjectName threadPoolON = new ObjectName(threadPoolONStr);
Map<String, Object> threadPoolMap = Maps.newHashMap();
Object maxConnections = mBeanServer.getAttribute(threadPoolON, "maxConnections");
threadPoolMap.put("maxConnections", maxConnections);
Object maxThreads = mBeanServer.getAttribute(threadPoolON, "maxThreads");
threadPoolMap.put("maxThreads", maxThreads);
Object minSpareThreads = mBeanServer.getAttribute(threadPoolON, "minSpareThreads");
threadPoolMap.put("minSpareThreads", minSpareThreads);
Object acceptorThreadCount = mBeanServer.getAttribute(threadPoolON, "acceptorThreadCount");
threadPoolMap.put("acceptorThreadCount", acceptorThreadCount);
Object pollerThreadCount = mBeanServer.getAttribute(threadPoolON, "pollerThreadCount");
threadPoolMap.put("pollerThreadCount", pollerThreadCount);
Object pollerThreadPriority = mBeanServer.getAttribute(threadPoolON, "pollerThreadPriority");
threadPoolMap.put("pollerThreadPriority", pollerThreadPriority);
connector.put("threadPool", threadPoolMap);
}else if(protocolHandlerClassName.toString().equals(bioProtocol)){
String threadPoolONStr = domain + ":type=ThreadPool,name=\"http-bio-" + localPort + "\"";
ObjectName threadPoolON = new ObjectName(threadPoolONStr);
Map<String, Object> threadPoolMap = Maps.newHashMap();
Object maxConnections = mBeanServer.getAttribute(threadPoolON, "maxConnections");
threadPoolMap.put("maxConnections", maxConnections);
Object maxThreads = mBeanServer.getAttribute(threadPoolON, "maxThreads");
threadPoolMap.put("maxThreads", maxThreads);
Object minSpareThreads = mBeanServer.getAttribute(threadPoolON, "minSpareThreads");
threadPoolMap.put("minSpareThreads", minSpareThreads);
Object acceptorThreadCount = mBeanServer.getAttribute(threadPoolON, "acceptorThreadCount");
threadPoolMap.put("acceptorThreadCount", acceptorThreadCount);
connector.put("threadPool", threadPoolMap);
}
}
} catch (Exception e) {
logger.warn("获取信息失败", e);
}
}
return map;
}
private String getTomcatDomian(MBeanServer mBeanServer) {
try {
mBeanServer.getAttribute(new ObjectName(tomcatEmbedDomain, "type", "Service"), "connectorNames");
return tomcatEmbedDomain;
} catch (Exception e) {
return tomcatDomain;
}
}
@Test
public void test(){
TomcatContainerMonitor wcm = new TomcatContainerMonitor();
wcm.doMonitor();
}
}
获取MAP对象通过json序列化输出到页面,如上面两张图。
#####4、需要依赖
com.alibaba
fastjson
1.2.39
com.google.collections
google-collections
1.0
org.slf4j
slf4j-api
1.7.20
#####5、原理总结
public abstract class LifecycleMBeanBase extends LifecycleBase
implements MBeanRegistration {}
Tomcat对server.xml文件解析,解析出来的组件和容器都继承了LifecycleMBeanBase类,其父类LifecycleBase实现了容器的生命周期管理,其接口MBeanRegistration由Java JMX(Java Management Extensions)框架提供,用于监听JMX注册事件;LifecycleMBeanBase的子类都遵循JMX标准,通过getObjectNameKeyProperties()抽象方法定义类的注册名称,并在每个对象初始化(执行生命周期init方法)时注册到MBeanServer中,MBeanServer给出了外部管理系统访问JMX框架的接口,因此外部系统或工具可以实时监控到MBean的各种数据信息。这就是我们能够通过MBeanServer获取到Tomcat各种配置信息的原理。
参考资料:
http://www.fanyilun.me/2016/10/10/Tomcat的启动分析/