Sharding-Jdbc (10)源码分析 jdbc-3

496 阅读2分钟

executor 分析

无论是分库分表、还是读写分离,一个SQL在DB上执行前都需要经过特定规则运算获得运行的目标库表信息。路由引擎的职责定位就是计算SQL应该在哪个数据库、哪个表上执行。 前者结果会传给后续执行引擎,然后根据其数据库标识获取对应的数据库连接;后者结果则会传给改写引擎在SQL执行前进行表名的改写,即替换为正确的物理表名 。

首先可以看一下shardingsphere.infra.executor.sql这个包下的一些架构

shardingsphere.infra.executor.sql
    ├── executor.sql.context		
    ├── executor.sql.excute		
    ├── executor.sql.hook	
    ├── executor.sql.log	
    ├── executor.sql.prepare	
    │       ├── dirver	
    │       │        ├── jdbc
    │       │        ├── DriverExecutionPrepareEngine
    │       │        ├── ExecutorDriverManager
    │       │        ├── SQLExecutionUnitBuilder
    │       │        ├── StorageResourceOption
    │       ├── raw	
    │       ├── AbstractExecutionPrepareEngine
    │       └── ExecutionPrepareEngine 
    │       └── ExecutionPrepareDecorator 
        ....

这个包下面的具体类太多,我们这里还是具体分析一个链路:昨天聊到的ShardingSpherePreparedStatement是整个ShardingSphere的基础类,在ShardingSphere启动时候,会调用ShardingSpherePreparedStatement的 初始化方法。把DataSource里面初始化好的上下文比如MetaDataContexts载入到ShardingSpherePreparedStatement 里面的各种配置参数和上下文中。调用execute方法时,会首先

executionContext = createExecutionContext();
if (metaDataContexts.getDefaultMetaData().getRuleMetaData().getRules().stream().anyMatch(each -> each instanceof RawExecutionRule)) {
    // TODO process getStatement
    Collection<ExecuteResult> executeResults = rawExecutor.execute(createRawExecutionGroups(), new RawSQLExecutorCallback());
    return executeResults.iterator().next() instanceof QueryResult;
}

在createExecutionContext创建的过程中 首先调用SQLCheckEngine.check检查sql

private ExecutionContext createExecutionContext() {
    LogicSQL logicSQL = createLogicSQL();
    SQLCheckEngine.check(logicSQL.getSqlStatementContext().getSqlStatement(), logicSQL.getParameters(), metaDataContexts.getDefaultMetaData(), metaDataContexts.getAuthentication());
    ExecutionContext result = kernelProcessor.generateExecutionContext(logicSQL, metaDataContexts.getDefaultMetaData(), metaDataContexts.getProps());
    findGeneratedKey(result).ifPresent(generatedKey -> generatedValues.addAll(generatedKey.getGeneratedValues()));
    return result;
}

SQLChecker是 org.apache.shardingsphere.infra.audit 包中基本工具

/**
 * SQL checker.
 * 
 */
public interface SQLChecker extends OrderedSPI {
    
 
    SQLCheckType getSQLCheckType();
    

    SQLCheckResult check(SQLStatement sqlStatement, List<Object> parameters, ShardingSphereMetaData metaData, Authentication auth);
}

检查sql格式以后调用了generateExecutionContext方法创建了ExecutionContext

public ExecutionContext generateExecutionContext(final LogicSQL logicSQL, final ShardingSphereMetaData metaData, final ConfigurationProperties props) {
    RouteContext routeContext = route(logicSQL, metaData, props);
    SQLRewriteResult rewriteResult = rewrite(logicSQL, metaData, props, routeContext);
    ExecutionContext result = createExecutionContext(logicSQL, metaData, routeContext, rewriteResult);
    logSQL(logicSQL, props, result);
    return result;

}

在创建ExecutionContext中第一步是调用route路由

private RouteContext route路由(final LogicSQL logicSQL, final ShardingSphereMetaData metaData, final ConfigurationProperties props) {
    return new SQLRouteEngine(metaData.getRuleMetaData().getRules(), props).route(logicSQL, metaData);
}

SQLRouteEngine 路由引擎

public final class SQLRouteEngine {
    
    private final Collection<ShardingSphereRule> rules;
    
    private final ConfigurationProperties props;
    
    private final SPIRoutingHook routingHook = new SPIRoutingHook();
    

    public RouteContext route(final LogicSQL logicSQL, final ShardingSphereMetaData metaData) {
        routingHook.start(logicSQL.getSql());
        try {
            SQLRouteExecutor executor = isNeedAllSchemas(logicSQL.getSqlStatementContext().getSqlStatement()) ? new AllSQLRouteExecutor() : new PartialSQLRouteExecutor(rules, props);
            RouteContext result = executor.route(logicSQL, metaData);
            routingHook.finishSuccess(result, metaData.getSchema());
            return result;
            // CHECKSTYLE:OFF
        } catch (final Exception ex) {
            // CHECKSTYLE:ON
            routingHook.finishFailure(ex);
            throw ex;
        }
    }
    
    // TODO use dynamic config to judge UnconfiguredSchema
    private boolean isNeedAllSchemas(final SQLStatement sqlStatement) {
        return sqlStatement instanceof MySQLShowTablesStatement;
    }
}

路由引擎第一步调用了

public final class SPIRoutingHook implements RoutingHook {

RoutingHook注册在ShardingSphereServiceLoader

ShardingSphereServiceLoader.register(RoutingHook.class)
 */
public final class AllSQLRouteExecutor implements SQLRouteExecutor {
    
    @Override
    public RouteContext route(final LogicSQL logicSQL, final ShardingSphereMetaData metaData) {
        RouteContext result = new RouteContext();
        for (String each : metaData.getResource().getDataSources().keySet()) {
            result.getRouteUnits().add(new RouteUnit(new RouteMapper(each, each), Collections.emptyList()));
        }
        return result;
    }
}

AllSQLRouteExecutor 实现了SQLRouteExecutor 中调用

result.getRouteUnits().add(new RouteUnit(new RouteMapper(each, each), Collections.emptyList()));

RouteUnit的实现

public final class RouteUnit {
    
    private final RouteMapper dataSourceMapper;
    
    private final Collection<RouteMapper> tableMappers;
    
    /**
     * Get logic table names.
     *
     * @return  logic table names
     */
    public Set<String> getLogicTableNames() {
        return tableMappers.stream().map(RouteMapper::getLogicName).collect(Collectors.toCollection(() -> new HashSet<>(tableMappers.size(), 1)));
    }
    
    /**
     * Get actual table names.
     *
     * @param logicTableName logic table name
     * @return actual table names
     */
    public Set<String> getActualTableNames(final String logicTableName) {
        return tableMappers.stream().filter(each -> logicTableName.equalsIgnoreCase(each.getLogicName())).map(RouteMapper::getActualName).collect(Collectors.toSet());
    }
    
    /**
     * Find table mapper.
     *
     * @param logicDataSourceName logic data source name
     * @param actualTableName actual table name
     * @return table mapper
     */
    public Optional<RouteMapper> findTableMapper(final String logicDataSourceName, final String actualTableName) {
        for (RouteMapper each : tableMappers) {
            if (logicDataSourceName.equalsIgnoreCase(dataSourceMapper.getLogicName()) && actualTableName.equalsIgnoreCase(each.getActualName())) {
                return Optional.of(each);
            }
        }
        return Optional.empty();
    }
}

RouteMapper的实现

public final class RouteMapper {
    
    private final String logicName;
    
    private final String actualName;
}