如何优雅地处理微服务间调⽤的异常
现在微服务架构盛⾏,其中spring cloud⽅案就很具有代表。
那么在微服务之间进⾏调⽤,如果被调⽤的服务挂了,调⽤⽅如何感知呢?
⼀、加上hystrix熔断
在定义feignClient的地⽅指定熔断,如下图
当被调⽤服务不可⽤或者被调⽤⽅发⽣错误的时候,会触发熔断,但是,如果被调⽤⽅抛出异常,调⽤⽅怎么知道究竟是出了什么问题呢?那,这就出现了
⼆、feign全局异常处理
我们不得不提到feign提供的⼀个接⼝叫做ErrorDecoder, 是⽤来处理feign异常的,有⼀个⽅法需要实现 public Exception decode(String s, Responseresponse)如下图:
这样就会替换它默认的feign异常处理,这样就可以捕捉全局的异常了,但是⼜带来⼀个新的问题,如果使⽤这个ErrorDecoder,得关闭熔断,否者这⾥抛出的FeignBadRequestException异常⼜会被hystrix吞掉,那,有没有更好的办法呢?
在被调⽤的controller⽅法⾥⾯捕捉全局异常,发⽣错误的时候,把异常保存⼀个对象⾥⾯,然后⽤该对象进⾏服务间的通信,调⽤⽅收到结果再检查是否包含错误,这个⽅式确实可以解决,但,这肯定不是个好办法;那么有没有更好的办法呢?跳出ErrorDecoder后,会经过在AbstractCommand⾥⾯的⼀个executeCommandAndObserve⽅法⾥⾯有个function叫做 handleFallback
由此可知,当抛出的异常是HystrixBadRequestException时,直接抛出异常,不再经过fallback,那么我们的解决办法就有了,那就是调整FeignBadRequestException 的继承对象,如图
现在,就实现了服务端不可⽤和服务端报错的异常分离,但是可能,有的同学已经注意到了,FeignClientErrorDecoder这个异常处理类⾥⾯是是针对状态为SERVICE_UNAVAILABLE的进⾏了特别处理,为社么要这样?这是因为与被调⽤⽅约定当状态码为 SERVICE_UNAVAILABLE 的时候视为被调⽤⽅主动抛出的异常需要注意的地⽅
我们的被调⽤⽅除了提供微服务之间调⽤,很可能也提供了⾯向前端的接⼝,为了封装我们程序内部的异常,通常我们会定义个全局异常捕捉类,即使报错了,我们也提供⼀个友好的交互⽅式,⽐如下⾯这样
第⼀处我们除了系统内部抛出的异常,第⼆处处理其它异常,也就是说,不管抛出什么错,该服务都会返回⼀个状态值为200的信息出去,那么问题来了,我们微服务间的调⽤也会被处理成200,从⽽导致feign会以为服务是正常的,正常返回结果了,没有报错,那怎么办?
三、针对内部调⽤的特殊处理
当然,你可能想到了,我们可以在提供给内部使⽤的接⼝进⾏异常转换,然后让全局异常处理处不进⾏这样的处理,但是,每个内部接⼝都要进⾏全局异常捕捉,然后转换,这,明显不是最好的做法四、通过注解标记为接⼝为内部调⽤接⼝先定义⼀个注解,直接上图
我们定义该注解为⽅法和类上都可以使⽤,然后再定义处理程序
处理程序要做⼀件事情,就是在程序⽅法异常的时候,将http状态码设置为我们约定的SERVICE_UNAVAILABLE,然后看下怎么使⽤
这样我们把该⽅法标记为了供内部使⽤的接⼝,当然,同样也可以将注解放在类上,这样就可以把该类的全部⽅法标记为内部调⽤。
⾄此,我们现在调⽤其它服务的⽅法,就可以调⽤本地的⼀样了,当然,如果你想在调⽤其它服务的⽅法报错后想继续执⾏其它逻辑,同样也可以,直接catch FeignBadRequestException 这个异常就可以了
微服务间调⽤异常改参
两个微服务之间通过feign调⽤时,后台抛出异常:
feign.RetryableException: Read timed out executing POST
解决⽅法:
在你的yml⽂件中添加
ribbon:
ReadTimeout: 60000 ConnectTimeout: 60000
以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。
因篇幅问题不能全部显示,请点此查看更多更全内容