关于java:程序员你是如何解决软件系统的易排错性

4次阅读

共计 9352 个字符,预计需要花费 24 分钟才能阅读完成。

心愿大家能够播种:

1,背景剖析是否贴合工作的理论场景,是否涉及痛点;

2,对立的技术计划,并演示最终的实现成果;

3,前端和后端绝对残缺的技术实现计划,零碎的思考形式;

背景和需要

不同人群对错误处理的冀望不同:这里基于业务零碎简略列表汇总;

人群 谬误提醒的冀望
业务零碎产品经理 谬误提醒也是产品设计的一部分,标识失常业务的边界,
基于谬误提醒能够疾速的进行业务性能的边界条件,要害流程流向提醒;
业务零碎测试人员 能定提醒到是到底是前端还是后端的问题,疾速的分类 bug,指派给对应的开发人员;
进行需要的二次确认,一些参数边界的提示信息必须合乎产品规约。
业务零碎前端开发人员 联调的时候,后端的谬误能够提醒哪里出错了,

如果是参数谬误,让我指引我哪个参数错了,我好调整
如果是后端逻辑或者外部谬误,不便我提供截图和 traceId 给到后端开发,让后端去解决;|
| 业务零碎运维人员 | 后端资源耗尽了,最好能够提醒我哪块资源有余,如何补充;
中间件有问题了,告知我哪个中间件,倡议的运维办法;
如果切实无奈在界面上通知我,能够疾速看到对应的利用日志,丢回给开发去进一步定位问题。|
| 业务零碎后端开发人员 | 开发和集成测试环境,最好在界面上或者控制台能看到堆栈信息,哪行代码出错了;
最次也要能从界面或者控制台,或者抓包中找到 traceId,不便我从日志中或者调用链跟踪零碎中疾速的定位问题,
不便疾速解决问题;|
| 业务零碎管理层 | 可服务性好,
站在用户的角度,心愿有标准的提醒和回到正确流程的提醒;
站在客户方的二开或者集成工程师角度,心愿错误码能对立,并且对提醒,不便我疾速集成和二开;
站在开发周期来说,心愿谬误提醒能够放慢前后端联调,测试的工作效率;|
| 架构师 | 错误处理公共组件化,兼顾开发期的可扩展性,复用性,易用性,以及兼顾运行期的可服务性;|
| 二开用户(业务零碎 B 端 - 开发人员)| 我要谬误编码,还要领导提醒,最好在本接口中返回给我,或者指引我一个文档,
我依照编码去查;能减速我疾速的集成或者二开;|
| 用户:业务零碎 B - C 端用户 | 通知我哪里出错了,正确的应用办法,让我能够回到正确的流程;最好还能显示级别;
提醒不能为空,不能有英文,不能有堆栈信息,不能有我看不懂的信息 |
| 客户:业务零碎 B 端利用配置人员 | 同 C 端用户,次要是通知我哪里操作错了,让我能够回到正确的流程中;|

上面进行形象和汇总。

一个适合的错误处理计划应该是怎么的?

对立技术计划

地位 解决要点 阐明
前端 前端实现 axios 拦截器异样捕捉,封装组件实现,展现逻辑 & 模式 ** 准则:

服务端能响应的、能返回谬误的,提醒语应用后端返回
服务端不能响应的、不能返回谬误的,提醒语应用前端约定 ** |
| 后端 | 对 rest 接口进行对立异样的捕捉并转换为错误码,谬误音讯;

对间接组装的对立错误码,谬误音讯,进行对立的治理,依照微服务进行错误码进行封装;

封装为组件模式,错误码依照接口的规约进行限度,利用级别的错误码和谬误音讯扩散在微服务中;| 谬误分两种模式:
1,通过异样输入谬误;
2,通过组装错误码和谬误音讯拼装谬误返回信息;

异样分为 3 类:
1,参数校验或者接口 url 资源定位不到,须要提醒前端调整;
2,外部的逻辑谬误或者 jvm 异样,通过 RuntimeException 抛出;
3,依赖的公共组件谬误,给出环境问题或者调用问题的提醒;|

后端

模式:中间件 的形式,定义裸露的配置属性,对异样进行对立的解决封装;

这里做一下调整,对立把扩散在微服务外面错误码枚举放到团队公共的 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 可能是英语,看不懂;|
| 间接拼装错误码和谬误音讯 | 扩散在业务代码中,见上面的截图和局部代码截取 | 无奈对立治理错误码和错误信息,并且错误信息中无正确操作指引信息 |

老业务零碎对立异样解决分类

异样分类 父类 错误码 阐明
RuntimeException Exception _SERVER_ERROR_(50000L, “ 服务异样 ”) 运行时异样
MissingServletRequestParameterException ServletRequestBindingException-》NestedServletException-》

