[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 接口定义(推荐)


    // 原接口(期望返回 List)
    @FeignClient(name = "clazz-type-service")
    public interface ClazzTypeFeignClient {
        @PostMapping("/api/clazz-type/list")
        List<ClazzType> listAllByClazzTypeNumberList(@RequestBody ClazzTypeRequest request);
    }
    
    // 修复为:返回统一响应对象
    @FeignClient(name = "clazz-type-service")
    public interface ClazzTypeFeignClient {
        @PostMapping("/api/clazz-type/list")
        ResponseWrapper<List<ClazzType>> listAllByClazzTypeNumberList(@RequestBody ClazzTypeRequest request);
    }
    
    // 响应包装类
    @Data
    public class ResponseWrapper<T> {
        private Integer code;
        private Boolean success;
        private String msg;
        private T data;  // 实际数据
        private String stackMessage;
    }
    

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


    // 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 降级策略


    @FeignClient(
        name = "clazz-type-service",
        fallback = ClazzTypeFeignClientFallback.class
    )
    public interface ClazzTypeFeignClient {
        @PostMapping("/api/clazz-type/list")
        ResponseWrapper<List<ClazzType>> listAllByClazzTypeNumberList(@RequestBody ClazzTypeRequest request);
    }
    
    // 降级实现
    @Component
    public class ClazzTypeFeignClientFallback implements ClazzTypeFeignClient {
        
        @Override
        public ResponseWrapper<List<ClazzType>> listAllByClazzTypeNumberList(ClazzTypeRequest request) {
            // 返回默认值或空列表,避免级联故障
            ResponseWrapper<List<ClazzType>> wrapper = new ResponseWrapper<>();
            wrapper.setCode(200);
            wrapper.setSuccess(true);
            wrapper.setMsg("服务降级返回");
            wrapper.setData(Collections.emptyList());
            return wrapper;
        }
    }
    

    4. ️ 预防措施

    ️ 最佳实践建议


  • 统一响应格式

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

    • 完善的降级机制

    •    # 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 详细日志,查看原始请求和响应

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

      检查清单


    • [ ] 远程服务