wordpress的菜单和页面跳转什么是搜索引擎优化的核心
2025/12/30 12:03:06 网站建设 项目流程
wordpress的菜单和页面跳转,什么是搜索引擎优化的核心,新网建站教程,惠州网站建设是什么目录 一、核心问题#xff1a;ThreadLocal 的局限性 示例#xff1a;ThreadLocal 在 Feign 中丢失 二、TransmittableThreadLocal 核心原理 核心设计 执行流程 三、快速使用 TTL 1. 引入依赖 2. 替换 ThreadLocal 为 TTL 3. 包装线程池#xff08;核心#xff01;…目录一、核心问题ThreadLocal 的局限性示例ThreadLocal 在 Feign 中丢失二、TransmittableThreadLocal 核心原理核心设计执行流程三、快速使用 TTL1. 引入依赖2. 替换 ThreadLocal 为 TTL3. 包装线程池核心方式 1手动包装 Runnable/Callable方式 2包装线程池推荐全局生效四、集成 OpenFeign生产环境实战步骤 1替换上下文 Holder 为 TTL步骤 2配置 Feign 使用 TTL 线程池步骤 3Feign 拦截器传递上下文步骤 4Web 拦截器初始化上下文入口五、高级特性1. 手动控制上下文传递2. 忽略特定 TTL 数据3. 与 Spring 异步框架集成六、生产环境最佳实践1. 核心配置建议2. 关键注意事项1必须清理上下文2避免存储大对象3兼容 ThreadLocal4监控 TTL 数据5禁用不必要的传递3. 性能优化七、常见问题排查1. TTL 数据传递失败2. 数据串用租户 ID 错误3. 异步任务获取不到 TTL 数据4. 与其他框架冲突八、总结TransmittableThreadLocal是阿里巴巴开源的线程上下文传递工具核心解决ThreadLocal在线程池场景下的上下文丢失问题如 Feign 线程池、异步线程、定时任务是微服务链路追踪、租户隔离、权限传递等场景的核心依赖。本文从原理、使用、集成结合 OpenFeign、最佳实践等维度全面讲解 TTL。一、核心问题ThreadLocal 的局限性ThreadLocal用于存储线程私有数据但在线程池复用线程场景下会失效线程池中的线程是复用的线程执行完任务后不会自动清理ThreadLocal可能导致数据串用子线程如异步线程、Feign 执行线程无法继承父线程的ThreadLocal数据导致上下文丢失如 TraceId、租户 ID 传递失败。示例ThreadLocal 在 Feign 中丢失java运行// 存储租户ID的ThreadLocal public class TenantContextHolder { private static final ThreadLocalString TENANT_ID new ThreadLocal(); public static void setTenantId(String tenantId) { TENANT_ID.set(tenantId); } public static String getTenantId() { return TENANT_ID.get(); } public static void clear() { TENANT_ID.remove(); } } // Feign拦截器尝试获取租户ID线程池执行时会丢失 Component public class TenantFeignInterceptor implements RequestInterceptor { Override public void apply(RequestTemplate template) { String tenantId TenantContextHolder.getTenantId(); // 线程池执行时tenantId为null if (tenantId ! null) { template.header(X-Tenant-Id, tenantId); } } }二、TransmittableThreadLocal 核心原理TTL 是ThreadLocal的增强版核心能力跨线程传递子线程 / 线程池执行时自动携带父线程的 TTL 数据自动清理线程池任务执行完后自动恢复线程的 TTL 数据避免串用兼容 ThreadLocalAPI 与ThreadLocal完全一致可无缝替换。核心设计组件作用TransmittableThreadLocal存储需要传递的上下文数据替代ThreadLocalTtlRunnable/TtlCallable包装 Runnable/Callable捕获父线程 TTL 数据并传递到子线程TtlExecutors包装线程池自动将任务包装为TtlRunnable/TtlCallableTransmitter底层工具类负责捕获、传递、恢复 TTL 数据执行流程父线程执行任务前TtlRunnable捕获当前线程的 TTL 数据线程池执行子任务时将捕获的 TTL 数据注入子线程子任务执行完成后恢复子线程原有 TTL 数据避免污染。三、快速使用 TTL1. 引入依赖xml!-- Maven 依赖最新版本可查Maven中央仓库 -- dependency groupIdcom.alibaba/groupId artifactIdtransmittable-thread-local/artifactId version2.14.2/version /dependency2. 替换 ThreadLocal 为 TTL将所有需要跨线程传递的ThreadLocal替换为TransmittableThreadLocaljava运行import com.alibaba.ttl.TransmittableThreadLocal; public class TenantContextHolder { // 核心替换ThreadLocal → TransmittableThreadLocal private static final TransmittableThreadLocalString TENANT_ID new TransmittableThreadLocal(); public static void setTenantId(String tenantId) { TENANT_ID.set(tenantId); } public static String getTenantId() { return TENANT_ID.get(); } public static void clear() { TENANT_ID.remove(); } } // 链路追踪TraceId示例 public class TraceContextHolder { private static final TransmittableThreadLocalString TRACE_ID new TransmittableThreadLocal(); public static void setTraceId(String traceId) { TRACE_ID.set(TRACE_ID.get() null ? traceId : TRACE_ID.get()); } public static String getTraceId() { return TRACE_ID.get() null ? UUID.randomUUID().toString() : TRACE_ID.get(); } public static void clear() { TRACE_ID.remove(); } }3. 包装线程池核心TTL 仅替换ThreadLocal不够需包装线程池才能让 TTL 数据传递到线程池线程方式 1手动包装 Runnable/Callablejava运行import com.alibaba.ttl.TtlRunnable; // 原任务 Runnable task () - { String tenantId TenantContextHolder.getTenantId(); System.out.println(线程池任务获取租户ID tenantId); }; // 包装为TtlRunnable Runnable ttlTask TtlRunnable.get(task); // 提交到线程池 ExecutorService executor Executors.newFixedThreadPool(5); executor.submit(ttlTask);方式 2包装线程池推荐全局生效java运行import com.alibaba.ttl.TtlExecutors; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; Configuration public class TtlThreadPoolConfig { /** * 包装Feign默认线程池解决Feign上下文传递 */ Bean public Executor feignExecutor() { // 1. 创建原生线程池 ThreadPoolExecutor executor new ThreadPoolExecutor( 5, // 核心线程数 20, // 最大线程数 60, TimeUnit.SECONDS, // 空闲线程存活时间 new LinkedBlockingQueue(1000), // 任务队列 r - new Thread(r, feign-ttl-thread- System.currentTimeMillis()), // 线程工厂 new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略 ); // 2. 包装为TTL线程池核心 ExecutorService ttlExecutor TtlExecutors.getTtlExecutor(executor); return ttlExecutor; } }四、集成 OpenFeign生产环境实战Feign 默认使用线程池执行请求导致ThreadLocal上下文丢失结合 TTL 可完美解决步骤 1替换上下文 Holder 为 TTLjava运行// 租户ID Holder public class TenantContextHolder { private static final TransmittableThreadLocalString TENANT_ID new TransmittableThreadLocal(); public static void setTenantId(String tenantId) { TENANT_ID.set(tenantId); } public static String getTenantId() { return TENANT_ID.get(); } public static void clear() { TENANT_ID.remove(); } } // TraceId Holder public class TraceContextHolder { private static final TransmittableThreadLocalString TRACE_ID new TransmittableThreadLocal(); public static void setTraceId(String traceId) { if (traceId null || traceId.isEmpty()) { traceId UUID.randomUUID().toString().replace(-, ); } TRACE_ID.set(traceId); } public static String getTraceId() { String traceId TRACE_ID.get(); return traceId null ? UUID.randomUUID().toString().replace(-, ) : traceId; } public static void clear() { TRACE_ID.remove(); } }步骤 2配置 Feign 使用 TTL 线程池java运行import com.alibaba.ttl.TtlExecutors; import feign.Feign; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; Configuration public class FeignTtlConfig { /** * 自定义Feign线程池包装为TTL线程池 */ Bean public Executor feignExecutor() { ThreadPoolExecutor executor new ThreadPoolExecutor( Runtime.getRuntime().availableProcessors() * 2, // 核心线程数 200, // 最大线程数 60, TimeUnit.SECONDS, new LinkedBlockingQueue(1000), r - new Thread(r, feign-ttl- System.currentTimeMillis()), new ThreadPoolExecutor.CallerRunsPolicy() ); // 包装为TTL线程池自动传递上下文 return TtlExecutors.getTtlExecutor(executor); } /** * 配置Feign使用TTL线程池 */ Bean public Feign.Builder feignBuilder(Executor feignExecutor) { return Feign.builder() .executor(feignExecutor) // 设置Feign执行器 .retryer(Retryer.NEVER_RETRY); // 禁用Feign原生重试避免叠加 } }步骤 3Feign 拦截器传递上下文java运行import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.stereotype.Component; Component public class FeignContextInterceptor implements RequestInterceptor { Override public void apply(RequestTemplate template) { // 1. 传递租户ID String tenantId TenantContextHolder.getTenantId(); if (tenantId ! null) { template.header(X-Tenant-Id, tenantId); } // 2. 传递TraceId String traceId TraceContextHolder.getTraceId(); template.header(X-Trace-Id, traceId); // 3. 传递Token示例 String token AuthContextHolder.getToken(); if (token ! null) { template.header(Authorization, Bearer token); } } }步骤 4Web 拦截器初始化上下文入口java运行import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; Component public class WebContextInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 1. 从请求头获取租户ID存入TTL String tenantId request.getHeader(X-Tenant-Id); if (tenantId ! null) { TenantContextHolder.setTenantId(tenantId); } // 2. 从请求头获取TraceId无则生成 String traceId request.getHeader(X-Trace-Id); TraceContextHolder.setTraceId(traceId); // 3. 响应头返回TraceId便于排查 response.setHeader(X-Trace-Id, TraceContextHolder.getTraceId()); return true; } Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // 清理上下文避免线程复用导致数据串用 TenantContextHolder.clear(); TraceContextHolder.clear(); } } // 注册Web拦截器 Configuration public class WebConfig implements WebMvcConfigurer { Resource private WebContextInterceptor webContextInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(webContextInterceptor) .addPathPatterns(/**); // 拦截所有请求 } }五、高级特性1. 手动控制上下文传递若需手动传递 TTL 数据如异步任务可使用Transmitterjava运行import com.alibaba.ttl.Transmitter; // 父线程上下文 String tenantId tenant-123; TenantContextHolder.setTenantId(tenantId); // 捕获父线程上下文 Transmitter.Transmittee transmittee Transmitter.capture(); // 异步任务中恢复上下文 Runnable task () - { // 恢复上下文执行完自动清理 try (Transmitter.ReleaseIgnore release transmittee.retain()) { System.out.println(异步任务租户ID TenantContextHolder.getTenantId()); // 输出tenant-123 } }; // 提交任务 executor.submit(task);2. 忽略特定 TTL 数据通过TransmittableThreadLocal#disable()禁用某个 TTL 的传递java运行public class IgnoreContextHolder { private static final TransmittableThreadLocalString IGNORE_DATA new TransmittableThreadLocalString() { Override protected boolean isTransmittable() { return false; // 禁用传递 } }; }3. 与 Spring 异步框架集成java运行import com.alibaba.ttl.TtlTaskExecutor; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; Configuration EnableAsync public class AsyncConfig { Bean public ThreadPoolTaskExecutor asyncExecutor() { ThreadPoolTaskExecutor executor new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(20); executor.setQueueCapacity(1000); executor.setThreadNamePrefix(async-ttl-); executor.initialize(); // 包装为TTL任务执行器 return TtlTaskExecutor.getTtlTaskExecutor(executor); } }六、生产环境最佳实践1. 核心配置建议配置项推荐值说明核心线程数CPU 核心数 * 2避免线程过多导致上下文切换频繁任务队列容量1000-5000避免队列过大导致内存溢出拒绝策略CallerRunsPolicy核心线程池满时由调用线程执行任务避免任务丢失上下文清理必须执行在afterCompletion/ 任务结束后调用remove()清理 TTLTTL 版本2.12适配 JDK 8修复线程池复用的边界问题2. 关键注意事项1必须清理上下文线程池复用线程时若不清理 TTL 数据会导致数据串用如租户 A 的数据被租户 B 获取java运行// 错误示例未清理 Runnable badTask () - { String tenantId TenantContextHolder.getTenantId(); System.out.println(租户ID tenantId); // 可能获取到上一个任务的租户ID }; // 正确示例手动清理 Runnable goodTask () - { try { String tenantId TenantContextHolder.getTenantId(); System.out.println(租户ID tenantId); } finally { TenantContextHolder.clear(); // 必须清理 } };2避免存储大对象TTL 存储的数据会随线程传递若存储大对象如 100MB 字节数组会导致内存占用过高上下文传递耗时增加。3兼容 ThreadLocalTTL 可与ThreadLocal共存但仅 TTL 数据会跨线程传递ThreadLocal数据仍会丢失。4监控 TTL 数据通过日志记录关键 TTL 数据如 TraceId、租户 ID便于排查上下文传递问题java运行// 拦截器中记录日志 Component public class LogInterceptor implements RequestInterceptor { private static final Logger log LoggerFactory.getLogger(LogInterceptor.class); Override public void apply(RequestTemplate template) { String traceId TraceContextHolder.getTraceId(); String tenantId TenantContextHolder.getTenantId(); log.info(Feign请求url{}, traceId{}, tenantId{}, template.url(), traceId, tenantId); } }5禁用不必要的传递对不需要跨线程传递的 TTL 数据通过isTransmittable()禁用提升性能。3. 性能优化TTL 传递的性能损耗约 1%-5%可忽略无需过度优化避免在 TTL 中存储频繁修改的数据减少上下文复制开销使用TtlExecutors包装线程池时选择getTtlExecutor()轻量包装而非getTtlExecutorWithNullCheck()带空检查稍慢。七、常见问题排查1. TTL 数据传递失败检查线程池是否被TtlExecutors包装检查是否替换了所有ThreadLocal为TransmittableThreadLocal检查是否在任务执行后清理了 TTL 数据导致子线程获取不到。2. 数据串用租户 ID 错误检查是否在任务执行完后调用remove()清理 TTL检查线程池是否复用线程核心线程数设置过小避免在静态方法中直接使用 TTL 数据需确保上下文已初始化。3. 异步任务获取不到 TTL 数据检查 Spring 异步执行器是否被TtlTaskExecutor包装检查是否在异步方法执行前初始化了 TTL 数据。4. 与其他框架冲突TTL 与InheritableThreadLocal兼容但InheritableThreadLocal仅支持子线程继承不支持线程池若使用 Sentinel 限流需确保 TTL 包装线程池后再交给 Sentinel 包装。八、总结TransmittableThreadLocal是解决线程池上下文传递的工业级方案核心价值无缝替换ThreadLocal解决线程池 / 异步场景下的上下文丢失轻量、高性能无侵入式集成到 OpenFeign、Spring 异步等框架生产环境必备链路追踪、租户隔离、权限传递等场景的核心依赖。使用时需牢记替换 ThreadLocal 包装线程池 清理上下文三者缺一不可才能保证 TTL 数据正确传递且不串用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询