You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检查当前线程是否通过特定ManagedExecutor执行及关联用户ID

实现方案

要实现这个需求,核心是给每个用户对应的ManagedExecutor绑定用户ID,并让任务执行时能安全获取该ID,同时识别是否通过自定义Executor调用。推荐基于线程上下文传播的方案,符合Jakarta EE规范且适配SmallRye实现,具体步骤如下:

1. 自定义线程上下文提供者管理用户ID

创建一个ThreadContextProvider实现类,专门负责用户ID的上下文捕获、传播和清理:

public class UserIdThreadContextProvider implements ThreadContextProvider {
    public static final String USER_ID_CONTEXT_NAME = "userIdContext";
    private static final ThreadLocal<String> userIdHolder = new ThreadLocal<>();

    // 设置当前线程的用户ID
    public static void setCurrentUserId(String userId) {
        userIdHolder.set(userId);
    }

    // 获取当前线程绑定的用户ID(任务执行时调用)
    public static String getCurrentUserId() {
        return userIdHolder.get();
    }

    // 清除当前线程的用户ID,避免污染
    public static void clearCurrentUserId() {
        userIdHolder.remove();
    }

    @Override
    public ThreadContextSnapshot currentContext(Map<String, String> props) {
        // 捕获当前线程的用户ID快照
        String currentUserId = userIdHolder.get();
        return () -> {
            // 传播上下文:保存原ID,设置当前快照的ID
            String originalUserId = userIdHolder.get();
            userIdHolder.set(currentUserId);
            // 返回清理回调:恢复原ID
            return () -> userIdHolder.set(originalUserId);
        };
    }

    @Override
    public ThreadContextSnapshot clearedContext(Map<String, String> props) {
        return () -> {
            String originalUserId = userIdHolder.get();
            userIdHolder.remove();
            return () -> userIdHolder.set(originalUserId);
        };
    }

    @Override
    public String getThreadContextName() {
        return USER_ID_CONTEXT_NAME;
    }
}

2. 注册自定义上下文提供者

在项目的META-INF/services目录下创建文件jakarta.enterprise.concurrent.ThreadContextProvider,内容为自定义类的全限定名,让SmallRye能识别并加载这个上下文提供者:

com.yourpackage.UserIdThreadContextProvider

3. 绑定用户ID到ManagedExecutor

修改你的ManagedExecutor构建逻辑,在创建每个用户对应的Executor时,先设置当前线程的用户ID,确保上下文被传播到任务执行线程:

// 假设当前循环处理每个用户ID
String userId = "user_123";

// 临时设置当前线程的用户ID,用于上下文快照捕获
UserIdThreadContextProvider.setCurrentUserId(userId);

ManagedExecutor managedExecutor = ((SmallRyeManagedExecutor.Builder) ManagedExecutor.builder())
        .propagated(Set.of(ThreadContext.ALL_REMAINING, UserIdThreadContextProvider.USER_ID_CONTEXT_NAME))
        .maxAsync(1)
        .maxQueued(Integer.MAX_VALUE)
        .withExecutorService(parentManagedExecutor)
        .build();

// 清除当前线程的用户ID,避免污染后续逻辑
UserIdThreadContextProvider.clearCurrentUserId();

// 将Executor存入用户ID映射
userExecutorMap.put(userId, managedExecutor);

4. 在任务中获取用户ID并验证

在通过该ManagedExecutor执行的任务代码里,直接调用工具方法获取用户ID:

public class UserTask implements Runnable {
    @Override
    public void run() {
        String userId = UserIdThreadContextProvider.getCurrentUserId();
        if (userId != null) {
            // 确认是通过自定义ManagedExecutor执行,且拿到了关联的用户ID
            System.out.println("当前任务属于用户:" + userId);
        } else {
            // 非自定义Executor执行的任务
            System.out.println("任务未通过指定Executor执行");
        }
    }
}

备选方案:包装Executor绑定用户ID

如果不想扩展线程上下文,也可以直接包装ManagedExecutor,在提交任务时注入用户ID:

public class UserBoundExecutor implements ManagedExecutor {
    private final ManagedExecutor delegate;
    private final String userId;

    public UserBoundExecutor(ManagedExecutor delegate, String userId) {
        this.delegate = delegate;
        this.userId = userId;
    }

    @Override
    public void execute(Runnable command) {
        delegate.execute(() -> {
            try {
                UserIdThreadContextProvider.setCurrentUserId(userId);
                command.run();
            } finally {
                UserIdThreadContextProvider.clearCurrentUserId();
            }
        });
    }

    // 重写submit、invokeAll等所有方法,逻辑类似
}

创建映射时用这个包装类替代原Executor即可,任务中获取用户ID的方式和上面一致。

内容的提问来源于stack exchange,提问作者Dmitry Shohov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 11:03:20