成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一款優秀數據庫中間件的不完全解析

數據庫 其他數據庫
數據庫中間件可以理解為是一種具有連接池功能,但比連接池更高級的、帶很多附加功能的輔助組件,不僅可以租沖浪板,還可以提供地點推薦、上保險等等各類服務。

[[400698]]

本文內容概述

數據庫中間件有啥用

架構剖析之高屋建瓴

2.1. 整體概述

2.2. 組件圖看架構

細節剖析之一葉知秋

 3.1. 配置加載和bean初始化

3.2. 細說讀寫分離

總結

Part1數據庫中間件有啥用

有一天,你去三亞玩耍,就想玩個沖浪,即時你不差錢,難道還要自己采買快艇、滑板等等裝備來滿足這為數不多的心血來潮么。租一個就行了嘛。這其實就是連接池的作用。

數據庫中間件可以理解為是一種具有連接池功能,但比連接池更高級的、帶很多附加功能的輔助組件,不僅可以租沖浪板,還可以提供地點推薦、上保險等等各類服務。

從網上的資料看,zdal應該算是半開源的,好像是之前開源過,但后續沒有準備維護,然后就刪除了,不過github被fork下來好多,隨便一搜就是一片,當前,只是老的版本。目前螞蟻內部的zdal好像已經更新到zdal5了吧,那咱可就看不到了。

越復雜的系統,數據庫中間件的作用越大。就拿zdal來說,它提供分庫分表,結果集合并,sql解析,數據庫failover動態切換等數據訪問層統一解決方案。下面就一起來看下,其內部實現是怎么樣的。

Part2架構剖析之高屋建瓴

2.1整體概述

如上圖所示,zdal有四個重要的組成部分:

價值體現--客戶端Client包。對外暴露基本操作接口,用于業務層簡單黑盒的操作數據源;業務只和client交互,動態切換/路由等邏輯只需要進行規則配置,相關邏輯由zdal實現。

核心功能--連接管理datasource包。最核心的能力,提供多種類型數據庫的連接管理;不管功能多花哨,最終目的還是為了解決數據庫連接的問題。

關鍵能力--SQL解析parser包。基礎SQL解析能力;解析sql類型、字段名稱、數據庫等等,配合規則進行路由

擴展能力--庫表路由rule包。根據parser解析出的字段確定邏輯庫表和物理庫表。

2.2組件圖看架構

組件圖對整體架構和各組件及相互聯系的理解可以起到很好的幫助。一個簡版的組件圖畫了好久,還有不少錯,不過大概是這么個意思,哎,基本功要丟~

對照上圖可以比較清晰的看到:

Client包對應用層暴露的數據源、負責監聽配置動態變更的監聽組件、負責加載組織各部分的配置組件、負責加載spring bean 和庫表規則的配置組件;

 Client中加載了規則組件,實現邏輯表和數據庫的路由規則。

Client中的庫表配置調用datasource中的數據源管理服務并構建連接池的連接池;

Client中的SqlDispatcher服務調用SQL解析組件實現SQL解析。

Part3細節剖析之一葉知秋

3.1配置加載和bean初始化

大部分情況下,我們使用如mybatis這樣的ORM框架來進行數據庫操作,其實不管是ORM還是其他方式,應用層都需要對數據源進行配置。

所以,client對外暴露了一個符合JDBC標準的datasource數據源,用來滿足應用層ORM等框架配置數據源的要求--ZdalDataSource

