程序员你是如何解决软件系统的易排错性?

2024-04-02 17:48

本文主要是介绍程序员你是如何解决软件系统的易排错性?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

希望大家可以收获:

1,背景分析是否贴合工作的实际场景,能否触及痛点;

2,统一的技术方案,并演示最终的实现效果;

3,前端和后端相对完整的技术实现方案,系统的思考方式;

背景和需求

不同人群对错误处理的期望不同:这里基于业务系统简单列表汇总;

人群错误提示的期望
业务系统产品经理错误提示也是产品设计的一部分,标识正常业务的边界,基于错误提示可以快速的进行业务功能的边界条件,关键流程流向提示;
业务系统测试人员能定提示到是到底是前端还是后端的问题,快速的分类bug,指派给对应的开发人员;进行需求的二次确认,一些参数边界的提示信息必须符合产品规约。
业务系统前端开发人员联调的时候,后端的错误可以提示哪里出错了,如果是参数错误,让我指引我哪个参数错了,我好调整如果是后端逻辑或者内部错误,方便我提供截图和traceId给到后端开发,让后端去解决;
业务系统运维人员后端资源耗尽了,最好可以提示我哪块资源不足,如何补充;中间件有问题了,告知我哪个中间件,建议的运维方法;如果实在无法在界面上告诉我,可以快速看到对应的应用日志,丢回给开发去进一步定位问题。
业务系统后端开发人员开发和集成测试环境,最好在界面上或者控制台能看到堆栈信息,哪行代码出错了;最次也要能从界面或者控制台,或者抓包中找到traceId,方便我从日志中或者调用链跟踪系统中快速的定位问题,方便快速解决问题;
业务系统管理层可服务性好,站在用户的角度,希望有规范的提示和回到正确流程的提示;站在客户方的二开或者集成工程师角度,希望错误码能统一,并且对提示,方便我快速集成和二开;站在开发周期来说,希望错误提示可以加快前后端联调,测试的工作效率;
架构师错误处理公共组件化,兼顾开发期的可扩展性,复用性,易用性,以及兼顾运行期的可服务性;
二开用户(业务系统B端-开发人员)我要错误编码,还要指导提示,最好在本接口中返回给我,或者指引我一个文档,我按照编码去查;能加速我快速的集成或者二开;
用户:业务系统B-C端用户告诉我哪里出错了,正确的使用方法,让我可以回到正确的流程;最好还能显示级别;提示不能为空,不能有英文,不能有堆栈信息,不能有我看不懂的信息
客户:业务系统B端应用配置人员同C端用户,主要是告诉我哪里操作错了,让我可以回到正确的流程中;

下面进行抽象和汇总。

一个合适的错误处理方案应该是怎样的?

file

统一技术方案

位置处理要点说明
前端前端实现axios拦截器异常捕获,封装组件实现,展示逻辑&形式原则:服务端能响应的、能返回错误的,提示语使用后端返回服务端不能响应的、不能返回错误的,提示语使用前端约定
后端对rest接口进行统一异常的捕获并转换为错误码,错误消息;对直接组装的统一错误码,错误消息,进行统一的管理,按照微服务进行错误码进行封装;封装为组件形式,错误码按照接口的规约进行限制,应用级别的错误码和错误消息分散在微服务中;错误分两种形式:1,通过异常输出错误;2,通过组装错误码和错误消息拼装错误返回信息;异常分为3类:1,参数校验或者接口url资源定位不到,需要提示前端调整;2,内部的逻辑错误或者jvm异常,通过RuntimeException抛出;3,依赖的公共组件错误,给出环境问题或者调用问题的提示;

后端

形式: 中间件的方式,定义暴露的配置属性,对异常进行统一的处理封装;

file

这里做一下调整,统一把分散在微服务里面错误码枚举放到团队公共的SDK中;

后端错误的分类:

内部:主要是对前端,大部分错误通过异常的方式抛出,后端做统一的处理;

外部系统:主要对接外部系统,有些是直接拼接错误码和错误消息的方式输出的;

建立在服务可用,即httpStatus=200的基础上,内部异常的分类:

错误描述说明
输入参数非法参数缺失,参数不符合规则要求,请求类型不支持
逻辑错误不具备操作权限,jvm内部的异常,比如NPE等,方法超时,运行时异常(空指针等)
内部环境错误依赖的中间件不可用或者调用方法报错,比如SQL写错了了

