有时候我们想获取当前运行的Tomcat的各种参数,比如maxThreads、acceptThreadCount等。例如下面两张图:
1、Http11Protocol
1、Http11NioProtocol
3、实现代码
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、需要依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.39</version>
<!--<scope>provided</scope>-->
</dependency>
<dependency>
<groupId>com.google.collections</groupId>
<artifactId>google-collections</artifactId>
<version>1.0</version>
<!--<scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.20</version>
<!--<scope>provided</scope>-->
</dependency>
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%E7%9A%84%E5%90%AF%E5%8A%A8%E5%88%86%E6%9E%90/