【G】開源的分散式部署解決方案(一) - 開篇

来源:http://www.cnblogs.com/doddgu/archive/2017/01/23/g_1.html
-Advertisement-
Play Games

從功能上來說,請參考 預告篇,因自知當時預告篇沒有任何含金量,所以並沒有主動推送到首頁,而是私下的給一些人發的。 從個人角度上來說,我希望.net的環境會越來越好,就我自己的成長曲線是從mxdn開始自學、cxdn嘗試解答問題、博客園讀別人博客再到自己寫博客、最後到工作中經常使用到的stackxxxx ...


做這個開源項目的意義是什麼?(口水自問自答,不喜可略過)

從功能上來說,請參考 預告篇,因自知當時預告篇沒有任何含金量,所以並沒有主動推送到首頁,而是私下的給一些人發的。

從個人角度上來說,我希望.net的環境會越來越好,就我自己的成長曲線是從mxdn開始自學、cxdn嘗試解答問題、博客園讀別人博客再到自己寫博客、最後到工作中經常使用到的stackxxxxflow、gxxgle。這其中我當然是走了很多彎路,踩過無數的坑,也埋了無數的坑。現在自己有了一點小小的能力,想盡自己所能,通過一個項目整理出來給大家參考、學習的同時也能給自己一些意見。對新人來說也算是一種回饋,對老人來說也算是互相切磋互相提升。

 

什麼是分散式部署解決方案?

部署也有很多人叫發佈。那正常的流程是什麼樣的呢?

 

相信這張圖大家都不會太陌生。一般少數幾台伺服器的時候直接這樣發佈是最省時省力的。

但是,如果有一天,你的伺服器變成幾十臺,然後又面臨著經常發佈的情況該怎麼辦呢?

@echo off
echo Start to Publish ApplicationName All Site
set local="C:\inetpub\ApplicationName"
Set RemotePath=C$\inetpub\ApplicationName

For %%I in (192.168.0.2 192.168.0.3 ... 192.168.0.n) do (
echo "Publish ApplicationName %%I"
net use \\%%I\ipc$ Password /User:UserName
For %%P in (default) do (
echo "Publish \\%%I\%RemotePath%\"dd
xcopy /e /y %local% "\\%%I\%RemotePath%\"
)
net use \\%%I\ipc$ /del
)

:End
pause;

上面這段代碼,相信也有一部分人用過吧。使用流程也比較簡單,先用vs發佈到本地,然後再使用批處理文件,通過發佈命令來進行多台網站的部署。

 

突然有一天,你是上司跟你說,你的這段批處理有很多問題,比如不利於維護、沒有部署記錄、發佈後首次訪問很慢、沒有版本管理、部署過程一旦發生異常也沒有容錯機制等等。

是的,這些問題都還是基礎的。如果你的伺服器數量達到一定級別以後,你的部署會變得異常艱難。某台伺服器上部署了多少個服務、某幾台伺服器是一組,這些會讓你抓狂,甚至想說髒話,就如同下圖。

由於後面還會有一系列文章要出,此處就不再過多的解釋,當你看到完整的項目之後,相信你會對它有一個新的認識。

 

第一個正式版包含哪些功能?

請允許我偷個懶,從預告篇把尚不成熟的功能列表copy一份過來:

 

1 基礎的許可權控制(可能只支持一個角色,甚至是寫死)

2 部署項目管理(以Jenkins為例,做自動化構建方案)

3 部署伺服器管理(以Aliyun伺服器為例)

4 部署組管理(包括部署組、部署組項)

5 創建部署任務(包括觸發自動化構建、部署文件關聯等)

6 部署伺服器常駐服務(以Windows Service的形式駐留,主要完成中控的指令協助部署)

7 一鍵部署(支持單台或部署組)

  7.1 支持手動綁定歷史部署文件版本(理論上支持所有的,如Jenkins這種構建方案還會涉及到保存構建副本數量和天數等,另本功能只支持單台部署,部署組的修改版本通過創建部署任務時統一指定)

  7.2 自動檢測部署文件包狀態(以Jenkins為例,會自動等待構建完畢,並會自行檢測構建狀態判斷是否繼續向下執行)

  7.3 下載部署文件包並解壓

  7.4 控制負載均衡降權

  7.5 部署(包括等待伺服器正在執行的請求完成的檢測)

  7.6 觸發緩存(也可以理解為觸發首次訪問,提升用戶體驗)

  7.7 控制負載均衡升權

 

PS:其中為了支持單台伺服器部署,所以一些擴展性功能會採用插件(暫定)的形式允許自行實現,同時也會包含一套以阿裡雲、jenkisn為基礎的示例實現。

後續也會考慮把一些功能提取出來做成API,允許把功能嵌入到自己的系統里去。當然,這個要看有多少人需要了。

 

核心技術與開發工具

後端:.net 4.6.1,EF6,mvc5(不上.net core的原因是1.1.0目前並不穩定,還是有不少bug,我真的親身踩到了,實在是不想無限踩過去,請原諒我沒有勇於踩坑的心,確確實實是精力有限)

