JSON序列化导致的feign接口调用熔断

/ bug / 0 条评论 / 541浏览

JSON序列化导致的feign接口调用熔断

一.简单描述和问题定位

服务提供方接口如下:

@FeignClient(name = "testService", configuration = CountryCodeFeignRequestInterceptor.class)
public interface TestServiceClient {
    @FeignLog
    @PostMapping("/api/v1/query")
    boolean query(@RequestBody Req reqDto);
}

调用方代码如下:

boolean b = queryClient.queryFlexiBlacklist1(reqDto);

现在由于异常的请求参数,导致testService服务的query接口内部逻辑报错(下游接口健壮性很差,没有参数校验),被testService全局异常捕获后返回了统一的json消息体结构,如下:

{"code":"00000000","msg":"success","data":null}

所以在将响应(上面的json)解析到feign的接收响应的模型(这里是boolean)就发生json反序列化失败的异常.报错信息如下:

Caused by: feign.codec.DecodeException: JSON parse error: Can not deserialize instance of boolean out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of boolean out of START_OBJECT token
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Can not deserialize instance of boolean out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of boolean out of START_OBJECT token
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of boolean out of START_OBJECT token

出现这种解析异常,会被认为是接口调用失败进而触发熔断,导致后续的正常参数的请求也出现问题;

解决办法是,需要统一这里的响应数据和全局异常的响应数据,然后调用方就不会出现这种解析异常了

二.详细解释

针对上面的报错信息,一眼看去,对于英语语法不好的同学,可能一时间不是很容易明白,其实这句话翻译出来大概是这个意思:

不能从START_OBJECT token中反序列化出boolean实例

这里造成困惑的可能就是START_OBJECT token,这是个什么玩意??下面我们介绍一下. 在jackson包(com.fasterxml.jackson.core)下有一个重要的枚举类enum JsonToken,这个类的作用是用来表示json的语法标识的,每一个枚举值代表json中的语法标识,如下:

public enum JsonToken
{
    NOT_AVAILABLE(null, JsonTokenId.ID_NOT_AVAILABLE),
    START_OBJECT("{", JsonTokenId.ID_START_OBJECT),
    END_OBJECT("}", JsonTokenId.ID_END_OBJECT),
    START_ARRAY("[", JsonTokenId.ID_START_ARRAY),
    END_ARRAY("]", JsonTokenId.ID_END_ARRAY),
    FIELD_NAME(null, JsonTokenId.ID_FIELD_NAME),
    VALUE_STRING(null, JsonTokenId.ID_STRING),
    VALUE_NUMBER_INT(null, JsonTokenId.ID_NUMBER_INT),
    VALUE_NUMBER_FLOAT(null, JsonTokenId.ID_NUMBER_FLOAT),
    VALUE_TRUE("true", JsonTokenId.ID_TRUE),
    VALUE_FALSE("false", JsonTokenId.ID_FALSE),
    VALUE_NULL("null", JsonTokenId.ID_NULL)
}

所以,这里的token不要理解为我们平时认为的令牌,而是看做是标记的意思,所以这个类可以解释为json标记. 所以START_OBJECT就是代表json对象的开始,所以我们可以知道了,START_OBJECT token就是json对象的开始标记,所以上面的报错的out of START_OBJECT token,这里面的START_OBJECT token就可以认为代指一个json对象了,所以我们翻译上面的报错为:

不能从JSON对象中反序列化出boolean实例

三.解决办法

需要保证服务提供方的响应数据结构需要和全局异常捕获响应的数据结构是一样的,这样调用方才不会出现解析异常的问题.并且下游服务接口的健壮性有待提高,不要相信上下游服务的正常的输入和输出,异常场景需要考虑到.