处理错误的智慧

处理异常的智慧

 

 

1. 忽略异常,往上抛

 

一般在打开Socket时出现的异常应该往上抛,不应该吃掉异常,致使上层的调用者无法感知这些异常。我们在Hadoop的RPC的Server初始化中可以看到这一点。如果在打开Socket的时候抛出异常,该异常一直往上抛,从而到达最顶端,即Namenode的初始化,从而导致Namenode初始化失败。因为RPC中Server的Socket是一个必不可少的组件,如果该组件初始化失败,会使服务不完整。所以,需要让上层调用者知道这个失败。

 

一般最底层处理IO的异常应该往上抛。例如在Hadoop的RPC的Server中的channelWrite(WriteableByteChannel channel, ByteBuffer buffer)中,channle的write方法产生的异常会往上抛,所以,channelWrite方法声明抛出IOException。我们可以看到,在最上层的处理逻辑中,会捕获IOException,做对应的一些业务处理:记录日志,或关闭这个客户端链接。

 

2. 将底层异常转换为业务异常

 

在Voldemort中,我们可以看到Voldemort会把底层的IOException转换为VolemortException。有意思的是,VoldemortException是一个RuntimeException,所以,Voldemort的代码看起来更加干净,清爽一些。

 

3. 异常可能影响后续的处理流程

 

下面是Hadoop的RPC的Server中的一段代码:

 

try {
        count = c.readAndProcess();
      } catch (InterruptedException ieo) {
        LOG.info(getName() + ": readAndProcess caught InterruptedException", ieo);
        throw ieo;
      } catch (Exception e) {
        LOG.info(getName() + ": readAndProcess threw exception " + e + ". Count of bytes read: " + count, e);
        count = -1; //so that the (count < 0) block is executed
      }
      if (count < 0) {
        if (LOG.isDebugEnabled())
          LOG.debug(getName() + ": disconnecting client " + 
                    c.getHostAddress() + ". Number of active connections: "+
                    numConnections);
        closeConnection(c);
        c = null;
      }
 

我们可以看到readAndProcess方法抛出的除了InterruptedException以外的其他异常被捕获到(当然包括IOExcetion),在记录异常的同时,设置count=-1,标识这次读写处理失败。在后面的逻辑中,如果count小于0,说明这个链接已经被损坏,需要调用closeException方法关闭这个链接。

 

4. 特定的异常需要被特别照顾

 

在上面的代码中,InterruptedException被特别捕获,并被抛出。在上层,该异常只是被记录下来,所以,该异常并不会引起链接的关闭。