前端:AdminLTE 2 支持PC、Mobile雙端

資料庫:SQL Server LocalDB

IDE:Visual Studio 2017 RC

源碼托管:oschina git

 

現在做到哪一步了?

先上一張解決方案資源管理器的截圖,簡單的看下項目結構。

可以看到,目前只有一個項目,而且比較亂,Business、Data、Models 各種亂入。

這麼低級的一個項目怎麼好意思開源的?

是的,沒錯,目前項目狀態就是這樣。

因為我要做的不是發佈出來一個成品,寫個介紹就完了。

我會從0代碼開始在git上保留整個過程。且根據進度不定期更新博文來講解編碼過程中的各種問題以及解決思路。

而下一篇的名字我也已經想好了。

【G】開源的分散式部署解決方案(二) -  好項目是從爛項目基礎上重構出來的

在我的這個開源系列博客中,你可以看到一個開源項目的完整成長歷程。我不知道這個項目會走多遠,但我堅信這個項目一定會幫到一部分人,這就足夠了。

下麵放幾張截圖,也是下一篇內容的主角。

(簡單的登錄頁)

(後臺UI的基礎框架)

(已完成的一個功能,部署伺服器管理)

(支持手機端的table)

 

登錄的實現

為了避免太水的嫌疑,上一點點乾貨。

新建項目時,點擊Change Authentication會看到4個選項。至於如何選擇我就不多說了,我先說說我選了 Identity(Individual User Accounts)踩過的坑。

因為我之前主要是做架構方向的,可以理解為這些都是所謂的“小弟”在做的功能,而我一個不小心就掉進了陷阱里去。說是陷阱吧,其實主要是因為自己不熟悉,畢竟mvc還是比較龐大的,即便你順著它的思路去做,也未必能做得對。

預設情況下生成好的項目是已經使用推薦的EF6實現了整個登錄流程。而我又希望自己可以掌控關鍵的驗證邏輯,所以一路摸下去把 ApplicationUser、AppplicationUserManager、UserManager、UserStore都看了一遍,經過各種嘗試,發現驗證邏輯封裝在Microsoft.AspNet.Identity.EntityFramework(這個是盲打的,已經回不去了,如果錯了歡迎指正)。

找到了目標當然第一想法是功課他,隨著不斷的踩坑發現工作量異常的大,遂放棄。想從另外一個角度入手,因為UserManager才是核心,那我順著這條線是否可以把他已經實現的EF那套開給剔除掉。

於是就有了下麵的代碼:

首先要改造Model,於是自己把 ApplicationUser、IdentityUser、UserStore都重寫了。前面兩個比較簡單,重點是UserStore,要實現IUserStore<TUser>這個介面,這裡是自行編寫驗證邏輯的轉折點。

其次,Controller也進行的小改造,去掉了SignInManager等,只保留了UserManager,同時Startup.Auth裡面的DbContext也換成了自己的。

最後把Name也一同存入了Cookie,方便layout裡面要顯示用戶名。

 

核心代碼如下:

1.IdentityUser 其實這個類可以跟 ApplicationUser合併的,我也是跟官方生成項目時的寫法保留下來的。可能故意分開在ApplicationUser裡面寫的是邏輯,而IdentityUser是對屬性的定義,這樣會顯得更清晰一點吧,雖然我也會這麼做,也很熱衷於這麼做,但區別是我不會讓ApplicationUser繼承IdentityUser,而是寫一個Business來實現方法做一個徹底的拆分。

    public class IdentityUser : IUser<string>
    {
        public string Id { get; set; }

        public string Name { get; set; }

        public string Password { get; set; }

        public string Role { get; set; }

        public string UserName { get; set; }
    }

