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

温州建站平台福建省建设厅网站资质查

温州建站平台,福建省建设厅网站资质查,环保材料东莞网站建设,html登录注册页面代码文章目录 一.业务背景二.TransmittableThreadLocal是什么#xff1f;三.问题复现1.定义注解DigitalAngel2.定义切面3.TransmittableThreadLocal相关4.线程池配置信息5.Controller6.Service7.测试结果8.问题分析9 解决办法及代码改造10.最终测试#xff1a; 四.与 ThreadLocal… 文章目录 一.业务背景二.TransmittableThreadLocal是什么三.问题复现1.定义注解DigitalAngel2.定义切面3.TransmittableThreadLocal相关4.线程池配置信息5.Controller6.Service7.测试结果8.问题分析9 解决办法及代码改造10.最终测试 四.与 ThreadLocal 和 InheritableThreadLocal 的对比五.TransmittableThreadLocal典型应用场景六.小结 一.业务背景 这版本我有一个需求是这样的要针对项目里面总计归纳有6个接口要去判断有没有天使用户角色“DIGITAL_ANGEL”来做特殊化查询逻辑。如果登录用户有这天使用户角色那么看到的数据范围更大。否则就只能看到登录用户自己权限范围内的数据。不知道我描述的这业务逻辑是否清楚了0.0小伙伴们能get到这意思吗…这6个接口是其他同事做的已上线此次需求交给我来做了因为是已上线接口我尽量做到业务侵入最小化植入我此次需求最小代码。 好的然后我的设计思路是这样来做的 定义注解DigitalAngel放到6个请求接口上定义切面切注解然后切面里面统一判断当前登录用户是否有DIGITAL_ANGEL角色有则把这个布尔值存入TransmittableThreadLocal(因为有些接口里面开了子线程涉及到父子线程传值)在接口业务层面拿到TransmittableThreadLocal里面的布尔值如果为true 执行我们的天使用户查询逻辑 执行历史逻辑 一顿操作猛如虎开始开发环境测试刚开始都喜笑颜开小小需求随便拿捏。 后面等我多测几次发现不对劲了出现了诡异的事情切面里面判断某个人有这角色布尔值为true然后子线程里面里面竟然出现了false,然后这问题还是偶发有个时候又频率高有个时候又没问题我都一脸懵逼了课余赶紧补充知识。。。 二.TransmittableThreadLocal是什么 ‌TransmittableThreadLocalTTL是阿里巴巴提供的一个工具包中的类主要用于解决线程池场景下的变量传递问题。‌ 它继承自InheritableThreadLocal提供了一种机制使得在线程池中的线程能够传递和继承ThreadLocal变量的值‌。TTL与普通的ThreadLocal不同普通的ThreadLocal变量在线程之间是隔离的每个线程只能访问自己的ThreadLocal变量无法在线程切换时传递变量值。而TTL允许在线程切换时保留原始线程的变量值并在新线程中恢复这些值使得新线程能够继续使用原始线程的变量‌。TTL的核心工作原理基于Java的ThreadLocal机制并通过扩展InheritableThreadLocal来实现。在任务提交到线程池之前会将当前线程的ThreadLocal变量值保存到一个中间结构中。当任务在子线程中执行时会从这个中间结构中恢复这些变量值并设置到子线程的ThreadLocal副本中从而实现跨线程传递‌ 三.问题复现 因为我们是内网开发不方便给大家直观展示我把实现思路这里简单写一遍一样的可以复现。 1.定义注解DigitalAngel Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface DigitalAngel {String memo(); }2.定义切面 Aspect Component Slf4j public class RoleCheckAspect {Around(annotation(com.tuling.tulingmall.annotation.DigitalAngel))public Object checkRole(ProceedingJoinPoint joinPoint) throws Throwable {// 模拟角色检查逻辑这里检查是否拥有 DIGITAL_ANGEL 角色boolean hasRole Math.random() 0.5;log.info(Aspect: 检查角色是否拥有DIGITAL_ANGEL角色:{} , hasRole);RoleContext.setHasRole(hasRole);try {return joinPoint.proceed();} finally {// 清理 ThreadLocal避免内存泄漏RoleContext.clear();}} }3.TransmittableThreadLocal相关 public class RoleContext {private static final TransmittableThreadLocalBoolean hasDigitalAngelRole new TransmittableThreadLocal();public static void setHasRole(Boolean hasRole) {hasDigitalAngelRole.set(hasRole);}public static Boolean getHasRole() {return hasDigitalAngelRole.get();}public static void clear() {hasDigitalAngelRole.remove();} }4.线程池配置信息 为了让问题更容易复现 使用线程池中的线程复用问题可以增加线程池的复用频率和负载减少线程重新创建的机会。通过设置线程池的核心线程数较小同时增加任务提交量让线程池更频繁地复用同一个线程这样在上下文未正确传播时更容易复现问题。控制 TransmittableThreadLocal 的传递与清理在一些关键位置手动调用 RoleContext.clear()或者模拟在子线程中上下文值被修改或清理的场景。延迟任务执行可以通过引入一定的延迟或模拟复杂任务使得线程的生命周期较长以增加上下文丢失的几率。多个异步任务并行运行提交大量异步任务给线程池增加并发量让上下文丢失更容易出现。 我把核心线程数变小同时提交10个异步任务然后给任务引入一定的延迟来增大问题复现概率 Configuration public class ThreadPoolConfiguration {Bean(commonPool)public ExecutorService commonThreadPoolExecutor(){return new TulingMallThreadPoolExecutor(测试用例专用线程池,2,5).getLhrmsThreadPoolExecutor();} }5.Controller ApiOperation(测试-TransmittableThreadLocal) DigitalAngel(memo 标注该接口需要校验是否存在DIGITAL_ANGEL角色) RequestMapping(value /testTransmittableThreadLocal, method RequestMethod.POST) public CommonResultString testTransmittableThreadLocal() {testCaseService.testTransmittableThreadLocal();return CommonResult.success(成功); }6.Service AutowiredQualifier(commonPool)private ExecutorService tulingThreadPoolExecutor;Overridepublic void testTransmittableThreadLocal() {// 主线程获取角色信息Boolean hasRole RoleContext.getHasRole();log.info(主线程: 获取角色信息是否拥有 DIGITAL_ANGEL 角色: hasRole);// 提交多个任务到自定义线程池for (int i 0; i 10; i) {CompletableFuture.runAsync(() - {try {// 增加任务的执行时间模拟长时间运行的任务TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 1000));// 在子线程中获取角色信息Boolean childThreadHasRole RoleContext.getHasRole();log.info(子线程: 获取角色信息是否拥有 DIGITAL_ANGEL 角色: childThreadHasRole);} catch (InterruptedException e) {e.printStackTrace();}}, tulingThreadPoolExecutor);}}7.测试结果 正常我们如果没有仔细了解过TransmittableThreadLocal是不是认为都使用TransmittableThreadLocal了在切面里面(主线程)都打印有这个DIGITAL_ANGEL角色编码了那么子线程拿到的肯定是true对吧。但是上面代码经测试会出现下面的异常情况切面和主线程判断是有这个角色编码的可是我们的子线程获取的都是false,认为它没有天使用户角色从而造成我们的业务逻辑查的有问题 8.问题分析 然后去查资料总结了精辟的点。遇到的问题与 线程池复用 和 TransmittableThreadLocal 的传播机制 有关。 TransmittableThreadLocal 可以在父子线程之间传递值但需要满足一些前提条件比如子线程是在父线程创建后立即启动的。如果业务逻辑涉及到线程池或者异步执行任务可能会出现以下两种情况 线程池线程复用问题 在线程池中线程是被复用的因此当某个线程完成任务后会被放回线程池而不会立即销毁。接下来的任务可能会复用这个线程。在这种情况下如果你没有正确清理 TransmittableThreadLocal线程复用后可能获取到之前任务的值或者无法获取到期望的值。 解决方案手动清理 TransmittableThreadLocal 的值。确保每次在线程池中使用线程执行任务时清理上一次任务残留的数据。 try {// 在子线程中执行任务并获取 TTL 中的布尔值 } finally {// 任务执行结束清理 TransmittableThreadLocalTransmittableThreadLocal.clear(); }子线程启动时机问题 TransmittableThreadLocal 的值只能在子线程创建时从父线程中拷贝。如果子线程是在父线程存储值之前启动的那么子线程可能无法获取父线程的 ThreadLocal 值。如果有异步任务尤其是使用 Async 或其他异步编程模型的情况下线程的启动顺序可能会影响值的传递。 解决方案确保在启动子线程之前父线程已经将值存入 TransmittableThreadLocal。可以通过控制异步任务的触发时机来避免这一问题。 // 在主线程中存入 TransmittableThreadLocal 的值 boolean roleExists checkRole(); TransmittableThreadLocal.set(roleExists);// 确保子线程在设置完值后启动 executorService.submit(() - {// 子线程中获取值Boolean value TransmittableThreadLocal.get();// 执行业务逻辑 });异步框架或线程池管理问题 如果你使用了异步框架比如 Spring 的 Async或自定义的线程池管理逻辑确保这些线程池或任务管理器支持 TransmittableThreadLocal 的上下文传播。默认的线程池可能不支持自动传播上下文数据导致无法在子线程中获取到父线程的数据。 解决方案确保线程池或任务管理框架使用了 TTL 的增强版线程池TTLExecutorService 等这可以保证 TransmittableThreadLocal 的值在线程池中正确传播。 // 使用 TTL 版本的线程池来保证父子线程之间的值传递 ExecutorService ttlExecutorService TtlExecutors.getTtlExecutorService(executorService);ttlExecutorService.submit(() - {Boolean value TransmittableThreadLocal.get();// 子线程中业务逻辑 });很明显我们上面的代码问题符合上面“异步框架或线程池管理问题”。我们的线程池没有使用TTL 的增强版线程池 总结如下3点 线程池复用 导致的值残留或丢失。子线程启动时机 导致的值未传递。线程池的上下文传播 未正确设置。 可以从这几方面排查和修正你的问题确保 TransmittableThreadLocal 在父子线程之间正确传播值。 9 解决办法及代码改造 因为我们项目里面的线程池是放在了Spring容器中然后有其他场景也在使用为了影响最小化。直接在开异步任务的时候CompletableFuture传入 TTL 的增强版线程池TTLExecutorService 。 AutowiredQualifier(commonPool)private ExecutorService tulingThreadPoolExecutor;Overridepublic void testTransmittableThreadLocal() {// 主线程获取角色信息Boolean hasRole RoleContext.getHasRole();log.info(主线程: 获取角色信息是否拥有 DIGITAL_ANGEL 角色: hasRole);// 提交多个任务到自定义线程池for (int i 0; i 10; i) {CompletableFuture.runAsync(() - {try {// 增加任务的执行时间模拟长时间运行的任务TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 1000));// 在子线程中获取角色信息Boolean childThreadHasRole RoleContext.getHasRole();log.info(子线程: 获取角色信息是否拥有 DIGITAL_ANGEL 角色: childThreadHasRole);} catch (InterruptedException e) {e.printStackTrace();}}, TtlExecutors.getTtlExecutorService(tulingThreadPoolExecutor));}}10.最终测试 请求了10次每次结果都是主线程认为没有角色权限则子线程也没有角色权限主线程认为有角色权限则子线程也有角色权限。 四.与 ThreadLocal 和 InheritableThreadLocal 的对比 传统 ThreadLocal 的局限性 ThreadLocal 是 Java 提供的线程本地存储工具用于每个线程存储和访问自己的独立变量副本。但是在多线程环境下尤其是线程池场景中ThreadLocal 有以下局限性 线程隔离每个线程有自己独立的 ThreadLocal 值子线程不能继承父线程中的 ThreadLocal 值。线程复用问题在线程池中线程会被复用导致某些上下文信息例如 ThreadLocal值在不同任务之间泄漏或未能正确传递到子线程中。 InheritableThreadLocal 的不足 InheritableThreadLocal 是 ThreadLocal 的一个子类它允许子线程继承父线程的 ThreadLocal 值但它有以下几个问题 线程池场景不适用InheritableThreadLocal 只能在父线程创建子线程时传递 ThreadLocal值对于线程池中的线程复用场景无效因为线程池中的线程在父线程运行之前就已创建。线程复用导致的值污染线程池中的线程在执行完一个任务后会被重复利用如果 InheritableThreadLocal 的值没有被清理干净可能导致数据污染。 TransmittableThreadLocal 的原理 TransmittableThreadLocal 解决了 ThreadLocal 和 InheritableThreadLocal 在多线程和线程池环境中的局限性能够在父线程和子线程包括线程池中的线程之间传递 ThreadLocal 的值。 其核心思想是通过任务提交的时机在任务进入线程池执行前主动捕获当前线程的 ThreadLocal 值并在子线程中恢复从而确保上下文的传递。 核心技术原理 1.拦截任务提交在任务提交给线程池时TransmittableThreadLocal 会拦截任务并记录当前父线程中的 ThreadLocal 值。这是通过对 ExecutorService、Runnable、Callable 等任务接口的增强来实现的。 2.线程上下文的传递当任务在子线程中执行时TransmittableThreadLocal 将会把父线程的 ThreadLocal 上下文传递到子线程并在子线程执行任务时还原这些上下文。 3.任务执行完成后的清理任务执行结束后TransmittableThreadLocal 会清理子线程中的上下文避免这些上下文在线程复用时污染其他任务。 工作流程 以下是 TransmittableThreadLocal 在多线程场景下的典型工作流程 1.父线程设置 ThreadLocal 值在父线程中使用 TransmittableThreadLocal 设置一些上下文信息例如用户信息、请求 ID 等。 2.任务提交到线程池父线程将任务提交给线程池执行此时 TransmittableThreadLocal 会通过 capture() 方法捕获当前线程的 ThreadLocal 值。 3.子线程执行任务在线程池中的某个子线程执行任务之前TransmittableThreadLocal 的 replay() 方法会在子线程中恢复父线程的 ThreadLocal 值。 4.任务执行完成后清理上下文任务执行结束后TransmittableThreadLocal 的 restore() 方法会清理子线程中的 ThreadLocal 上下文防止上下文污染。 五.TransmittableThreadLocal典型应用场景 TransmittableThreadLocal 特别适用于以下场景 分布式追踪在分布式系统中传递请求上下文信息如 Trace ID到不同线程保证日志或追踪信息的一致性。异步任务处理在异步任务执行中需要传递用户会话信息或安全上下文到不同线程。 线程池环境下的上下文传递解决线程池复用带来的ThreadLocal 上下文传递问题。 六.小结 TransmittableThreadLocal 是对 ThreadLocal 和 InheritableThreadLocal 的增强解决了线程池复用和父子线程上下文传递问题。它在异步编程和多线程环境中尤其是线程池场景下有很大的应用价值适用于需要传递线程上下文信息的各种场景如分布式追踪、会话管理、日志追踪等。使用异步编程的时候我们肯定会接触到父子线程传值问题如果不使用TransmittableThreadLocal就得自己手动设置到每个子线程里面去很是麻烦。如果使用TransmittableThreadLocal需要注意线程池复用、子线程启动时机、线程池的上下文传播以及清理ThreadLocal避免内存泄漏哦
http://www.sczhlp.com/news/230287/

