面向外部的服务接口,我们一般会将接口的报文形式以JSON的方式进行响应,除了正常的数据报文外,我们一般会在报文格式中冗余一个响应码和响应信息的字段,如正常的接口成功返回: { “code”:“0”, “msg”:“success”, “data”:{ “userId”:“zhangsan”, “balance”:5000 } } 而如果出现异常或者错误,则会相应地返回错误码和错误信息,如: { “code”:"-1", “msg”:“请求参数错误”, “data”:null } 在编写面向外部的服务接口时,服务端所有的异常处理我们都要进行相应地捕获,并在controller层映射成相应地错误码和错误信息,因为面向外部的是直接暴露给用户的,是需要进行比较友好的展示和提示的,即便系统出现了异常也要坚决向用户进行友好输出,千万不能输出代码级别的异常信息,否则用户会一头雾水。对于客户端而言,只需要按照约定的报文格式进行报文解析及逻辑处理即可,一般我们在开发中调用的第三方开放服务接口也都会进行类似的设计,错误码及错误信息分类得也是非常清晰! 而微服务间彼此的调用在异常处理方面,我们则是希望更直截了当一些,就像调用本地接口一样方便,在基于SpringCloud的微服务体系中,微服务提供方会提供相应的客户端SDK代码,而客户端SDK代码则是通过FeignClient的方式进行服务调用,如:而微服务间彼此的调用在异常处理方面,我们则是希望更直截了当一些,就像调用本地接口一样方便,在基于SpringCloud的微服务体系中,微服务提供方会提供相应的客户端SDK代码,而客户端SDK代码则是通过FeignClient的方式进行服务调用,如: @FeignClient(value=“order”,configuration=OrderClientConfiguration.class,fallback=OrderClientFallback.class) publicinterfaceOrderClient{ //订单(内) @RequestMapping(value="/order/createOrder",method=RequestMethod.POST) OrderCostDetailVoorderCost(@RequestParam(value=“orderId”)StringorderId, @RequestParam(value=“userId”)longuserId, @RequestParam(value=“orderType”)StringorderType, @RequestParam(value=“orderCost”)intorderCost, @RequestParam(value=“currency”)Stringcurrency, @RequestParam(value=“tradeTime”)StringtradeTime) } 而服务的调用方在拿到这样的SDK后就可以忽略具体的调用细节,实现像本地接口一样调用其他微服务的内部接口了,当然这个是FeignClient框架提供的功能,它内部会集成像Ribbon和Hystrix这样的框架来实现客户端服务调用的负载均衡和服务熔断功能(注解上会指定熔断触发后的处理代码类),由于本文的主题是讨论异常处理,这里暂时就不作展开了。 现在的问题是,虽然FeignClient向服务调用方提供了类似于本地代码调用的服务对接体验,但服务调用方却是不希望调用时发生错误的,即便发生错误,如何进行错误处理也是服务调用方希望知道的事情。另一方面,我们在设计内部接口时,又不希望将报文形式搞得类似于外部接口那样复杂,因为大多数场景下,我们是希望服务的调用方可以直截了的获取到数据,从而直接利用FeignClient客户端的封装,将其转化为本地对象使用。 @Data @Builder publicclassOrderCostDetailVoimplementsSerializable{ privateStringorderId; privateStringuserId; privateintstatus;//1:欠费状态;2:扣费成功 privateintorderCost; privateStringcurrency; privateintpayCost; privateintoweCost; publicOrderCostDetailVo(StringorderId,StringuserId,intstatus,intorderCost,Stringcurrency,intpayCost, intoweCost){ this.orderId=orderId; this.userId=userId; this.status=status; this.orderCost=orderCost; this.currency=currency; this.payCost=payCost; this.oweCost=oweCost; } } 如我们在把返回数据就是设计成了一个正常的VO/BO对象的这种形式,而不是向外部接口那么样额外设计错误码或者错误信息之类的字段,当然,也并不是说那样的设计方式不可以,只是感觉会让内部正常的逻辑调用,变得比较啰嗦和冗余,毕竟对于内部微服务调用来说,要么对,要么错,错了就Fallback逻辑就好了。 不过,话虽说如此,可毕竟服务是不可避免的会有异常情况的。如果内部服务在调用时发生了错误,调用方还是应该知道具体的错误信息的,只是这种错误信息的提示需要以异常的方式被集成了FeignClient的服务调用方捕获,并且不影响正常逻辑下的返回对象设计,也就是说我不想额外在每个对象中都增加两个冗余的错误信息字段,因为这样看起来不是那么优雅!
推荐阅读:《java架构师指南》架构师需要掌握的知识结构有哪些