shardingsphere通过注解实现分库

1、前言

    上篇文章大概讲了下shardingSphere中的sharding-jdbc的实现原理(https://www.cnblogs.com/smileIce/p/11131053.html),接下来我们想正对大家使用场景来分析下。

    大家可以先看看shardingSphere的配置方式https://shardingsphere.apache.org/document/current/cn/manual/sharding-jdbc/configuration/config-spring-boot/

    我们可以发现shardingSphere是支持默认的 default-database-strategy或者针对表上的default-database-strategy。

    对于规模不太大的公司来说,针对每个表配置default-database-strategy工作量还是比较大的,一般情况会针对不同的业务使用多个库,一个工程可能会使用多个库的数据(可能有人会说,用微服务不应该有一个项目使用多个库的情况,但是在真实的业务场景确实可能一个项目需要调用多个库,可能为了性能考虑,可能有其它业务方面的考虑)。

    针对该场景可能通过注解的方式更好。用spring  AbstractRoutingDataSource 方式线下如下 https://juejin.im/post/5a927d23f265da4e7e10d740,但是该方式针对事务就无能为力(针对分布式事务后面会讲到)。那么sharding-sphere是否能实现通过配置一个注解就能简单的切换数据源昵,当然可以了,接下来看看怎么实现。

 

2、实现annotation的数据源原理。

    上一节提到HintManager,但没有细讲,这节可以讲讲HintManager

    

public final class HintManager implements AutoCloseable {
    
    private static final ThreadLocal<HintManager> HINT_MANAGER_HOLDER = new ThreadLocal<>();
    
    private final Multimap<String, Comparable<?>> databaseShardingValues = HashMultimap.create();
    
    private final Multimap<String, Comparable<?>> tableShardingValues = HashMultimap.create();
    
    private boolean databaseShardingOnly;
    
    private boolean masterRouteOnly;
    
    /**
     * Get a new instance for {@code HintManager}.
     *
     * @return  {@code HintManager} instance
     */
    public static HintManager getInstance() {
        Preconditions.checkState(null == HINT_MANAGER_HOLDER.get(), "Hint has previous value, please clear first.");
        HintManager result = new HintManager();
        HINT_MANAGER_HOLDER.set(result);
        return result;
    }
    
    /**
     * Set sharding value for database sharding only.
     *
     * <p>The sharding operator is {@code =}</p>
     *
     * @param value sharding value
     */
    public void setDatabaseShardingValue(final Comparable<?> value) {
        databaseShardingValues.clear();
        databaseShardingValues.put("", value);
        databaseShardingOnly = true;
    }
    
    /**
     * Add sharding value for database.
     *
     * <p>The sharding operator is {@code =}</p>
     *
     * @param logicTable logic table name
     * @param value sharding value
     */
    public void addDatabaseShardingValue(final String logicTable, final Comparable<?> value) {
        databaseShardingValues.put(logicTable, value);
        databaseShardingOnly = false;
    }
    
    /**
     * Add sharding value for table.
     *
     * <p>The sharding operator is {@code =}</p>
     *
     * @param logicTable logic table name
     * @param value sharding value
     */
    public void addTableShardingValue(final String logicTable, final Comparable<?> value) {
        tableShardingValues.put(logicTable, value);
        databaseShardingOnly = false;
    }
    
    /**
     * Get database sharding values.
     *
     * @return database sharding values
     */
    public static Collection<Comparable<?>> getDatabaseShardingValues() {
        return getDatabaseShardingValues("");
    }

    /**
     * Get database sharding values.
     *
     * @param logicTable logic table
     * @return database sharding values
     */
    public static Collection<Comparable<?>> getDatabaseShardingValues(final String logicTable) {
        return null == HINT_MANAGER_HOLDER.get() ? Collections.<Comparable<?>>emptyList() : HINT_MANAGER_HOLDER.get().databaseShardingValues.get(logicTable);
    }
    
    /**
     * Get table sharding values.
     *
     * @param logicTable logic table name
     * @return table sharding values
     */
    public static Collection<Comparable<?>> getTableShardingValues(final String logicTable) {
        return null == HINT_MANAGER_HOLDER.get() ? Collections.<Comparable<?>>emptyList() : HINT_MANAGER_HOLDER.get().tableShardingValues.get(logicTable);
    }
    
    /**
     * Judge whether database sharding only.
     *
     * @return database sharding or not
     */
    public static boolean isDatabaseShardingOnly() {
        return null != HINT_MANAGER_HOLDER.get() && HINT_MANAGER_HOLDER.get().databaseShardingOnly;
    }
    
    /**
     * Set database operation force route to master database only.
     */
    public void setMasterRouteOnly() {
        masterRouteOnly = true;
    }
    
    /**
     * Judge whether route to master database only or not.
     *
     * @return route to master database only or not
     */
    public static boolean isMasterRouteOnly() {
        return null != HINT_MANAGER_HOLDER.get() && HINT_MANAGER_HOLDER.get().masterRouteOnly;
    }
    
    /**
     * Clear threadlocal for hint manager.
     */
    public static void clear() {
        HINT_MANAGER_HOLDER.remove();
    }
    
    @Override
    public void close() {
        HintManager.clear();
    }
}

   通过源码我可以发现HintManager 维护了一个线程变量 HINT_MANAGER_HOLDER,我们这里就是通过该变量实现的注解分库。

还记得databaseShardingOnly这个变量吗?这个变量决定了是使用DatabaseHintSQLRouter还是使用ParsingSQLRouter

public final class ShardingRouterFactory {
    
    /**
     * Create new instance of sharding router.
     * 
     * @param shardingRule sharding rule
     * @param shardingMetaData sharding meta data
     * @param databaseType database type
     * @param showSQL show SQL or not
     * @return sharding router instance
     */
    public static ShardingRouter newInstance(final ShardingRule shardingRule, final ShardingMetaData shardingMetaData, final DatabaseType databaseType, final boolean showSQL) {
        return HintManagerHolder.isDatabaseShardingOnly() ? new DatabaseHintSQLRouter(shardingRule, showSQL) : new ParsingSQLRouter(shardingRule, shardingMetaData, databaseType, showSQL);
    }
}

         我们只要通过调用HintManager 的setDatabaseShardingValue,我们就可以设置为只进行分库并把分库的数据Id 填入其中

      

   public void setDatabaseShardingValue(final Comparable<?> value) {

        databaseShardingValues.clear();
        databaseShardingValues.put("", value);
        databaseShardingOnly = true;
    }

 

  3、实现逻辑

定义数据库类型

public enum DataSourceType {

    DEFAULT("default", "default"),
    DATASOURCE_1("datasource1", "datasource1"),
    DATASOURCE_2("datasource1", "datasource2"),

;
private String name; private String identity; DataSourceType(String name, String identity) { this.name = name; this.identity = identity; } public String getName() { return name; } public String getIdentity() { return identity; } public static DataSourceType getDataSourceTypeByName(String name) { for (DataSourceType dataSourceType : DataSourceType.values()) { if (dataSourceType.name.equals(name)) { return dataSourceType; } } throw new RuntimeException("db is not exist." + name); } }

 

 

定义注解类

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {

    DataSourceType value();

    boolean isDatabaseShardingOnly() default true;
}

 

定义AOP拦截器

@Aspect
@Order(-10)
@Component

public class DynamicDataSourceAspect {


    private static final Logger LOGGER = LogManager.getLogger(DynamicDataSourceAspect.class);


    @Before("within(@com.newretail.datasource.TargetDataSource *) || @annotation(com.newretail.datasource.TargetDataSource)")
    public void changeDataSource(JoinPoint point) {
        MethodSignature joinPointObject = (MethodSignature) point.getSignature();
        TargetDataSource targetDataSource = null;
        if (joinPointObject.getDeclaringType().isAnnotationPresent(TargetDataSource.class)) {
            targetDataSource = (TargetDataSource) joinPointObject.getDeclaringType().getAnnotation(TargetDataSource.class);
        }
        Method method = joinPointObject.getMethod();
        if (method.isAnnotationPresent(TargetDataSource.class)) {
            targetDataSource = method.getAnnotation(TargetDataSource.class);
        }
        if (targetDataSource.isDatabaseShardingOnly()) {
            //获取当前的指定的数据源;
            DataSourceType dsId = targetDataSource.value();
            HintManager.getInstance().setDatabaseShardingValue(dsId.getIdentity());
        }

    }


    @After(value = "@annotation(com.newretail.datasource.TargetDataSource)")
    public void restoreDataSource(JoinPoint point) {

        //方法执行完毕之后,销毁当前数据源信息,进行垃圾回收。
        HintManagerHolder.clear();

    }


}

 

实现HintShardingAlgorithm算法。

public class AnnotationHintShardingAlgorithm implements HintShardingAlgorithm {


    private final static Logger logger = LoggerFactory.getLogger(AnnotationHintShardingAlgorithm.class);

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, ShardingValue shardingValue) {
        if (shardingValue instanceof ListShardingValue && !CollectionUtils.isEmpty(((ListShardingValue) shardingValue).getValues())) {
            logger.info("---------------------"+((ListShardingValue) shardingValue).getValues());
            return availableTargetNames.stream().filter(availableTargetName ->
                    ((ListShardingValue) shardingValue).getValues().contains(availableTargetName)).collect(Collectors.toList());
        }
        return availableTargetNames;

    }
}

 

配置如下

# 数据源 load.datasources 可以指定需要加载的数据源,没配置加载所有的
sharding:
  jdbc:
    datasource:
      names:
        datasource1,datasource2
datasource1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbcUrl:

username:
        password: 
      datasource2:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbcUrl: 
        username: 
        password: 
    config:
      sharding:
        defaultDatabaseStrategy:
          hint:
            algorithmClassName: com.newretail.datasource.AnnotationHintShardingAlgorithm
      props:
        sql:
          show: false

 

 

public interface DeliveryTargetMapper {

    @TargetDataSource(value = DataSourceType.DATASOURCE_1)
    List<City> getCityList();

}

 

        

   

 

转载于:https://www.cnblogs.com/smileIce/p/11131868.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值