关于异常处理:网易云信-Crash-异常治理实践-智企技术委员会技术专题系列

前言Crash(造成用户无奈应用客户端所承载的服务)作为客户端稳固治理的最次要问题之一。云信作为国内业界当先的 RTC/IM PaaS 服务商,对于客户端 SDK(PaaS 服务商对外服务的次要载体)的 Crash 治理再器重也不为过。对于客户端 Crash 稳定性治理,只管业界已有多个成熟的监控平台,实现了从埋点、上报、展现到报警一站式服务,如 Bugly 平台、Fabric 平台等,也有 Crash 捕获的工具 SDK,不便在此基础上按需定制监控平台,如 KSCrash、Breakpad 等。但对于云信这样的 RTC&IM PaaS 服务商来讲,无论是市面上现有 APP Crash 治理平台,还是 Crash 捕获工具 SDK 都是无奈解决针对 PaaS 服务商 Crash 异样治理痛点。 • 平台类型反对无限 业内平台往往只笼罩支流的两个挪动端平台 iOS/Android、对于 Mac OS/PC/Linux/Flutter 不反对。 • APP 与 SDK 监控数据无奈无效隔离 业内对外服务的 Crash 收集平台根本都是设计服务于 APP,后盾剖析与展现无奈同时对 APP 和 SDK 进行辨别。 • 问题治理实现不了闭环解决 以业内驰名 Bugly 为例,只提供了对外闭源 SDK,Bugly 后端目前也没有凋谢第三方接口,解体信息都要人工去被动搜寻解决,无奈与 PaaS 服务商外部 Bug 管理系统进行联动,异样感知也没法主动感知与推送,造成不了 Crash 问题从捕捉到研发人员响应解决高效的闭环。 Crash 异样治理建设为了解决以上 PaaS 服务商稳定性问题治理的痛点,构建一套从异样问题捕捉到研发人员高效问题解决的监控剖析零碎就显得分外火烧眉毛了,云信研发团队从以下三个维度思考: ...

March 8, 2023 · 2 min · jiezi

关于异常处理:处理-Exception-的几种实践被很多团队采纳

