关于异常的总结

编程技术  /  houtizong 发布于 3年前   109

 

 

 

什么是异常

=========================

 

个人认为异常是语言发明者提供给开发者的一种表达工具。开发者

在设计接口时,可以使用异常与外界(接口的调用方)进行交流。

 

 

为什么是异常

=========================

 

在早期,语言没有提供异常机制时,方法内部的“异常”情况通常

会用特殊返回值的方式来告知方法的调用者。对于这种特殊的返回

值,调用方需要写额外的代码来处理,这在一定程度上保证了程序

的健壮性,因为我们将最坏的情况都考虑并且处理掉了(是否优雅

地处理还要因人而异),但是也很容易使代码看起来臃肿,正常的

逻辑往往陷入到一堆代码中,变得支离破碎。

 

然而,异常机制的出现可以改善这种状况。开发者通过定义不同的

异常类型来提高接口的可读性和可维护性。开发者可以只关注正常

情况下的逻辑,这样做的好处是代码看起来清晰,流畅,易读。大

多数情况下,异常的出现会导致整个处理过程中断,这个时候做一

些补救的措施已经无济于事,通常会做一些善后工作,比如资源清

理,事务回滚,错误记录等等,但是,这些操作都会集中于一个共

用的模块。同时,异常对于运维也很友好,可以根据异常的堆栈来

定位问题。简而言之,程序应该集中精力关注正常的逻辑。

 

此外,异常也能够提高代码的可读性。通过定义异常,接口可以清

晰地告诉代码阅读者出现了异常的类型是什么。错误码和异常对于

计算机没有区别,但是对于代码的维护者和阅读者,后者肯定更易

读,并且提供更多的信息。举个栗子,在获得备份文件的接口

(get_backup_file)中,返回文件的绝对路径,如果文件不存在怎

么办,怎么告诉调用方,当然,可以返回None,也可以声明一个

NoBackupFileError的异常。个人认为抛出异常更舒服,易读,也

更不容易产生BUG,如果调用方认为文件不存在会返回空字符串,

或者哪一天另外一个开发者维护接口,将接口逻辑改为返回空字符串,

都有可能产生BUG。舒服。在下面的章节中,我们可以看到各种开源

软件定义的一些异常。

 

 

他山之石可以攻玉

=========================

 

一些优秀的开源软件都会设计一个清晰完整的异常体系,从这些

设计中可以管中窥豹,大概领略一些良好异常设计的味道。

 

-- redis-py

 

reids-py是我们在用的redis的Python客户端,可以从这里:

https://github.com/andymccurdy/redis-py 得到源码。他特别

设计了一个exception.py的包来定义自己的异常:

 

"Core exceptions raised by the Redis client"

 

 

class RedisError(Exception):

    pass

 

 

class AuthenticationError(RedisError):

    pass

 

 

class ConnectionError(RedisError):

    pass

 

 

class ResponseError(RedisError):

    pass

 

 

class InvalidResponse(RedisError):

    pass

 

 

class DataError(RedisError):

    pass

 

 

class PubSubError(RedisError):

    pass

 

 

class WatchError(RedisError):

    pass

 

class NoScriptError(ResponseError):

    pass

 

他的代码里适时地抛出上面的异常,例如,他将底层的网络通信

错误封装成ConnectionError抛给上层,如果链接认证失败,他会

抛出AuthenticationError异常。我们在使用他的接口,大脑里

其实是假定我们的操作都会成功,他的接口看起来也很清晰。其

时,正确的做法是在必要的时候,我们要捕捉RedisError的异常

来进行额外的处理。我们调用Redis类提供的一系列接口时,并没

有被这些异常所困扰,我们也不会根据一个特殊的返回值,比如

None来判断接口是否正常调用成功(实际上,某些接口在“正常”

的状况下也会返回None)。我们使用的方便,归功于作者把这些

形形色色的错误用异常给隐藏掉了,我们有权力选择是否或者何时

处理这些异常。

 

-- mysql-replicant-python

 

这个库是《Mysql High Availability》

