咨詢電(diàn)話(huà):023-88959644    24小(xiǎo)時(shí)服務熱線:400-023-8809
NEWS CENTER ·
新聞動态
關注中技(jì)互聯 關注前沿

Discuz!NT論壇數(shù)據庫讀寫分離方案!具體(tǐ)操作(zuò)步驟。

發表日期:2010-09-21    文章編輯:王東    浏覽次數(shù):36    标簽:

目前在Discuz!NT這個(gè)産品中,數(shù)據庫作(zuò)為(wèi)數(shù)據持久化工具,必定在并發訪問頻繁且負載壓力較大(dà)的情況下成 為(wèi)系統性能的‘瓶頸’。即使使用本地緩存等方式來(lái)解決頻繁訪問數(shù)據庫的問題,但(dàn)仍舊(jiù)會(huì)有(yǒu)大(dà)量的并發請(qǐng)求要訪問動态數(shù)據,雖然 SQL2005及2008以上(shàng)版本中性能不斷提升,查詢計(jì)劃和(hé)存儲過程運行(xíng)得(de)越來(lái)越高(gāo)效,但(dàn)最終還(hái)是 要面臨‘瓶頸’這一問 題。當然這也是許多(duō)大(dà)型網站(zhàn)不斷研究探索各式各樣的方案來(lái)有(yǒu)效降低(dī)數(shù)據訪問負荷的原 因, 其中的‘讀寫分離’方案就是一種被廣泛采用的方案。

      目前在Discuz!NT這個(gè)産品中,數(shù)據庫作(zuò)為(wèi)數(shù)據持久化工具,必定在并發訪問頻繁且負載壓力較大(dà)的情況下成 為(wèi)系統性能的‘瓶頸’。即使使用本地緩存等方式來(lái)解決頻繁訪問數(shù)據庫的問題,但(dàn)仍舊(jiù)會(huì)有(yǒu)大(dà)量的并發請(qǐng)求要訪問動态數(shù)據,雖然 SQL2005及2008以上(shàng)版本中性能不斷提升,查詢計(jì)劃和(hé)存儲過程運行(xíng)得(de)越來(lái)越高(gāo)效,但(dàn)最終還(hái)是 要面臨‘瓶頸’這一問 題。當然這也是許多(duō)大(dà)型網站(zhàn)不斷研究探索各式各樣的方案來(lái)有(yǒu)效降低(dī)數(shù)據訪問負荷的原 因, 其中的‘讀寫分離’方案就是一種被廣泛采用的方案。
      Discuz!NT這個(gè)産品在其企業版中提供了對‘讀寫分離’機制(zhì)的支持,使對CPU及內(nèi)存消耗嚴重的操作(zuò)(CUD)被 分離到一台或幾台性能很(hěn)高(gāo)的機器(qì)上(shàng),而将頻繁讀取的操作(zuò)(select)放到幾台配置較低(dī)的機器(qì)上(shàng),然後通(tōng)過‘事務 發布訂閱機制(zhì)’,實現了在多(duō)個(gè)sqlserver數(shù)據庫之間(jiān)快速高(gāo)效同步數(shù)據,從而達到了将‘讀寫請(qǐng)求’按實際負載 情況進行(xíng)均衡分布的效果。
 
      下面就簡要介紹一下其實現思路。注:有(yǒu)關數(shù)據同步的工具已在sqlserver中自帶了,可(kě)以參考這篇文章
     
      将相應的數(shù)據由Master(主)數(shù)據庫中‘發布’出來(lái),然後使用推送的方式(注:事務發布可(kě)以指定是‘通(tōng)過主 數(shù)據庫推送’ 還(hái)是‘訂閱服務器(qì)去獲取’)發送到訂閱它的數(shù)據庫中,就實現了數(shù)據同步功能。
 
      下面就介紹一下如何通(tōng)過改變既有(yǒu)代碼來(lái)實現在‘幾個(gè)從數(shù)據庫(類似快照)’間(jiān)進行(xíng)讀取數(shù)據的負載均衡。
 
      原有(yǒu)的代碼中因為(wèi)使用了分層機制(zhì),所以我們隻要在‘數(shù)據訪問層’動一下心思就可(kě)以了。在這裏我的一個(gè)設 計(jì)思路就是不改變已有(yǒu)的數(shù)據庫訪問接口(包括參數(shù)等)的前提下,實現底層自動将現有(yǒu)的數(shù)據訪問操作(zuò)進行(xíng)負載 均衡。這樣做(zuò)的好處不用多(duō)說了,同時(shí)也讓這個(gè)負載均衡功能與數(shù)據訪問層相分離,不要耦合的太緊密,同時(shí)如果不曉得(de)底層 的實現原理(lǐ)也可(kě)以隻通(tōng)過一個(gè)開(kāi)關(後面會(huì)介紹),就可(kě)以讓自己的sql語句自動實現動态負載均衡。
    
      說到這裏,我來(lái)對照代碼進一步闡述:
 
      首先就是(Discuz.Data\DbHelper.cs)代碼,主要變動如下(新增方法部分):   
 

