[http-nio-28688-exec-5] [com.gaotu.course.center.webcommon.handler.GlobalExcepti...

2026年01月04日 16:59 状态: error

🚨 错误信息

2026-01-04 09:53:07.038 ERROR [http-nio-28688-exec-5] [com.gaotu.course.center.webcommon.handler.GlobalExceptionHandler.exceptionHandler:76] ce6ff49829b0eaefd892f646288c3010 - [TID: e23154f8-3b1c-4a57-9b9b-6bcd3f06f6ff.0.3] 请求处理异常,url:[http://test-mi.gaotu100.com/b/teachClazz/search], params:[requestBody={"arrangeModeTypeList":[4],"arrangeStatus":1,"pager":{"current":1,"pageSize":10,"pageNum":1}}] com.netflix.hystrix.exception.HystrixRuntimeException: ClazzTypeFeignClient#listAllByClazzTypeNumberList(ClazzTypeRequest) failed and no fallback available. at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:819) at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:804) at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:140) at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87) at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87) at com.netflix.hystrix.AbstractCommand$DeprecatedOnFallbackHookApplication$1.onError(AbstractCommand.java:1472) at com.netflix.hystrix.AbstractCommand$FallbackHookApplication$1.onError(AbstractCommand.java:1397) at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87) at rx.observers.Subscribers$5.onError(Subscribers.java:230) at rx.internal.operators.OnSubscribeThrow.call(OnSubscribeThrow.java:44) at rx.internal.operators.OnSubscribeThrow.call(OnSubscribeThrow.java:28) at rx.Observable.unsafeSubscribe(Observable.java:10151) at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51) at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) at rx.Observable.unsafeSubscribe(Observable.java:10151) at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.Observable.unsafeSubscribe(Observable.java:10151) at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) at rx.Observable.unsafeSubscribe(Observable.java:10151) at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.Observable.unsafeSubscribe(Observable.java:10151) at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.Observable.unsafeSubscribe(Observable.java:10151) at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:142) at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87) at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87) at com.netflix.hystrix.AbstractCommand$HystrixObservableTimeoutOperator$2.onError(AbstractCommand.java:1194) at rx.internal.operators.OperatorSubscribeOn$1$1.onError(OperatorSubscribeOn.java:59) at rx.observers.Subscribers$5.onError(Subscribers.java:230) at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87) at rx.observers.Subscribers$5.onError(Subscribers.java:230) at com.netflix.hystrix.AbstractCommand$DeprecatedOnRunHookApplication$1.onError(AbstractCommand.java:1431) at com.netflix.hystrix.AbstractCommand$ExecutionHookApplication$1.onError(AbstractCommand.java:1362) at rx.observers.Subscribers$5.onError(Subscribers.java:230) at rx.observers.Subscribers$5.onError(Subscribers.java:230) at rx.internal.operators.OnSubscribeThrow.call(OnSubscribeThrow.java:44) at rx.internal.operators.OnSubscribeThrow.call(OnSubscribeThrow.java:28) at rx.Observable.unsafeSubscribe(Observable.java:10151) at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51) at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.Observable.unsafeSubscribe(Observable.java:10151) at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51) at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) at rx.Observable.unsafeSubscribe(Observable.java:10151) at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.Observable.unsafeSubscribe(Observable.java:10151) at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94) at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:56) at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:47) at com.baijia.gapm.apm.plugin.hystrix.v1.SWHystrixConcurrencyStrategyWrapper$WrappedCallable.call(SWHystrixConcurrencyStrategyWrapper.java:57) at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction.call(HystrixContexSchedulerAction.java:69) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:750) Caused by: feign.codec.DecodeException: field null expect '[', but {, pos 1, line 1, column 2{"code":500,"success":false,"msg":"系统异常,请稍后重试","stackMessage":""} at feign.SynchronousMethodHandler.decode(SynchronousMethodHandler.java:169) at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:133) at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76) at feign.hystrix.HystrixInvocationHandler$1.run$original$a2LxnmiX(HystrixInvocationHandler.java:108) at feign.hystrix.HystrixInvocationHandler$1.run$original$a2LxnmiX$accessor$Mhn1DSWz(HystrixInvocationHandler.java) at feign.hystrix.HystrixInvocationHandler$1$auxiliary$OU51SbMH.call(Unknown Source) at com.baijia.gapm.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:92) at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java) at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302) at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298) at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46) ... 27 more Caused by: com.alibaba.fastjson.JSONException: field null expect '[', but {, pos 1, line 1, column 2{"code":500,"success":false,"msg":"系统异常,请稍后重试","stackMessage":""} at com.alibaba.fastjson.parser.DefaultJSONParser.parseArray(DefaultJSONParser.java:738) at com.alibaba.fastjson.serializer.CollectionCodec.deserialze(CollectionCodec.java:132) at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:705) at com.alibaba.fastjson.JSON.parseObject(JSON.java:394) at com.alibaba.fastjson.JSON.parseObject(JSON.java:476) at com.alibaba.fastjson.JSON.parseObject(JSON.java:584) at com.alibaba.fastjson.JSON.parseObject(JSON.java:551) at com.gaotu.ces.teach.product.api.configration.CesTeachProductFastJsonFeignConfig$CustomDecoder.decode(CesTeachProductFastJsonFeignConfig.java:46) at feign.SynchronousMethodHandler.decode(SynchronousMethodHandler.java:165) ... 37 more

🤖 AI解决方案

Hystrix + Feign JSON 反序列化错误分析

1. 错误翻译

原文:


com.netflix.hystrix.exception.HystrixRuntimeException: ClazzTypeFeignClient#listAllByClazzTypeNumberList(ClazzTypeRequest) failed and no fallback available.

译文:


ClazzTypeFeignClient 的 listAllByClazzTypeNumberList 方法调用失败,且没有可用的降级处理机制。

原文:


com.alibaba.fastjson.JSONException: field null expect '[', but {, pos 1, line 1, column 2

译文:


FastJSON 解析异常:期望字段是数组(以 '[' 开头),但实际接收到的是对象(以 '{' 开头),位置在第1行第2列。

2. 原因分析

根本原因


  • JSON 响应格式不匹配: 远程服务返回的 JSON 响应格式与 Feign Client 预期的格式不一致

  • FastJSON 解析失败: 期望解析的是一个数组(List/Collection)类型,但实际接收到的是一个对象

  • Hystrix 降级缺失: Feign 调用失败时,Hystrix 没有配置降级策略(fallback)
  • 错误位置


  • 远程服务: ClazzTypeFeignClient#listAllByClazzTypeNumberList(ClazzTypeRequest)

  • 调用链: Feign Client → Hystrix → FastJSON 反序列化

  • 具体问题: 第1行第2列的 { 字符,说明返回的是对象(如 {"code":500,...}),而不是数组
  • 3. 解决方案

    方案一:修复 Feign Client 接口定义(推荐)


    java
    // 原接口(期望返回 List)
    @FeignClient(name = "clazz-type-service")
    public interface ClazzTypeFeignClient {
    @PostMapping("/api/clazz-type/list")
    List listAllByClazzTypeNumberList(@RequestBody ClazzTypeRequest request);
    }

    // 修复为:返回统一响应对象
    @FeignClient(name = "clazz-type-service")
    public interface ClazzTypeFeignClient {
    @PostMapping("/api/clazz-type/list")
    ResponseWrapper> listAllByClazzTypeNumberList(@RequestBody ClazzTypeRequest request);
    }

    // 响应包装类
    @Data
    public class ResponseWrapper {
    private Integer code;
    private Boolean success;
    private String msg;
    private T data; // 实际数据
    private String stackMessage;
    }

    方案二:自定义 Feign Decoder 处理响应


    java
    // 1. 创建自定义解码器
    @Component
    public class CustomFastJsonDecoder implements Decoder {

    @Override
    public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
    String bodyStr = Util.toString(response.body().asReader(Util.UTF_8));

    // 先解析为 ResponseWrapper
    ResponseWrapper wrapper = JSON.parseObject(bodyStr, ResponseWrapper.class);

    // 检查响应状态
    if (!wrapper.getSuccess()) {
    throw new RuntimeException("远程服务异常: " + wrapper.getMsg());
    }

    // 提取实际数据部分进行反序列化
    String dataJson = JSON.toJSONString(wrapper.getData());
    return JSON.parseObject(dataJson, type);
    }
    }

    // 2. 在 Feign Client 配置中使用自定义解码器
    @FeignClient(
    name = "clazz-type-service",
    configuration = CustomFeignConfig.class
    )
    public interface ClazzTypeFeignClient {
    // ...
    }

    方案三:添加 Hystrix 降级策略


    java
    @FeignClient(
    name = "clazz-type-service",
    fallback = ClazzTypeFeignClientFallback.class
    )
    public interface ClazzTypeFeignClient {
    @PostMapping("/api/clazz-type/list")
    ResponseWrapper> listAllByClazzTypeNumberList(@RequestBody ClazzTypeRequest request);
    }

    // 降级实现
    @Component
    public class ClazzTypeFeignClientFallback implements ClazzTypeFeignClient {

    @Override
    public ResponseWrapper> listAllByClazzTypeNumberList(ClazzTypeRequest request) {
    // 返回默认值或空列表,避免级联故障
    ResponseWrapper> wrapper = new ResponseWrapper<>();
    wrapper.setCode(200);
    wrapper.setSuccess(true);
    wrapper.setMsg("服务降级返回");
    wrapper.setData(Collections.emptyList());
    return wrapper;
    }
    }

    4. ️ 预防措施

    ️ 最佳实践建议


  • 统一响应格式

  • - 所有 Feign 接口统一使用 ResponseWrapper 包装响应
    - 避免直接返回 List 或基础类型

  • 完善的降级机制

  • yaml
    # application.yml 配置
    feign:
    hystrix:
    enabled: true
    hystrix:
    command:
    default:
    execution:
    isolation:
    thread:
    timeoutInMilliseconds: 5000
    circuitBreaker:
    requestVolumeThreshold: 20
    sleepWindowInMilliseconds: 5000

    开发工具推荐


  • Postman/Insomnia: 独立测试远程接口的返回格式

  • Spring Cloud Sleuth: 分布式追踪,快速定位问题节点

  • Feign Logger: 启用 Feign 详细日志,查看原始请求和响应

  • java
    @Configuration
    public class FeignConfig {
    @Bean
    Logger.Level feignLoggerLevel() {
    return Logger.Level.FULL;
    }
    }

    检查清单


  • [ ] 远程服务