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被特别捕获,并被抛出。在上层,该异常只是被记录下来,所以,该异常并不会引起链接的关闭。