以下这段文字的素材来自这篇文章 https://weblogs.java.net/blog/emcmanus/archive/2007/05/making_a_jmx_co.html
这几天工作中遇到点问题,排查的过程中怀疑是获取jmx链接是hang住了程序的执行。所以就找一些关于jmx链接超时时间设置方式相关方面的资料,看到上述那篇文章,深深的被感动了,说是感动一点也不过分,因为一年多的时间,在做java偏业务方面的程序,发现自己的思维方式越来越简单了,对程序没有了深入的思考。而这篇文章,作者不断的求精的过程,对我有很大的刺激。让我对自己有了思考。
二者,这篇文章中设计到的技术也是挺好的,让我对java代码有了新的认识。
两方面的原因,促使我对这篇文章写点东西。
在socket层,进行socket链接是,设置超时等参数是比较方便了,就像作者第一段代码一样
-
Socket s = new Socket(host, port);
-
SocketAddress addr = new InetSocketAddress(host, port);
-
Socket s = new Socket();
-
s.connect(addr, timeoutInMilliSeconds);
但是jmx层编程的时候,jmx api没有提供相关的设置链接超时的参数(可能有,但是我还没有找到,如果你找到了麻烦告诉我一下,万分感谢),所以就需要另想办法。
最基本的思想就是创建一个线程,在线程中链接,主方法中等待一定的时间,如果没有等到链接返回,那么相应的做超时处理。但是这样的开线程,可能带来线程过多的风险。所以就有了作者说的 single-thread executor的方式,这是java提供的基础设施。
第一版的方法如下:
-
JMXConnector connectWithTimeout(JMXServiceURL url, long timeout, TimeUnit unit) {
-
ExecutorService executor = Executors.newSingleThreadExecutor();
-
Future<JMXConnector> future = executor.submit(new Callable<JMXConnector>() {
-
public JMXConnector call() {
-
return JMXConnectorFactory.connect(url);
-
}
-
});
-
return future.get(timeout, unit);
-
}
正如作者所说,这里是有问题的。在超时之前链接建立的情况下是正确的,但是如果超时之后建立了链接呢,是滴,一个不再被使用的链接被隐藏的建立了。太可怕了。
所有了第二个版本:
-
JMXConnector connectWithTimeout(JMXServiceURL url, long timeout, TimeUnit unit) {
-
final BlockingQueue<Object> mailbox = new ArrayBlockingQueue<Object>(1);
-
final ExecutorService executor = Executors.newSingleThreadExecutor();
-
executor.submit(new Runnable() {
-
public void run() {
-
JMXConnector connector = JMXConnectorFactory.connect(url);
-
if (!mailbox.offer(connector))
-
connector.close();
-
}
-
});
-
Object result = mailbox.poll(timeout, unit);
-
if (result == null) {
-
if (!mailbox.offer(""))
-
result = mailbox.take();
-
}
-
return (JMXConnector) result;
-
}
这个版本解决了上述的问题,即超时之后及时建立链接,线程执行函数会自动将其关闭。这里注意 BlockingQueue的用法,jdk文档里有详细的说明,我在这里就不再说了。深深的觉得应该使用c++的方式实现这样一个队列,肯定有用,这是后话^_^
到这里我认为都可以用了,但是作者还有下文,这是作者深深感动我的地方。其实程序最麻烦的地方就是异常情况的处理,但是永远不能逃避他,至少作为一个成熟的程序员应该这样要求自己。所有了如下的代码:
-
public static JMXConnector connectWithTimeout(
-
final JMXServiceURL url, long timeout, TimeUnit unit)
-
throws IOException {
-
final BlockingQueue<Object> mailbox = new ArrayBlockingQueue<Object>(1);
-
ExecutorService executor = Executors.newSingleThreadExecutor(daemonThreadFactory);
-
executor.submit(new Runnable() {
-
public void run() {
-
try {
-
JMXConnector connector = JMXConnectorFactory.connect(url);
-
if (!mailbox.offer(connector))
-
connector.close();
-
} catch (Throwable t) {
-
mailbox.offer(t);
-
}
-
}
-
});
-
Object result;
-
try {
-
result = mailbox.poll(timeout, unit);
-
if (result == null) {
-
if (!mailbox.offer(""))
-
result = mailbox.take();
-
}
-
} catch (InterruptedException e) {
-
throw initCause(new InterruptedIOException(e.getMessage()), e);
-
} finally {
-
executor.shutdown();
-
}
-
if (result == null)
-
throw new SocketTimeoutException("Connect timed out: " + url);
-
if (result instanceof JMXConnector)
-
return (JMXConnector) result;
-
try {
-
throw (Throwable) result;
-
} catch (IOException e) {
-
throw e;
-
} catch (RuntimeException e) {
-
throw e;
-
} catch (Error e) {
-
throw e;
-
} catch (Throwable e) {
-
// In principle this can't happen but we wrap it anyway
-
throw new IOException(e.toString(), e);
-
}
-
}
-
-
private static <T extends Throwable> T initCause(T wrapper, Throwable wrapped) {
-
wrapper.initCause(wrapped);
-
return wrapper;
-
}
-
-
private static class DaemonThreadFactory implements ThreadFactory {
-
public Thread newThread(Runnable r) {
-
Thread t = Executors.defaultThreadFactory().newThread(r);
-
t.setDaemon(true);
-
return t;
-
}
-
}
-
private static final ThreadFactory daemonThreadFactory = new DaemonThreadFactory();
这里有太多的值得学习的地方,如daemon的考虑,工厂模式的使用,executor线程的最终关闭处理等等。读者需要从代码中慢慢体会。
在后面的回复中有这样一段,我觉得也应该考虑一下:
I think there's still a possibility for a connection to leak..
If the main thread is interrupted while waiting I think we then also
need to signal the connecting thread to close the connection...
-
} catch (InterruptedException e) {
-
mailbox.offer("");
-
throw initCause(new InterruptedIOException(e.getMessage()), e);
-
} finally {<br />
这些都思考的模式,精益求精的思考模式,值得学习。在刚离开书本的时候我还知道这些道理,但是工作了一段时间之后,麻木了,这些都当成耳旁风了,深深的惭愧,幸好我看到了这篇文章,我会继续努力的,践行那些遗忘的至理。
作者这篇文章中设计的技术也挺值得学习的,需体会!
阅读(3804) | 评论(0) | 转发(0) |