相关文章:

  • 鄞州区建设网站政务移动门户网站建设
  • 58同城给做网站商城类网站模板
  • 网站的首页标题在哪里设置的阿里云注册域名
  • 免费建网站软件下载手机运行时间 wordpress
  • 平湖新埭哪里有做网站的软件开发需要多少资金
  • 广东平台网站建设哪家好织梦如何生成网站地图
  • 网站备案由别人代运营是做什么的工作
  • 做物流有哪些网站旅行社酒店分销平台
  • 网站seo专员新网站如何做网站优化
  • CF1111A Superhero Transformation
  • 做网络销售都做什么网站自己做网站还是开通阿里巴巴诚信通
  • 排名好的徐州网站开发营销策划师
  • 手机传奇网站模板下载常州网站建设公司如何
  • 南京网站排名优化费用python自学网站
  • 网站开发a — ajax哈尔滨专业网站建设哪个好
  • 网站建设怎样布局html5 手机 网站
  • 设计很好的视觉很棒的网站商业设计
  • c2c商城网站建设广告投放都有哪些平台
  • 做百度推广需要自己有个网站吗网站描述怎么写利于seo
  • 有做a50期货的网站dw网页制作详细步骤
  • 旅游网站开发的需求wordpress标签id在哪里
  • 建设学校网站需要具备数据网站建设多少钱
  • 旅游网站建设公司排名甘肃手机网站建设
  • 网站建设 业务建站模板免费
  • 上海小学网站建设招标企业运营管理师证书
  • 推广网站免费网页制作模板成品免费
  • 做五金建材这几个网站网站建设 经典书籍
  • 有源代码如何做网站app拉新推广赚佣金
  • qq推广引流网站河北省招标投标公共服务平台
  • 网站开发税收标准上海网络推广培训学校