代碼
/// <summary>
/// 獲取使用的數(shù)據庫(或快照)鏈接串
/// </summary>

/// <param name="commandText">存儲過程名或都SQL命令文本</param>
/// <returns></returns>
public static string GetRealConnectionString(string commandText)
{
    
if (DbSnapConfigs.GetConfig() != null &&
 DbSnapConfigs.GetConfig().AppDbSnap)
    {
        commandText 
=
 commandText.Trim().ToLower();
        
if (commandText.StartsWith("select"|| ((commandText.StartsWith(BaseConfigs.GetTablePrefix) &&
 UserSnapDatabase(commandText))))
        {
            DbSnapInfo dbSnapInfo 
=
 GetLoadBalanceScheduling.GetConnectDbSnap();

            
if (DbSnapConfigs.GetConfig().RecordeLog && snapLogList.Capacity >
 snapLogList.Count)
                snapLogList.Add(
string.Format("{{'SouceID' : {0}, 'DbconnectString' : '{1}', 'CommandText' : '{2}', 'PostDateTime' : '{3}'}},"
,
                                 dbSnapInfo.SouceID,
                                 dbSnapInfo.DbconnectString,
                                 commandText.Replace(
"'",""
),
                                 Discuz.Common.Utils.GetDateTime()));

            
return
 dbSnapInfo.DbconnectString;
        }
    }

    
return
 ConnectionString;
}

       上(shàng)面的方法将會(huì)對傳入的sql語句進行(xíng)分析,找出其中是CUD操作(zuò)還(hái)是SELECT操作(zuò),來(lái)區(qū)别是讀還(hái)是寫操作(zuò)。而snapLogList列表則是之前所配置的‘事務發布訂閱’模式下的相關‘從數(shù)據庫’(Slave Database)鏈接串的列表,例如(dbsnap.config文件的DbSnapInfoList節點): 

代碼
<?xml version="1.0"?>
<DbSnapAppConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  
<AppDbSnap>true</AppDbSnap>
  
<WriteWaitTime>1</WriteWaitTime>
  
<LoadBalanceScheduling>RoundRobinScheduling</LoadBalanceScheduling> --WeightedRoundRobinScheduling
  
<RecordeLog>false</RecordeLog>

  
<DbSnapInfoList>
    
<DbSnapInfo>
      
<SouceID>1</SouceID>
      
<Enable>true</Enable>
      
<DbconnectString>Data Source=DAIZHJ\DNT_DAIZHJ;User ID=sa;Password=123123;Initial Catalog=dnt_snap;Pooling=true</DbconnectString>
      
<Weight>4</Weight>
    
</DbSnapInfo>
      
<DbSnapInfo>
      
<SouceID>2</SouceID>
      
<Enable>true</Enable>
      
<DbconnectString>Data Source=DAIZHJ-PC\2222;User ID=sa;Password=123;Initial Catalog=tabletest;Pooling=true</DbconnectString>
      
<Weight>3</Weight>
    
</DbSnapInfo>
  
</DbSnapInfoList>
</DbSnapAppConfig>

       有(yǒu)關相應配置節點和(hé)負載均衡算(suàn)法會(huì)在後面提到,這裏為(wèi)了保持文章內(nèi)容的連續性暫且跳(tiào)過,下面接着浏覽一下上(shàng)面調用的‘UserSnapDatabase’方法:

代碼
/// <summary>
/// 是否使用快照數(shù)據庫
/// </summary>

/// <param name="commandText">查詢</param>
/// <returns></returns>
private static bool UserSnapDatabase(string commandText)
{
    
// 如果上(shàng)次刷新cookie間(jiān)隔小(xiǎo)于5分鍾, 則不刷新數(shù)據庫最後活動時(shí)間(jiān)

    if (commandText.StartsWith(BaseConfigs.GetTablePrefix + "create"))
    {
        Utils.WriteCookie(
"JumpAfterWrite"
, Environment.TickCount.ToString());
        
return false
;
    }
    
else if (!String.IsNullOrEmpty(Utils.GetCookie("JumpAfterWrite")) && (Environment.TickCount - TypeConverter.StrToInt(Utils.GetCookie("JumpAfterWrite"), Environment.TickCount)) < DbSnapConfigs.GetConfig().WriteWaitTime * 1000
)
        
return false
;
    
else if (!commandText.StartsWith(BaseConfigs.GetTablePrefix + "get"
))
        
return false
;

    
return true
;
}

 

      該方法的作(zuò)用很(hěn)簡單,就是當數(shù)據庫有(yǒu)CUD操作(zuò)時(shí),通(tōng)過寫cookie的方式向客戶端寫一個(gè)鍵值‘JumpAfterWrite’,這個(gè)鍵值很(hěn)重要,就是提供一個(gè)标簽(flag)來(lái)指示:‘當前用戶執行(xíng)cud操作(zuò)時(shí),頁面跳(tiào)轉到其它頁面而主數(shù)據庫還(hái)沒來(lái)得(de)及将數(shù)據推送到從數(shù)據庫’這一情況而造成的‘數(shù)據不同步’問題。
     舉了例子,當在一個(gè)版塊中‘發表主題’後系統自動跳(tiào)轉到‘顯示該主題頁面’時(shí),如果主數(shù)據庫中插入了一個(gè)新主題而從數(shù)據庫沒有(yǒu)被及時(shí)更新這一主題信息時(shí),就會(huì)報‘主題不存在’這個(gè)錯誤。所以這裏加了一個(gè)設置,就是下面這一行(xíng): 

(Environment.TickCount - TypeConverter.StrToInt(Utils.GetCookie("JumpAfterWrite"), Environment.TickCount)) < DbSnapConfigs.GetConfig().WriteWaitTime * 1000)

 

    
      它所做(zuò)的就是确保用戶cud操作(zuò)之後,在規定的時(shí)間(jiān)內(nèi)還(hái)是訪問主數(shù)據庫,當時(shí)間(jiān)超過時(shí),才将當前用戶的訪問請(qǐng)求(select)均衡到其它從數(shù)據庫中。

      當然,在GetRealConnectionString()方法中,還(hái)有(yǒu)一行(xíng)代碼很(hěn)重要,就是下面這一行(xíng):    
    