起源:http://ww7.rowkey.me/在 Java 中解决异样并不是一个简略的事件。不仅仅初学者很难了解,即便一些有教训的开发者也须要破费很多工夫来思考如何解决异样,包含须要解决哪些异样,怎么解决等等。 这也是绝大多数开发团队都会制订一些规定来标准对异样的解决的起因。而团队之间的这些标准往往是截然不同的。 本文给出几个被很多团队应用的异样解决最佳实际。 1. 在 Finally 块中清理资源或者应用 try-with-resource 语句当应用相似 InputStream 这种须要应用后敞开的资源时,一个常见的谬误就是在 try 块的最初敞开资源。 public void doNotCloseResourceInTry() {    FileInputStream inputStream = null;    try {        File file = new File("./tmp.txt");        inputStream = new FileInputStream(file);        // use the inputStream to read a file        // do NOT do this        inputStream.close();    } catch (FileNotFoundException e) {        log.error(e);    } catch (IOException e) {        log.error(e);    }}上述代码在没有任何 exception 的时候运行是没有问题的。然而当 try 块中的语句抛出异样或者本人实现的代码抛出异样,那么就不会执行最初的敞开语句,从而资源也无奈开释。 正当的做法则是将所有清理的代码都放到 finally 块中或者应用 try-with-resource 语句。 public void closeResourceInFinally() {    FileInputStream inputStream = null;    try {        File file = new File("./tmp.txt");        inputStream = new FileInputStream(file);        // use the inputStream to read a file    } catch (FileNotFoundException e) {        log.error(e);    } finally {        if (inputStream != null) {            try {                inputStream.close();            } catch (IOException e) {                log.error(e);            }        }    }}public void automaticallyCloseResource() {    File file = new File("./tmp.txt");    try (FileInputStream inputStream = new FileInputStream(file);) {        // use the inputStream to read a file    } catch (FileNotFoundException e) {        log.error(e);    } catch (IOException e) {        log.error(e);    }}2. 指定具体的异样尽可能的应用最具体的异样来申明办法,这样能力使得代码更容易了解。 public void doNotDoThis() throws Exception {    ...}public void doThis() throws NumberFormatException {    ...}如上,NumberFormatException 字面上即能够看出是数字格式化谬误。 3. 对异样进行文档阐明当在办法上申明抛出异样时,也须要进行文档阐明。和后面的一点一样,都是为了给调用者提供尽可能多的信息,从而能够更好地防止 / 解决异样。异样解决的 10 个最佳实际,这篇也举荐看下。 在 Javadoc 中退出 throws 申明,并且形容抛出异样的场景。 /** * This method does something extremely useful ... * * @param input * @throws MyBusinessException if ... happens */public void doSomething(String input) throws MyBusinessException {    ...}4. 抛出异样的时候蕴含形容信息在抛出异样时,须要尽可能准确地形容问题和相干信息,这样无论是打印到日志中还是监控工具中,都可能更容易被人浏览,从而能够更好地定位具体错误信息、谬误的重大水平等。 但这里并不是说要对错误信息简明扼要,因为原本 Exception 的类名就可能反映谬误的起因,因而只须要用一到两句话形容即可。 try {    new Long("xyz");} catch (NumberFormatException e) {    log.error(e);}NumberFormatException 即通知了这个异样是格式化谬误,异样的额定信息只须要提供这个谬误字符串即可。当异样的名称不够显著的时候,则须要提供尽可能具体的错误信息。 5. 首先捕捉最具体的异样当初很多 IDE 都能智能提醒这个最佳实际,当你试图首先捕捉最抽象的异样时,会提醒不能达到的代码。当有多个 catch 块中,依照捕捉程序只有第一个匹配到的 catch 块能力执行。因而,如果先捕捉 IllegalArgumentException,那么则无奈运行到对 NumberFormatException 的捕捉。 public void catchMostSpecificExceptionFirst() {    try {        doSomething("A message");    } catch (NumberFormatException e) {        log.error(e);    } catch (IllegalArgumentException e) {        log.error(e)    }}6. 不要捕捉 ThrowableThrowable 是所有异样和谬误的父类。你能够在 catch 语句中捕捉,然而永远不要这么做。如果 catch 了 throwable,那么不仅仅会捕捉所有 exception,还会捕捉 error。而 error 是表明无奈复原的 jvm 谬误。因而除非相对必定可能解决或者被要求解决 error,不要捕捉 throwable。 ...

July 16, 2021 · 1 min · jiezi

关于异常处理:论一个优秀的工程师应该如何做好异常处理和日志记录

