当前位置: 首页 > news >正文

多线程之HardCodedTarget(type=OssFileClient, name=file, url=http://file)异常

多线程之HardCodedTarget(type=OssFileClient, name=file, url=http://file)异常

摘要: 文档描述了多线程环境下调用Feign客户端OssFileClient时出现的HardCodedTarget异常。异常发生在异步保存文件到ES时,Feign调用未返回预期结果而直接打印了客户端对象。问题分析指出可能原因:1)Feign调用失败但未被捕获;2)异步线程缺少Spring上下文导致认证失败。解决方案包括:1)配置支持上下文传播的线程池TaskDecorator;2)手动传递请求和Security上下文到异步线程。文中提供了ThreadPoolConfig配置类和ContextCopyingTaskDecorator实现,确保主线程上下文能正确传递到异步任务中。

前言
1,异常场景如下,文件上传使用多线程调用微服务OssFile异步保存文件,日志报多线程之HardCodedTarget(type=OssFileClient, name=file, url=http://file)异常

原代码如下

1,主业务代码

    @Override@Transactional(rollbackFor = Exception.class)public Long inserTemergencyProcessingMessage(TemergencyProcessingDto dto) {// 1.获取当前登录人的信息// 2.紧急处理演练文档保存...... // 5.异步添加索引到ESCompletableFuture.runAsync(() -> {log.info("紧急处理演练文档开始异步:{} , {}",temergencyPlanEntity , flIds);temergencyProcessingEsService.indextemergencyProcessing(temergencyPlanEntity, flIds);}, threadCustomPoolExecutor);return planEntity.getId();}

保存ES的代码如下

 @Overridepublic void indextemergencyProcessing(TemergencyPlanEntity planEntity, Set<Long> flIds) {try {// ... 业务代码查询文件信息 // 3.构建ES文档TemergencyPlanEsDocument esDocument = buildEsDocument(planEntity, fileListEntities, fileContentsMap);// 4.索引到ES// 新增文档 - 请求对象IndexRequest indexRequest = new IndexRequest("temergencn_plans").id(planEntity.getId().toString());// 添加文档数据,数据转换为Json...} catch (IOException e) {log.error("...的ES失败,...的id是:{} ", planEntity.getId(), e);}}

2,错误日志如下

… … fiIds:[54]2025-08-26 18:52:52.905 [pool-2-thread-1] INFO com.xx.xxxefileapi.service.impl.TemergencyProcessingEsService - smartFileClient 获取的数据是:HardCodedTarget(type=OssFileClient, name=file, url=http://file)
2025-08-26 18:52:52.910 [http-nio-16710-exec-3] INFO

3,问题排查思路

1,发现异步线程中调用了smartOssFileClient.queryFileListByIds(flIds),但是在日志中并没有打印出调用该Feign客户端后的结果(即没有打印fileListEntitiesr 返回的数据是:),而是直接打印了ossFileClient对象,显示为HardCodedTarget(type=OssFileClient, name=file, url=http://file)。

1.1 Feign客户端调用失败,但是没有异常捕获,但是查看代码,在indextemergencyProcessing方法中捕获的是IOException,而Feign调用可能抛出的是FeignException,属于RuntimeException,所以没有被捕获,但奇怪的是也没有看到异常日志。

1.2 线程上下文问题:Feign调用通常依赖于Spring的上下文(如请求拦截器、负载均衡等),而在异步线程中,可能无法获取到正确的上下文,导致Feign调用失败。
但是,从日志中看到,在异步线程中打印了ossFileClient对象,说明该对象不是null,而且Feign客户端已经正常创建。

另外,注意到在异步线程打印日志的同时,主线程(http-nio-16710-exec-3)打印了AuthInterceptor的后置处理日志。这提示我们可能异步线程中缺少了某些上下文,例如安全上下文、请求头等,导致Feign调用时没有正确的认证信息。

解决方案:
1,确保Feign调用能够传递必要的请求头(如认证信息)。可以使用Feign的拦截器,或者自定义请求拦截器,在异步线程中手动设置请求头。
2,检查异步线程的线程池配置,是否支持上下文传播。如果你使用的是Spring Boot 3.x,可以考虑使用Spring Boot的异步支持并配置任务装饰器(TaskDecorator)来传递上下文。如果是较低版本,可以考虑使用其他方式(如InheritableThreadLocal)或者手动传递上下文。

4,手动传递上下文到异步线程

1,配置线程支持上下文传递

如果使用自定义线程池(threadCustomPoolExecutor),需确保其支持上下文传播。推荐使用 TaskDecorator:


import lombok.Data;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskDecorator;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** @author psd*/
@Data
@Configuration
public class ThreadPoolConfig {@Bean("threadCustomPoolExecutorAsync")public ThreadPoolTaskExecutor threadCustomPoolExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(coreSize);executor.setMaxPoolSize(maxSize);executor.setQueueCapacity(blockQueueSize);executor.setTaskDecorator(new ContextCopyingTaskDecorator());executor.setThreadNamePrefix("Async-Executor-");executor.initialize();return executor;}public static class ContextCopyingTaskDecorator implements TaskDecorator {@Overridepublic @NotNull Runnable decorate(@NotNull Runnable runnable) {// 捕获主线程上下文RequestAttributes requestContext = RequestContextHolder.currentRequestAttributes();SecurityContext securityContext = SecurityContextHolder.getContext();return () -> {try {// 将主线程上下文设置到异步线程RequestContextHolder.setRequestAttributes(requestContext, true);SecurityContextHolder.setContext(securityContext);runnable.run();} finally {// 清理异步线程上下文RequestContextHolder.resetRequestAttributes();SecurityContextHolder.clearContext();}};}}
}
2,手动传递上下文到异步线程

@Resource
private ThreadPoolExecutor threadCustomPoolExecutor;@Override
@Transactional(rollbackFor = Exception.class)
public Long inserTemergencyProcessingMessage(ContingencyPlanDto dto) {// ... [原有代码] ...// 捕获当前请求上下文和安全上下文RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();SecurityContext securityContext = SecurityContextHolder.getContext();// 5.异步添加索引到ESCompletableFuture.runAsync(() -> {try {// 恢复上下文到异步线程RequestContextHolder.setRequestAttributes(requestAttributes);SecurityContextHolder.setContext(securityContext);log.info("x x x开始异步:{} , {}", planEntity, flIds);temergencyProcessingEsService.indextemergencyProcessing(temergencyPlanEntity, flIds);} finally {// 清理上下文避免内存泄漏RequestContextHolder.resetRequestAttributes();SecurityContextHolder.clearContext();}}, threadCustomPoolExecutor);return planEntity.getId();
}

最终代码调试到重点:
1,手动传递并恢复上下文(RequestContext + SecurityContext)
2,配置线程池的TaskDecorator确保上下文传播。
3,确认Feign拦截器正确配置用于 token 传递。

喜欢我的文章记得点个在看,或者点赞,持续更新中ing…


文章转载自:

http://o88PDC18.hnrLs.cn
http://WqwO4aeU.hnrLs.cn
http://dLiEHSbu.hnrLs.cn
http://tKxMgE8O.hnrLs.cn
http://4Kfp2B3E.hnrLs.cn
http://1Wox4VAY.hnrLs.cn
http://QPLqK5Qr.hnrLs.cn
http://KIsLyNui.hnrLs.cn
http://z1WPMwC5.hnrLs.cn
http://kfTxIBIC.hnrLs.cn
http://lkoY8d2X.hnrLs.cn
http://7k74FTOy.hnrLs.cn
http://pnkx1t3G.hnrLs.cn
http://c0F3uXwR.hnrLs.cn
http://VqLE8XxO.hnrLs.cn
http://9wHa2inS.hnrLs.cn
http://0lAPEQ8E.hnrLs.cn
http://7Mp7LpNw.hnrLs.cn
http://d6TA2mYt.hnrLs.cn
http://n7D2jZIK.hnrLs.cn
http://suaJTpkV.hnrLs.cn
http://q8C9tR3E.hnrLs.cn
http://3HTmSD57.hnrLs.cn
http://w4eEQIga.hnrLs.cn
http://PmPK3yAn.hnrLs.cn
http://Qc3fslPv.hnrLs.cn
http://wcXntsR7.hnrLs.cn
http://ig4kBa3o.hnrLs.cn
http://IJNu3KFh.hnrLs.cn
http://oLYL0VbW.hnrLs.cn
http://www.dtcms.com/a/372409.html

相关文章:

  • HTTP协议​​和​​API设计规范​​
  • 三种傍路设备配置实验
  • 踏脚迈入奇幻乐园
  • torch.dot()向量点乘,向量点乘计算方法, torch.dot如何计算的
  • Java 模块系统深度解析:从 Jigsaw 到现代模块化开发
  • 九联UNT403G/UN413G-内存大小区分参考指南
  • 基于HanLP的地址信息识别:识别出人名、地址和电话号码
  • 【java执行python】
  • 【数据结构】强化训练:从基础到入门到进阶(1)
  • 三.动态规划算法
  • Maya绑定:驱动关键帧动画案例,小球穿过自动门
  • Android影像基础--cameraAPI2核心流程
  • Ollama Python库的使用
  • 【数据结构入门】排序算法(3):了解快速排序
  • 运筹学——对偶问题的建模,以及它的基本性质
  • 【PyTorch】图像多分类
  • 【0基础PS】PS工具详解--渐变工具
  • FPGA数据流分析
  • 用最简单的方法讲通Java快慢指针思想
  • 棱镜的技术加持:线扫相机如何同时拍RGB和SWIR?
  • [光学原理与应用-460]:波动光学 - 光的折射会改变光的偏振方向,但具体改变程度取决于入射角、介质折射率以及光的初始偏振状态
  • 《sklearn机器学习——管道和复合估算器》可视化复合估计器
  • 七.克鲁斯卡尔(Kruskal)算法
  • 区块链—NFT介绍及发行
  • JavaSSM框架-MyBatis 框架(一)
  • c6-类和对象-对象特征-初始化列表
  • ThermoSeek:热稳定蛋白数据库
  • 不同Autosar CAN版本的主要实现差异
  • Jakarta EE课程扩展阅读(二)
  • 算法模板(Java版)