DbSnapInfo dbSnapInfo = GetLoadBalanceScheduling.GetConnectDbSnap();

 

    
    它的作(zuò)用就是加載配置文件信息,其中最主要的就是相應的‘負載均衡算(suàn)法實例’來(lái)獲取相應的從數(shù)據庫鏈接串,下面先看一
下‘靜态屬性’GetLoadBalanceScheduling的相關信息:

 

代碼
/// <summary>
/// 負載均衡調度接口
/// </summary>

private static ILoadBalanceScheduling m_loadBalanceSche;
/// <summary>

/// 初始化負載均衡調度接口實例
/// </summary>

private static ILoadBalanceScheduling GetLoadBalanceScheduling
{
    
get

    {
        
if (m_loadBalanceSche == null)
        {
            
try

            {
                m_loadBalanceSche 
= (ILoadBalanceScheduling)Activator.CreateInstance(Type.GetType(string.Format("Discuz.EntLib.{0}, Discuz.EntLib", DbSnapConfigs.GetConfig().LoadBalanceScheduling), falsetrue));
            }
            
catch

            {
                
throw new Exception("請(qǐng)檢查config/dbsnap.config中配置是否正确");
            }
        }
        
return
 m_loadBalanceSche;
    }
}

       它主要是通(tōng)過反射的方法将Discuz.EntLib.dll文件中的相應負載均衡算(suàn)法實例進行(xíng)綁定,然後以m_loadBalanceSche這個(gè)靜态變量進行(xíng)保存,而m_loadBalanceSche本身就是ILoadBalanceScheduling接口變量,該接口即是相應負載均衡算(suàn)法的實現接口。同樣因為(wèi)文章內(nèi)容的連續性,這裏先不深挖相應的實現算(suàn)法,我會(huì)在後面進行(xíng)介紹。下面再來(lái)看一下GetRealConnectionString()中還(hái)有(yǒu)一段代碼,如下:    