如圖片被壓縮看不清,后臺回復<zdal類圖>獲取 

  1. //只提供了一個init方法,這也是spring啟動時時,必須要調用的初始化方法,所有功能,都從這里開始  
  2. public class ZdalDataSource extends AbstractZdalDataSource implements DataSource{  
  3.     public void init() {  
  4.         try {  
  5.             super.initZdalDataSource();  
  6.         } catch (Exception e) {  
  7.             CONFIG_LOGGER.error("...");  
  8.             throw new ZdalClientException(e);  
  9.         }  
  10.     } 

ZdalDataSource#init() 方法即為配置加載的核心入口,init中負責加載spring配置,根據配置初始化數據源,并創建連接池,同時,將邏輯表和物理庫的對應關系都維護起來供后續路由調用。 

  1.     /*父類的init方法*/  
  2. protected void initZdalDataSource() {  
  3.     /*用FileSystemXmlApplicationContext方式加載配置文件中的數據源和規則,轉化成zdalConfig對象*/  
  4.     this.zdalConfig = ZdalConfigurationLoader.getInstance().getZdalConfiguration(appName,dbmode, appDsName, configPath);  
  5.     this.dbConfigType = zdalConfig.getDataSourceConfigType();  
  6.    this.dbType = zdalConfig.getDbType();  
  7.    //初始化數據源  
  8.    this.initDataSources(zdalConfig);  
  9.    this.inited.set(true);  
  10.     }  

從上面的類圖和這里的兩個入口方法大概了解到zdal配置加載的啟動流程。下面我們就來詳細看一下,讀寫分離和分庫分表的規則是怎么被加載,怎么起作用的。

3.2細說讀寫分離

讀寫分離配置的加載

首先,我們需要有數據源的相關配置,如下圖:

圖片此XML配置會在init方法被調用時,被初始化,解析成ZdalConfig類的屬性,ZdalConfig類的主要成員見下面代碼: 

  1. public class ZdalConfig {  
  2.     /** key=dsName;value=DataSourceParameter 所有物理數據源的配置項,比如用戶名,密碼,庫名等 */  
  3.     private Map<String, DataSourceParameter> dataSourceParameters = new ConcurrentHashMap<String, DataSourceParameter>();  
  4.     /** 邏輯數據源和物理數據源的對應關系:key=logicDsName,value=physicDsName */  
  5.     private Map<String, String>              logicPhysicsDsNames  = new ConcurrentHashMap<String, String>();  
  6.     /** 數據源的讀寫規則,比如只讀,或讀寫等配置*/  
  7.     private Map<String, String>              groupRules           = new ConcurrentHashMap<String, String>();  
  8.     /** 異常轉移的數據源規則*/  
  9.     private Map<String, String>              failoverRules        = new ConcurrentHashMap<String, String>();  
  10.     //一份完整的讀寫分離和分庫分表規則配置  
  11.     private AppRule                          appRootRule; 

可以看到,xml中的規則,被解析到xxxRules里。這里以groupRules為例,failover同理。

下一步則是通過解析得到的zdalConfig 來初始化數據源: 

  1. protected final void initDataSources(ZdalConfig zdalConfig) {  
  2.     //DataSourceParameter中存的是數據源參數,如用戶名密碼,最大最小連接數等  
  3.     for (Entry<String, DataSourceParameter> entry : zdalConfig.getDataSourceParameters().entrySet()) {  
  4.         try {  
  5.            //初始化連接池  
  6.            ZDataSource zDataSource = new ZDataSource(/*設置最大最小連接數*/createDataSourceDO(entry.getValue(),zdalConfig.getDbType(), appDsName + "." + entry.getKey())); 
  7.             this.dataSourcesMap.put(entry.getKey(), zDataSource); 
  8.          } catch (Exception e) {  
  9.             //...  
  10.         }  
  11.    }  
  12.   //其他分支略,只看最簡單的分組模式  
  13.   if (dbConfigType.isGroup()) {  
  14.        //讀寫配置賦值  
  15.        this.rwDataSourcePoolConfig = zdalConfig.getGroupRules();  
  16.        //初始化多份讀庫下的負載均衡  
  17.        this.initForLoadBalance(zdalConfig.getDbType());  
  18.   }  
  19.   //注冊監聽:為了滿足動態切換  
  20.   this.initConfigListener();  

initForLoadBalance的方法如下: 

  1. private void initForLoadBalance(DBType dbType) {  
  2.     Map<String, DBSelector> dsSelectors = this.buildRwDbSelectors(this.rwDataSourcePoolConfig);  
  3.     this.runtimeConfigHolder.set(new ZdalRuntime(dsSelectors));  
  4.     this.setDbTypeForDBSelector(dbType);  

可以看到,首先構建出了DB選擇器,然后賦值給了runtimeConfigHolder供運行時獲取。而構建DB選擇器的時候,其實是按讀寫兩個維度,把所有數據源都構建了一遍,即group_r和group_w下都包含5個數據源,只不過各自的權重不一樣: 

  1. //比如按上面的配置寫庫只有一個,但是也會包含全數據源  
  2. group_0_w_0 :< bean:read0DataSource , writeWeight:0>  
  3. group_0_w_1 :< bean:writeDataSource , writeWeight:10>  
  4. group_0_w_2 :< bean:read1DataSource , writeWeight:0>  
  5. group_0_w_3 :< bean:read2DataSource , writeWeight:0>  
  6. group_0_w_4 :< bean:read3DataSource , writeWeight:0>  
  7. //上述就是寫相關的DBSelecter的內容。 

讀寫分離怎么起作用

以delete為例,更新刪除是要操作寫庫的 

  1. public void delete(ZdalDataSource dataSource) {  
  2.     String deleteSql = "delete from test" 
  3.     Connection conn = null 
  4.     PreparedStatement pst = null 
  5.     try {  
  6.        conn = dataSource.getConnection();  
  7.        pst = conn.prepareStatement(deleteSql);  
  8.        pst.execute(); 
  9.     } catch (Exception e) {  
  10.            //...  
  11.     } finally {  
  12.           //資源關閉  
  13.     }  

getConnection會從上文中提到的runtimeConfigHolder中獲取DBSelecter,然后執行execute方法 

  1. public boolean execute() throws SQLException {  
  2.    SqlType sqlType = getSqlType(sql);  
  3.    // SELECT相關的就選擇group_r對應的DBSelecter  
  4.   if (sqlType == SqlType.SELECT || sqlType == SqlType.SELECT_FOR_UPDATE|| sqlType == SqlType.SELECT_FROM_DUAL) {  
  5.     //略  
  6.    return true;  
  7.    //update/delete相關的就選擇group_w對應的DBSelecter  
  8.  } else if (sqlType == SqlType.INSERT || sqlType == SqlType.UPDATE|| sqlType == SqlType.DELETE) {  
  9.       if (super.dbConfigType == DataSourceConfigType.GROUP) {  
  10.           executeUpdate0();  
  11.       } else {  
  12.           executeUpdate();  
  13.      }  
  14.      return false;  
  15.  }  

如果是讀取相關的,那就選_r的DBSelecter,如果是寫相關的,那就選_W的DBSelecter。那么executeUpdate0中是怎么執行區分讀寫數據源的呢,其實就是把這一組的數據源根據權重篩選一遍。 

  1. // WeightRandom#select(int[], java.lang.String[])  
  2. private String select(int[] areaEnds, String[] keys) {  
  3.    //這里的areaEnds數組,是一個累加范圍值數據  
  4.    //比如三個庫權重    10   9   8  
  5.    //那么areaEnds就是  10  19  27 是對每個權重的累加,最后一個值是總和  
  6.    int sum = areaEnds[areaEnds.length - 1];  
  7.    //這樣隨機出來的數,是符合權重分布的  
  8.    int rand = random.nextInt(sum);  
  9.    for (int i = 0; i < areaEnds.length; i++) {  
  10.        if (rand < areaEnds[i]) {  
  11.            return keys[i];  
  12.    }  
  13.    return null;  

Part4總結

本篇文章,把阿里數據庫中間件相關的組件和加載流程進行了總結,就一個最基本的分組讀寫分離的流程,對內部實現進行了闡述。說是解析,其實是提供給大家一種閱讀的思路,畢竟篇幅有限,如果對中間件感興趣的同學,可以fork下代碼,按上述邏輯自己閱讀下。

看源碼時,比如dubbo這些中間件其實是比較容易入手的,因為他們都依托于Spring進行JavaBean的裝載,所有,對Spring容器暴露的那些init、load方法,就是很好的切入點。個人思路,希望對大家有所幫助。 

 

責任編輯:龐桂玉 來源: Coder的技術之路
相關推薦

2017-12-01 05:04:32

數據庫中間件Atlas

2011-08-30 09:35:10

OracleRMAN不完全恢復基于時間恢復

2011-08-30 09:50:22

OracleRMAN不完全恢復基于SCN恢復

2017-11-27 05:36:16

數據庫中間件TDDL

2017-11-27 05:06:42

數據庫中間件cobar

2017-04-13 08:46:41

oracle數據挖掘

2021-12-16 08:21:31

高并發消息中間件

2018-02-24 19:37:33

Java8數據庫中間件

2011-08-10 13:03:58

CJDBC數據庫集群

2017-05-23 18:55:05

mysql-proxy數據庫架構

2017-12-11 13:30:49

Go語言數據庫中間件

2017-07-26 09:41:28

MyCATSQLMongoDB

2024-01-02 16:43:58

2015-09-01 10:42:15

編程規范完全指南

2024-12-06 08:29:29

2021-07-27 05:49:59

MySQL數據庫中間件

2011-08-30 10:02:18

OracleRMAN不完全恢復基于日志序列號恢復

2017-11-27 06:01:37

數據庫中間件中間層

2017-12-01 05:40:56

數據庫中間件join

2021-06-01 06:03:28

Css前端CSS 特效
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: aⅴ色国产 欧美 | 久久高清 | 亚洲第一av | 亚洲国产成人av | 成人在线观看免费 | 精品在线一区 | 日韩视频一区二区三区 | 日日天天 | 日本不卡免费新一二三区 | 日韩亚洲一区二区 | 国产在视频一区二区三区吞精 | 成人性视频免费网站 | 亚洲免费在线观看 | 国产一区二区在线免费观看 | 黄色片在线观看网址 | 色天堂影院 | 精品国产一区二区久久 | www.狠狠操 | 国产目拍亚洲精品99久久精品 | 一区二区三区四区日韩 | 欧美a在线 | 久久久国产一区 | 国产真实精品久久二三区 | 欧美激情精品久久久久久 | 免费一区二区三区在线视频 | 久久精品国产a三级三级三级 | 亚洲区一区二区 | 91麻豆精品国产91久久久久久久久 | 1级毛片 | 国产精品区二区三区日本 | 国产精品久久久久久久久久免费看 | 国产精品成人国产乱 | 久久久精品一区 | a级大片免费观看 | 亚洲最新在线视频 | 精品乱码一区二区三四区 | 天堂久久久久久久 | 日韩在线一区二区三区 | 日韩高清一区二区 | 天天弄天天操 | 精品自拍视频在线观看 |