如果网关服务不可用: nginx需要有对应的40X , 友好json数据

如果网关后面的后端服务不可用: 后端服务需要返回 40X,50X的友好json数据;

用户nginx 故障后端网关后端服务
前端资源404友好提示页面不经过不经过
前端访问后端资源url错误,浏览器默认404页面路由找不到,404转换为json数据40x转换为json数据 50x自然转换成了json数据

查看老业务系统的代码,现在后端的错误处理方式分两种:

错误处理方式说明目前的缺点
统一异常处理通过在web-api-service工程中 通过@RestControllerAdvice 标注一个统一的异常处理类 对每一种类别的异常进行处理统计如下表异常的层级和分类不够清晰有些异常 e.getmessage可能是英语,看不懂;
直接拼装错误码和错误消息分散在业务代码中,见下面的截图和部分代码截取无法统一管理错误码和错误信息,并且错误信息中无正确操作指引信息

老业务系统统一异常处理分类

异常分类父类错误码说明
RuntimeExceptionExceptionSERVER_ERROR(50000L, "服务异常")运行时异常
MissingServletRequestParameterExceptionServletRequestBindingException-》NestedServletException-》ServletException-》ExceptionILLEGAL_PARAMETER_ERR(10005L, "非法的参数")缺少必传参数+paramName接口参数绑定异常
HttpMessageNotReadableExceptionHttpMessageConversionException-》NestedRuntimeException-》RuntimeException-》ExceptionCLASS_CAST_ERR(10009L, "类型转换异常"),JSON转换异常,包含更多的消息转换异常
HttpRequestMethodNotSupportedExceptionServletException-》ExceptionMETHOD_NOT_SUPPORT(40007L, "请求方法不正确"),ajax的http方法写错,或者签名不对,如媒体类型等;
ServiceException
RuntimeException->Exception引擎层自定义的code,msg,异常数据在引擎层进行了编码和MSG的规范
BusinessRuleExceptionRuntimeException->Exception引擎层自定义的code,msg,异常数据2在引擎层进行了编码和MSG的规范
PortalExceptionRuntimeException->ExceptionWeb Api异常 Portal 抛出的自定义异常 code msg 异常数据自定义webAPI异常范围太广泛 code msg 的定义不太规范,有随意定义的代码出现
RemotingExceptionException调用远程服务失败 REMOTING_ERR(10006L, "调用远程服务失败")webAPI调用引擎的dubbo服务异常
RpcExceptionRuntimeException->Exception调用远程服务失败 REMOTING_ERR(10006L, "调用远程服务失败")dubbo框架异常
UndeclaredThrowableExceptionRuntimeException->Exception一般用在在调用代理的方法调用的时候抛出的检查异常__​ 分别匹配_LicenseException ServiceException RuntimeException 如果类型匹配不上,code,msg _​__SERVER_ERROR(50000L, "服务异常"),未定义异常 未定义抛出异常 这里做了一个统一处理,理论上是不起作用的,会提前分流到对应的异常类型中去
InvocationTargetExceptionReflectiveOperationException-》Exception用在调用代理的方法或者构造函数的时候抛出的检查型异常SERVER_ERROR(50000L, "服务异常"),进入托底异常
LicenseExceptionServiceException-》RuntimeException->Exception校验许可证抛出的异常code msg 异常数据自定义许可证异常,引擎层的自定义异常
ConstraintViolationExceptionValidationException->RuntimeException-》ExceptionILLEGAL_PARAMETER_ERR(10005L, "非法的参数"),参数校验异常
MethodArgumentNotValidExceptionMethodArgumentNotValidException->Exception
MaxUploadSizeExceededExceptionMultipartException-》NestedRuntimeException-》RuntimeException-》ExceptionOSS_UPLOAD_SIZE_LIMIT_EXCEEDED(10025L,"文件上传超出大小限制")oss上传文件超出大小限制异常

直接拼接错误码返回

file

 if (oauth2Authentication.isAuthenticated()) {UserModel user = dubboConfigService.getSystemSecurityFacade().getUserByUsername(oauth2Authentication.getName());return ResponseResult.builder().errcode(0L).data(user).errmsg("授权用户信息加载成功").build();} else {return ResponseResult.builder().errcode(10403L).errmsg("未授权").build();}

异常的知识补充:

file