代碼
    if (DbSnapConfigs.GetConfig().RecordeLog && snapLogList.Capacity > snapLogList.Count)
                        snapLogList.Add(
string.Format("{{'SouceID' : {0}, 'DbconnectString' : '{1}', 'CommandText' : '{2}', 'PostDateTime' : '{3}'}},"
,
                                         dbSnapInfo.SouceID,
                                         dbSnapInfo.DbconnectString,
                                         commandText.Replace(
"'",""
),
                                         Discuz.Common.Utils.GetDateTime()));

     
return dbSnapInfo.DbconnectString;

 

     
     上(shàng)面代碼将當前的負載均衡得(de)到的鏈接串保存到一個(gè)snapLogList列表中,該列表聲明(míng)如下:     
    

 List<string> snapLogList = new List<string>(400)

 

     
      為(wèi)什麽要提供這個(gè)列表并進行(xíng)記錄?主要是為(wèi)了考查負載均衡算(suàn)法的工作(zuò)情況,因為(wèi)在數(shù)據訪問層獲取相應鏈接串信息并進行(xíng)記錄很(hěn)不方便,所以我用這個(gè)變量記錄大(dà)約400條‘負載均衡’數(shù)據鏈接串,以便在相應的Discuz.EntLib.ToolKit工具包中進行(xíng)觀察,監視(shì)其‘工作(zuò)情況’。這裏我們隻要知道(dào)通(tōng)過GetRealConnectionString()方法就實現了對sql語句或存儲過程進行(xíng)分析并進行(xíng)負載均衡的效果了(注:該操作(zuò)可(kě)能會(huì)耗時(shí),所以在DbSnapConfigs中提供了一個(gè)開(kāi)關‘RecordeLog’來(lái)進行(xíng)控制(zhì),後面會(huì)介紹)。
    

     下面再來(lái)簡單介紹一下,如何改造DbHelper.cs中原有(yǒu)方法,使其支持負載均衡功能。這裏強調一點,就是:
    
     GetRealConnectionString()方法隻是造了一個(gè)房(fáng)子,裏面的家(jiā)具還(hái)是要自己搬。
    
     而家(jiā)具就是那(nà)些(xiē)老的方法,比如:

 

代碼
public static object ExecuteScalar(DbConnection connection, CommandType commandType, string commandText, params DbParameter[] commandParameters)
{
    
if (connection == nullthrow new ArgumentNullException("connection"
);

    
//connection.Close();

    connection.ConnectionString = GetRealConnectionString(commandText);//負載均衡改造完成的方法
    connection.Open();

    
// 創建DbCommand命令,并進行(xíng)預處理(lǐ)

    DbCommand cmd = Factory.CreateCommand();

    
bool mustCloseConnection = false
;
    PrepareCommand(cmd, connection, (DbTransaction)
null, commandType, commandText, commandParameters, out
 mustCloseConnection);

    
// 執行(xíng)DbCommand命令,并返回結果.

    object retval = cmd.ExecuteScalar();

    
// 清除參數(shù),以便再次使用.

    cmd.Parameters.Clear();

    
if
 (mustCloseConnection)
        connection.Close();

    
return
 retval;
}

    
      上(shàng)面的 ‘connection.ConnectionString =’之前綁定的ConnectionString這個(gè)靜态屬性,而這個(gè)屬性鏈接的就是‘主數(shù)據庫’,
這裏我們隻要将GetRealConnectionString(commandText)賦值給它就可(kě)以了,還(hái)是那(nà)句話(huà),在GetRealConnectionString()就實現了
數(shù)據庫鏈接串的負載均衡,呵呵。類似上(shàng)面的變動在DbHelper.cs還(hái)有(yǒu)幾處,好在變化不太大(dà),當然更不需要改變原有(yǒu)的數(shù)據訪問層
(比如IDataProvider.cs文件)了。

