支持异步线程自动传递上下文(例如当前请求)的工具类(支持自定义上下文传递逻辑,支持拦截所有异步操作)

news2024/9/21 18:37:44

文章目录

  • 支持异步线程自动传递上下文(例如当前请求)的工具类(支持自定义上下文传递逻辑,支持拦截所有异步操作)
    • 使用示范
    • ContextSupportedAsyncUtil .java
    • 自动拦截所有异步线程池操作
      • ContextSupportedExecutorAspect.java
    • 自定义上下文注入逻辑示范

支持异步线程自动传递上下文(例如当前请求)的工具类(支持自定义上下文传递逻辑,支持拦截所有异步操作)

当我们使用异步线程去执行一些耗时操作的时候,这些异步操作中可能需要获取当前请求等上下文信息
若未做传递逻辑默认是获取不到的,因此写了一个自动传递的工具和切面,可使用工具手动调用时会自动拷贝上下文信息,加载切面后会拦截所有异步操作自动拷贝上下文信息。
同时,上下文信息的加载拷贝移除逻辑也可实现接口自定义,自行扩展。

和阿里巴巴TransmittableThreadLocal(TTL)类似,多支持了可以自定义上下文传递逻辑,你可以认为是阿里TTL手写版本知乎介绍阿里TTL原理
在这里插入图片描述

使用示范

异步执行lambda表达式中的代码,代码中获取当前请求的URL并打印
若未使用该工具,是无法获取到当前请求的。