Exception: 可以预见到的异常情况,应该被捕获或者处理,在java中,分为检查异常(编译期)和不检查异常(运行期)。

Error: 出现了错误系统不能正常运行或者恢复,一般情况不容易发生;

共同点:都继承自Throwable,在java中只有Throwable的子类可以被catch或者throw;

ERROR一般是后端服务挂了,一般无法恢复,提示501 服务不可用或者404 ;

Throwable 异常的基类,一般不直接处理;

重点处理的Exception和RuntimeException

异常分类说明
CheckedException 检查型异常,一般直接继承Exception,(RuntimeException除外),需要显示的try-catch 否则编译报错
RuntimeException 运行时异常程序运行过程中发生的异常,编译器无法提前发现,一般的业务异常都是运行时异常;

在处理异常的时候,有4个基本规则需要注意:

  1. 不要catch 最普遍的Exception ,而应该优先捕获具体的异常,可以留下足够的诊断信息;
  2. 不要生吞异常,应该尝试抛出或者写到日志,否则无法判断异常发生的位置;
  3. 不要使用e.printStackTrace(),在分布式系统中,无法确定输出到了什么位置,应该输出到日志中;
  4. 提早抛出,晚点捕获;提高效率

自定义异常的时候需要注意两点: 1,尽量不要定义检查异常 2,异常需要保留足够的诊断信息,但是也需要脱敏;

新业务系统错误码统一管理

  1. 按照微服务统一的规范枚举统一管理错误码,错误信息,并填充建议操作信息,可通过共同接口进行规范;

比如design微服务定义微服务级别的错误码枚举 需要实现 ErrorCodeI接口,填充服务名称,错误码,错误提示信息,正确操作指引信息;

public interface ErrorCodeI {/*** 错误码* @return*/String getErrCode();/*** 错误描述* @return*/String getErrDesc();/*** 获取微服务的名称* @return*/default String getServiceName(){return null;}/*** 获取恢复错误的正确指导* @return*/default String getCorrectGuid(){return null;}
}
  1. 收缩自定义的errcode,errmessage到对应的枚举中,进行统一的编码和错误信息配置;
  1. 网关提供接口和前端页面,展示所有的错误码和错误信息,建议处理方法; 作为一个补充的查找建议操作的方案;(可在网关汇聚所有的微服务中的错误码信息,并展示出来)

新业务系统异常体系分级分类

1,输入参数异常(提示给到前端)

2,逻辑业务异常,JVM运行时异常(各微服务按照类别自行扩展)

3,内部异常(中间件的连接错误和异常,进行统一封装)

4, 托底的异常捕获(如果不是以上的异常,直接提示服务器内部错误,并提供可以查错的地方;)

分别配置好对应的errcode, errmsg需要考虑到英文的情况,可对已经发现的中间件异常进行定义前置名称,英文异常信息通过翻译接口解决,最差要做托底中文信息替换;

前端

*原则: 服务端能响应的、能返回错误的,提示语使用后端返回 服务端不能响应的、不能返回错误的,提示语使用前端约定 *1. 状态码一览表

Http StatusCodeError Code等级提示语备注
200200B
201B
300300---通常不需要提示
...---同上
400400B
B
B
B
B
B
B
B
B
B
B
B
401B权限相关,提示登陆或无权限
402
403F/B
404FNOT FOUND
...
500500F服务端异常
501F
502F

  1. 后端返回数据格式
    字段说明
    errCode错误码
    errMessage提示消息
    data返回结果
    traceIdskywalking的跟踪ID
  1. 前端实现axios拦截器异常捕获,封装组件实现,展示逻辑&形式

file

组件文档: 部署到服务器上再统一放开

file

更多的需求点

岗位角度需求补充
后端1,提供一个统一操作异常的工具类,替代throw new Exception(),规范异常的抛出 2,错误码的规则:8位 1-2服务 3-4异常分类 5-8 序号,防止多微服错误码重叠 3,错误提示信息和正确引导分成两个字段返回到前端;
前端1,提示的风格具体应该是什么样的,可能是UED来定义,但是我们的框架要支持灵活的去扩展实现这种信息提示的展示, 2,再提供一套风格的UI,手动关闭toast提示;3,前端出错了的错误堆栈或者位置信息应该有地方可查;
测试
管理1,后期可考虑加入客户主动反馈错误功能,通过业务人员间接传递特别影响技术团队的口碑;(福春)
运维实施1,错误码的提示可以直接链接到统一的错误码说明页面,加快实施人员的效率;2. 如果提示不够,通过traceID能到对应的分布式日志系统查到调用链的信息;

