整体架构
- Proxy最主要是要连接CLIENT和MYSQL,通信至关重要,以Netty作为通信组件,封装MYSQL协议,主要解决粘包和拆包问题
- MYSQL协议层,解析mysql协议,主要负责将Netty接收的TCP包,按照MYSQL协议,解析成SQL语句;还有就是将处理完合并后的SQL语句解析成MYSQL协议发送到指定的地方
- MYSQL协议解析成SQL语句之后,使用SqlParser组件将MYSQL解析成语法树,获取SQL的各个部分的信息
- 然后使用SqlRouter组件读取配置的分库分表或者其他策略,重写SQL语句,并且选择路由的节点
- 然后把这些SQL放到SqlExecutor组件中,选择合适的执行策略,例如线程池,栅栏,信号量等
- 最后再通过通信层将这些需要执行的SQL发送到MYSQL
- mysql处理完成之后,返回的数据通过通信层接收,SqlExecutor组件执行,最后的结果可能需要通过ResultMerger组件进行合并之后,再通过通信层发送回客户端
MYSQL 协议
mysql协议基础packet
- payload length 代表此packet的装的数据是多少byte,最大2的24次方=16M
- sequence_id packet的顺序,一旦顺序乱了,mysql客户端就会报错,这个尤其在ResultMerger进行协议重组的过程中需要注意,这个当初我在这上面栽了很多跟头
- payload 真实的传输数据
Connection 协议
客户端和Mysql本身就是一个握手协议,只有当Mysql发送OK协议之后,Client才能发送SQL语句
- 当我只想限制发送到MYSQL的请求量时,此时就不需要解析SQL语句,只需要在转发的过程中添加一些限制;
- 可以使用信号量来控制去往MYSQL的流量,可以动态控制的MYSQL的负载情况
- 可以屏蔽掉一些危险SQL
注意:连接数并没有降低
- 为了降低连接数,自己必须实现一个连接池,注意连接池中的连接的保活策略,MYSQL默认每隔8小时会自动kill掉连接
- 连接池中加入每一个连接之前都要先进行握手协议
- 对于Client,PROXY要模拟成MYSQL进行通信,对于MYSQL,PROXY要模拟成Client进行通信
- 由PROXY保存所有的MYSQL数据源密码,同样以MYSQL的加密方式保存
- 当执行一个SQL语句时,从连接池中选择一个连接来执行,执行完了之后,立即归还回连接池。这样能极大地降低MYSQL的连接数。
- 如果执行的SQL语句处于事务当中,那么此连接不能释放,必须事务完成之后才能归还回连接池
验证过程
- 公式:Client_Secure_Password= SHA1( password ) XOR SHA1( “20-bytes random data from server” SHA1( SHA1( password ) ) )
- MYSQL存储密码:Server_Password = SHA1( SHA1( password ) )
- 推论:Stash_Password=SHA1( password )
- 验证:SHA1(Stash_Password) =?= Server_Password
- ”20-bytes random data from server”,这个是PROXY生成发送给客户端的数据
- Server_Password,这个是根据密码生成,并配置到PROXY中的
- Client_Secure_Password,这个数据是,mysql客户端根据20bit 随机数生成的,然后发送到PROXY
MYSQL Query协议
- column definitions 包括 field-count, 多个field,最后会有一个 EOF,表示结束
- row definitions 包括多个row数据,最后会有一个 EOF,表示结束
Netty线程模型
线程模型
早期线程模型
- 调度和执行混合,层次不分明
- 并发处理太复杂
- 错误处理太繁琐
现在的线程模型
- 调度和执行分离,只有一个执行池
- session的所有执行动作封装成task
- 相同session的task串行执行
Worker线程池实现
- task继承Runnable和comparable接口
- SchedulerWorker 中有n个连接池,session创建计算对应索引
- 所有的业务逻辑都继承manager接口
- 使用优先级队列,实现task优先级