      其實本文中介紹的數(shù)據庫層負載均衡實現方法在MYSQL中早有(yǒu)相應的插件實現了,參見這篇文章。      
  

     

 

 

        該文章中的LUA腳本實現方式與本文類似,如下:    
    

代碼
--發送所有(yǒu)的非事務性SELECT到一個(gè)從數(shù)據庫
    
if is_in_transaction == 0 and packet:byte() == proxy.COM_QUERY and packet:sub(27== "SELECT"
 then
      local max_conns 
= -1
   
      local max_conns_ndx 
=
 0    
      
for i = 1#proxy.servers do 

           local s = proxy.servers[i]
           
--
 選擇一個(gè)擁有(yǒu)空(kōng)閑連接的從數(shù)據庫
           
if s.type == proxy.BACKEND_TYPE_RO and s.idling_connections >
 0 then   
              
if max_conns == -1 or  s.connected_clients <
 max_conns then          
                  max_conns 
=
 s.connected_clients          
                  max_conns_ndx 
=
 i        
              end 
           end
      end
    .....

      接着,我再介紹一下相應的配置文件和(hé)負載均衡算(suàn)法的實現情況:)
   
   
      配置文件(比如:Discuz.EntLib.ToolKit\config\dbsnap.config):     
     
 

代碼
<?xml version="1.0"?>
<DbSnapAppConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  
<AppDbSnap>true</AppDbSnap>
  
<WriteWaitTime>1</WriteWaitTime>
  
<LoadBalanceScheduling>RoundRobinScheduling</LoadBalanceScheduling> --WeightedRoundRobinScheduling
  
<RecordeLog>false</RecordeLog>

  
<DbSnapInfoList>
    
<DbSnapInfo>
      
<SouceID>1</SouceID>
      
<Enable>true</Enable>
      
<DbconnectString>Data Source=DAIZHJ\DNT_DAIZHJ;User ID=sa;Password=123123;Initial Catalog=dnt_snap;Pooling=true</DbconnectString>
      
<Weight>4</Weight>
    
</DbSnapInfo>
    
<DbSnapInfo>
      
<SouceID>2</SouceID>
      
<Enable>true</Enable>
      
<DbconnectString>Data Source=DAIZHJ-PC\2222;User ID=sa;Password=123;Initial Catalog=tabletest;Pooling=true</DbconnectString>
      
<Weight>3</Weight>
    
</DbSnapInfo>
    
<DbSnapInfo>
      
<SouceID>3</SouceID>
      
<Enable>true</Enable>
      
<DbconnectString>Data Source=DAIZHJ-PC\333333;User ID=sa;Password=123;Initial Catalog=tabletest;Pooling=true</DbconnectString>
      
<Weight>2</Weight>
    
</DbSnapInfo>
    
<DbSnapInfo>
      
<SouceID>4</SouceID>
      
<Enable>true</Enable>
      
<DbconnectString>Data Source=DAIZHJ-PC\44444444;User ID=sa;Password=123;Initial Catalog=tabletest;Pooling=true</DbconnectString>
      
<Weight>2</Weight>
    
</DbSnapInfo>
  
</DbSnapInfoList>
</DbSnapAppConfig>

