微服务场景下的异常处理设计

4 篇文章 0 订阅
2 篇文章 0 订阅

主要解决的问题

  • 异常在系统内部的应用和处理
  • 异常在服务之间的应用和处理
  • 异常在网关和前端交互中的处理

异常定义

异常是高级语言出现的定义,它用于强制程序员在编码中处理它。
这里有个难懂的概念,什么是强制处理。在C语言中没有异常机制,判断一个程序是否执行顺利需要通过方法的返回值来判断,如果是1则表示执行完成,0表示执行错误。在这种机制下,如果程序员在调用方法时没有去判断1/0,而继续执行后续的编码,系统也是会“错误”地运行下去。在java中,遇到异常必须抛出或者catch,否则代码编译不通过,这样就强制程序员去处理它。

异常在系统内部的应用

异常通常表示一个程序无法继续执行,所以在系统内部编写代码时,如果发现插入数量不为1,入参格式不正确,必填入参未填写,都可以直接抛出异常到客户端,因为遇到这些异常表示程序无法继续运行了。
实现这个需求需要定义以下类:

// 通用返回信息实体类
class ApiResult<T>{
	private int code;
	private String message;
	private T data;
	private ApiResult(int code, String message, T data){...}
	public static ApiResult success(String message){
		return new ApiResult(200,message,null);
	}
}

// 通用业务端异常
class ServiceException extends RuntimeException{}
// 通用请求异常
class RequestException extends RuntimeException{}

// 全局异常处理
@ControllerAdvice
class GlobalExceptionHandler{
	@ExceptionHandler(ServiceException.class)
	public ModelAndView exception(ServiceException e, HttpServletRequest request, HttpServletResponse response) {
		ModelAndView modelAndView = new ModelAndView(new MappingJackson2JsonView());
		modelAndView.addObject("code", 500);
		log.error(e.getMessage());
		modelAndView.addObject("message", e.getMessage());
		return modelAndView;
	}
}

// 断言类
class Assert{
	public static void isTrue(boolean operation, String message){
		if(!operation){
			throw new ServiceException(message);
		}
	}
}

有了以上配置后写业务代码的基础校验会非常简单和清晰

@RequestMapping("car")
class CarController{
	private CarService carService;
	@PostMapping("save")
	public ApiResult save(Car car){
		carService.create(car);
		return ApiResult.success("保存成功");
	}
}
class CarService{
	private CarMapper carMapper;
	public void create(Car car){
		Assert.isTrue(StringUtil.isNotEmpty(car.getNo()),"编号不能为空");
		Assert.isTrue(StringUtil.isNotEmpty(car.getName()),"名称不能为空");
		Assert.isTrue(this.findByNo(car.getNo())==0,"编号已使用");
		int num = carMapper.insert(car);
		Assert.isTrue(num==1, "插入失败");
	}
}

上述代码如果出现了错误会在全局异常里直接处理,业务代码不需要关注是否有异常情况,除非需要忽略异常的时候,才会catch并无视,其他不需要catch。
在一些老的代码里会出现用map来返回是否处理成功,上游需要取map值判断再继续进行的,这种代码会十分冗长。

异常在服务之间的应用和处理

这里只讨论feign下的设计。
异常在服务之间会被转成json,如ApiResult也会被转成对应的json,然后在上游系统中再从json转成ApiResult。那么这里有个问题就是上游系统调用feign接口的时候都需要判断code是不是200,如果是500的话要把message取出来并抛异常。每一个feign接口都这么做的代码是很冗余也很浪费的。
所以系统设计上可以在feignClient外层包一个切面,在返回时统一判断code是否是200,如果不是就转成Service异常,对于调用端来讲,感觉就是在调系统内部的接口。

public class ExceptionHandleAspect {
    @Around(value="@within(feignClient)")
    Object aroundMethod(ProceedingJoinPoint joinPoint, FeignClient feignClient){
        Object rtn = null;
        try {
            rtn = joinPoint.proceed();
        } catch (Throwable throwable) {
            throw new ServiceException("服务调用异常");
        }
        if(rtn instanceof ApiResult){
            ApiResult apiResult = (ApiResult) rtn;
            if(apiResult.getCode() != 200){
                throw new ServiceException(apiResult.getMessage());
            }
        }
        return rtn;
    }
}

异常在网关和前端交互中的处理

我们知道系统里有很多自带的标准编码和异常,如未授权401,服务器错误500,但是我们在ApiResult中又有自己的一套异常。在转给前端前必须要做一次异常的转化,将服务异常转化为我们的ApiResult,这样对于前端或者异构系统来讲,不会出现一个系统多套编码的问题。
这个功能需要改gateway的默认配置,gateway里默认有一个DefaultErrorWebExceptionHandler类,但是这个类不满足需要,需要覆盖原有的类并重写转化方法

public class GlobalErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {
    public GlobalErrorWebExceptionHandler(ErrorAttributes errorAttributes, WebProperties.Resources resources, ErrorProperties errorProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resources, errorProperties, applicationContext);
    }
    @Override
    protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
    	Map<String, Object> error = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
        // 取到error后根据系统自定义规则来转成ApiResult并写到response里
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、课程介绍 2、解决方案的效果演示(结合支付系统真实应用场景 3、常用的分布式事务解决方案介绍 4、消息发送一致性(可靠消息的前提保障) 5、消息发送一致性的异常流程处理 6、常规MQ队列消息的处理流程和特点 7、消息重复发送问题及业务接口的幂等性设计 8、可靠消息最终一致性方案1(本地消息服务) 9、可靠消息最终一致性方案2(独立消息服务)的设计 10、可靠消息服务的设计与实现--消息服务子系统 11、可靠消息服务的设计与实现--消息管理子系统 12、可靠消息服务的设计与实现--消息状态确认子系统 13、可靠消息服务的设计与实现--消息恢复子系统 14、可靠消息服务的设计与实现--实时消息服务子系统 15、可靠消息最终一致性方案在支付系统中的实战应用介绍 16、可靠消息最终一致性方案在支付系统中的实战应用部署 17、可靠消息最终一致性方案的实战应用测试 18、可靠消息最终一致性方案的优化提升 19、最大努力通知型方案的应用场景介绍 20、最大努力通知型方案的方案设计 21、最大努 力通知型方案的实战应用与部署 22、最大努力通知型方案的优化提升 23、TCC型事务方案介绍 24、TCC型事务架构设计分析 25、TCC型事务框架的源码实现讲解 26、TCC型事务方案的项目实战应用介绍 27、TCC型事务方案的项目实战应用部署 28、TCC型事务方案的项目实战应用测试 29、TCC型事务方案的应用优化提升 30、课程总结

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值