第九章 shardingsphere源码-SQL结果返回


上一篇文章说了shardingsphereSQL执行的大体源码流程,可以快速帮助新了解的人员。实际上最终依旧由JDBC的JAR进行请求数据库。

一、分析

1.准备

代理的依旧是mysql数据库,假设在t1表中将 vc_source已经进行加密了,此时按照之前的文章,我们在访问原库的时候已经变成密文,那么连接到代理库的时候查询的是明文。那么再查询出结果的时候到底在代理中干了些什么?
在这里插入图片描述

2.源码分析

从上篇开始,真正将这个sql发送给数据库,依旧由各自的驱动jar进行执行的,部分调用链路
JDBCExecutor.execute => JDBCExecutorCallback.execute => ProxyJDBCExecutorCallback.executeSQL =>
ProxyStatementExecutorCallback.execute => 各自的驱动执行

 private List<ExecuteResult> doExecute(final ExecutionContext executionContext) throws SQLException {
        String databaseName = backendConnection.getConnectionSession().getDatabaseName();
        // 加密规则 包含表的对应关系 字段对应关系 那个表的那个字段需要加解密,用的那个加密器; 包含加密器,map《加密器名称,加密器实例》;
        Collection<ShardingSphereRule> rules = ProxyContext.getInstance().getContextManager().getMetaDataContexts().getMetaData().getDatabase(databaseName).getRuleMetaData().getRules();
        int maxConnectionsSizePerQuery = ProxyContext.getInstance()
                .getContextManager().getMetaDataContexts().getMetaData().getProps().<Integer>getValue(ConfigurationPropertyKey.MAX_CONNECTIONS_SIZE_PER_QUERY);
        boolean isReturnGeneratedKeys = executionContext.getSqlStatementContext().getSqlStatement() instanceof MySQLInsertStatement;
        return hasRawExecutionRule(rules) ? rawExecute(executionContext, rules, maxConnectionsSizePerQuery) // 一般走 useDriverToExecute,驱动jar执行
                : useDriverToExecute(executionContext, rules, maxConnectionsSizePerQuery, isReturnGeneratedKeys, SQLExecutorExceptionHandler.isExceptionThrown());
    }

ProxyJDBCExecutorCallback.executeSQL

ResultSet resultSet = statement.getResultSet(); 我们发现了一个地方,完全类似于jdbc连接数据库时 将结果封装在ResultSet 中

    private ExecuteResult executeSQL(final String sql, final Statement statement, final ConnectionMode connectionMode, final boolean withMetaData, final DatabaseType storageType) throws SQLException {
        databaseCommunicationEngine.add(statement);
        if (execute(sql, statement, isReturnGeneratedKeys)) {
            ResultSet resultSet = statement.getResultSet();
            databaseCommunicationEngine.add(resultSet);
            return createQueryResult(resultSet, connectionMode, storageType);
        }
        return new UpdateResult(statement.getUpdateCount(), isReturnGeneratedKeys ? getGeneratedKey(statement) : 0L);
    }

在这里插入图片描述

最后依旧到统一流转的地方CommandExecutorTask.executeCommand() ,咱们之前sql真正准备执行的入口再这Collection<DatabasePacket<?>> responsePackets = commandExecutor.execute();

private boolean executeCommand(final ChannelHandlerContext context, final PacketPayload payload) throws SQLException {
        CommandExecuteEngine commandExecuteEngine = databaseProtocolFrontendEngine.getCommandExecuteEngine(); 
        CommandPacketType type = commandExecuteEngine.getCommandPacketType(payload);
        CommandPacket commandPacket = commandExecuteEngine.getCommandPacket(payload, type, connectionSession);
        CommandExecutor commandExecutor = commandExecuteEngine.getCommandExecutor(type, commandPacket, connectionSession);
        try {
            Collection<DatabasePacket<?>> responsePackets = commandExecutor.execute();
            if (responsePackets.isEmpty()) {
                return false;
            }
            responsePackets.forEach(context::write);
            if (commandExecutor instanceof QueryCommandExecutor) {
                commandExecuteEngine.writeQueryData(context, connectionSession.getBackendConnection(), (QueryCommandExecutor) commandExecutor, responsePackets.size());
            }
            return true;
        } catch (final SQLException | ShardingSphereSQLException | SQLDialectException ex) {
            databaseProtocolFrontendEngine.handleException(connectionSession, ex);
            throw ex;
        } finally {
            commandExecutor.close();
        }
    }

commandExecuteEngine.writeQueryData()方法中表名了将一行行的数据重新组装成报文进行返回,再获取一行数据的时候,会进行一个判断,判断是否需要进行解密,如果需要解密,先进行解密,具体步骤:
MySQLCommandExecuteEngine.writeQueryData =>
MySQLComQueryPacketExecutor.getQueryRowPacket =>
DatabaseCommunicationEngine.getRowData =>
EncryptMergedResult.getValue => 解密算法decrypt()

 public Object getValue(final int columnIndex, final Class<?> type) throws SQLException {
        Optional<EncryptContext> encryptContext = metaData.findEncryptContext(columnIndex);
        if (!encryptContext.isPresent() || !metaData.isQueryWithCipherColumn(encryptContext.get().getTableName(), encryptContext.get().getColumnName())) {
            return mergedResult.getValue(columnIndex, type); // 是非加密字段,返回
        }
        Optional<StandardEncryptAlgorithm> encryptAlgorithm = metaData.findEncryptor(encryptContext.get().getTableName(), encryptContext.get().getColumnName());
        if (!encryptAlgorithm.isPresent()) {
            return mergedResult.getValue(columnIndex, type);
        }
        Object cipherValue = mergedResult.getValue(columnIndex, Object.class); // 获取加密字段的值 ,会到 JDBCStreamQueryResult 先判断类型,decrypt解密
        return null == cipherValue ? null : encryptAlgorithm.get().decrypt(cipherValue, encryptContext.get());
    }

二、总结

本篇主要介绍了,sql执行返回的结果依旧遵守JDBC,以及到底在什么地方判断进行解密操作,对于整个shardingsphere代码来说比较复杂,复杂的点主要在里面封装了很多层上下文调用链之长,实现类之多 ,非常不好排查,代码量也非常大,再制作镜像时有500M,但是实际上如果理清楚了整个流程那么还是比较简单得,本栏目总共使用了9篇来帮助你理清它们之间的关键的流程。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值