       上(shàng)面的DbSnapInfoList就是相應的slave數(shù)據庫鏈接列表,其中它的相應節點信息說明(míng)如下(Discuz.Config\DbSnapInfo.cs):     
 

代碼
[Serializable]
public class
 DbSnapInfo 
{
    
/// <summary>

    
/// 源ID,用于唯一标識快照在數(shù)據庫負載均衡中的信息
    
/// </summary>

    private int _souceID;
    
/// <summary>

    
/// 源ID,用于唯一标識快照在數(shù)據庫負載均衡中的信息
    
/// </summary>

    public int SouceID
    {
        
get { return
 _souceID; }
        
set { _souceID =
 value; }
    }

    
/// <summary>

    
/// 快照是否有(yǒu)效
    
/// </summary>

    private bool _enable;    
    
/// <summary>

    
/// 是否有(yǒu)效
    
/// </summary>

    public bool Enable
    {
        
get { return
 _enable; }
        
set { _enable =
 value; }
    }

    
/// <summary>

    
/// 快照鏈接
    
/// </summary>

    private string _dbConnectString;
    
/// <summary>

    
/// 快照鏈接
    
/// </summary>

    public string DbconnectString
    {
        
get { return
 _dbConnectString; }
        
set { _dbConnectString =
 value; }
    }

    
/// <summary>

    
/// 權重信息,該值越高(gāo)則意味着被輪循到的次數(shù)越多(duō)
    
/// </summary>

    private int _weight;
    
/// <summary>

    
/// 權重信息,該值越高(gāo)則意味着被輪循到的次數(shù)越多(duō)
    
/// </summary>

    public int Weight
    {
        
get { return
 _weight; }
        
set { _weight =
 value; }
    }
}

 
      
       當然DbSnapAppConfig作(zuò)為(wèi)DbSnapInfo列表的容器(qì),其結構如下:    
 

代碼
[Serializable]
public class
 DbSnapAppConfig : Discuz.Config.IConfigInfo
{
    
private bool
 _appDbSnap;
    
/// <summary>

    
/// 是否啓用快照,如不使用,則即使DbSnapInfoList已設置有(yǒu)效快照信息也不會(huì)使用。
    
/// </summary>

    public bool AppDbSnap
    {
        
get { return
 _appDbSnap; }
        
set { _appDbSnap =
 value; }
    }

    
private int _writeWaitTime = 6
;
    
/// <summary>

    
/// 寫操作(zuò)等待時(shí)間(jiān)(單位:秒(miǎo)), 說明(míng):在執行(xíng)完寫操作(zuò)之後,在該時(shí)間(jiān)內(nèi)的sql請(qǐng)求依舊(jiù)會(huì)被發往master數(shù)據庫
    
/// </summary>

    public int WriteWaitTime
    {
        
get { return
 _writeWaitTime; }
        
set { _writeWaitTime =
 value; }
    }

    
private string _loadBalanceScheduling = "WeightedRoundRobinScheduling"
;
    
/// <summary>

    
/// 負載均衡調度算(suàn)法,默認為(wèi)權重輪詢調度算(suàn)法 http://www.pcjx.com/Cisco/zhong/209068.html
    
/// </summary>
    public string LoadBalanceScheduling
    {
        
get { return
 _loadBalanceScheduling; }
        
set { _loadBalanceScheduling =
 value; }
    }

    
private bool _recordeLog = false
;
    
/// <summary>

    
/// 是否記錄日志(zhì)
    
/// </summary>

    public bool RecordeLog
    {
        
get { return
 _recordeLog; }
        
set { _recordeLog =
 value; }
    }
    

    
private  List<DbSnapInfo>
 _dbSnapInfoList;
    
/// <summary>

    
/// 快照輪循列表
    
/// </summary>

    public  List<DbSnapInfo> DbSnapInfoList
    {
        
get { return
 _dbSnapInfoList; }
        
set { _dbSnapInfoList =
 value; }
    }
}

    通(tōng)過這兩個(gè)配置文件,就可(kě)以實現對數(shù)據訪問層負載均衡的靈活配置了,不過上(shàng)面的DbSnapAppConfig還(hái)有(yǒu)一個(gè)非常重要的
屬性沒有(yǒu)介紹清楚,就是‘LoadBalanceScheduling’,其接口聲明(míng)如下:

代碼
    /// <summary>
    
///  負載均衡調度接口
    
/// </summary>

    public interface ILoadBalanceScheduling
    {
        
/// <summary>

        
/// 獲取應用當前負載均衡調度算(suàn)法下的快照鏈接信息
        
/// </summary>

        
/// <returns></returns>
        DbSnapInfo GetConnectDbSnap();
    }

