java—添加新数据源,但仍指向defaulttargetdatasource(动态多租户)

wn9m85ua  于 2021-07-24  发布在  Java
关注(0)|答案(0)|浏览(239)

我有一个spring boot后端,其中有一个主数据库,保存租户数据库的连接字符串,启动应用程序时,我调用主数据库以获取所有租户的连接信息,并将它们放入目标数据源中,每个符号都正常工作,我可以进行多个api调用,响应将根据我正在处理的租户而定,问题是当我向主数据库添加新租户时,我不想重新启动后端,因此我对数据源bean进行了另一个调用,以便它得到刷新,它会被刷新,查找键也会更改,但我从默认数据源而不是刚才添加的数据源获取数据。
我主要是受到这篇文章的启发,下面是我所拥有的一些代码
这是租户代码的contextholder,我将其用作数据源的查找键

public final class TenantContextHolder {

    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    public static final List<String> supportedTenants = new ArrayList<>();

    private TenantContextHolder () {
        super();
    }

    public static void setTenantContext(String tenantCode) {
        threadLocal.set(tenantCode);
    }

    public static String getTenantContext() {
        return threadLocal.get();
    }
}

这是一个简单的拦截器,我用它来检查租户是否已经存在于支持的租户列表中,这样我就可以再次调用datasourcebean,因为它可能刚刚添加到masterdb中

public class DataSourceInterceptor extends HandlerInterceptorAdapter {
    private final DBConfig dbConfig;

   @Autowired
    public DataSourceInterceptor(DBConfig dbConfig) {
        this.dbConfig = dbConfig;
    }

   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
           throws Exception {
        if (request.getHeader(TENANT_HEADER_NAME) != null) {
            try {
                String tenantCode= request.getHeader(TENANT_HEADER_NAME);
                TenantContextHolder.setTenantContext(tenantCode);
                if (TenantContextHolder.supportedTenants.contains(tenantCode)) {
                    return super.preHandle(request, response, handler);
                } else {
                    log.error("tenant provided {} not supported, trying to refresh dataSource list ...", request.getHeader(TENANT_HEADER_NAME));
                    dbConfig.dataSource();
                    if (TenantContextHolder.supportedTenants.contains(tenantCode)) {
                        return super.preHandle(request, response, handler);
                    }
                    log.error("Tenant provided {} is still not supported, can't proceed to datasource",
                            request.getHeader(TENANT_HEADER_NAME));
                    throw new HttpCustomException(SOME_CODE);
                }
            } catch (IllegalArgumentException e) {
                log.error("Tenant provided not valid, can't proceed to datasource", e);
                throw new HttpCustomException(SOME_CODE);
            }
        } else {
            log.error("No tenant provided, can't proceed to datasource");
            throw new HttpCustomException(SOME_CODE); }
        }
    }

这是我的数据源bean

@Bean()
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public DataSource dataSource() {
        TenantContextHolder.supportedTenants.clear();

        List<DataSourceInfo> dataSourcesInfo = getTenantDataSourceInfoFromMasterDB();
        TenantContextHolder.supportedTenants.addAll(dataSourcesInfo.stream().map(DataSourceInfo::getTenantCode).collect(Collectors.toList()));
        CustomRoutingDataSource customDataSource = new CustomRoutingDataSource();

        customDataSource.setTargetDataSources(tenantsDataSource);
        customDataSource.setDefaultTargetDataSource(tenantsDataSource.get(dataSourcesInfo.get(0).getTenantCode()));//i'm aware of the 0 here it's just the default datasource, for when i don't have a lookup key or when it's not found
        return customDataSource;
    }

最后,这是保存连接信息的datasourceinfo实体的结构

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class DataSourceInfo {
    Integer id;
    private String url;
    private String username;
    private String password;
    private String driverClassName;
    private String tenantCode;
    ...
}

正如我所说,每当我重新启动应用程序时,一切都很好,但是当我在主数据库中添加一个新租户,并为此租户进行api调用时,我会得到一个日志,说明它不存在,然后它会再次调用bean,它会将此新租户添加到目标数据源中,但响应总是我设置为默认值的第一个租户,没有缓存,所以肯定不是这样,即使有缓存,每个租户的缓存都是不同的

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题