ContextSupportedAsyncUtil.execute(()->{System.out.println("我在异步线程中获取到的当前请求URL是"+((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getRequestURI());});

ContextSupportedAsyncUtil .java


import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;

/**
 * @author humorchen
 * date: 2024/7/30
 * description: 支持上下文自动传递的异步工具
 * 默认支持自动传递 HttpServletRequest 到异步线程中
 * 其他上下文可自行创建类实现 AsyncContextInjector 接口,并调用ContextSupportedAsyncUtil.registerContextInjector 将其注册上去。
 * 可参考 SpringWebRequestContextInjector。class
 **/
@Slf4j
public class ContextSupportedAsyncUtil {
    private static final int CORE_SIZE = 8;
    private static final int MAX_SIZE = 32;
    private static final int QUEUE_SIZE = 1024;
    private static final int KEEP_ALIVE = 5;
    private static final TimeUnit KEEP_ALIVE_UNIT = TimeUnit.MINUTES;
    private static final ArrayBlockingQueue<Runnable> QUEUE = new ArrayBlockingQueue<>(QUEUE_SIZE);
    private static final AtomicInteger THREAD_NUM = new AtomicInteger(0);
    private static final RejectedExecutionHandler REJECT_POLICY = new ThreadPoolExecutor.CallerRunsPolicy();
    private static final List<Class<? extends AsyncContextInjector>> ASYNC_CONTEXT_INJECTOR_CLS_LIST = new ArrayList<>();
    private static ContextSupportedThreadPoolExecutor EXECUTOR = new ContextSupportedThreadPoolExecutor(CORE_SIZE, MAX_SIZE, KEEP_ALIVE, KEEP_ALIVE_UNIT, QUEUE, (r) -> new Thread(r, "AsyncUtil-thread-" + THREAD_NUM.incrementAndGet()), REJECT_POLICY);

    static {
        // 默认支持spring mvc 的RequestHolder自动传递到异步线程中
        ASYNC_CONTEXT_INJECTOR_CLS_LIST.add(SpringWebRequestContextInjector.class);
    }

    /**
     * 支持自定义上下文传递的executor
     */
    public static class ContextSupportedThreadPoolExecutor extends ThreadPoolExecutor {

        public ContextSupportedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        }

        public ContextSupportedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
        }

        public ContextSupportedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
        }

        public ContextSupportedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
        }

        /**
         * @param command the task to execute
         */
        @Override
        public void execute(@NonNull Runnable command) {
            super.execute(command instanceof AsyncRunnable ? command : getAsyncRunnable(command));
        }
    }

    /**
     * 异步上下文注入器接口
     * 将A线程的上下文注入到执行Runnable的B线程,并在执行后清除
     */
    public interface AsyncContextInjector {
        /**
         * 初始化
         * 读取A线程的上下文并保存到当前对象
         */
        void init();

        /**
         * 注入上下文信息
         * 注入init阶段存储的上下文到B线程的上下文中
         */
        void inject();

        /**
         * 移除上下文信息
         * 清理B线程刚注入的上下文
         */
        void remove();
    }

    /**
     * 支持传递ServletRequestAttributes对象用于获取当前请求HttpServletRequest
     */
    public static class SpringWebRequestContextInjector implements AsyncContextInjector {
        private ServletRequestAttributes requestAttributes;

        /**
         * 初始化
         * 读取A线程的上下文并保存到当前对象
         */
        @Override
        public void init() {
            requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        }

        /**
         * 注入上下文信息
         * 注入init阶段存储的上下文到B线程的上下文中
         */
        @Override
        public void inject() {
            RequestContextHolder.setRequestAttributes(requestAttributes);
        }

        /**
         * 移除上下文信息
         * 清理B线程刚注入的上下文
         */
        @Override
        public void remove() {
            RequestContextHolder.resetRequestAttributes();
        }

    }

    /**
     * 抽象的注入器,将自定义的每个注入器执行
     */
    public static class AbstractAsyncContextInjector {
        private final List<AsyncContextInjector> list = new ArrayList<>(ASYNC_CONTEXT_INJECTOR_CLS_LIST.size());

        public void init() {
            // 初始化每个异步上下文注入器
            for (Class<? extends AsyncContextInjector> aClass : ASYNC_CONTEXT_INJECTOR_CLS_LIST) {
                try {
                    AsyncContextInjector asyncContextInjector = aClass.newInstance();
                    asyncContextInjector.init();
                    list.add(asyncContextInjector);
                } catch (Exception e) {
                    log.error("AbstractAsyncContextInjector init error", e);
                }
            }
        }

        public void inject() {
            for (AsyncContextInjector asyncContextInjector : list) {
                try {
                    asyncContextInjector.inject();
                } catch (Exception e) {
                    log.error("AbstractAsyncContextInjector inject error", e);
                }
            }
        }

        public void remove() {
            for (AsyncContextInjector asyncContextInjector : list) {
                try {
                    asyncContextInjector.remove();
                } catch (Exception e) {
                    log.error("AbstractAsyncContextInjector remove error", e);
                }
            }
        }
    }

    /**
     * 支持异步线程传递自定义上下文时使用的Runnable包装类
     */
    public static class AsyncRunnable extends AbstractAsyncContextInjector implements Runnable {
        private final Runnable runnable;


        public AsyncRunnable(Runnable runnable) {
            // 初始化保存A线程上下文信息
            init();
            this.runnable = runnable;
        }


        @Override
        public void run() {
            if (runnable == null) {
                return;
            }
            try {
                // 将保存的A线程的上下文信息恢复到B线程
                inject();
                runnable.run();
            } catch (Exception e) {
                log.error("【ContextSupportedAsyncUtil】 run error {}", e.getMessage());
                throw e;
            } finally {
                // 清理B线程的上下文
                remove();
            }
        }

    }

    /**
     * 异步执行任务
     *
     * @param runnable
     */
    public static void execute(Runnable runnable) {
        EXECUTOR.execute(runnable);
    }

    /**
     * 异步执行任务
     *
     * @param runnable
     */
    public static void submit(Runnable runnable) {
        execute(runnable);
    }

    /**
     * 执行异步任务并获取返回值
     *
     * @param supplier
     * @param <T>
     * @return
     */
    public static <T> CompletableFuture<T> execute(Supplier<T> supplier) {
        return CompletableFuture.supplyAsync(supplier, EXECUTOR);
    }

    /**
     * 获取线程池
     *
     * @return
     */
    public static ThreadPoolExecutor getExecutor() {
        return EXECUTOR;
    }

    /**
     * 设置本工具使用的线程池
     *
     * @param executor
     */
    public static void setExecutor(ContextSupportedThreadPoolExecutor executor) {
        if (executor == null) {
            throw new NullPointerException("executor 不得为空");
        }
        if (executor.isShutdown() || executor.isTerminated() || executor.isTerminating()) {
            throw new IllegalStateException("executor 状态不得为shutdown");
        }
        ContextSupportedThreadPoolExecutor old = EXECUTOR;
        EXECUTOR = executor;
        if (old != null) {
            old.shutdown();
        }
    }

    /**
     * 获取支持上下文注入的Runnable
     *
     * @param runnable
     * @return 包装后的runnable
     */
    public static AsyncRunnable getAsyncRunnable(Runnable runnable) {
        return new AsyncRunnable(runnable);
    }

    /**
     * 注册注入器
     *
     * @param cls
     */
    public static void registerContextInjector(Class<? extends AsyncContextInjector> cls) {
        if (cls != null && !ASYNC_CONTEXT_INJECTOR_CLS_LIST.contains(cls)) {
            ASYNC_CONTEXT_INJECTOR_CLS_LIST.add(cls);
        }
    }
}