     它就是負載均衡算(suàn)法的實現接口,為(wèi)了便于說明(míng)在Discuz.EntLib中內(nèi)置的兩個(gè)負載均衡算(suàn)法的實現情況,請(qǐng)先看下圖:    
    
    
       內(nèi)置的兩個(gè)負載均衡算(suàn)法,一個(gè)是RoundRobinScheduling,即輪叫調度(Round Robin Scheduling)算(suàn)法,它的實現比較簡單,就是對從數(shù)據庫鏈接列表的依次遍曆,如下:
代碼
/// <summary>
/// 輪叫調度(Round Robin Scheduling)算(suàn)法
/// </summary>

public class RoundRobinScheduling : ILoadBalanceScheduling
{
    
private static object lockHelper = new object
();
    
/// <summary>

    
/// 當前的快照索引和(hé)權重信息
    
/// </summary>

    static int curentSnapIndex = 0;

    
static
 RoundRobinScheduling()
    {}

    
public
  DbSnapInfo GetConnectDbSnap()
    {
        
lock
 (lockHelper)
        {
            
if (curentSnapIndex >=
 DbSnapConfigs.GetEnableSnapList().Count)
                curentSnapIndex 
= (curentSnapIndex) %
 DbSnapConfigs.GetEnableSnapList().Count;
         
            
return DbSnapConfigs.GetEnableSnapList()[curentSnapIndex++
];
        }
    }
}

 
   
     而另一種負載均衡算(suàn)法就相對負載了,不過它也更符合實際的應用場(chǎng)景,它使用了權重的方法來(lái)讓性能優良的機器(qì)分到
更多(duō)的任務來(lái)均衡整個(gè)方案的性能,即權重輪詢調度算(suàn)法,實現代碼如下:


 

代碼
/// <summary>
/// 權重輪詢調度算(suàn)法 
/// http://www.pcjx.com/Cisco/zhong/209068.html
 
/// http://id-phatman.spaces.live.com/blog/cns
!CA763CA8DB2378D1!627.entry
/// </summary>

public class WeightedRoundRobinScheduling : ILoadBalanceScheduling
{
    
private static object lockHelper = new object
();
    
/// <summary>

    
/// 快照的權重列表
    
/// </summary>

    static List<int> snapWeightList = new List<int>();
    
/// <summary>

    
/// 當前的快照索引和(hé)權重信息
    
/// </summary>

    static int curentSnapIndex, currentWeight;
    
/// <summary>

    
/// 快照權重列表中最大(dà)的權重值和(hé)最大(dà)公約數(shù)
    
/// </summary>

    static int maxWeight, gcd;

    
static
 WeightedRoundRobinScheduling()
    {
        curentSnapIndex 
= -1
;
        currentWeight 
= 0
;

        snapWeightList 
=
 GetSnapWeightList();
        maxWeight 
=
 GetMaxWeight(snapWeightList);
        gcd 
=
 GCD(snapWeightList);
    }

    
/// <summary>

    
/// 獲取應用當前負載均衡調度算(suàn)法下的快照鏈接信息
    
/// </summary>

    
/// <returns></returns>
    public  DbSnapInfo GetConnectDbSnap()
    {
        
lock
 (lockHelper)
        {
            DbSnapInfo current 
=
 RoundRobinScheduling();
            
if (current != null
)
                
return
 current;
            
else

                
return DbSnapConfigs.GetEnableSnapList()[0];
        }
    }

    
/// <summary>

    
/// 獲取快照權重的列表
    
/// </summary>

    
/// <returns></returns>
    static List<int> GetSnapWeightList()
    {
        List
<int> snapWeightList = new List<int>
();

        
foreach (DbSnapInfo dbSnapInfo in
 DbSnapConfigs.GetEnableSnapList())
        {
            snapWeightList.Add(dbSnapInfo.Weight);
        }
        
return
 snapWeightList;
    }

    
/// <summary>

    
/// 權重輪詢調度算(suàn)法
    
/// </summary>