2.UserStore其實並沒有多少借鑒的意義,因為這是很小學生的寫法了,主要是為了突出UserStore的重要性。

    public class UserStore : IUserStore<ApplicationUser>
    {
        public async Task CreateAsync(ApplicationUser user)
        {
            using (var dbContext = GDbContext.Create())
            {
                dbContext.User.Add(new User()
                {
                    UserName = user.UserName,
                    Password = user.Password,
                    Name = user.Name,
                    Role = user.Role,
                });

                await dbContext.SaveChangesAsync();
            }
        }

        public Task DeleteAsync(ApplicationUser user)
        {
            throw new NotImplementedException();
        }

        public void Dispose()
        {
            //nothing to do
        }

        public Task<ApplicationUser> FindByIdAsync(string userId)
        {
            int id = 0;
            if (!Int32.TryParse(userId, out id))
            {
                throw new ArgumentException(nameof(userId));
            }

            using (var dbContext = GDbContext.Create())
            {
                var user = dbContext.User.SingleOrDefault(u => u.ID == id && !u.IsDelete);

                return Task.FromResult(new ApplicationUser()
                {
                    Id = user.ID.ToString(),
                    Name = user.Name,
                    Password = user.Password,
                    Role = user.Role,
                    UserName = user.UserName
                });
            }
        }

        public Task<ApplicationUser> FindByNameAsync(string userName)
        {
            using (var dbContext = GDbContext.Create())
            {
                var user = dbContext.User.SingleOrDefault(u => u.UserName == userName && !u.IsDelete);

                return Task.FromResult(new ApplicationUser()
                {
                    Id = user.ID.ToString(),
                    Name = user.Name,
                    Password = user.Password,
                    Role = user.Role,
                    UserName = user.UserName
                });
            }
        }

        public Task UpdateAsync(ApplicationUser user)
        {
            throw new NotImplementedException();
        }
    }

 

 3.Startup.Auth ConfigureAuth方法進行瞭如下的改動,主要是修改 DbContext 的獲取,以及SecurityStampValidator.OnvalidateIdentity這個方法的參數調整。

           // 配置資料庫上下文、用戶管理器和登錄管理器,以便為每個請求使用單個實例
            app.CreatePerOwinContext(GDbContext.Create);
            app.CreatePerOwinContext(() => new UserManager<ApplicationUser>(new UserStore()));

            // 使應用程式可以使用 Cookie 來存儲已登錄用戶的信息
            // 並使用 Cookie 來臨時存儲有關使用第三方登錄提供程式登錄的用戶的信息
            // 配置登錄 Cookie
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login"),
                Provider = new CookieAuthenticationProvider
                {
                    // 當用戶登錄時使應用程式可以驗證安全戳。
                    // 這是一項安全功能,當你更改密碼或者向帳戶添加外部登錄名時,將使用此功能。
                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<UserManager<ApplicationUser>, ApplicationUser>(
                        validateInterval: TimeSpan.FromMinutes(30),
                        regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
                }
            });            

 

說起來簡單,但真的做起來還是踩過了大大小小的坑,即便是你覺得很不起眼的一個功能,都有可能折騰的你要死要活。

我會告訴你我新建項目、刪除項目、再新建、再刪除,這個過程已經重覆好多遍了嗎?

當你看到一段好的代碼在你面前時,又有幾個人能體會寫的人死了多少腦細胞?

我不會拒絕伸手黨,但我希望伸手的同時能再點個贊,你的支持是無數同行堅持在這條路上的動力。

 

聲明

1.在出第一個正式版之前不會使用任何開源協議

2.永遠免費,不會出什麼專業版、旗艦版之類的進行收費

3.第一個版本完結時間目前還不知道,我這個人懶散慣了,但我會儘量多抽出一些時間儘快寫出來。

 

最後,本項目暫定名為 G,唯一群號:7424099

Git地址:http://git.oschina.net/doddgu/G   (希望大家可以順手點個star,如果有喜歡的朋友捐贈下就更好了,感謝各位的支持)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 3. 添加一個視圖 · 原文地址:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-view · 譯文地址:http://www.cnblogs.com/powertoolsteam/ar ...
  • 2. 添加一個控制器 · 原文地址:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-controller · 譯文地址:http://www.cnblogs.com/powertools ...
  • 1.平臺互操作性和不安全的代碼:C#功能強大,但有些時候,它的表現仍然有些“力不從心”,所以我們只能摒棄它所提供的所有安全性,轉而退回到記憶體地址和指針的世界。 C#通過3種方式對此提供支持。 (1)第一種方式是通過平臺調用(Platform Invoke,P/Invoke)來調用非托管代碼DLL所公 ...
  • Socket裡面的協議解析是Socket通訊程式設計中最複雜的地方,如果你的應用層協議設計或實現不佳,Socket通訊中常見的粘包,分包就難以避免。SuperSocket內置了命令行格式的協議CommandLineProtocol,如果你使用了其它格式的協議,就必須自行實現自定義協議CustomPr ...
  • 1.在多個線程的同步數據中,避免使用this、typeof(type)、string進行同步鎖,使用這3個容易造成死鎖。 2.使用Interlocked類:我們一般使用的互斥鎖定模式(同步數據)為Lock關鍵字(即Monitor類),這個同步屬於代價非常高的一種操作。除了使用Monitor之外,還有 ...
  • 1.Task概述:Task是對操作系統線程的抽象,目的是使線程池能高效地管理線程的分配和回收,Task使用的底層線程屬於一種共用資源,任務需要互相協作,並及時歸還線程,以便用相同的共用資源(線程)滿足其他請求。 2.Task.AsyncState:獲取在創建 Task 時提供的狀態對象,如果未提供, ...
  • 1.List.BinarySearch():BinarySearch()採用的是二分搜索演算法,要求元素已經排好序,其特點是假如元素沒有找到,會返回一個負整數,該值的按位取反(~)結果是“大於被查找元素的下一個元素”的索引,如果沒有更大的值,則是元素的總數。這樣一來就可以在列表中的特定位置方便地插入新 ...
  • 1.在使用反射時,反射可以繞過安全訪問級別(private、protected)修飾的類或屬性,來獲取需要的信息。 2.泛型的反射:可以使用Type.ContainsGenericParameters這個屬性來判斷一個類或方法是否包含尚未設置的泛型實參,Type.IsGenericType屬性表示是 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...