Web項目演化系列--開啟分布式(分離數據層)

jopen 8年前發布 | 9K 次閱讀 分布式系統 .NET開發

原本上一篇是打算寫分離數據層的,但是在思考的過程當中發現分離數據層的時候,有一些操作是要依賴分布式鎖的,因此先寫了分布式鎖。

對于有些項目的數據層提供的是業務接口的(返回業務所需的數據),那么當數據層壓力逐漸增大的時候,如需要使用緩存的時候,就需要開發人員去修改相應的數據接口使其使用緩存,緩存和各種數據查詢接口交錯在一起,整個數據層的代碼變得非常混亂,連重構都無法進行,只能推倒重做。所以很多的文章中,在講解數據層的時候,都是使用統一的數據接口,如:Find、Add、Save等,那么當需要緩存的時候,就可以在Find中直接擴展使其支持緩存,甚至可以引入緩存配置管理,對于不同的表之間進行可調度的緩存周期管理,使開發人員不需要知道緩存的存在,他們還是使用原先的Find,但是速度卻更快了,又或者冷熱數據管理、搜索引擎等,這便是大神們在經過多年開發總結下來的經驗。

將數據層從項目中分離出來成為一個獨立的項目,并將其發布于獨立的服務器,相對于單機系統而言,有如下幾個優點:

1、便于整合各方資源

2、降低成本

3、每個層的性能更加優秀且可伸縮性強,在不斷增加的負荷下,可以便利的增加節點數量。

4、某些次服務層出現錯誤的時候,不會導致項目的整體癱瘓。

5、不同層次可用不同語言實現

有優點就有缺點,最大的缺點就是較單機系統而言,分布式系統更加復雜,不僅系統本身結構會變得復雜,不同組件間的網絡會引入影響因素,調試困難等等。

雖然有不少的缺點,但是不去實踐是不會發現另一番天地的,不去挑戰看看永遠都只是局限在單機系統上,也不用害怕會遇到哪些問題,畢竟只有發現問題之后,才能想辦法解決,這是從書上無法學習到的,那么我們就開始今天的文章吧。

基礎CRUD

數據層是提供于數據庫進行操作的中間件,最基礎的功能就是CRUD,單機系統當中,調用CRUD接口的時候,要么通過拼接SQL要么通過ORM直接連接數據庫,而由于現在是分布式的,因此原先的調用方式就需要通過通信協議來實現了,假設原先的接口為:

public class DbResult
{
    public bool Error { get; set; }

    public object Data { get; set; }
}

public interface IDb
{
    DbResult Find(Dictionary<string, object> query);

    DbResult Add(object entity);

    DbResult Save(object entity);

    DbResult Remove(object entity);
}

數據層那端的Find是以Query Object模式實現的,具體的格式可以查看此文章--《 Query Object--查詢對象模式(上) 》、《 Query Object--查詢對象模式(下) 》。

這里的數據層項目,如果使用原先抽取的mvcHandler方式來實現,那么IService的派生類就需要使用HttpWebRequest來實現了,大致的實現思路就是將相應的方法最后轉換成調用數據服務,Find實現代碼如下:

public DbResult Find(Dictionary<string, object> query)
{
    var url = string.Format("{0}/{1}/{2}", ConnectionUri, this.table, "find");
    var req = (HttpWebRequest)HttpWebRequest.Create(url);
    req.Method = "Post";
    req.Accept = "text/plain, */*; q=0.01";
    req.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
    req.Timeout = 1000 * 30;
    req.KeepAlive = true;
    try
    {
        byte[] bytes = Encoding.UTF8.GetBytes(
            JsonConvert.SerializeObject(query));
        req.ContentLength = bytes.Length;
        using (var reqStream = req.GetRequestStream())
        {
            Stream requestStream = req.GetRequestStream();
            requestStream.Write(bytes, 0, bytes.Length);
        }

        var res = new DbResult();
        using (var resp = (HttpWebResponse)req.GetResponse())
        {
            using (var respStream = resp.GetResponseStream())
            {
                using (var reader = new StreamReader(respStream, Encoding.UTF8))
                {
                    string respContent = reader.ReadToEnd();
                    return JsonConvert.DeserializeObject(respContent);
                }
            }
        }
    }
    catch
    {
        return new DbResult { Error = true };
    }
}

事務

有了基礎的CRUD以后,接下來就要實現事務了,如果參考單機系統來實現事務的話,那么開啟事務以后,如果業務層出現問題導致主機重啟或宕機,那么數據層的事務將會無法關閉,從而引發問題。

因此需要使用其他的方式來實現,觀察事務可以得出從事務開啟到提交是一個整體,只有事務提交的時候才需要有狀態來判定事務的執行情況,而事務開啟時并可以不需要有執行結果的判定,因此可以將事務看成一個隊列,而中間的每一個CUD操作可以看成是它的元素,而每一個操作需要知道對應的是哪個表、執行哪個操作以及相應的表數據即可,代碼實現如下:

public class TransactionAction
{
    public string Table { get; set; }

    public string Name { get; set; }

    public object Data { get; set; }
}

//IDb
public interface IDb
{
    //其他省略

    void BeginTx();

    DbResult CommitTx();
}

//IDb實現
private List actions;

public void BeginTx()
{
    this.actions = new List();
}

public DbResult CommitTx()
{
    //http訪問數據層并設置actions為null
}

public DbResult Add(object entity)
{
    if (this.actions != null)
    {
        this.actions.Add(new TransactionAction
        {
            Name = "add",
            Table = this.table,
            Data = entity
        });
    }
    else
    {
        //單個增加
    }
}

結束語

到這里數據層的分離基本上就完成了,如果想要更高效的數據獲取,可以將訪問方式修改成socket,之所以不直接使用WPF或者webService是因為這都是基于C#來開發的,而且如果直接使用現成的框架來用的話,能學到的東西就不多了。

就到這里了,如果有什么問題或者錯誤的話,請給我留言,謝謝。

來自: http://www.cnblogs.com/ahl5esoft/p/5111982.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!