    static DbSnapInfo RoundRobinScheduling()
    {
        
while (true
)
        {
            curentSnapIndex 
= (curentSnapIndex + 1%
 DbSnapConfigs.GetEnableSnapList().Count;
            
if (curentSnapIndex == 0
)
            {
                currentWeight 
= currentWeight -
 gcd;
                
if (currentWeight <= 0
)
                {
                    currentWeight 
=
 maxWeight;
                    
if (currentWeight == 0
)
                        
return null
;
                }
            }
            
if (DbSnapConfigs.GetEnableSnapList()[curentSnapIndex].Weight >=
 currentWeight)
                
return
 DbSnapConfigs.GetEnableSnapList()[curentSnapIndex];
        }
    }

    
/// <summary>

    
/// 獲取最大(dà)權重
    
/// </summary>

    
/// <param name="snapList"></param>
    
/// <returns></returns>
    static int GetMaxWeight(List<int> snapWeightList)
    {
        
int maxWeight = 0
;
        
foreach (int snapWeight in
 snapWeightList)
        {
            
if (maxWeight <
 snapWeight)
                maxWeight 
=
 snapWeight;
        }
        
return
 maxWeight;
    }

    
/// <summary>

    
/// 獲取權重的最大(dà)公約數(shù)
    
/// </summary>

    
/// <returns></returns>
    static int GCD(List<int> snapWeightList)
    {
        
// 排序,得(de)到數(shù)字中最小(xiǎo)的一個(gè) 

        snapWeightList.Sort(new WeightCompare());
        
int minNum = snapWeightList[0
];

        
//
 最大(dà)公約數(shù)肯定大(dà)于等于1,且小(xiǎo)于等于最小(xiǎo)的那(nà)個(gè)數(shù)。 
        
// 依次整除,如果餘數(shù)全部為(wèi)0說明(míng)是一個(gè)約數(shù),直到打出最大(dà)的那(nà)個(gè)約數(shù) 

        int gcd = 1;
        
for (int i = 1; i <= minNum; i++
)
        {
            
bool isFound = true
;
            
foreach (int snapWeight in
 snapWeightList)
            {
                
if (snapWeight % i != 0
)
                {
                    isFound 
= false
;
                    
break
;
                }
            }
            
if
 (isFound)
                gcd 
=
 i;
        }
        
return
 gcd;
    }

    
/// <summary>

    
/// 實現IComparer接口,用于對數(shù)字列表進行(xíng)排序
    
/// </summary>  

    private class WeightCompare : System.Collections.Generic.IComparer<int>
    {
        
public int Compare(int weightA, int weightB)
        {
            
return weightA -
 weightB;
        }
    }
}

        到這裏,主要的功能代碼就介紹的差不多(duō)了,我們可(kě)以通(tōng)過對dbsnap.config的相應節點配置,來(lái)靈活定制(zhì)我們的負載均衡方案。同時(shí),對一般開(kāi)發者而言,這種架構是透明(míng)的,大(dà)家(jiā)可(kě)以完全在不了解它的情況下開(kāi)發自己的數(shù)據訪問功能,并通(tōng)過相應開(kāi)關來(lái)讓自己的代碼支持均衡負載。

        當然這個(gè)方案還(hái)有(yǒu)一些(xiē)沒考慮到的問題比如:
        1.對‘主從數(shù)據庫的健康度檢查’,即如果主或從數(shù)據庫出現故障的時(shí)候該如何處理(lǐ),當然在sqlserver中還(hái)提供了鏡像功能來(lái)解決類似問題,所以它也可(kě)做(zuò)為(wèi)一個(gè)備選方案。

        2.當主數(shù)據庫被發布出去後,主數(shù)據庫的表和(hé)存儲過程就會(huì)被‘鎖定’,其不允許被再次修改了,所以還(hái)要繼續研究如何解決這一問題。

重慶中技互聯網信息咨詢有限公司
重慶網站(zhàn)建設事業部官方網:www.zjcoo.com
電(diàn)子商務建站(zhàn)事業部咨詢電(diàn)話(huà):023-67742189
門(mén)戶網站(zhàn)品牌加盟推廣電(diàn)話(huà):023-67742189
7*24小(xiǎo)時(shí)服務電(diàn)話(huà):023-67742189
媒體(tǐ)合作(zuò)電(diàn)話(huà):13883323406
投資合作(zuò)電(diàn)話(huà):13896068183
QQ及郵件地址:446515345@qq.com

如沒特殊注明(míng),文章均為(wèi)中技(jì)互聯原創,轉載請(qǐng)注明(míng)來(lái)自www.zjcoo.com
相關新聞

CopyrightZJCOO technology Co., LTD. All Rights Reserved.    

渝ICP 備11003429号

  • qq客服
  • 公衆号
  • 手機版
  • 新浪微博