`

Making a JMX connection with a timeout

    博客分类:
  • JAVA
阅读更多

转载自:http://weblogs.java.net/blog/2007/05/23/making-jmx-connection-timeout

 

One question I encounter frequently about the JMX Remote API is how to reduce the time taken to notice that a remote machine is dead when making a connection to it. The default timeout is typically a couple of minutes! Here's one way to do it.

Probably the cleanest technique for connection timeouts in general is to set a connection timeout on the socket. The idea is that instead of using...

Socket s = new Socket(host, port);

...you use...

SocketAddress addr = new InetSocketAddress(host, port);
Socket s = new Socket();
s.connect(addr, timeoutInMilliSeconds);

The problem is that this is at a rather low level. If you're making connections with the JMX Remote API you usually don't see Socket objects at all. It's still possible to use this technique, but it requires a certain amount of fiddling, and the particular fiddling you need depends on which connector protocol you are using.

A lot of the time, a much simpler and more general technique is applicable. You simply create the connection in another thread, and you wait for that thread to complete. If it doesn't complete before your timeout, you just abandon it. It might still take two minutes to notice that the remote machine is dead, but in the meantime you can continue doing other things.

If you're making a lot of connections to a lot of machines, you might want to think twice about abandoning threads, because you might end up with a lot of them. But in the more typical case where you're just making one connection, this technique may well be for you.

Assuming you're using at least Java SE 5, you'll certainly want to use java.util.concurrent to manage the thread creation and communication. There are a few ways of doing it, but the easiest is probably a single-thread executor.

The method below allows you to connect to a given JMXServiceURL with a timeout of five seconds like this:

JMXConnector jmxc = connectWithTimeout(jmxServiceURL, 5, TimeUnit
.SECONDS);

My first cut at the problem

In my first version of this entry, I proposed a solution with the following outline.

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);
}

Half an hour after posting, I suddenly realised that this version is incorrect. It reminds of the saying that for every complex problem there is a solution that is simple, obvious, and wrong.

This solution does the right thing when the connection succeeds within the time limit, and also in the case of the problem we are trying to solve, where it takes a very long time to fail. But if the connection succeeds after the time limit, the caller will already have returned, and we'll have made a connection that nobody knows about!

The second attempt

This is the outline of my second attempt, which I believe is correct. There are several refinements we'll need to apply before having a solution that actually works.

// This is just an outline: the real code appears later
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;
}

To understand how and why this works, notice that exactly one object always gets posted to the mailbox . There are three cases:

  • If the connection attempt finishes before the timeout, then the connector object will be posted to the mailbox and returned to the caller.
  • If the timeout happens, then the main thread will try to stuff the mailbox with an arbitrary object (here the empty string, but any object would do), so the connection thread will realise it has connected too late and close the newly-made connection.
  • If the timeout happens at exactly the same time as the connection is made, then the main thread may find that the mailbox is already full, in which case it again picks up the connector object and returns it.

Making it work

The code above is just an outline, and leaves out some necessary details. We need to refine it in several ways to make it work.

The first refinement we'll need is exception handling . The result of the connection attempt could be an exception instead of a JMXConnector. This doesn't change the reasoning above, but it does complicate the code.

The main thread calls BlockingQueue.poll , which can throw InterruptedException, so we must handle that.

About half of the final version of connectWithTimeout involves footering about with exceptions. It's times like this that I'm inclined to join the checked-exception-haters.

The second refinement is to clean up the connect thread when we're finished with it. The outline code doesn't call shutdown() on the ExecutorService, so every time connectWithTimeout is called, a new single-thread executor is created, and therefore a new thread. If you're lucky, the garbage-collector will pick up your executors and their threads at some stage, but you don't want to depend on luck.

A more subtle point about threads is that the outline code will create non-daemon threads . Your application will not exit when the main thread exits if there are any non-daemon threads. So as written, if you have a thread stuck in a connection attempt and your application is otherwise finished, it will stay around until the connection attempt finally times out. That's pretty much exactly the sort of thing we're trying to avoid. So we'll need to arrange to create a daemon thread instead.

All right, so here's the real code.

    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();

The initCause method is only used once but it's handy to have around for those troublesome exceptions that don't have a Throwable cause parameter.

I think it would be awfully nice if java.util.concurrent supplied DaemonThreadFactory rather than everyone having to invent it all the time.

Shouldn't this be simpler?

I admit I'm a bit uncomfortable with the code here. I'd be happier if I didn't need to reason about it in order to convince myself that it's correct. But I don't see any simpler way of using the java.util.concurrent API to achieve the same effect. Uses of cancel or interrupt tend to lead to race conditions, where the task can be cancelled after it has already delivered its result, and again we can get a JMXConnector leak; or we might close a JMXConnector that the main thread is about to return. I'd be interested in suggestions for simplification.

Conclusion of the foregoing

This is a useful technique in many cases, subject to the caution above. It's not limited to the JMX Remote API, either; you might use it when accessing a remote web service or EJB or whatever, without having to figure out how to get hold of the underlying Socket so you can set its timeout.

My thanks to Sébastien Martin for the discussion that led to this entry.

分享到:
评论

相关推荐

    Monitoring Apache Tomcat with JMX.pdf

    "Monitoring Apache Tomcat with JMX" 是一种利用Java管理扩展(Java Management Extensions, 简称JMX)来实现这一目标的方法。JMX是一种标准的API,允许我们管理和监控Java应用程序,包括Tomcat,获取各种运行时...

    JMX实战 JMX开发

    JMX实战 书中不仅有对于基础知识的介绍,还有对于JMX开发中重大的体系架构问题的深入探讨,总结了大量JMX开发中的设计模式,并讨论了框架、安全性与性能等等。书中提供了几个典型的例子,兼顾各种开发平台,这些...

    jmx三种访问方式

    Java Management Extensions(JMX)是Java平台中用于管理和监控应用程序、操作系统、网络设备等资源的一种标准框架。通过JMX,开发者可以创建管理代理(MBeans),这些代理暴露了各种管理特性,使得系统管理员可以...

    jmx监控activeMQ监控

    jmx监控ActiveMQ监控 jmx(Java Management Extensions)是一种Java技术,为Java应用程序提供了管理和监控的功能。ActiveMQ是Apache软件基金会下的一个开源消息队列系统,提供了高效、可靠的消息传递服务。在生产...

    jmx中包含的主要API

    `jmx.remote.connection.failed` 用于表示连接异常终止,而 `jmx.remote.connection.notifs.lost` 则可能表明客户端失去了接收通知的能力。`JMXConnectionNotification` 包含了连接 ID,可用于标识特定的连接,并且...

    JMX一步一步来,快速学会开发JMX应用

    【JMX 一步步来】 JMX(Java Management Extensions)是一个用于在Java应用程序中添加管理和监控功能的框架。它提供了一套标准的接口和服务,使得开发者能够轻松地在Java应用中集成管理和监控的能力。JMX的核心概念...

    jboss远程调用JMX

    当我们谈论"jboss远程调用JMX"时,我们关注的是如何通过网络从一个JMX客户端连接到运行在JBoss服务器上的JMX服务,以便进行远程管理和监控。 首先,我们需要了解JMX的基本概念。MBean是JMX的核心,它是一个Java对象...

    jmx一步步来 jmx快速上手指南

    ### JMX快速上手指南 #### 一、JMX简介 JMX,全称为Java Management Extensions,是一项由Sun Microsystems提出并被广泛采纳的标准技术。它主要用于监控和管理系统资源,包括但不限于应用程序、硬件设备以及网络...

    jmx入门

    为什么JMX那么受欢迎,JMX到底有那些优势只得人们去学习和理解,本文从JMX的基本架构、hellowold jmx以及spring对JMX的支持讲起,希望大家能通过本文对JMX有个基础的认识,并能通过本文为今后学习JMX打个基础

    Fiddler导出jmx文件

    Fiddler导出jmx文件,解决Fiddler导出文件中 没有jmx文件选项,各个版本fiddler都适用

    jmx监控weblogic,tomcat,websphere源码

    Java管理扩展(JMX)是Java平台提供的一种标准机制,用于管理和监视应用程序、服务和设备。在本项目中,"jmx监控weblogic,tomcat,websphere源码"涉及了使用JMX来监控三个主流的Java应用服务器:WebLogic、Tomcat和...

    jmx相关jar包

    Java Management Extensions(JMX)是Java平台上的一个标准技术,用于管理和监控应用程序、操作系统和网络设备。它提供了一种统一的方式来创建、配置、查询和控制管理资源,使得开发者能够轻松地在他们的应用中添加...

    jmx-1.2.1(jmxri+jmxtools) jar

    这个"jmx-1.2.1(jmxri+jmxtools) jar"包含了JMX的两个核心组件:JMX Remote Interface (jmxri) 和 JMX Tools。 1. **JMX Remote Interface (jmxri)**: JMX Remote Interface 是JMX框架的一部分,它允许远程访问和...

    书籍JMX-IN-ACTION

    《JMX in Action》是一本深入探讨Java管理扩展(Java Management Extensions, JMX)技术的专业书籍,对于希望理解和掌握JMX的IT从业者来说,是一份不可或缺的参考资料。JMX是Java平台上的一个标准组件,它提供了管理...

    jmx-tools.zip

    Java Management Extensions(JMX)是Java平台上的一个标准技术,用于管理和监控应用程序、服务和设备。JMX提供了创建、配置、查询和管理管理对象(MBeans)的能力,这些对象可以代表任何可管理的资源,从操作系统到...

    Jmx实例demo下载

    Java Management Extensions(JMX)是Java平台上的一个标准API,用于管理和监控应用程序、服务和设备。JMX允许开发者创建管理接口,这些接口可以被本地或远程的管理工具使用,以便监控和配置应用的状态。在本实例中...

    jmxdemo.rar

    这通常涉及到查找并实例化适当的MBean服务器连接器,如JMXMP(JMX Message Protocol),RMI-JRMPI(Remote Method Invocation with Java Remote Management Interface)等,然后使用连接器连接到服务端。 VisualVM...

    jmxri-1.2.1.jar+jmxtools-1.2.1.jar

    Java Management Extensions (JMX) 是Java平台上的一个标准技术,用于管理和监控应用程序、操作系统和网络设备。`jmxri-1.2.1.jar` 和 `jmxtools-1.2.1.jar` 是与JMX相关的两个核心库文件,它们在Java应用程序中扮演...

    Hbase和Hadoop JMX监控实战

    JMX(Java Management Extensions)是一种Java平台标准,用于管理和监控应用程序。在本实战中,我们将深入探讨如何利用JMX对HBase和Hadoop进行监控,以确保系统的稳定性和性能。 首先,我们需要理解HBase。HBase是...

Global site tag (gtag.js) - Google Analytics