赞
踩
目录
在分布式系统中,系统与系统之间通常使用FeignClient
进行数据交互,但当被调用方需要抛出特定异常时,调用方不能捕获到异常信息进行业务处理,本文档对此种现象进行了说明,并提供一种解决方案。
client
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-openfeign</artifactId>
- </dependency>
- @FeignClient(name = "service-a", contextId = "service-client-a", url = "${feign.service.url}", path = "/test")
- public interface TestApi {
- @GetMapping("/no-throws-ex")
- String noThrowsEx();
-
- @GetMapping("/has-throws-ex")
- String hasThrowsEx(@RequestParam Integer code) throws SysEx;
- }
- @Getter
- @Builder
- public class SysEx extends RuntimeException{
- private ResCode resCode;
-
- public SysEx(ResCode resCode) {
- super(resCode.getMsg());
- this.resCode = resCode;
- }
- }
package org.example; import lombok.Getter; @Getter public enum ResCode { OK(0,"ok"), E_500(500,"500的异常"), E_1000(1000,"1000的异常"), E_1001(1001,"1001的异常"), ; private int code; private String msg; ResCode(int code, String msg) { this.code = code; this.msg = msg; } public static ResCode ofCode(int code){ ResCode[] values = ResCode.values(); for (ResCode resCode : values){ if (code == resCode.code){ return resCode; } } return ResCode.E_500; } }
- package org.example;
-
- import lombok.Data;
- import lombok.experimental.Accessors;
-
- @Data
- @Accessors(chain = true)
- public class R<T> {
- private int code;
-
- private String msg;
-
- private T data;
-
- private boolean ok;
-
- private R(int code, String msg, T data, boolean ok) {
- this.code = code;
- this.msg = msg;
- this.data = data;
- this.ok = ok;
- }
-
- public static <T> R<T> ok(T t){
- return new R(ResCode.OK.getCode(), ResCode.OK.getMsg(), t, true);
- }
-
- public static <T> R<T> error(ResCode resCode){
- return new R(resCode.getCode(), resCode.getMsg(), null, false);
- }
- }
service 底层服务(被调用方) 提供2个测试接口,并对特定异常进行统一异常处理
- @GetMapping("/no-throws-ex")
- public ResponseEntity<String> noThrowsEx(){
- return ResponseEntity.ok("noThrowsEx");
- }
-
- @GetMapping("/has-throws-ex")
- public ResponseEntity<String> hasThrowsEx(@RequestParam Integer code){
- switch (code){
- case 1:
- throw new SysEx(ResCode.E_1000);
- default:
- return ResponseEntity.ok("hasThrowsEx");
- }
- }
- @ExceptionHandler(SysEx.class)
- public ResponseEntity<R<?>> sysEx(SysEx e){
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(R.error(e.getResCode()));
- }
api 调用方 接口调用service服务中的方法
- @Autowired
- private TestApi testApi;
- @GetMapping("/test")
- public ResponseEntity<String> test(@RequestParam Integer code) {
- switch (code){
- case 1:
- case 2:
- try {
- String s = testApi.hasThrowsEx(code);
- return ResponseEntity.ok(s);
- } catch (SysEx e){
- log.error(e.getResCode());
- return ResponseEntity.ok("抛出异常");
- }
- case 3:
- return ResponseEntity.ok(testApi.noThrowsEx());
- default:
- return ResponseEntity.ok("异常");
- }
- }
-
- @ExceptionHandler(Exception.class)
- public ResponseEntity<String> runtimeEx(Exception e){
- return ResponseEntity.status(500).body("error");
- }
-
- @ExceptionHandler(SysEx.class)
- public ResponseEntity<R<?>> sysEx(SysEx e){
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(R.error(e.getResCode()));
- }
在浏览器中访问接口http://127.0.0.1:9003/test/test?code=3
返回数据noThrowsEx
证明服务之间数据交互正常;
在浏览器中访问接口http://127.0.0.1:9003/test/test?code=2
返回数据hasThrowsEx
,接口hasThrowsEx
未抛出异常,正常返回;
在浏览器中访问接口http://127.0.0.1:9003/test/test?code=1
,由于调用方也存在异常处理机制,当出现Exception
时,则返回数据error
;证明当code=1时,服务抛出异常,导致系统出现错误,未能响应数据抛出异常
。此时当在浏览器中访问接口http://127.0.0.1:9001/test/has-throws-ex?code=1
时,由于服务中统一异常处理机制,返回数据为:
- {
- "code": 1000,
- "msg": "1000的异常",
- "data": null,
- "ok": false
- }
由上述几次测试可知,当底层服务出现异常时,上层服务不能正确的捕获底层服务抛出的异常信息,并出现了500的错误;且未能将底层服务相关提示信息展示到上层服务,这种现象对于上层调用方极其不友好,下面对client
中代码进行了改造:
TestApi
@FeignClient(name = "service-a", contextId = "service-client-a", url = "${feign.service.url}", path = "/test",fallback = ErrorFallback.class)
在代码中添加了fallback = ErrorFallback.class
;
ErrorFallback
package org.example; import cn.hutool.json.JSONUtil; import feign.Response; import feign.codec.ErrorDecoder; import lombok.extern.log4j.Log4j2; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; import org.springframework.util.StreamUtils; import java.io.IOException; import java.nio.charset.Charset; @Configuration @Log4j2 public class ErrorFallback implements ErrorDecoder { @Override public Exception decode(String s, Response response) { if (response.status() != HttpStatus.OK.value()){ log.info("s: {}",s); log.info("status: {}" ,response.status()); try { String content = StreamUtils.copyToString(response.body().asInputStream(), Charset.defaultCharset()); log.info("content: {}", content); R r = JSONUtil.toBean(content, R.class); return new SysEx(ResCode.ofCode(r.getCode())); } catch (IOException e) { e.printStackTrace(); } } return new SysEx(ResCode.E_500); } }
重新启动服务后,访问接口http://127.0.0.1:9003/test/test?code=1
,返回数据为抛出异常
,证明
ErrorFallback
配置成功
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。