(http://shop.oreilly.com/product/9780596807290.do)

的作者在该书中提到的一个用于mysql复制操作的模块。该模块

也有该书作者实现。可以从这里看到源码:

http://bazaar.launchpad.net/~mkindahl/mysql-replicant-python/trunk/files/head:/

下面是该库定义的一些异常类:

 

"""

Module holding all the exceptions of the Replicant package.

"""

 

class Error(Exception):

    """

    Base class for all exceptions in this package

    """

    pass

 

class EmptyRowError(Error):

    """

    Class to handle attempts to fetch a key from an empty row.

    """

    pass

 

class NoOptionError(Error):

    "Exception raised when ConfigManager does not find the option"

    pass

 

class SlaveNotRunningError(Error):

    "Exception raised when slave is not running but were expected to run"

    pass

 

class NotMasterError(Error):

    """Exception raised when the server is not a master and the

    operation is illegal."""

    pass

 

class NotSlaveError(Error):

    """Exception raised when the server is not a slave and the

    operation is illegal."""

    pass

 

class QueryStatusVariableError(Error):

    """Exception raised when a bad number for a non-existing status

    variable is seen in a query event.

    """

    pass

 

class BinlogMagicError(Error):

    """Exception raised when the binary log magic number is not

    correct. This usally indicates that it's not a binary log file,

    but it could also mean that the file is corrupt.

    """

    pass

 

class UnrecognizedSchemeError(Error):

    """Exception raised when a URL is used with an unrecognized scheme.

    """

    pass

 

这个库的功能和我们系统的部分模块相似。作者将涉及复制操作中可能

出现的错误抽象成一系列的异常,我认为这提高了代码的可读性,对于

排查问题也提供了便利。

 

-- Voldemort

 

Voldemort是LinkedIn开源的基于Amazon Dynamo那篇论文一个NoSql

项目。在这里可以得到更多的信息:

http://www.project-voldemort.com/voldemort/design.html

 

我早先研究了Voldemort部分代码,给我感触最大的是他的异常设计。

Voldemort只设计了极少的几个异常,根异常是VoldemortException,

该类是一个RuntimeException(在Java中,RuntimeException不需要

强制捕获处理)。Voldemort将底层的存储异常,网络异常等等都

用VoldemortException封装,使代码看起来非常清爽,因为你看到

的代码都是正常的业务逻辑。在Voldemort中,只有在极少的地方对

某些异常进行了处理,很多情况下,异常会直接抛给客户端。

 

 

我们的系统是如何做的

=========================

 

大部分的接口都是用特殊的返回值来标识接口出现了异常,也有

一部分模块直接抛出Exception,然后由公共模块来处理这些

Exception。目前没有定义的业务异常,使代码阅读和修改

的成本非常高,因为每调用一个接口,都要考虑到该接口用于

异常的特殊返回值,对这些返回值还需要做额外的处理,其实

大部分的处理都是打一条log,然后返回(False, MSG)。我个人

认为这是一种不必要的做法。我们可以直接把异常往上抛,由

公共模块做善后工作,或者在特殊的地方对特殊的异常做额外

的处理即可,代码写起来和读起来也顺畅。

 

因为,我们知道自己在干什么,我们调了一个接口,我们就已经对

接口的返回值有把握,只要我们是按照正常的逻辑往下走。

 

在这种情况下发生异常,其实我们也无能为力,唯一能做的是在元

数据库做一些记录,记录日志,然后人工去处理。

 

一个令人印象深刻的特点是,代码里充斥着大量的if else。

这其实是一种坏味道。

 

 

总结

=========================

 

关于异常和特殊返回值的讨论一直都是仁者见仁智者见智。这里

只是提供了一些个人的看法,观察了一下其他优秀开源工程的做

法,以及发表了一些对系统目前代码的个人观点。我认为:

 

    1)必要的业务异常必不可少。

    2)对底层的错误,类似网络异常,数据库访问异常等等,我

       觉得不应该吃掉这些异常,最好是往上抛,让上层的逻辑

       能感受到异常的发生。


请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!

留言需要登陆哦

技术博客集 - 网站简介:
前后端技术:
后端基于Hyperf2.1框架开发,前端使用Bootstrap可视化布局系统生成

网站主要作用:
1.编程技术分享及讨论交流,内置聊天系统;
2.测试交流框架问题,比如:Hyperf、Laravel、TP、beego;
3.本站数据是基于大数据采集等爬虫技术为基础助力分享知识,如有侵权请发邮件到站长邮箱,站长会尽快处理;
4.站长邮箱:[email protected];

      订阅博客周刊 去订阅

文章归档

文章标签

友情链接

Auther ·HouTiZong
侯体宗的博客
© 2020 zongscan.com
版权所有ICP证 : 粤ICP备20027696号
PHP交流群 也可以扫右边的二维码
侯体宗的博客