日常开发中如何使用

错误码: 如果是系统内部的,即不对外部的第三方系统开发,错误码使用字符串,可读性更好;

如果是对外部的第三方系统的,可使用统一的数字编码,也可使用字符串,根据需要来;

加入你负责一个服务的开发,下面两种场景是你必须要考虑的。

团队公用SDK中定义微服务对应的错误枚举

写法如下:在client包中;

package com.xxx.app.paas.client.exception;import com.alibaba.cola.dto.ErrorCodeI;
import lombok.AllArgsConstructor;
import lombok.Getter;/*** @author carter* create_date  2020/7/7 13:55* description     应用级别的错误码统一定义*/@AllArgsConstructor
public enum  ConsoleErrorCodeEnum implements ErrorCodeI {PARAM_ERR("11010000","参数检查出错","请按照输入提示输入或者选择"),BIZ_CREATE_GATEWAY_ERR("11020001","创建网关出错","请检查应用的编码最好只包含字母和数字"),BIZ_CREATE_CONFIG_ERR("11020002","创建配置文件出错","请联系运维人员检查应用的配置文件模板在nacos中是否存在"),SYS_DATABASE_LINK_ERR("11030001","数据库出错","请联系运维人员检查数据库的连接信息"),SYS_NPE_ERR("11030002","空指针错误","请联系开发人员解决问题"),//已知异常转换DIVISOR_CAN_NOT_BE_ZERO("DIVISOR_CAN_NOT_BE_ZERO","除数不能为0","请联系开发人员检查你的除数是不是0"),//安装部署INSTALL_ERR_START_PATH("11019000","启动失败,无法获取正确的pid","请输入正确的包文件路径或启动命令有误"),INSTALL_ERR_START_FILE("11019001","文件不存在","请确保包文件存在"),INSTALL_ERR_SAVE_NS("11019002","相同的命名空间不能安装第二套云枢","请正确选择注册中心的namespace"),;private String errorCode;private String errorDesc;@Getterprivate String correctGuid;@Overridepublic String getErrCode() {return errorCode;}@Overridepublic String getErrDesc() {return errorDesc;}@Overridepublic String getServiceName() {return "app-paas";}}

如何抛出业务或者系统异常?

统一通过类Exceptions来抛出异常;

com.alibaba.cola.exception.Exceptions

使用实例如下:

package com.xxx.app.paas.controller;import com.alibaba.cola.exception.Exceptions;
import com.xxx.app.paas.domain.exception.ConsoleErrorCodeEnum;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author carter* create_date  2020/7/8 11:19* description     错误测试接口*/
@RestController
public class ErrorCodeControllerI {//校验异常@GetMapping("/error/check_param")public void checkParamException() {Assert.isTrue(1 == 2, "xxx参数校验错误");}//业务异常@GetMapping("/error/biz")public void bizException() {Exceptions.throwBizException(ConsoleErrorCodeEnum.BIZ_CREATE_CONFIG_ERR);}//系统异常@GetMapping("/error/sys")public void sysException() {String a = null;a.getBytes();}//已经识别的系统异常,可以给特定的错误提示和错误码@GetMapping("/error/sys2")public void sys2Exception() {try {int i = 3 / 0;} catch (Exception exception) {Exceptions.throwSysException(ConsoleErrorCodeEnum.DIVISOR_CAN_NOT_BE_ZERO, exception);}}}

前端进行预设样式的提示。

已有的错误处理融合

如果已经有自己的错误处理了,跟统一异常处理进行融合,融合方式具体情况具体分析。提供统一的扩展方式。

可单独找 @李福春(lifuchun) 一起看整改方式 。

工程分工和任务跟进

完成标志:在测试环境中提示规范,有价值。

明确指出是哪个服务的什么问题,建议提示一定要准确到位。 测试做验证。

小结

作为一名程序员,需要站在更高的角度,用产品思维,系统思维,终局思维,商业运营思维去看待出现的痛点问题。

通过从前到后的约定错误和异常提示,解决各个岗位对软件系统排错性的建设。

下期我直接输出一个统一的java异常处理的后端SDK样例。

原创不易,关注诚可贵,转发价更高!转载请注明出处,让我们互通有无,共同进步,欢迎沟通交流。

这篇关于程序员你是如何解决软件系统的易排错性?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/870655

相关文章

如何解决线上平台抽佣高 线下门店客流少的痛点!

目前,许多传统零售店铺正遭遇客源下降的难题。尽管广告推广能带来一定的客流,但其费用昂贵。鉴于此,众多零售商纷纷选择加入像美团、饿了么和抖音这样的大型在线平台,但这些平台的高佣金率导致了利润的大幅缩水。在这样的市场环境下,商家之间的合作网络逐渐成为一种有效的解决方案,通过资源和客户基础的共享,实现共同的利益增长。 以最近在上海兴起的一个跨行业合作平台为例,该平台融合了环保消费积分系统,在短

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

【VUE】跨域问题的概念,以及解决方法。

目录 1.跨域概念 2.解决方法 2.1 配置网络请求代理 2.2 使用@CrossOrigin 注解 2.3 通过配置文件实现跨域 2.4 添加 CorsWebFilter 来解决跨域问题 1.跨域概念 跨域问题是由于浏览器实施了同源策略,该策略要求请求的域名、协议和端口必须与提供资源的服务相同。如果不相同,则需要服务器显式地允许这种跨域请求。一般在springbo

速盾高防cdn是怎么解决网站攻击的?

速盾高防CDN是一种基于云计算技术的网络安全解决方案,可以有效地保护网站免受各种网络攻击的威胁。它通过在全球多个节点部署服务器,将网站内容缓存到这些服务器上,并通过智能路由技术将用户的请求引导到最近的服务器上,以提供更快的访问速度和更好的网络性能。 速盾高防CDN主要采用以下几种方式来解决网站攻击: 分布式拒绝服务攻击(DDoS)防护:DDoS攻击是一种常见的网络攻击手段,攻击者通过向目标网

Jenkins 插件 地址证书报错问题解决思路

问题提示摘要: SunCertPathBuilderException: unable to find valid certification path to requested target...... 网上很多的解决方式是更新站点的地址,我这里修改了一个日本的地址(清华镜像也好),其实发现是解决不了上述的报错问题的,其实,最终拉去插件的时候,会提示证书的问题,几经周折找到了其中一遍博文

Redis中使用布隆过滤器解决缓存穿透问题

一、缓存穿透(失效)问题 缓存穿透是指查询一个一定不存在的数据,由于缓存中没有命中,会去数据库中查询,而数据库中也没有该数据,并且每次查询都不会命中缓存,从而每次请求都直接打到了数据库上,这会给数据库带来巨大压力。 二、布隆过滤器原理 布隆过滤器(Bloom Filter)是一种空间效率很高的随机数据结构,它利用多个不同的哈希函数将一个元素映射到一个位数组中的多个位置,并将这些位置的值置

linux 下Time_wait过多问题解决

转自:http://blog.csdn.net/jaylong35/article/details/6605077 问题起因: 自己开发了一个服务器和客户端,通过短连接的方式来进行通讯,由于过于频繁的创建连接,导致系统连接数量被占用,不能及时释放。看了一下18888,当时吓到了。 现象: 1、外部机器不能正常连接SSH 2、内向外不能够正常的ping通过,域名也不能正常解析。

proxy代理解决vue中跨域问题

vue.config.js module.exports = {...// webpack-dev-server 相关配置devServer: {host: '0.0.0.0',port: port,open: true,proxy: {'/api': {target: `https://vfadmin.insistence.tech/prod-api`,changeOrigin: true,p

解决Office Word不能切换中文输入

我们在使用WORD的时可能会经常碰到WORD中无法输入中文的情况。因为,虽然我们安装了搜狗输入法,但是到我们在WORD中使用搜狗的输入法的切换中英文的按键的时候会发现根本没有效果,无法将输入法切换成中文的。下面我就介绍一下如何在WORD中把搜狗输入法切换到中文。

【Python报错已解决】AttributeError: ‘list‘ object has no attribute ‘text‘

🎬 鸽芷咕:个人主页  🔥 个人专栏: 《C++干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 文章目录 前言一、问题描述1.1 报错示例1.2 报错分析1.3 解决思路 二、解决方法2.1 方法一:检查属性名2.2 步骤二:访问列表元素的属性 三、其他解决方法四、总结 前言 在Python编程中,属性错误(At