ServletException-》
Exception
| _ILLEGAL_PARAMETER_ERR_(10005L, “ 非法的参数 ”)
短少必传参数 +paramName | 接口参数绑定异样 |
| HttpMessageNotReadableException | HttpMessageConversionException-》NestedRuntimeException-》
RuntimeException-》
Exception | _CLASS_CAST_ERR_(10009L, “ 类型转换异样 ”), | JSON 转换异样,蕴含更多的音讯转换异样 |
| HttpRequestMethodNotSupportedException | ServletException-》
Exception | _METHOD_NOT_SUPPORT_(40007L, “ 申请办法不正确 ”), | ajax 的 http 办法写错,或者签名不对,如媒体类型等;|
| ServiceException
| RuntimeException->
Exception | 引擎层自定义的 code,msg, 异样数据 | 在引擎层进行了编码和 MSG 的标准 |
| BusinessRuleException | RuntimeException->
Exception | 引擎层自定义的 code,msg, 异样数据 2 | 在引擎层进行了编码和 MSG 的标准 |
| PortalException | RuntimeException->
Exception | Web Api 异样 _Portal 抛出的自定义异样_
code msg 异样数据自定义 | webAPI 异样范畴太宽泛
code msg 的定义不太标准,有随便定义的代码呈现 |
| RemotingException | Exception | 调用近程服务失败
_REMOTING_ERR_(10006L, “ 调用近程服务失败 ”) | webAPI 调用引擎的 dubbo 服务异样 |
| RpcException | RuntimeException->
Exception | 调用近程服务失败
_REMOTING_ERR_(10006L, “ 调用近程服务失败 ”) | dubbo 框架异样 |
| UndeclaredThrowableException | RuntimeException->Exception | 个别用在在调用代理的办法调用的时候抛出的查看异样

_别离匹配_LicenseException
ServiceException
RuntimeException
如果类型匹配不上,code,msg

_SERVER_ERROR_(50000L, “ 服务异样 ”), | 未定义异样
未定义抛出异样

这里做了一个对立解决,实践上是不起作用的,会提前分流到对应的异样类型中去 |
| InvocationTargetException | ReflectiveOperationException-》Exception | 用在调用代理的办法或者构造函数的时候抛出的查看型异样

_SERVER_ERROR_(50000L, “ 服务异样 ”), | 进入托底异样 |
| LicenseException | ServiceException-》
RuntimeException->
Exception | 校验许可证抛出的异样
code msg 异样数据自定义 | 许可证异样,引擎层的自定义异样 |
| ConstraintViolationException | ValidationException
->RuntimeException
-》Exception | _ILLEGAL_PARAMETER_ERR_(10005L, “ 非法的参数 ”),
| 参数校验异样 |
| MethodArgumentNotValidException | MethodArgumentNotValidException
->Exception | | |
| MaxUploadSizeExceededException | MultipartException-》NestedRuntimeException-》RuntimeException-》Exception | _OSS_UPLOAD_SIZE_LIMIT_EXCEEDED_(10025L,“ 文件上传超出大小限度 ”) | oss 上传文件超出大小限度异样 |

间接拼接错误码返回

 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();}

异样的常识补充:

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 Status Code Error Code 等级 提醒语 备注
200 200 B
201 B
300 300 通常不须要提醒
同上
400 400 B
B
B
B
B
B
B
B
B
B
B
B
401 B 权限相干,提醒登陆或无权限
402
403 F/B
404 F NOT FOUND
500 500 F 服务端异样
501 F
502 F

  1. 后端返回数据格式

    字段 阐明
    errCode 错误码
    errMessage 提醒音讯
    data 返回后果
    traceId skywalking 的跟踪 ID
  1. 前端实现 axios 拦截器异样捕捉,封装组件实现,展现逻辑 & 模式

组件文档:部署到服务器上再对立放开

更多的需要点

岗位角度 需要补充
后端 1,提供一个对立操作异样的工具类,代替 throw new Exception(), 标准异样的抛出

2,错误码的规定:8 位 1- 2 服务 3- 4 异样分类 5-8 序号,避免多微服错误码重叠
3,谬误提示信息和正确引导分成两个字段返回到前端;|
| 前端 | 1,提醒的格调具体应该是什么样的,可能是 UED 来定义,然而咱们的框架要反对灵便的去扩大实现这种信息提醒的展现,
2,再提供一套格调的 UI, 手动敞开 toast 提醒;
3,前端出错了的谬误堆栈或者地位信息应该有中央可查;|
| 测试 | |
| 治理 | 1,前期可思考退出客户被动反馈谬误性能,通过业务人员间接传递特地影响技术团队的口碑;(福春)|
| 运维施行 | 1,错误码的提醒能够间接链接到对立的错误码阐明页面,放慢施行人员的效率;

  1. 如果提醒不够,通过 traceID 能到对应的分布式日志零碎查到调用链的信息;
    |

解决前端国际化的样例参考

example:

 谬误等级 / 模式(前序) 谬误代号(后序) 合并代码(errcode) 含意 枚举项 API 返回范例 前端提醒范例
1000
(A 模型 / 畛域) 1 10001 参数不正确 REQUIRED: “ 必填 ”

CHAR_ONLY: “ 仅字母 ”
LENGTH_EXCESS: “ 长度太长 ”
LENGTH_NOT_MATCH: “ 长度不合乎 ”

| http status: 400
{
   errcode: 10001,
   errdetail: “username=REQUIRED; password=LENGTH_EXCESS;”
   errmsg: …
} | 题目:10001 谬误
内容:用户名必填,明码长度太长 |
| | 2 | 10002 | 申请办法不反对 | | http status: 400
{
   errcode: 10001,
   errdetail: “”
   errmsg: …
} | 题目:10002 谬误
内容:近程调用形式失败!
           |
| | … | | | | | |
| 2000
(B 模型 / 畛域) | 1
| 20001 | … | … | … | … |
| … | | | | | | |

日常开发中如何应用

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

如果是对外部的第三方零碎的,可应用对立的数字编码,也可应用字符串,依据须要来;

退出你负责一个服务的开发,上面两种场景是你必须要思考的。

团队专用 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;

    @Getter
    private String correctGuid;




    @Override
    public String getErrCode() {return errorCode;}

    @Override
    public String getErrDesc() {return errorDesc;}

    @Override
    public 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 样例。

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

正文完
 0