Spring @async子线程上下文多租户



我有一个应用程序,它可以向我的java应用程序发送一些REST调用。我必须在@Async模式下运行最后一个命令,以便用户可以在异步任务执行另一个 SOAP 调用时继续使用该应用程序。

问题是我已经自动连接了数据库中的租户上下文和多个租户标识符。执行异步任务时,它具有我的主线程的租户上下文,并为错误的租户保存数据。

这是我的JpaTransactionManager,它被调用到数据库中的每个事务:

@Autowired
private TenantContext tenantContext;
@Autowired
private Flyway flyway;
@Autowired
private Environment environment;
@Override
protected void doBegin(final Object transaction, final TransactionDefinition definition)
{
    super.doBegin(transaction, definition);
    final EntityManagerHolder entityManagerHolder = (EntityManagerHolder) TransactionSynchronizationManager.getResource(getEntityManagerFactory());
    final EntityManager entityManager = entityManagerHolder.getEntityManager();
    String subAccountId = "";
    if (environment.getActiveProfiles().length == 0 || !environment.getActiveProfiles()[0].equals("production"))
    {
        subAccountId = "SCAN4CLOUD";
    } else
    {
        subAccountId = tenantContext.getTenant().getAccount().getId().toUpperCase();
    }
    entityManager.setProperty("tenant", subAccountId);
}

我尝试拦截具有以下类的异步调用以设置正确的租户上下文。

@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport
{
@Autowired
private TenantContext tenantContext;
@Autowired
private Environment environment;
private HashMap<String, ContextAwarePoolExecutor> tenantThreadPoolTaskExecutor = new HashMap<String, ContextAwarePoolExecutor>();
@Override
@Bean
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Executor getAsyncExecutor()
{
    String subAccountId = "";
    if (environment.getActiveProfiles().length == 0 || !environment.getActiveProfiles()[0].equals("production"))
    {
        subAccountId = "SCAN4CLOUD";
    } else
    {
        subAccountId = tenantContext.getTenant().getAccount().getId().toUpperCase();
    }
    if (!tenantThreadPoolTaskExecutor.containsKey(subAccountId))
    {
        tenantThreadPoolTaskExecutor.put(subAccountId, new ContextAwarePoolExecutor(tenantContext));
    }
    return tenantThreadPoolTaskExecutor.get(subAccountId);
}

}

ContextAwarePoolExecutor:

public class ContextAwarePoolExecutor extends ThreadPoolTaskExecutor
{
    private TenantContext tenantContext;
    public ContextAwarePoolExecutor(TenantContext tenantContext)
    {
        this.tenantContext = tenantContext;
    }
    @Override
    public <T> Future<T> submit(Callable<T> task)
    {
        return super.submit(new ContextAwareCallable(task, tenantContext));
    }
    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task)
    {
        return super.submitListenable(new ContextAwareCallable(task, tenantContext));
    }
}

上下文感知可调用:

public class ContextAwareCallable<T> implements Callable<T>
{
private Callable<T> task;
private TenantContext tenantContext;
public ContextAwareCallable(Callable<T> task, TenantContext tenantContext)
{
    this.task = task;
    this.tenantContext = tenantContext;
}
@Override
public T call() throws Exception
{
    if (tenantContext != null)
    {
        return tenantContext.execute(tenantContext.getTenant().getId(), task);
    }
    return task.call();
    }
}

但它仍然没有给我父线程的正确租户。

对此有什么建议或其他方法吗?

谢谢诺斯塔

我已经通过在上下文可感知的构造函数中直接添加租户 id 来解决它。 现在它正在工作。

Tenantcontext 类来自 SAP: import com.sap.cloud.account.TenantContext;https://appdoc.app/artifact/com.sap.cloud/neo-java-web-api/1.105.21/com/sap/cloud/account/TenantContext.html

import java.util.concurrent.Callable;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import com.sap.cloud.account.TenantContext;
public class ContextAwareCallable<T> implements Callable<T>
{
    private Callable<T> task;
    private TenantContext tenantContext;
    private String tenantId;
    private RequestAttributes context;
    public ContextAwareCallable(Callable<T> task, TenantContext tenantContext, RequestAttributes context)
    {
        this.task = task;
        this.tenantContext = tenantContext;
        this.context = context;
        if (tenantContext != null)
        {
            this.tenantId = tenantContext.getTenant().getId();
        }
    }
    @Override
    public T call() throws Exception
    {
        if (context != null)
        {
            RequestContextHolder.setRequestAttributes(context);
        }
        if (tenantId != null)
        {
            return tenantContext.execute(tenantId, task);
        }
        return task.call();
    }
}

最新更新