整體架構
- 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優先級