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 HINT_MANAGER_HOLDER = new ThreadLocal<>();
    
    private final Multimap> databaseShardingValues = HashMultimap.create();
    
    private final Multimap> 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.
     *
     * 

The sharding operator is {@code =}

* *
@param value sharding value */ public void setDatabaseShardingValue(final Comparable value) { databaseShardingValues.clear(); databaseShardingValues.put("", value); databaseShardingOnly = true; } /** * Add sharding value for database. * *

The sharding operator is {@code =}

* *
@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. * *

The sharding operator is {@code =}

* *
@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> getDatabaseShardingValues() { return getDatabaseShardingValues(""); } /** * Get database sharding values. * * @param logicTable logic table * @return database sharding values */ public static Collection> getDatabaseShardingValues(final String logicTable) { return null == HINT_MANAGER_HOLDER.get() ? Collections.>emptyList() : HINT_MANAGER_HOLDER.get().databaseShardingValues.get(logicTable); } /** * Get table sharding values. * * @param logicTable logic table name * @return table sharding values */ public static Collection> getTableShardingValues(final String logicTable) { return null == HINT_MANAGER_HOLDER.get() ? Collections.>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 doSharding(Collection 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 getCityList();

}

 

        

   

 

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

你可能感兴趣的:(shardingsphere通过注解实现分库)