异样解决Java类库中定义的能够通过预查看形式躲避的RuntimeException异样不应该通过catch形式来解决: NullPointerExceptionIndexOutofBoundsException无奈通过预查看的异样除外: 在解析字符串模式数字时,不得不通过catch NumberFormatException来实现 if (obj != null) {}异样不要用来做流程管制,条件管制: 异样设计的初衷是解决程序运行中的各种意外状况,且异样的解决效率比条件判断形式要低很多应用catch时要辨别稳固代码和非稳固代码: 稳固代码: 无论如何不会出错的代码非稳固代码: 非稳固代码的catch尽可能辨别异样类型,再做对应解决对于大段代码进行try - catch,会使得程序无奈依据不同的异样做出正确的应激反应,也不利于定位问题 在用户注册场景中,如果用户输出非法字符,或者用户名称已存在,或者用户明码过于简略,在程序上作出分门别类的判断,并提醒给用户捕捉异样是为了解决,不要捕捉了什么都不解决.如果不须要解决,应该将异样抛给调用者 最外层的业务使用者,必须解决异样,将其转化为用户能够了解的内容如果有try块放到了事务代码中 ,catch异样后,如果须要回滚事务,肯定要留神手动回滚事务finally块必须对资源对象,流对象进行敞开,有异样也要做try - catch JDK 7当前,能够应用try - with - resources 形式不要在finally块中应用return: finally块中的return返回后办法完结执行,不会再执行try块中的return语句捕捉异样与抛出异样必须齐全匹配,或者是抛异样的父类办法的返回值能够为null,不强制返回空集合或者空对象等,必须增加正文充分说明什么状况下会返回null值 即便调用办法返回空集合或者空对象,对于调用者来说,必须思考到近程调用失败,序列化失败,运行时异样等返回null的场景肯定要防止出现NPE异样,留神NPE产生的场景: 返回类型为根本数据类型,return包装数据类型的对象时, 主动拆箱有可能产生NPE数据库的查问后果可能为null汇合里的元素即便isNotEmpty, 取出的数据元素也可能为null近程调用返回对象时,一律要进行空指针判断,避免NPE对于Session中获取的数据,倡议进行NPE查看,防止空指针级联调用obj.getA().getB.getC(), 一连串的调用,容易产生NPEJDK 8应用Optional类来避免NPE问题定义时辨别unchecked和checked异样,防止间接抛出new RuntimeException(), 不容许抛出Exception或者Throwable, 应该应用有业务含意的自定义异样 举荐应用业务界已定义过的异样: DAOExceptionServiceException对于公司外的http或者api凋谢接口必须应用 "错误码"; 利用外部举荐异样抛出; 跨利用间的RPC调用优先思考应用Result形式,封装isSuccess()办法,错误码,谬误简短信息 RPC办法应用Result形式的起因: 应用抛异样返回形式,调用方如果没有捕捉到就会产生运行时谬误如果不加栈信息,只是new自定义异样,退出本人了解的error message, 对于调用端解决问题的帮忙不会太多.如果加了栈信息,在频繁调用出错的状况下,数据序列化和传输的性能损耗也是问题避免出现反复的代码,即DRY(Don't Repeat Yourself)准则: 反复的代码在当前的批改时,须要批改所有的正本,容易脱漏抽取共性办法,或者形象公共类,或者组件化 一个类中有多个public办法,都须要进行数行雷同的参数校验工作,这个时候就要进行抽取: private boolean checkParam(DTO dto) {...}日志规约利用中不可间接应用日志零碎(log4j,logback)中的API,应该应用日志框架slf4j中的API, 应用门面模式的日志框架,有利于保护和各个类的日志解决形式对立日志文件至多保留15天,因为有些异样具备以 "周" 为频次产生的特点利用中的扩大日志(打点,长期监控,拜访日志等)命名形式: appName_logType_logName.log logType: 日志类型,如 stats,monitor,accesslogName: 日志形容这样通过文件名就能够晓得日志文件属于什么利用,什么类型,什么目标,也不便归类查找 mppserver利用中独自监控时区转换异样: mppserver_monitor_timeZoneConvert.log对日志进行分类,比方将谬误日志和业务日志离开寄存,便于开发人员查看,也便于对日志零碎进行及时监控对 trace,debug,info级别的日志输入,必须应用条件输入模式或者应用占位符形式 logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);如果日志级别是warn: 上述日志不会打印,然而或执行字符串拼接操作如果symbol是对象,会执行toString() 办法,节约了系统资源,执行上述操作,最终日志却没有打印应用条件输入模式: ...

June 30, 2021 · 1 min · jiezi

关于异常处理:SpringBoot-工程中项目异常的处理

背景剖析 在我的项目的开发中,不论是对底层的数据逻辑操作过程,还是业务逻辑的处理过程,还是管制逻辑的解决过程,都不可避免会遇到各种可预知的、不可预知的异样。解决好异样对系统有很好的爱护作用,同时会大大提高用户的体验。 异样解决剖析Java我的项目中解决异样形式无非两种,要么执行trycatch操作,要么执行throw操作(抛给其它对象解决),无论采纳哪种形式,其目标是让咱们的系统对异样要有反馈。但当初的问题是咱们如何让这种反馈代码的编写即简略又直观、敌对。 Java我的项目异样解决标准咱们在解决异样的过程中通常要遵循肯定的设计规范,例如: 第一:捕捉异样时与抛出的异样必须齐全匹配,或者捕捉异样是抛出异样的父类类型。第二:防止间接抛出RuntimeException,更不容许抛出Exception或者Throwable,应应用有业务含意的自定义异样(例如ServiceException)。第三:捕捉异样后必须进行解决(例如记录日志)。如果不想解决它,须要将异样抛给它的调用者。第四:最外层的逻辑必须解决异样,将其转化成用户能够了解的内容。第五:避免出现反复的代码(Don’t Repeat Yourself),即DAY准则。 SpringBoot 工程下的异样解决实际第一种形式:间接在Controller办法中进行try操作 try{.....}catch(Exception e){.....}如果controller中每个办法都要这样做,代码量大并且可重用性太差,难以保护. 第二种形式:在Controller中定义一个或多个异样解决办法,要害代码如下: @ExceptionHandler(RuntimeException.class)@ResponseBodypublic String doHandleException(RuntimeException e){ log.error("exception {}",e.getMessage()); return e.getMessage();}所有的异样解决办法须要应用@ExceptionHandler进行形容进行形容,并申明它形容的办法能够解决的异样类型,然而对于此种形式而言,它只能解决以后Controller中各个办法呈现的RuntimeException或者是其子类类型的异样,如果多个Controller类中须要同样形式的异样解决办法,那间接在Controller类中定义异样解决办法并不是一种很好的抉择. 第三种形式:在管制逻辑层定义全局异样解决类以及异样解决办法,要害代码如下: @Slf4j//@ResponseBody//@ControllerAdvice@RestControllerAdvicepublic class GlobalExceptionHandler { 异样解决办法 @ExceptionHandler(IllegalArgumentException.class) public String doHandleException(IllegalArgumentException e){ log.error("IllegalArgumentException.exception {}",e.getMessage()); return e.getMessage();} 异样解决办法 @ExceptionHandler(RuntimeException.class) public String doHandleException(RuntimeException e){ log.error("RuntimeException.exception {}",e.getMessage()); return e.getMessage();}其中,@RestControllerAdvice 注解形容的类为全局异样解决类,当管制层办法中的异样没有本人捕捉,也没有定义其外部的异样解决办法,底层默认会查找全局异样解决类,调用对应的异样解决办法进行异常解决。

March 13, 2021 · 1 min · jiezi

关于异常处理:异常处理

什么时候应该抛出异样当一个类型的口头成员不能正在残缺口头工作时,就应该抛出异样告诉调用者。 *口头成员指类型自身或者类型实例能够执行的操作,如C#中StringBuilder中定义的Append,Insert等 捕获异样代码构造private void DoSomething(){ try { //将可能产生异样的代码放在这里 } catch (InvalidOperationException) { //捕捉到InvalidOperationException异样,对应的解决代码放在这里 } catch (IOException) { //捕捉到IOException异样,对应的解决代码放在这里 } catch (Exception) { //捕捉到除了上述之外的其它异样,对应的解决代码放在这里 //这里是将异样抛出 throw; } finally { //这里的代码总是被执行 } //如果try块没有抛出异样或者某个catch捕捉到异样却没有抛出就执行以下的代码,否则以下代码不执行}try块try块中蕴含的是可能会产生异样的代码,异样复原代码应放在一个或多个catch块中。针对应用程序平安的复原某一种异样都须要有一个对应的catch块。一个try块至多要关联一个catch块或finally块,独自一个try块C#是不容许的,而且这样也没有意义。 *如果一个try块中蕴含执行多个可能抛出同一个异样类型的操作,但不同的操作对应的复原措施不同,咱们就应该将这些操作拆分到它本人的try块中,以保障正确的复原状态 catch块catch块蕴含的是响应一个异样须要执行的代码。一个try块能够关联0个或多个catch块。如果try块中的代码没有异样产生,CLR永远不会执行catch块中的代码。线程将跳过所有catch块,直至finally块(外面有代码的话)中的代码。catch关键字前面圆括号中的表达式称为捕获类型,即要捕获的异样类型。 *应用Visual Studio调试catch块时,能够在监督窗口中增加非凡的变量名称$exception来查看以后抛出异样的对象 catch块检索程序与注意事项CLR是自上而下检索一个匹配的catch块,所以编程的时候应留神将派生水平最大的异样类型放在顶部,接着是它们的基类,最初才是System.Exception,如果程序没有放对,例如将最具体的异样类型放在了最底部的catch块中,C#编译器将会产生谬误,因为这个catch块无奈被执行到。 一旦try块中的代码产生异样,而没有与之匹配的catch块的话,CLR会去调用栈的更高一层搜寻与之匹配的异样类型,如果到了栈的顶部还是没有找到,就会产生一个未解决的异样。 在catch块的开端能够做的事件从新抛出雷同的异样,向调用栈高一层的代码告诉该异样的产生抛出一个不同的异样,向调用栈高一层的代码提供更加丰盛的异样信息让线程从catch块的底部退出前两种技术CLR将回溯调用栈,查找捕获类型与抛出异样的类型匹配的catch块并抛出一个异样。如果抉择让线程从catch块的底部退出,将立刻执行蕴含在finally块中的代码,结束后执行紧跟在finally块之后的语句。如果不存在finally块,线程将从最初一个catch块之后的语句开始执行。 finally块finally块中的代码是保障肯定会执行的代码且肯定要在所有catch块的前面,通常蕴含的是对try块中的口头所要求的资源清理操作。一个try块最多只能关联一个finally块。 private void ReadFile(string path){ FileStream fs = null; try { fs = new FileStream(path, FileMode.Open); //解决文件数据... } catch (IOException) { //IOException异样复原代码 } finally { //确保文件敞开 if (fs != null) fs.Close(); }}上述代码,无论try块代码有没有产生异样,文件都肯定会被敞开。如果将敞开文件的代码放在finally块语句之后是不正确的,因为如果抛出异样但没有捕捉到,finally块之后的语句将永远不会被执行,直到下一次垃圾回收才会敞开文件。 ...

December 22, 2020 · 1 min · jiezi

关于异常处理:第三阶段-Day06-图片回显-异常处理机制

1 商品后盾治理1.1 动静实现商品分类展示1.1.1 异步树加载控件阐明1).树形控件树形 `每个节点都具备以下属性:id:节点ID,对加载近程数据很重要。text:显示节点文本。state:节点状态,'open' 或 'closed',默认:'open'。如果为'closed'的时候,将不主动开展该节点。checked:示意该节点是否被选中。attributes: 被增加到节点的自定义属性。children: 一个节点数组申明了若干节点。` 2).异步树阐明树控件读取URL。子节点的加载依赖于父节点的状态。当开展一个关闭的节点,如果节点没有加载子节点,它将会把节点id的值作为http申请参数并命名为’id’,通过URL发送到服务器下面检索子节点。如果用户点击父节点须要开展子节点时,会将父节点的id的值当做参数传递. 1.1.2 编辑ItemCatController(形式一) `/** * 业务需要: 用户通过ajax申请,动静获取树形构造的数据. * url: http://localhost:8091/item/cat/list * 参数: 只查问一级商品分类信息 parentId = 0 * 返回值后果: List<EasyUITree> * * 注意事项: * 1.树形构造初始化时不会传递任何信息.只有开展子节点时传递Id * 2.页面传递什么样的数据,后端必须接管什么样的数据 * * id: 296 */ @RequestMapping("/list") public List<EasyUITree> findItemCatList(Long id){ Long parentId = (id==null?0L:id); return itemCatService.findItemCatList(parentId); }` 1.1.3 编辑ItemCatController(形式二)能够利用注解动静获取参数,并且实现转化. `//2.个别写业务须要见名知意 @RequestMapping("/list") public List<EasyUITree> findItemCatList( @RequestParam(value = "id",defaultValue = "0") Long parentId){ //形式1 //Long parentId = (id==null?0L:id); return itemCatService.findItemCatList(parentId); }` ...

October 22, 2020 · 2 min · jiezi