Chinaunix首页 | 论坛 | 博客
  • 博客访问: 146967
  • 博文数量: 27
  • 博客积分: 531
  • 博客等级: 一等列兵
  • 技术积分: 332
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-25 18:31
文章分类

全部博文(27)

文章存档

2015年(4)

2014年(3)

2013年(6)

2012年(14)

我的朋友

分类: Java

2014-04-19 22:35:13

以下这段文字的素材来自这篇文章 https://weblogs.java.net/blog/emcmanus/archive/2007/05/making_a_jmx_co.html

这几天工作中遇到点问题,排查的过程中怀疑是获取jmx链接是hang住了程序的执行。所以就找一些关于jmx链接超时时间设置方式相关方面的资料
,看到上述那篇文章,深深的被感动了,说是感动一点也不过分,因为一年多的时间,在做java偏业务方面的程序,发现自己的思维方式越来越简单了,对程序没有了深入的思考。而这篇文章,作者不断的求精的过程,对我有很大的刺激。让我对自己有了思考。

二者,这篇文章中设计到的技术也是挺好的,让我对java代码有了新的认识。

两方面的原因,促使我对这篇文章写点东西。

在socket层,进行socket链接是,设置超时等参数是比较方便了,就像作者第一段代码一样

点击(此处)折叠或打开

  1. Socket s = new Socket(host, port);
  2. SocketAddress addr = new InetSocketAddress(host, port);
  3. Socket s = new Socket();
  4. s.connect(addr, timeoutInMilliSeconds);
但是jmx层编程的时候,jmx api没有提供相关的设置链接超时的参数(可能有,但是我还没有找到,如果你找到了麻烦告诉我一下,万分感谢),所以就需要另想办法。

最基本的思想就是创建一个线程,在线程中链接,主方法中等待一定的时间,如果没有等到链接返回,那么相应的做超时处理。但是这样的开线程,可能带来线程过多的风险。所以就有了作者说的 single-thread executor的方式,这是java提供的基础设施。

第一版的方法如下:

点击(此处)折叠或打开

  1. JMXConnector connectWithTimeout(JMXServiceURL url, long timeout, TimeUnit unit) {
  2.     ExecutorService executor = Executors.newSingleThreadExecutor();
  3.     Future<JMXConnector> future = executor.submit(new Callable<JMXConnector>() {
  4.         public JMXConnector call() {
  5.          return JMXConnectorFactory.connect(url);
  6.         }
  7.     });
  8.     return future.get(timeout, unit);
  9. }
正如作者所说,这里是有问题的。在超时之前链接建立的情况下是正确的,但是如果超时之后建立了链接呢,是滴,一个不再被使用的链接被隐藏的建立了。太可怕了。

所有了第二个版本:

点击(此处)折叠或打开

  1. JMXConnector connectWithTimeout(JMXServiceURL url, long timeout, TimeUnit unit) {
  2.     final BlockingQueue<Object> mailbox = new ArrayBlockingQueue<Object>(1);
  3.     final ExecutorService executor = Executors.newSingleThreadExecutor();
  4.     executor.submit(new Runnable() {
  5.     public void run() {
  6.      JMXConnector connector = JMXConnectorFactory.connect(url);
  7.      if (!mailbox.offer(connector))
  8.         connector.close();
  9.     }
  10.     });
  11.     Object result = mailbox.poll(timeout, unit);
  12.     if (result == null) {
  13.     if (!mailbox.offer(""))
  14.      result = mailbox.take();
  15.     }
  16.     return (JMXConnector) result;
  17. }
这个版本解决了上述的问题,即超时之后及时建立链接,线程执行函数会自动将其关闭。这里注意 BlockingQueue的用法,jdk文档里有详细的说明,我在这里就不再说了。深深的觉得应该使用c++的方式实现这样一个队列,肯定有用,这是后话^_^

到这里我认为都可以用了,但是作者还有下文,这是作者深深感动我的地方。其实程序最麻烦的地方就是异常情况的处理,但是永远不能逃避他,至少作为一个成熟的程序员应该这样要求自己。所有了如下的代码:

点击(此处)折叠或打开

  1. public static JMXConnector connectWithTimeout(
  2.     final JMXServiceURL url, long timeout, TimeUnit unit)
  3.     throws IOException {
  4.     final BlockingQueue<Object> mailbox = new ArrayBlockingQueue<Object>(1);
  5.     ExecutorService executor = Executors.newSingleThreadExecutor(daemonThreadFactory);
  6.     executor.submit(new Runnable() {
  7.      public void run() {
  8.         try {
  9.          JMXConnector connector = JMXConnectorFactory.connect(url);
  10.          if (!mailbox.offer(connector))
  11.             connector.close();
  12.         } catch (Throwable t) {
  13.             mailbox.offer(t);
  14.         }
  15.       }
  16.     });
  17.     Object result;
  18.     try {
  19.      result = mailbox.poll(timeout, unit);
  20.      if (result == null) {
  21.         if (!mailbox.offer(""))
  22.          result = mailbox.take();
  23.      }
  24.     } catch (InterruptedException e) {
  25.      throw initCause(new InterruptedIOException(e.getMessage()), e);
  26.     } finally {
  27.      executor.shutdown();
  28.     }
  29.     if (result == null)
  30.      throw new SocketTimeoutException("Connect timed out: " + url);
  31.     if (result instanceof JMXConnector)
  32.      return (JMXConnector) result;
  33.     try {
  34.      throw (Throwable) result;
  35.     } catch (IOException e) {
  36.      throw e;
  37.     } catch (RuntimeException e) {
  38.      throw e;
  39.     } catch (Error e) {
  40.      throw e;
  41.     } catch (Throwable e) {
  42.      // In principle this can't happen but we wrap it anyway
  43.      throw new IOException(e.toString(), e);
  44.     }
  45.     }

  46.     private static <T extends Throwable> T initCause(T wrapper, Throwable wrapped) {
  47.     wrapper.initCause(wrapped);
  48.     return wrapper;
  49.     }

  50.     private static class DaemonThreadFactory implements ThreadFactory {
  51.     public Thread newThread(Runnable r) {
  52.      Thread t = Executors.defaultThreadFactory().newThread(r);
  53.      t.setDaemon(true);
  54.      return t;
  55.     }
  56.     }
  57.     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...

点击(此处)折叠或打开

  1. } catch (InterruptedException e) {
  2.     mailbox.offer(&quot;&quot;);
  3.     throw initCause(new InterruptedIOException(e.getMessage()), e);
  4. } finally {<br />
这些都思考的模式,精益求精的思考模式,值得学习。在刚离开书本的时候我还知道这些道理,但是工作了一段时间之后,麻木了,这些都当成耳旁风了,深深的惭愧,幸好我看到了这篇文章,我会继续努力的,践行那些遗忘的至理。

作者这篇文章中设计的技术也挺值得学习的,需体会!

阅读(3687) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~