自动拦截所有异步线程池操作

ContextSupportedExecutorAspect.java



import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * @author humorchen
 * date: 2024/8/7
 * description: 支持上下文的executor
 **/
@Component
@Aspect
@Slf4j
public class ContextSupportedExecutorAspect {

    
    @Around("execution(* java.util.concurrent.Executor.execute(java.lang.Runnable))")
    public Object contextSupportedExecutor(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0) {
            Object arg = args[0];
            if (arg instanceof Runnable && !(arg instanceof ContextSupportedAsyncUtil.AsyncRunnable)) {
                args[0] = ContextSupportedAsyncUtil.getAsyncRunnable((Runnable) arg);
            }
        }
        return joinPoint.proceed(args);
    }


}

自定义上下文注入逻辑示范

SpringWebRequestContextInjector.java
异步线程上下文自动注入当前请求(默认提供)

/**
     * 支持传递ServletRequestAttributes对象用于获取当前请求HttpServletRequest
     */
    public static class SpringWebRequestContextInjector implements AsyncContextInjector {
        private ServletRequestAttributes requestAttributes;

        /**
         * 初始化
         * 读取A线程的上下文并保存到当前对象
         */
        @Override
        public void init() {
            requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        }

        /**
         * 注入上下文信息
         * 注入init阶段存储的上下文到B线程的上下文中
         */
        @Override
        public void inject() {
            RequestContextHolder.setRequestAttributes(requestAttributes);
        }

        /**
         * 移除上下文信息
         * 清理B线程刚注入的上下文
         */
        @Override
        public void remove() {
            RequestContextHolder.resetRequestAttributes();
        }

    }

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2146774.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python基础】Python文件处理

本文收录于 《Python编程入门》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程基础知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、打开文件三、读取文件内容四、写入文件内容五、高级文件操作六、总结 一、前言 ​ 在Python中&am…

VMware安装飞牛私有云fnOS并挂载小雅Alist实现异地远程访问

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

地平线4登录xbox后提示需要登录档案怎么解决

这个游戏是真nt&#xff08;在联机上&#xff09;&#xff0c;典型搞联机2小时游玩半小时&#xff0c;多半时间都花费在联机上了&#xff0c;不是为了联机和朋友跑车&#xff0c;早给他卸载了。 本人的游戏问题&#xff1a;看了一些视频感觉没什么作用&#xff0c;我的现象就是…

防火墙配置变更管理

在任何组织中&#xff0c;当涉及到网络安全时&#xff0c;频繁地更换防火墙是必要的&#xff0c;实施简化的防火墙更改管理策略模板可以减少管理时间&#xff0c;还可以减少每次变更引入新的安全性或合规性问题的可能性。典型的防火墙变更管理流程将包括以下步骤&#xff1a; …

【Finetune】(一)、transformers之BitFit微调

文章目录 0、参数微调简介1、常见的微调方法2、代码实战2.1、导包2.2、加载数据集2.3、数据集处理2.4、创建模型2.5、BitFit微调*2.6、配置模型参数2.7、创建训练器2.8、模型训练2.9、模型推理 0、参数微调简介 参数微调方法是仅对模型的一小部分的参数&#xff08;这一小部分可…

Java lambda表达式的变量捕获

有人看到这个lambda表达式能够访问isQuit这个变量而且还是可以被修改的变量&#xff0c;就发出疑问了&#xff0c;之前不是说lambda不能不或变量吗&#xff1f; 1.规则 java的lambda表达式变量捕获规则只是针对于外部作用域的局部变量来说的&#xff01;&#xff01;&#xf…

3D虚拟商城是什么?有哪些优势?

在数字化转型的澎湃浪潮中&#xff0c;3D虚拟商店作为一股革新力量&#xff0c;正逐步构筑起商业展示与交易的全新维度&#xff0c;成为企业及商户不可或缺的战略资产。视创云展为品牌搭建3D虚拟商城提供技术支持&#xff0c;凭借高度精细的三维模拟空间&#xff0c;不仅为顾客…

vue2项目实现国际化(若依框架示例)

本文主要梳理vue2项目实现全项目格式化&#xff0c;在导航栏中切换&#xff0c;页面中所有的组件的默认语言随之切换&#xff0c;搭配vue-i18n插件 文章目录 基础准备引入插件vue-i18n 实现示例流程1. 创建国际化文件1.1 element文件夹1.2 locales文件夹1.3 index.js1.4 change…

设计模式之访问者模式:灵活访问对象结构的强大工具

