0
点赞
收藏
分享

微信扫一扫

maven基础学习

敬亭阁主 2023-09-05 阅读 22

前言

grpc + springboot + mybatis-plus 动态配置数据源

一. 源码解析

1.1 项目初始化

项目初始化的时候会调用com.baomidou.dynamic.datasource.DynamicRoutingDataSource对象的addDataSource方法添加数据源,数据源存进dataSourceMap中。

请添加图片描述

1.2 接口请求时候

进行数据操作时,方法会被com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationInterceptor拦截

请添加图片描述

intercept 方法中,会解析方法上的 @DS 注解,获取注解中指定的数据源名称。然后,它会调用 DynamicDataSourceContextHolder 类的 setDataSource 方法来切换数据源。

1692177342027

进行数据操作时,会调用org.springframework.jdbc.datasource.getConnection()方法;getConnection()方法最终调用了com.baomidou.dynamic.datasource.ds.AbstractRoutingDataSource的getConnection()方法

1692178232591

上面的determineDataSource是由子类com.baomidou.dynamic.datasource.DynamicRoutingDataSource实现,可以看到DynamicRoutingDataSource从DynamicDataSourceContextHolder获取数据源名称

1692179899274

请添加图片描述

此时的datasource已经切换成了我们需要的数据源

4、数据操作完成后,方法返回第二步中的拦截器,执行DynamicDataSourceContextHolder.poll();清除掉此次Threadlocal中的数据源,避免影响后续数据操作。

二. web应用

参考文档

  1. 引入依赖dynamic-datasource-spring-boot-starter
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  <version>${version}</version>
</dependency>
  1. 配置数据源
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为 master
      primary: master
      datasource:
        # 主库数据源
        master:
          driverClassName: org.postgresql.Driver
          url: jdbc:postgresql://xx.xx.xx.xx:5432/hwaf?currentSchema=common
          username: root
          password: 123456
        # 主库数据源
        master1:
          driverClassName: org.postgresql.Driver
          url: jdbc:postgresql://xx.xx.xx.xx:5432/hwaf?currentSchema=common
          username: root
          password: 123456
  1. 使用 @DS 切换数据源。

@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解

注解结果
没有@DS默认数据源
@DS(“dsName”)dsName可以为组名也可以为具体某个库的名称
@Service
@DS("slave")
public class UserServiceImpl implements UserService {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  public List selectAll() {
    return  jdbcTemplate.queryForList("select * from user");
  }
  
  @Override
  @DS("slave_1")
  public List selectByCondition() {
    return  jdbcTemplate.queryForList("select * from user where age >10");
  }
}
  1. 根据传递的参数动态切换数据源

您可以按照以下步骤使用mybatis-plush的dynamic-datasource-spring-boot-starter来根据前端传递的参数动态切换数据源:

在您的Spring Boot应用程序中添加dynamic-datasource-spring-boot-starter依赖项。

在应用程序配置文件中定义数据源配置信息,包括主数据源和其他数据源。

在需要动态切换数据源的方法上使用@DS注释指定数据源,例如:

@DS("#dataSourceName")
public List<User> getUserList(String dataSourceName) {
    // 方法实现
}

在这个例子中,#dataSourceName表示从方法参数中获取数据源名称,并将其用作数据源选择策略。

当调用getUserList方法时,将要使用的数据源名称作为参数传递。例如:

List<User> userList = userService.getUserList("dataSource2");

这将使用名为dataSource2的数据源来执行getUserList方法。

三. grpc应用程序

我想实现的需求是根据传递的参数动态切换数据源,可是我使用的是grpc,没办法像web一样在接口去传递参数

1692236885912

为了改动最小,我打算使用拦截器的方式获取传递的值

@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Component
public class DataSourceInterceptor implements ServerInterceptor {

    private static final Metadata.Key<String> DATA_SOURCE_KEY =
        Metadata.Key.of("data-source", Metadata.ASCII_STRING_MARSHALLER);

    @Override
    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
        ServerCall<ReqT, RespT> call,
        Metadata headers,
        ServerCallHandler<ReqT, RespT> next) {
        String dataSource = headers.get(DATA_SOURCE_KEY);
        if (dataSource != null) {

           DynamicDataSourceContextHolder.setDataSource(dataSource);
        }

        return Contexts.interceptCall(Context.current(), call, headers, next);

    }
}


然后起一个上下文线程去存储这个值

public class DynamicDataSourceContextHolder {

    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    public static void setDataSource(String dataSource) {
        CONTEXT_HOLDER.set(dataSource);
    }

    public static String getDataSource() {
        return CONTEXT_HOLDER.get();
    }

    public static void clearDataSource() {

        CONTEXT_HOLDER.remove();
    }
}

然后重写DsProcessor对象中的doDetermineDatasource,目的是为了能够获取你存储在DynamicDataSourceContextHolder的数据源参数

请添加图片描述

@Component
public class DsMetaProcessor extends DsProcessor {
    private static final String DATE_PREFIX = "#dataSource";

    public DsMetaProcessor() {
    }


    @Override
    public boolean matches(String key) {
        return key.startsWith("#dataSource");
    }

    @Override
    public String doDetermineDatasource(MethodInvocation invocation, String key) {

        try {
            return DynamicDataSourceContextHolder.getDataSource();
        } finally {
            // 在执行后清理数据源
            DynamicDataSourceContextHolder.clearDataSource();
        }

    }
}

最后在方法接口上通过以下方式注入就可以了

1692237675155

举报

相关推荐

0 条评论