前面写过对于webapp,用javamelody来监控、分析性能是挺方便的;那要对普通的java应用进行监控,只需要在应用上启动一个嵌入式web容器就可以了。
javamelody里面的war包就用了winstone 作为Servlet容器,可以直接启动;本文是以Jetty为例,在应用中嵌入一个jetty:
import java.util.EventListener;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.handler.ContextHandlerCollection;
import org.mortbay.jetty.handler.ResourceHandler;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.DefaultServlet;
import org.mortbay.jetty.servlet.FilterHolder;
import org.mortbay.thread.QueuedThreadPool;
import org.springframework.web.context.ContextLoaderListener;
/**
* 启动一个jetty容器,结合javamelody用于监控应用性能
* @author langke
* 2012-12-21
*/
public class JavaMelodyMonitorServer {
private ESLogger log = Loggers.getLogger(JavaMelodyMonitorServer.class);
Server webServer;
/**
*
* @param serverName 应用名称
* @param host 绑定的IP地址
* @param serverPort 应用端口,jetty启动的端口默认会在此基础上加1000,如果配置文件有配置jetty.listen.port则配置优先
*/
public JavaMelodyMonitorServer(String serverName,String host,int serverPort) {
init(serverName, host, serverPort);
start();
final JavaMelodyMonitorServer server = this;
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
log.info("shutdown mointorServer:{}", server);
server.stop();
} catch (Exception e) {
log.error("run main stop error!", e);
}
}
});
}
private void init(String serverName,String host,int serverPort){
int defaultValue = serverPort+1000;
int port = Config.get().getInt("jetty.listen.port", defaultValue);
Connector connector = new SocketConnector();
webServer = new Server();
QueuedThreadPool pool = new QueuedThreadPool();
pool.setMinThreads(Config.get().getInt("jetty.pool.MinThread", 3));
pool.setMaxThreads(Config.get().getInt("jetty.pool.MaxThread", 32));
String server = host;
pool.setName(serverName+"-monitor");
pool.setDaemon(true);
webServer.setThreadPool(pool);
connector = new SocketConnector();
connector.setPort(port);
connector.setHost(server);
connector.setMaxIdleTime(60000); // 1 min
webServer.addConnector(connector);
ContextHandlerCollection col = new ContextHandlerCollection();
Context context = new Context(col, "/", Context.SESSIONS);
ResourceHandler resourceHandler = new ResourceHandler();
webServer.setHandlers(new Handler[] {col,resourceHandler });
webServer.addHandler(context);
// Set Java Melody storage Directory
System.setProperty("javamelody.storage-directory", "javamelody-"+pool.getName());
//add filter
Filter monitoringFilter = new net.bull.javamelody.MonitoringFilter();
context.addFilter(new FilterHolder(monitoringFilter), "/monitoring", Handler.REQUEST);
Map
initParams.put("contextConfigLocation", "classpath:net/bull/javamelody/monitoring-spring.xml");
context.setInitParams(initParams);
//add listener
EventListener listener = new ContextLoaderListener();
context.addEventListener(listener);
context.addServlet(DefaultServlet.class, "/*");
}
private void start(){
try{
webServer.join();
webServer.start();
}catch (Exception e){
log.error("Error starting httpserver", e);
}
}
private void stop(){
try{
webServer.stop();
webServer.destroy();
}catch (Exception e){
log.error("Error stop httpserver", e);
}
}
}
这个jetty加了shutdown hook,应用关闭的时候会自己关闭容器。
需要的jar依赖名如下:
servlet-api-3.0.jar
org.springframework.web-3.1.0.RELEASE.jar
jrobin-1.5.9.1.jar
jetty-util-6.1.26.jar
jetty-6.1.26.jar
javamelody.jar
com.springsource.net.sf.cglib-2.2.0.jar
如果使用spring框架,监控配置就比较简单,可以对某个包所有类进行监控:
监控数据源:
需要在web.xml中实现一个监听,这个监听回使web应用在读取web.xml时,加载指定的spring文件。
org.springframework.web.context.ContextLoaderListener
然后我们通过设置参数,设置启动的spring文件
classpath:net/bull/javamelody/monitoring-spring.xml
classpath:context/services.xml
classpath:context/data-access-layer.xml
/WEB-INF/applicationContext.xml
注意monitoring-spring.xml与applicaitonContext.xml的位置,我好多次使用这种方式都没有成功,貌似就是这个位置的顺序颠倒了。是否是这个原因,还有待验证(明天测试,现在没有环境)。
整个web.xml的配置文件,参考下面:
classpath:net/bull/javamelody/monitoring-spring.xml
classpath:bean.xml
org.springframework.web.context.ContextLoaderListener