访问者模式 访问者模式&#xff08;Visitor Pattern&#xff09;是一种行为型设计模式&#xff0c;允许在不改变数据结构的前提下定义在这些结构上的新操作。它将操作行为与对象结构分离&#xff0c;使得可以在不修改对象结构的情况下添加新的操作行为。 访问者模式的应用场景…

在网络环境中怎么保护个人信息安全?

在网络环境中保护个人信息安全非常重要&#xff0c;以下是一些基本的建议来帮助您保护自己的个人信息&#xff1a; 使用强密码&#xff1a;确保您的所有在线账户都使用强密码。强密码通常包含大写字母、小写字母、数字以及特殊字符&#xff0c;并且长度至少为12位以上。 启用双…

【Node.js】初识微服务

概述 Node.js 的微服务架构是一种通过将应用程序分解为独立的、松耦合的小服务的方式进行系统设计。 每个微服务负责处理一个特定的业务功能&#xff0c;并且这些服务可以独立开发、部署、扩展和管理&#xff0c;并且可以通讯。 它的核心思想就是解耦。 微服务和微前端是类…

《中国数据库前世今生》观后感:数据库与中国IT的崛起

文章目录 1. 数据库技术的演进与挑战2. 开发者眼中的数据库3. 数据库未来展望4. 结语 作为一名程序员&#xff0c;观看了《中国数据库前世今生》纪录片后&#xff0c;我感受到了数据库技术在中国发展的巨大变化。中国IT行业的快速崛起&#xff0c;数据库技术无疑扮演了重要角色…

2.C++中程序的语法基础--关键字与分隔符

现在回过头来看上一篇中所写的程序&#xff1a; #include <bits/stdc.h> using namespace std; int main() {// 程序主体cout << "HelloWorld" << endl; return 0; } 我们会看到许多英文单词&#xff0c;像"include"、“using”&…

智能车镜头组入门(四)元素识别

元素识别是摄像头部分中难度最大的一部分&#xff0c;也是我花时间最长的一部分&#xff0c;前前后后画了很长时间&#xff0c;最后还是勉勉强强完成了。 基础的元素识别主要有两个&#xff1a;十字&#xff0c;圆环&#xff0c;和斑马线。十字要求直行&#xff0c;圆环需要进…

科技修复记忆:轻松几步,旧照变清晰

在时间的长河中&#xff0c;旧照片承载着无数珍贵的记忆与故事。然而&#xff0c;随着岁月的流逝&#xff0c;这些照片往往变得模糊不清&#xff0c;色彩黯淡&#xff0c;令人惋惜。 幸运的是&#xff0c;随着科技的发展&#xff0c;我们有了多种方法来修复这些旧照片的画质&a…

【Python基础】Python模块(提高代码可维护性与重用性的关键)

本文收录于 《Python编程入门》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程基础知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、什么是Python模块&#xff1f;三、创建模块四、导入模块五、使用if __name__ "__main__&quo…

(黑马点评) 五、探店达人系列功能实现

5.1 发布和查看探店笔记 5.1.1 发布探店笔记 这块代码黑马已经完成了&#xff0c;在发布探店笔记界面&#xff0c;有两块内容是需要上传的。一是笔记内容&#xff0c;二是笔记配图。其中笔记配图部分黑马使用的是上传到本地前端服务器上面的。我我觉得可以将图片文件发布在阿里…

开始你的博客之旅:从零到一的详细指南

创建博客不仅是表达自我的方式&#xff0c;更是与世界分享知识、塑造个人品牌、甚至实现商业变现的强大工具。本文将详细介绍从确定主题到实际运营的每个步骤&#xff0c;帮助你顺利开启个人博客的旅程。 确定博客的主题和目标 在开始博客之前&#xff0c;首先要明确博客的主…

windows环境下安装python第三方包

python环境下&#xff0c;通常通过Anaconda来管理多个python环境&#xff1b; 即通过Anaconda创建python不用的虚拟环境&#xff1b; 1. 安装更新python第三方包&#xff0c;打开Anaconda&#xff0c;在右侧的搜索需要的python包并进行安装&#xff1b; 2.如果没有搜索到&…

【线性规划求解系列】MATLAB中使用linprog解决线性规划问题

linprog - 求解线性规划问题 - MATLAB - MathWorks 中国https://ww2.mathworks.cn/help/optim/ug/linprog_zh_CN.html 本文详细介绍了如何在MATLAB中使用linprog函数来解决各种类型的线性规划问题。首先概述了linprog的基本语法&#xff0c;随后通过五个具体实例演示了如何处理…