PIXNET Logo登入

互聯網 - 大數據

跳到主文

本部落格為互聯網熱門頭條訊息管理中心

部落格全站分類:生活綜合

  • 相簿
  • 部落格
  • 留言
  • 名片
  • 3月 09 週四 201720:41
  • ASP.NET Core Linux下為 dotnet 創建守護進程(必備知識)

image
文章出處
前言
在上篇文章中介紹了如何在 Docker 容器中部署我們的 asp.net core 應用程序,本篇主要是怎么樣為我們在 Linux 或者 macOs 中部署的 dotnet 程序創建一個守護進程,來保證我們的程序在異常或者是電腦重啟的時候仍然能夠正常訪問。
如果你以后用準備使用 asp.net core來開發項目的話,程序并且部署到 Linux 上的話,那么此篇文章你值得收藏。
如果你覺得對你有幫助的話,不妨點個【推薦】。
目錄
  • 什么是守護進程

  • Supervisor 介紹

  • Supervisor 安裝

  • Supervisor 配置,常用命令

  • Supervisor UI管理臺

  • 什么是守護進程

    在linux或者unix操作系統中,守護進程(Daemon)是一種運行在后臺的特殊進程,它獨立于控制終端并且周期性的執行某種任務或等待處理某些發生的事件。由于在linux中,每個系統與用戶進行交流的界面稱為終端,每一個從此終端開始運行的進程都會依附于這個終端,這個終端被稱為這些進程的控制終端,當控制終端被關閉的時候,相應的進程都會自動關閉。但是守護進程卻能突破這種限制,它脫離于終端并且在后臺運行,并且它脫離終端的目的是為了避免進程在運行的過程中的信息在任何終端中顯示并且進程也不會被任何終端所產生的終端信息所打斷。它從被執行的時候開始運轉,直到整個系統關閉才退出。


    此處的創建守護進程,是指發布在Linux上 asp.net core 程序的dotnet xxx.dll命令的宿主進程創建一個守護進程。
    在 Linux 上有很多可以管理進程的工具,我們使用 Supervisor 來做這個事情。
    原因有兩點:
    1、它是微軟官方文檔推薦的,降低學習成本。
    2、它并不一定是最好的,但一定是文檔最全的。
    Supervisor 介紹
    Supervisor是采用 Python(2.4+) 開發的,它是一個允許用戶管理 基于 Unix 系統進程的 Client/Server 系統,提供了大量功能來實現對進程的管理。
    官方文檔:http://supervisord.org/
    Supervisor 安裝
    在 masOS 中直接使用brew工具進行安裝即可:
    brew install supervisor
    在 linux 中使用以下命令進行安裝:
    ubuntu
    sudo apt-get install supervisor
    centos
    yum install supervisor
    python
    pip install supervosor
    easy_install supervisor
    安裝完成之后:
    mac:~ yangxiaodong$ brew install supervisor
    Warning: supervisor-3.2.1 already installed

    Supervisor 配置,常用命令
    安裝完成之后,在 /ect/supervisor/confg.d/ 目錄下新建一個配置文件(touch HelloWebApp.conf),取名為 HelloWebApp.conf
    打開HelloWebApp.conf (vim HelloWebApp.conf),寫入如下命令:
    [program:HelloWebApp]
    command=dotnet HelloWebApp.dll #要執行的命令
    directory=/home/yxd/Workspace/publish #命令執行的目錄
    environment=ASPNETCORE__ENVIRONMENT=Production #環境變量
    user=www-data #進程執行的用戶身份
    stopsignal=INT
    autostart=true #是否自動啟動
    autorestart=true #是否自動重啟
    startsecs=1 #自動重啟間隔
    stderr_logfile=/var/log/HelloWebApp.err.log #標準錯誤日志
    stdout_logfile=/var/log/HelloWebApp.out.log #標準輸出日志

    配置好以后 (:wq保存退出),需要重新加載一下配置
    sudo supervisorctl shutdown && sudo supervisord -c /etc/supervisor/supervisord.conf
    或者你可以直接重啟 Supervisor:
    sudo service supervisor stop
    sudo service supervisor start

    如果啟動的時候報錯,可以打開位于/etc/log/supervisor/supervisord.log文件來查看具體的日志。
    其中dotnet 命令輸出的日志文件分別為位于
    /var/log/HelloWebApp.err.log
    /var/log/HelloWebApp.out.log

    在這些文件里面你可以查看程序中的異常信息或者是運行信息。
    打開瀏覽器,輸入 http://localhost:5000 發現已經可以瀏覽了。
    Supervisor 常用命令
    supervisorctl shutdown #關閉所有任務
    supervisorctl stop|start program_name
    supervisorctl status #查看所有任務狀態

    Supervisor UI 管理臺
    Supervisor 默認給我們提供了一個圖形界面來供我們管理進程和任務,在 macOS 中默認配置的有,但是在 Linux 中我們需要手動開啟一下。
    打開位于/etc/supervisor/supervisord.conf文件,添加inet_http_server 節點
    然后就可以通過界面來查看運行的進程了:
    測試一下
    最后,我們測試一下是否會自動重啟,開機自動運行?
    1、進程管理中干掉dot net ,發現可以重新啟動。以下是日志:
    2016-07-09 12:24:18,626 INFO spawned: 'HelloWebApp' with pid 1774
    2016-07-09 12:24:19,766 INFO success: HelloWebApp entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
    2016-07-09 12:27:43,208 INFO exited: HelloWebApp (exit status 0; expected)
    2016-07-09 12:27:44,223 INFO spawned: 'HelloWebApp' with pid 3687
    2016-07-09 12:27:45,243 INFO success: HelloWebApp entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

    2、重啟機器,發現可以自動運行。


    本文地址:http://www.cnblogs.com/savorboard/p/dotnetcore-supervisor.html
    作者博客:Savorboard
    歡迎轉載,請在明顯位置給出出處及鏈接


    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(44)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:41
    • ASP.NET Core 十種方式擴展你的 Views


    文章出處

    原文地址:http://asp.net-hacker.rocks/2016/02/18/extending-razor-views.html
    作者:Jürgen Gutsch
    翻譯:楊曉東(Savorboard)


    現在,已經有很多種方式來擴展Razor視圖了,我們循循漸進,先從最簡單的開始。
    如果你之前熟悉MVC5(以及之前的MVC)中的視圖的話,有一部分你應該已經很熟悉了。在新的ASP.NET Core 中,那些你熟悉的方式有一部分仍然能用,只是Core版本針對視圖又添加了一些東西。這篇文章,我們就來一起看看吧。
    #1:數據視圖(Typed Views)
    這是一個不具有動態內容的最基本的一個視圖,就是你定義一個ViewModel , 然后ViewModel具有一些默認值,在視圖上直接呈現而已。定死的ViewModel,好像不是很常見,以至于你使用Visual Studio新建一個默認的Web應用程序的話,都看不到它。它就看起來像一個*.cshtml結尾的HTML文件,但是,cshtml文件卻是服務端可以解析的一種文件,所以你可以在里面使用一些Razor語法,比如HtmlHelpers,UrlHelpers等。同樣,你可以使用ViewBag或者ViewData來從Controller的Action傳輸數據到View里面,來讓它具有動態的內容。但是ViewBag和ViewData都是弱類型的,所以沒有智能提示,用起來略不爽。
    要在你的視圖中使用強類型數據對象,你需要定義一個Model來在視圖中使用。
    @model ExtendViews.ViewModels.AboutModel
    <!-- usage: --->
    @Model.FullName

    這種方式是不是很常見的? 下一種方式是一個更好的方式來布局我們的視圖:
    #2:布局(Layouts):
    相當于ASP.NET的WebForms的母版頁,不過它是定義Razor視圖的基本布局的一種方式。它就是_Layout.cshtml, 位于 Views\Shared\ 文件夾里 。通常情況下這個文件通常包含HTML的header,body和公用的一些東西。你可以多建幾個互相進行組合,來完成整個站點的布局。其他頁面引用布局視圖頁的時候,是這樣子寫的(注意不需要擴展名):
    @{
    Layout = "_Layout";
    }

    此調用需要在您的視圖的第一行中。但你不需要在每一個視圖中定義布局,如果你使用Visual Studio新建一個ASP.NET Core項目,Views文件夾有一個_ViewStart.cshtml,在運行的時候它會自動的導入到每個視圖中去。
    在_Layout.cshtml有一個方法法叫 RenderBody(),它就是用來渲染詳細的視圖頁到模板布局視圖中:
    @RenderBody()
    在此方法的位置,詳情視圖就會被渲染到這里。
    #3: 區域(Sections)
    有時候子視圖中想在主視圖中顯示一部分html代碼,比如javascript代碼或者是css,這個時候就可以使用Sections,通常情況下在頁面的結尾部分。
    在主視圖中(_Layout.cshtml)定義一個Javascripts Section:

    @RenderSection("scripts", required: false)

    有一個required參數來聲明這個Section是否必須的。然后你就可以在子視圖中這樣使用:
    @section scripts
    {
    <script>
    $(function() {
    // some more js code here;
    });
    </script>
    }

    如果你使用嵌套的布局,你可能需要嵌套這個區域。意思就是你在Section里面嵌套調用RenderSection():
    @section scripts
    {
    @RenderSection("scripts", required: false)
    }

    #4: 分部視圖( PartialViews)
    你可以提取html頁面中重用的部分,把它放到一個新的Razor視圖中,這個視圖沒有自己的Action,這種視圖就叫做分部視圖。 分部視圖通常也在Views\Shard\文件夾。
    分部視圖同樣也可以是一個數據視圖,它可以從父視圖中獲取數據(但不是必須的):
    @model IEnumerable<UserModel>
    @if (Model.Any())
    {
    <ul>
    @foreach (var user in Model)
    {
    <li>@user.FullName</li>
    }
    </ul>
    }

    這個分部視圖需要從父視圖中獲取用戶列表的數據
    @{ await Html.RenderPartialAsync("Users", Model.Users);}
    如果你的分部視圖沒有定義用戶模型,你就不需要傳第二個參數。
    #5:視圖組件(ViewComponents)
    這個 ASP.NET Core特有的。

    譯者注:類似于以前的用戶控件


    有時候你需要做一些分部視圖的事情,但是又包含一些業務邏輯在里面。在過去,你可以使用ChildAction渲染結果到一個視圖中,但是,在 ASP.NET Core中,有一種新的方式來做這件事情,它就是ViewComponents(我已經寫了一篇關于ViewComponents的博文)。它類似于在MVC中的一種迷你的MVC,也就是說他們可以有自己的Controller,和單個的action以及view。ViewComponents是完全獨立于你的當前視圖的,但是可以通過你當前的視圖傳輸數據。
    想這樣調用它,來渲染一個ViewComponents:
    @Component.Invoke("Top10Articles");
    可以看我的博客來學習怎么創建自己的ViewComponent。
    #6: HTML助手(HtmlHelpers)
    在HTMLHelper類中,你可以創建你自己的擴展方法來擴展Razor語法:
    public static class HtmlHelperExtensions
    {
    public static HtmlString MyOwnHtmlHelper(this HtmlHelper helper, string message)
    {
    return new HtmlString($"<span>{message}<span>");
    }
    }

    在你的視圖中,創建一個可重用的部分是非常有用的,它比分部視圖多包含了一些業務邏輯。比HTMLHelpers擴展更好的是新的TagHelpers,但是在擴展你視圖的時候,HTMLHelpers仍然有它自己的一些適用的地方。
    #7: 標簽助手(TagHelper)
    這是 ASP.NET Core 非常好的一個新特性。
    一個擴展你視圖的小助手,它看起來像一個原生的HTML標簽一樣。 在ASP.NET Core MVC中你應該使用 TagHelpers 來替換 HtmlHelpers,因為它們更加的簡潔和容易使用。另一個巨大的好處就是依賴注入,在HtmlHelpers中是使用不了的,因為HtmlHelpers 擴展的都是靜態內容。 但TagHelpers是一個公共類,我們可以很容易的在它的構造函數中注入服務。
    下面是一個很簡單的小示例,來展示怎么樣定義一個TagHelper:
    [TargetElement("hi")]
    public class HelloTagHelper : TagHelper
    {
    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
    output.TagName = "p";
    output.Attributes.Add("id", context.UniqueId);
    output.PreContent.SetContent("Hello ");
    output.PostContent.SetContent(string.Format(", time is now: {0}",
    DateTime.Now.ToString("HH:mm")));
    }
    }

    這里定義了一個叫做hi的標簽,它以HTML標記來呈現,p標簽的內容是當前時間。
    使用:
    <hi>John Smith</hi>
    結果:
    <p>Hello John Smith, time is now: 18:55</p>
    ASP.NET Core MVC 已經默認提供了很多TagHelpers來替換以前的HtmlHelpers。例如ActionLink已經被新的TagHelper所替換:
    @Html.ActionLink(“About me”, “About”, “Home”)
    新的TagHelper像這樣來創建一個link:
    <a asp-controller=”Home” asp-action=”About”>About me</a>
    以上兩種方式來創建一個a標簽的結果:
    <a href=”/Home/About”>About me</a>
    可以看到,TagHelpers看起來更像原生的HTML,他們在視圖中更加的直觀,更高的可讀性,并且更容易使用。
    #8: 依賴注入(Dependency Injection)
    這也是ASP.NET Core的新特性。
    在擴展你的視圖的時候,可以使用依賴注入了,這是一個非常大改進。是的,你可以在你的視圖中使用DI了。
    在StackOverflow和reddit有人這樣問?
    這真的有意義嗎? 這不是會搞亂我的視圖嗎? 這不是與MVC模式背離嗎?
    我認為,不是這樣的。 的確,在真正需要的地方你才使用,并且,你使用的時候需要非常小心。 有這樣一個有效的場景:你創建一個表單來編輯用戶的資料信息(User Profile), 用戶可以添加他的公司位置,地址, 國家城市等等,我不愿意從Action到View中傳輸公司位置 ,地址和國家城市。我只愿意通過用戶資料本身(User Profile), 我只想在 Action 中處理用戶資料(User Profile)。這時候可以注入服務來給我查詢數據,這就是為什么這種情況下它是非常有用的。它可以讓我們的Action和ViewModel 保持非常的干凈。
    在Startup.cs中的ConfigureServices來注冊你具體的服務,然后你就可以在視圖中這樣來使用,只需要一行代碼:
    @inject DiViews.Services.ICountryService CountryService;
    現在你可以在你的視圖中使用ContryService來填充國家下拉列表。
    我在這篇博客中寫了很多關于依賴注入的博文。
    #9: 函數(Functions)
    在一個ASP.NET MVC 項目中,我從來沒有真正的使用過函數這個功能。我只在一個Umbraco的CMS系統中用過一次。不管怎么說,這也是擴展你視圖的另一種小技巧。也許你有很復雜視圖方面的業務邏輯,在這種情況下,你可以在你的視圖中寫C#方法:
    @functions
    {
    public string ReverseString(string input)
    {
    return String.Join("", input.Reverse());
    }
    }

    #10: 配置全局視圖(Global view configuration)
    最后一點,你可以在_ViewImports.cshtml文件中,來配置你其他視圖中使用的一些比較公用的 using 引用,依賴注入等。
    總結
    不管是以前的MVC還是新的Core MVC, 都有很多方法來擴展視圖,雖然擴展這些視圖的方式有些類似,但是每一種都有它最適合的地方,所以我們在使用這些特性來解決我們的問題的時候,我們應該多加思考,找到最合適的方式。

    譯者注:本文翻譯并非逐字翻譯,由于水平有限,難免出現一些錯誤和翻譯不準確的地方,希望讀者能夠指出并堪正,不勝感激。


    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(125)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:41
    • ASP.NET Core 緩存技術 及 Nginx 緩存配置

    image
    文章出處
    前言
    在Asp.Net Core Nginx部署一文中,主要是講述的如何利用Nginx來實現應用程序的部署,使用Nginx來部署主要有兩大好處,第一是利用Nginx的負載均衡功能,第二是使用Nginx的反向代理來降低我們后端應用程序的壓力。那除了以上兩點之外,其實我們還可以利用代理服務器的緩存功能來進一步的降低后端應用程序的壓力,提升系統的吞吐量(tps)。這一篇就來看一下具體應該如何去做吧。
    目錄
  • WEB 緩存

  • ASP.NET Core 緩存
    內存緩存
    分布式緩存
    Response 緩存

  • Nginx 緩存

  • 總結

  • WEB 緩存
    之所以加這個目錄是因為有一部分初學者對緩存的認知不夠,特別是WEB中的緩存。
    緩存它是一種空間換取時間的一種技術。
    Web緩存(或HTTP緩存)是用于Web文檔,如HTML頁面和圖像,減少帶寬的使用,服務器的負載的一種信息技術。一個Web緩存系統存儲通過Cache來傳遞的文件的副本;如果滿足某些條件,則可以從緩存中得到后續的請求。
    WEb緩存有幾種方式:
    1、服務端緩存
    利用 Memcached,Redis,In-Memery 等緩存技術實現對數據的緩存。
    2、代理服務器緩存
    利用類似nginx的反向代理服務器,對請求的url對應的輸出的進行緩存。這個緩存和應用程序實現的動態頁面緩存類似,只不過用反向代理充當了應用程序的緩存實現。
    3、客戶端緩存
    瀏覽器緩存,其實主要就是HTTP協議定義的緩存機制(如: Last-Modified,If-Modified-Since,Expires; Cache-control等)。
    ASP.NET Core 緩存
  • 內存緩存

  • 最簡單的一種緩存,ASP.NET Core 提供了 IMemoryCache 接口來供我們使用。它存儲在本地的 WEB 服務器內容中,注意是單機的 WEB 服務器,如果你需要部署的是一個服務器集群的話,那么你應該用分布式緩存,而不是選擇這個。
    就不詳細介紹了,想了解的可以直接看官方文檔。
  • 分布式緩存

  • 隨著云應用和服務器集群以及 docker 等技術的成熟,越來越多的應用程序開始考慮集群部署,因為它具有更好的性能和可伸縮可擴展性。那么這個時候就需要用到分布式緩存了。
    在 ASP.NET Core應用中,已經對分布式緩存做了抽象,提供了 IDistributedCache 接口,該接口提供了添加,檢索,刪除等的同步和異步的方法。并且還默認提供了 Redis 和 SQLServer 的分布式緩存實現,我們也可以實現 IDistributedCache 接口來擴展自己的緩存系統。
    需要說明的是Get,GetAsync和Set,SetAsync。 這兩個接口方法默認是使用的byte[],之所以沒有提供直接存儲對象的方法是因為微軟想把這個默認序列化的選擇交給用戶,因為每一個團隊的偏好是不一樣的,有些團隊喜歡使用 XML,有些喜歡使用 JSON,有些喜歡使用 Protobuf 等,所以在 項目中,你可以根據自己的偏好來擴展想要的方法。
    具體使用方法還是直接看官方文檔好了。
    關于使用也可以查看我的另外一篇博客: ASP.NET Core 使用 Redis 和 Protobuf 進行 Session 緩存。
  • Response 緩存

  • 在 ASP.NET Core中,有一種緩存叫做Response緩存,這個緩存主要是用來做代理服務器的緩存。它主要原理是在輸出的HTTP Response的header里面添加指定的緩存標記。這些緩存標記用來讓客戶端或者代理服務器來識別需要緩存的內容。然后當客戶端有請求到代理服務器的時候,代理服務器可以識別出一部分請求,然后直接把結果返回給瀏覽器,從而提高后端應用程序的性能和吞吐。
    從這個圖中看出來,在第一次的時候,一個客戶端請求經過代理服務器請求的我們后端的WEB服務器上,然后WEB服務器在返回結果的META上添加了cache-control標簽,它的值為public。
    下面是cache-control標簽一些值的說明:
    public 指示響應可被任何緩存區緩存。
    private 指示對于單個用戶的整個或部分響應消息,不能被共享緩存處理。這允許服務器僅僅描述當用戶的部分響應消息,此響應消息對于其他用戶的請求無效。
    no-cache 指示請求或響應消息不能緩存(HTTP/1.0用Pragma的no-cache替換)根據什么能被緩存
    max-age 指示客戶機可以接收生存期不大于指定時間(以秒為單位)的響應。
    min-fresh 指示客戶機可以接收響應時間小于當前時間加上指定時間的響應。
    max-stale 指示客戶機可以接收超出超時期間的響應消息。如果指定max-stale消息的值,那么客戶機可以接收超出超時期指定值之內的響應消息。
    Expires 表示存在時間,允許客戶端在這個時間之前不去檢查(發請求),等同max-age的
    效果。但是如果同時存在,則被Cache-Control的max-age覆蓋。
    格式:
    Expires = "Expires" ":" HTTP-date
    通過HTTP的META設置expires和cache-control
    <meta http-equiv="Cache-Control" content="max-age=7200" />
    <meta http-equiv="Expires" content="Mon, 20 Jul 2016 23:00:00 GMT" />

    在 ASP.NET Core MVC 中,提供了ResponseCache這個特性用來做上面這些事情。它被作為一個Attribute添加的Controller的Action上。
    Duration 指示緩存的過期時間,對應到Cache-Control 的 max-age 。
    Location 有三個值Any,Client,None分別對應到Cache-Control的 public,private,no-cache。
    NoStore 設置值是否被存儲。如果是true,它將設置Cache-Control為no-store
    VaryByHeader 將在header中添加Vary標記。
    CacheProfileName 使用的策略,在startup.cs中設置。
    Order 在過濾器中的排序。
    現在,我們已經知道了如果在Action中設置緩存標記了。
    Nginx 緩存
    對于一些靜態文件,比如程序用到的圖片,css,js等,Nginx是可以直接處理的,只需要配置一下。
    如果使用Nginx來處理靜態文件的話,那么程序中startup.cs就可以不用添加app.UseStaticFiles();中間件了。
  • 配置
    打開nginx.conf文件,在ubuntu系統下位于/etc/nginx/conf.d/nginx.conf沒有的話就新建一個。內容如下:

  • proxy_temp_path /usr/local/nginx/proxy_temp_dir 1 2; #注:proxy_temp_path和proxy_cache_path指定的路徑必須在同一分區
    #keys_zone=cache1:100m 表示這個zone(緩存區域)名稱為cache1,分配的內存大小為100MB
    #/usr/local/nginx/proxy_cache_dir/cache1 表示cache1這個zone的文件要存放的目錄
    #levels=1:2 表示緩存目錄的第一級目錄是1個字符,第二級目錄是2個字符,即/usr/local/nginx/proxy_cache_dir/cache1/a/1b這種形式
    #inactive=1d 表示這個zone中的緩存文件如果在1天內都沒有被訪問,那么文件會被cache manager進程刪除掉
    #max_size=10g 表示這個zone的硬盤容量為10GB
    proxy_cache_path /usr/local/nginx/proxy_cache_dir/cache1 levels=1:2 keys_zone=cache1:100m inactive=1d max_size=10g;
    #upstream web-app {
    # server webapp1:5090;
    # server webapp2:5090;
    #}
    server {
    listen 80;
    server_name *.example.com;
    #在日志格式中加入$upstream_cache_status
    log_format format1 '$remote_addr - $remote_user [$time_local] '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent" $upstream_cache_status';
    #訪問日志
    access_log log/access.log fomat1;
    #$upstream_cache_status表示資源緩存的狀態,有HIT MISS EXPIRED三種狀態
    add_header X-Cache $upstream_cache_status;
    #命中的正則表達式
    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css|html)$ {
    proxy_pass http://127.0.0.1:5000;
    #proxy_pass http://web-app;
    #proxy_set_header Host $host;
    #proxy_set_header X-Real-IP $remote_addr;
    #proxy_set_header X-Forwarded-For $remote_addr;
    #proxy_set_header Accept-Encoding "none";
    #設定proxy_set_header Accept-Encoding '';(或是后臺服務器關閉gzip),這樣這臺機器才不會緩存被壓縮的文件,造成亂碼
    #proxy_set_header Accept-Encoding ""; 這個也可
    #如果后端的服務器返回502、504、執行超時等錯誤,自動將請求轉發到upstream負載均衡池中的另一臺服務器,實現故障轉移。
    #proxy_next_upstream http_502 http_504 error timeout invalid_header;
    #設置資源緩存的zone
    proxy_cache cache1;
    #設置緩存的key
    proxy_cache_key $host$uri$is_args$args;
    #設置狀態碼為200和304的響應可以進行緩存,并且緩存時間為10分鐘
    proxy_cache_valid 200 304 10m;
    # **!!!重要!!!** 這段配置加上后,proxy_cache就能支持后臺設定的Cache-Control,Expires。
    proxy_ignore_headers "Cache-Control" "Expires";
    expires 30d;
    }
    }

    上面有一個配置項在 ASP.NET Core 程序中比較重要,就是proxy_ignore_headers這個配置項,它代表支持后臺設定Cache-Control,Expires等。
    其中upstream節點是用來配置負載均衡的服務器的,proxy_pass用來設置代理到upstream節點,proxy_next_upstream是用來配置故障轉移。
  • 應用配置

  • 使用sudo nginx -s reload命令來重新加載配置。
    總結
    關于緩存
    緩存確實是提升應用程序性能最快也是效果最明顯的方式之一,ASP.NET Core也為提供了很多種緩存方法。但是,在使用之前一定要了解每一種緩存的技術實現,切不可盲目使用。
    關于部署
    個人認為,在 ASP.NET Core 理想的分布式部署環境有兩種:
    第一種是基于云的部署,比如使用Azure,AWS,阿里云等,那么我們可以使用他們提供的負載均衡器來幫助我們攔截洪水般的請求,然后借助于云提供的高可用的實例集群或者Docker集群來降低應用程序的壓力,提升吞吐。
    比如我們項目現在使用的AWS的部署環境,借助于AWS來實現企業的私有云,包括高可用的Redis集群,彈性EC2集群,RDS集群,S3等,這個時候只需要專注于業務。
    第二種是自己搭建集群環境,可以在服務器前端使用Nginx的負載均衡和緩存來攔截大部分的HTTP請求,然后后端使用Docker集群來做部署。
    Docker部署可以參見本人的另外一篇文章:http://www.cnblogs.com/savorboard/p/dotnetcore-docker.html
    在版本的快速迭代過程中,你還需要做的工作有如何提高部署的工作效率,那么可以使用一些Docker集群管理工具,后面會寫一篇文章專門介紹Docker的集群管理和 ASP.NET Core的一鍵發布。


    本文地址:http://www.cnblogs.com/savorboard/p/dotnetcore-nginx-cache.html
    作者博客:Savorboard
    歡迎轉載,請在明顯位置給出出處及鏈接


    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(58)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:41
    • ASP.NET Core Kestrel 中使用 HTTPS (SSL)


    文章出處
    在ASP.NET Core中,如果在Kestrel中想使用HTTPS對站點進行加密傳輸,可以按照如下方式
    申請證書
    這一步就不詳細說了,有免費的和收費的,申請完成之后會給你一個*.pfx結尾的文件。
    添加NuGet包
    nuget中查找然后再程序中添加引用Microsoft.AspNetCore.Server.Kestrel.Https
    配置
    把*.pfx結尾的文件拷貝的程序的Web根目錄,然后修改Programs.cs文件:
    public class Program
    {
    public static void Main(string[] args) {
    var config = new ConfigurationBuilder().AddCommandLine(args).AddEnvironmentVariables("ASPNETCORE_").Build();
    var host =
    new WebHostBuilder().UseConfiguration(config).UseKestrel(ConfigHttps()).UseContentRoot(
    Directory.GetCurrentDirectory()).UseIISIntegration().UseStartup<Startup>().Build();
    host.Run();
    }
    private static Action<KestrelServerOptions> ConfigHttps() {
    return x => {
    var pfxFile = Path.Combine(Directory.GetCurrentDirectory(), "*.pfx");
    //password 填寫申請的密鑰
    var certificate = new X509Certificate2(pfxFile, "password");
    x.UseHttps(certificate);
    };
    }
    }

    然后命令行窗口運行dotnet xxx.dll --server.urls https://www.example.com:port即可。


    本文地址:http://www.cnblogs.com/savorboard/p/aspnetcore-kestrel-https.html
    作者博客:Savorboard
    歡迎轉載,請在明顯位置給出出處及鏈接


    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(380)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:41
    • ASP.NET Core 數據保護(Data Protection)【上】

    文章出處
    前言

    上一篇博客記錄了如何在 Kestrel 中使用 HTTPS(SSL), 也是我們目前項目中實際使用到的。


    數據安全往往是開發人員很容易忽略的一個部分,包括我自己。近兩年業內也出現了很多因為安全問題導致了很多嚴重事情發生,所以安全對我們開發人員很重要,我們要對我們的代碼的安全負責。
    在工作中,我們常常會見到 encode,base64,sha256, rsa, hash,encryption, md5 等,一些人對他們還傻傻分不清楚,也不知道什么時候使用他們,還有一些人認為MD5就是加密算法。
    在 ASP.NET Core 中,為數據保護相關提供了一批新的 API,包括加密解密機制,下面就讓我們來看看吧。
    目錄
  • 加密,編碼,哈希之間的區別

  • 數據保護(Data Protection)介紹

  • ASP.NET Core 中的數據保護

  • 總結

  • 編碼,加密,哈希之間的區別
    編碼
    編碼是信息從一種形式或格式轉換為另一種形式的過程,他們是可逆的。
    如 url、base64、jsunicode、utf-8等等。
    加密
    加密是可逆的,類似于編碼也是把數據從一種形式轉換為另一種形式,它通過一個特定的加密的密匙,相對應的有解密的過程。加解密的算法有2種:對稱加密算法和非對稱加密算法。
    對稱:DES、AES、SM1、RC4 等等。
    非對稱:RSA、ECC、SM2 等等。
    哈希
    又叫"散列",就是把任意長度的數據轉換成固定長度的“指紋”,這個過程是不可逆的。而且只要輸入發生改變,輸出的 hash值也會有很大不同。
    它還有一個特性是相同的輸入總是有相同的結果, 這種特性恰好合適用來用來保存密碼。
    如:MD5、SHA256, SHA512, RipeMD, WHIRLPOOL等等。
    數據保護(Data Protection)介紹
    在看數據保護官方文檔的時候,微軟的文檔是這樣寫的,大致意思就是他們基于幾點需求,要開發一套數據保護的庫以便用來給受信任的客戶端和不受信任的客戶端來使用。這幾點要求就是:
    1、真實性、完整性
    舉了一個身份驗證cookie的例子,就是服務端生成了一個包含xyz權限的token,然后會在將來的某個時間過期,這個時候就需要重新請求生成一個,怎么樣來保證請求的token不是被篡改過的。
    2、機密性
    服務器要保證請求是受信任的,所以就需要一些包含特定操作環境的信息,比如一個路徑,一個權限或者一個句柄或者其他的一些東西特定于服務器的東西,這些信息不應該透漏給不受信任的客戶端,也就是說類似于私鑰。
    3、隔離性
    然后就是要求做成一個組件,并且這個組件具有獨立性,可以不依賴于系統中的其他組件。如一個bearer token的組件,它要使用這個組件的話,也不需要引用anti-CSRF這種機制了。
    再進一步的縮小需求范圍,加密的數據不需要在系統之外的其他系統中使用,另外處理速度要盡可能的快,因為每一次web請求都會使用加密組件一次或者多次。
    基于以上要求,微軟提出來可以使用密碼學,因為這是一個典型的密碼學應用的場景。確實這是一個密碼學的應用場景,并且是一個非對稱加密算法的場景。但是大家都知道,非對稱加密是由一個公鑰和私鑰用來保證安全性的,即使公鑰遭泄露,整個通訊仍然是安全的,這就是它比對稱加密的好處。但是非對稱加密也是有缺點的,就是加密和解密花費的時間長,速度慢。
    但是上面的要求又是需要速度盡可能快,怎么辦呢? 于是微軟的工程師們想出了可以通過精簡并且優化非對稱加密機制,來達到這個要求。因為不需要跨系統或者跨語言什么的,所以也不需要什么協議之類的,這就給優化帶來了更多的可能性。
    到這里,我就想,如果讓我來基于以上幾點來設計開發這樣一個系統,我應該怎么樣設計?怎么樣達到要求?
    帶著這個問題,我們來進一步看看微軟是怎么樣做的吧?
    下面是一些總結的設計原則 :
    1、配置應該盡量的簡單,默認情況下應該可以零配置,開發人員可以直接運行。
    2、提供一個簡單的API,應該容易使用,并且不會輕易用錯。
    3、開發人員不需要專門學習怎么樣管理這些鑰(公鑰,私鑰),系統應該自動的選擇算法和管理鑰的生命周期。理想情況下開發人員都不應該訪問這些鑰的原始文件。
    4、鑰應該是受保護的,不會被遠程調用到。系統應該有一個自動保護機制并且可以自動應用。
    如果讓我設計這樣一個庫,我可能不會想到這么多,也許只會想到前3點。
    再看一下針對的受眾群體:
    1、應用程序開發人員和框架開發人員(不需要學習任何知識)。
    2、應用開發人員和系統管理員(不使用默認配置,只是設定一些路徑等)。
    3、針對具有更高安全意識的開發人員提供可擴展api,或特定需求擴展(需要重寫系統的組件,有一些獨特的需求)。
    以上,可以看到微軟在開發一個組件的時候對問題的分析,也許我們可以從中學到一些東西。
    ASP.NET Core 中的數據保護
    Web應用程序中經常需要存儲一些敏感數據(如用戶密碼),Windows 系統為桌面程序提供了DPAPI用來使用,但是并不適用于 Web 系統。ASP.NET Core提供了一套簡單易用的API 用來保護數據。
    ASP.NET Core 中,數據保護主要是用來給服務端設計的,用來替換ASP.NET 1.x-4.x中的,machineKey主要是用來保證使用Form身份驗證時Cookie數據的加密解密,以確保不會被修改。或者ViewState數據的加密解密不被篡改,以及對session狀態標識進行驗證。


    先看一下最簡單的使用方法:


    using System;
    using Microsoft.AspNetCore.DataProtection;
    using Microsoft.Extensions.DependencyInjection;
    public class Program
    {
    public static void Main(string[] args)
    {
    // 添加數據保護到服務中
    var serviceCollection = new ServiceCollection();
    serviceCollection.AddDataProtection();
    var services = serviceCollection.BuildServiceProvider();
    // 從DI中創建一個MyClass的實例
    var instance = ActivatorUtilities.CreateInstance<MyClass>(services);
    instance.RunSample();
    }
    public class MyClass
    {
    IDataProtector _protector;
    // 參數 'provider' 來自 DI
    public MyClass(IDataProtectionProvider provider)
    {
    _protector = provider.CreateProtector("Contoso.MyClass.v1");
    }
    public void RunSample()
    {
    Console.Write("Enter input: ");
    string input = Console.ReadLine();
    // 加密
    string protectedPayload = _protector.Protect(input);
    Console.WriteLine($"Protect returned: {protectedPayload}");
    // 解密
    string unprotectedPayload = _protector.Unprotect(protectedPayload);
    Console.WriteLine($"Unprotect returned: {unprotectedPayload}");
    }
    }
    }
    /*
    * 輸出:
    *
    * Enter input: Hello world!
    * Protect returned: CfDJ8ICcgQwZZhlAlTZT...OdfH66i1PnGmpCR5e441xQ
    * Unprotect returned: Hello world!
    */

    在CreateProtector("Contoso.MyClass.v1")中,參數“Contoso.MyClass.v1”可以理解為一個公鑰,因為 ASP.NET Core Data Protection 是非對稱加密(見前面介紹),所以系統中應該還有一個密鑰,那么此處的密鑰 ASP.NET Core 在系統內部幫你維護了。


    讀到這里,有同學可能會問了,那系統中是如何幫我維護我的密鑰的呢? 我們不妨先來做一個測試。


    首先,我在我的開發環境中,先把上面的程序中的解密部分代碼注釋掉,然后運行上面的程序,輸入一個“Hello World!” ,得到了一個加密的字符串CfDJ8ICcgQwZZhlAlTZT...OdfH66i1PnGmpCR5e441xQ(略寫)。


    然后我把同樣的程序拷貝到另外一臺開發環境的機器上,然后把上面的加密部分代碼注釋掉,使用第一步生成的CfDJ8ICcgQwZZhlAlTZT...OdfH66i1PnGmpCR5e441xQ來解密,注意這兩步中我們都使用 "Contoso.MyClass.v1" 來做為公鑰。


    運行程序,查看結果:
    image


    程序拋出了一個“System.Security.Cryptography.CryptographicException”異常的結果。


    為什么呢? 這是因為每一臺機器都有一個自有的私鑰,由于在解密的過程中,這個私鑰是不同的,所以解密失敗,拋出了一個異常。


    私鑰


    私鑰存放在哪里呢?


    1、如果程序寄宿在 Microsoft Azure下,存儲在“%HOME%\ASP.NET\DataProtection-Keys” 文件夾。


    2、如果程序寄宿在IIS下,它被保存在HKLM注冊表的ACLed特殊注冊表鍵,并且只有工作進程可以訪問,它使用windows的DPAPI加密。


    3、如果當前用戶可用,即win10或者win7中,它存儲在“%LOCALAPPDATA%\ASP.NET\DataProtection-Keys”文件夾,同樣使用的windows的DPAPI加密。


    4、如果這些都不符合,那么也就是私鑰是沒有被持久化的,也就是說當進程關閉的時候,生成的私鑰就丟失了。


    下面是博主機器上的私鑰文件:


    一個xml配置文件,位于C:\Users\用戶名\AppData\Local\ASP.NET\DataProtection-Keys文件夾,名為:key-c37e3ed9-fbb5-47bc-9e8c-128afaf1c6d9.xml,內容如下:


    <?xml version="1.0" encoding="utf-8"?>
    <key id="c37e3ed9-fbb5-47bc-9e8c-128afaf1c6d9" version="1">
    <creationDate>2016-08-15T05:21:16.7925949Z</creationDate>
    <activationDate>2016-08-15T05:21:16.7165905Z</activationDate>
    <expirationDate>2016-11-13T05:21:16.7165905Z</expirationDate>
    <descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
    <descriptor>
    <encryption algorithm="AES_256_CBC" />
    <validation algorithm="HMACSHA256" />
    <encryptedSecret decryptorType="Microsoft.AspNetCore.DataProtection.XmlEncryption.DpapiXmlDecryptor, Microsoft.AspNetCore.DataProtection, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60" xmlns="http://schemas.asp.net/2015/03/dataProtection">
    <encryptedKey xmlns="">
    <!-- This key is encrypted with Windows DPAPI. -->
    <value>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAArS6GBZu5C024S8VcNDckGgAAAAACAAAAAAAQZgAAAAEAACAAAABBUO4j0CscEZsdcHDAStXnDvtx+zFucmsG90sdhyjfgQAAAAAOgAAAAAIAACAAAABGr9fgvZkLAlgIZkGym5uLiufpaEcuVsp35+J96ItTYlABAADEZxVArK0QtxufuaRt/kVR2ZBZEoLhlYJ44BhvQDd6b9tN0L9Y7W2eeBPBefcZaGZk5xILwZYI5box9omwC/mp8t9wopVaratjZuNs21Al+JzxS+PeV9X0iPtRyfx2K7DJYOUT6IqoFR2ykL5MI9jvkIbUxcQOs0BKOwAHl4yAlYF2tR8pz1FkXKqZafovc11aOZeZhkfd2hiA53tan94bQOP43Z4HF+QWSazrq5IIqdFSOyZQemWL9Z7eYyoNpEktf3eGZQu/KBOg/BH5yizWa+6b7RLcEX6JdQ2/jpmnHNl+HPMIah3UZV0mRfAE2j58cUjosnV+LDQZoLn4OP70YWtO/tTBc4tsEY3n/WboL4PgPPmQ+2jfd/zmEQIon+4d7TY+mGh4c6wXAmAZF517UAHQMC1icx4HSJC8DTuWPlINihPyufejuPmLqW6CW8NAAAAA7ziObXv+Ax4Mm0AtZiGw0/IepDv/gJSxhEwLIDhfvQIQJv//G500EYtIbZJW6sWit//ypfjrUZYglHgKV+GpbA==</value>
    </encryptedKey>
    </encryptedSecret>
    </descriptor>
    </descriptor>
    </key>

    文件包含一個創建日期,一個過期日期。間隔為90天,當90天之后密鑰就會失效,系統將自動生成一個新的密鑰并設置新的密鑰作為活動的密鑰。只要已過期的密鑰還存在于系統上,你仍然可以解密任何受保護的數據。


    文章不宜太長,下篇再接著寫。
    如果您覺得本篇文章對你有用的話,不妨點個【推薦】。


    總結


    這篇文章算是對ASP.NET Core Data Protection做了一個大致的介紹,并且包含了一個簡單的使用方法。 在實際使用過程中,其實很多組件內部都會使用到它,比如Session中間件,Identity中間件,Authercation中間件等等,對于普通開發人員在編碼的時候可能不會用到,但是在做系統分布式部署的時候如果你不了解這個機制可能就會遇到麻煩了(詳見蟋蟀博客的這篇文章),所以還是可以期待一下下文,更加深入的了解它,掌握它。





    本文地址:http://www.cnblogs.com/savorboard/p/dotnetcore-data-protection.html
    作者博客:Savorboard
    歡迎轉載,請在明顯位置給出出處及鏈接



    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(319)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:41
    • ASP.NET Core 數據保護(Data Protection)【中】


    文章出處
    前言
    上篇主要是對 ASP.NET Core 的 Data Protection 做了一個簡單的介紹,本篇主要是介紹一下API及使用方法。
    API 接口
    ASP.NET Core Data Protectio 主要對普通開發人員提供了兩個接口,IDataProtectionProvider 和 IDataProtector。
    我們先看一下這兩個接口的關系:
    namespace Microsoft.AspNetCore.DataProtection
    {
    //
    // 摘要:
    // An interface that can provide data protection services.
    public interface IDataProtector : IDataProtectionProvider
    {
    byte[] Protect(byte[] plaintext);
    byte[] Unprotect(byte[] protectedData);
    }
    }

    可以看到,IDataProtector繼承自IDataProtectionProvider ,并且提供了兩個方法 Protect 和 Unprotect ,從命名來看,一個是加密,一個是解密。而他們的簽名都是傳入一個byte數組,這也就意味著他們可以加密和解密一切對象。返回的也是byte數組,也就是說在實際的使用過程中,我們應該自己添加或者使用系統的一些擴展方法來具體化我們的需求。
    我們再看一下IDataProtectionProvider接口:
    namespace Microsoft.AspNetCore.DataProtection
    {
    public interface IDataProtectionProvider
    {
    IDataProtector CreateProtector(string purpose);
    }
    }

    IDataProtectionProvider提供了一個方法,通過傳入一個 purpose字符串(見后面詳細介紹)來生成一個IDataProtector接口對象。
    從這個接口的命名來看,它以Provider結尾,也就是說這部分我們可以實現自己的一套加解密的東西。

    我們在閱讀微軟項目的源代碼的時候,經常看一些以xxxxProvider結尾的對象,那么它的職責是什么,同時扮演什么樣的角色呢?
    其實這是微軟專門為ASP.NET設計的一個設計模式,叫Provider Model設計模式,也可以說它是由微軟發明的,它不屬于23種設計模式中的一種,從功能上來看的話,應該是工廠和策略的結合體。自ASP.NET 2.0開始,微軟就開始引入這種設計模式,最開始主要是用于實現應用程序的配置的多個實現。比如開發者最熟悉的web.config中, 針對于數據庫連接字符串的配置, 還有二進制,再比如XML啊等等很多,現在其他地方這種模式也用的越來越多起來。


    再來說一下CreateProtector方法簽名中的 purpose 這個字符串,在上一篇博文中為了讀者好理解,我把傳入的purpose說成可以理解為一個公鑰,其實這個說法是不嚴謹的,可以理解為一個標識,指示當前Protector的用途。
    在使用IDataProtector的時候,會發現它還有一些擴展方法位于Microsoft.AspNetCore.DataProtection命名空間下:
    public static class DataProtectionCommonExtensions
    {
    public static IDataProtector CreateProtector(this IDataProtectionProvider provider, IEnumerable<string> purposes);
    public static IDataProtector CreateProtector(this IDataProtectionProvider provider, string purpose, params string[] subPurposes);
    public static IDataProtector GetDataProtector(this IServiceProvider services, IEnumerable<string> purposes);
    public static IDataProtector GetDataProtector(this IServiceProvider services, string purpose, params string[] subPurposes);
    public static string Protect(this IDataProtector protector, string plaintext);
    public static string Unprotect(this IDataProtector protector, string protectedData);
    }

    可以看到,CreateProtector還提供了可以傳多個purpose的方法(IEnumerable,params string[]),為什么會有這種需求呢?


    其實DataProtector是有層次結構的,再看一下IDataProtector接口,它自身也實現了IDataProtectionProvider接口,就是說IDataProtector自身也可以再創建IDataProtector。


    舉個例子:我們在做一個消息通訊的系統,在消息通訊的過程中,需要對用戶的會話進行加密,我們使用CreateProtector("Security.BearerToken")加密。但是加密的時候并不能保證消息是不受信任的客戶端發過來的,所以想到了CreateProtector("username")來進行加密,這個時候假如有一個用戶的用戶名叫“Security.BearerToken”,那么就和另外一個使用Security.BearerToken作為標示的 Protector 沖突了,所以我們可以使用
    CreateProtector([ “Security.BearerToken”, “User: username” ])這種方式。它相當于
    provider.CreateProtector(“Security.BearerToken).CreateProtector(“User: username”)。 意思就是先創建一個Protector叫“Security.BearerToken”,然后再在purpose1下創建一個名為“User: username”的Protector。


    用戶密碼哈希


    在Microsoft.AspNetCore.Cryptography.KeyDerivation命名空間下提供了一個KeyDerivation.Pbkdf2方法用來對用戶密碼進行哈希。


    具有生命周期限制的加密


    有些時候,我們需要一些具有過期或者到期時間的加密字符串,比如一個用戶在找回密碼的時候,我們向用戶的郵箱發送一封帶有重置命令的一封郵件,這個重置命令就需要有一個過期時間了,超過這個過期時間后就失效,在以前我們可能需要向數據庫存儲一個時間來標記發送時間,然后再解密對比和數據庫的時間差來驗證。


    現在我們不需要這么做了,ASP.NET Core 默認提供了一個接口叫 ITimeLimitedDataProtector ,我們先看一下這個接口的定義:


    CreateProtector(string purpose) : ITimeLimitedDataProtector This API is similar to the existing IDataProtectionProvider.CreateProtector in that it can be used to create purpose chains from a root time-limited protector.
    Protect(byte[] plaintext, DateTimeOffset expiration) : byte[]
    Protect(byte[] plaintext, TimeSpan lifetime) : byte[]
    Protect(byte[] plaintext) : byte[]
    Protect(string plaintext, DateTimeOffset expiration) : string
    Protect(string plaintext, TimeSpan lifetime) : string
    Protect(string plaintext) : string

    ITimeLimitedDataProtector提供了數個重載方法用來設定帶有生命周期的加密方法,用戶可以通過Date TimeOffset,TimeSpan等參數來設置時間。


    有對應的加密,就有相對應的解密方法,在這里就不詳細介紹了。有興趣的同學可以去看一下官方文檔。


    配置數據保護


    在我們的 ASP.NET Core 運行的時候,系統會基于當前機器的運行環境默認配置一些關于 Data Protection 的東西,但是有些時候可能需要對這些配置做一些改變,比如在分布式部署的時候,在上一篇博文的末尾也提到過,下面就來看一下具體怎么配置的吧。


    上篇文章已經提到過,我們通過以下方式來把 Data Protection 注冊到服務中:


    public void ConfigureServices(IServiceCollection services)
    {
    services.AddDataProtection();
    }

    其中AddDataProtection 返回的是一個 IDataProtectionBuilder 接口,這個接口提供了一個擴展方法PersistKeysToFileSystem() 來存儲私鑰。可以通過它傳入一個路徑來指定私鑰存儲的位置:


    public void ConfigureServices(IServiceCollection services)
    {
    services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));
    }

    可以傳入一個共享文件夾,來存儲私鑰,這樣在不同機器的私鑰就可以保存到一個位置了。可以通過此種方式在分布式部署的時候,隔離開了機器的差異化。
    如果你覺得不安全,還可以配置一個X.509證書來,進行加密:


    public void ConfigureServices(IServiceCollection services)
    {
    services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
    .ProtectKeysWithCertificate("thumbprint");
    }

    上篇文章講過,Data Protection 的默認保存時間是90天,你可以通過以下方式來修改默認的保存時間:


    public void ConfigureServices(IServiceCollection services)
    {
    services.AddDataProtection()
    .SetDefaultKeyLifetime(TimeSpan.FromDays(14));
    }

    默認情況下,即使使用相同的物理密鑰庫,Data Protection 也會把不同的應用程序隔離開,因為這樣可以防止從一個應用程序獲取另外一個應用程序的密鑰。所以如果是相同的應用程序,可以設置相同的應用程序名稱:


    public void ConfigureServices(IServiceCollection services)
    {
    services.AddDataProtection()
    .SetApplicationName("my application");
    }

    有時候需要禁用應用程序生成密鑰,或者是說我只有一個程序用來生成或者管理密鑰,其他程序只是負責讀的話,那么可以這樣:


    public void ConfigureServices(IServiceCollection services)
    {
    services.AddDataProtection()
    .DisableAutomaticKeyGeneration();
    }

    修改加密算法


    可以使用UseCryptographicAlgorithms方法來修改ASP.NET Core Data Protection的默認加密算法,如下:


    services.AddDataProtection()
    .UseCryptographicAlgorithms(new AuthenticatedEncryptionSettings()
    {
    EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
    ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
    });

    總結


    本篇主要是介紹了一些常用的API, 下篇介紹一些高級的用法。





    本文地址:http://www.cnblogs.com/savorboard/p/dotnet-core-data-protection.html
    作者博客:Savorboard
    歡迎轉載,請在明顯位置給出出處及鏈接



    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(56)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:41
    • ASP.NET Core 優雅的在開發環境保存機密(User Secrets)

    image
    文章出處
    前言
    在應用程序開發的過程中,有的時候需要在代碼中保存一些機密的信息,比如加密密鑰,字符串,或者是用戶名密碼等。通常的做法是保存到一個配置文件中,在以前我們會把他保存到web.config中,但是在ASP.NET Core中,這一方式或許發生了改變,或者說你有更多多元化的方法, 以及更加優雅的的配置來設置或者保存這些機密資料。
    起初我以為這個UserSecrets它并沒有什么用,因為我有需要配置的地方我直接配置到appsetting.json文件中就可以了,直到一次開發過程中,我才感受到了它真正的用途。
    目錄
  • 用戶機密介紹

  • 如何添加用戶機密

  • 在應用程序中使用用戶機密

  • 總結

  • 用戶機密介紹
    有以下場景大家可以想一下在以前的代碼中我們是怎么樣處理的:
  • 需要保存一些和第三方網站對接的密鑰,比如和 微信,微博站點使用的 appkey

  • 給每個開發人員配置不用的用戶名密碼來訪問一些資源

  • 開發人員在開發過程中使用各自本機的數據庫,如何配置數據庫地址、賬號和密碼

  • 假設說最后一項,每個開發要使用自己本機的數據庫,你可能會說讓每個人修改自己的web.config,在提交代碼的時候不提交就行了。那么如果在web.config添加其他配置項的時候,顯然不提交web.config文件不合理的。
    現在,ASP.NET Core 提供了一種很優雅簡潔的方式 User Secrets 用來幫助我們解決這個事情。
    在新建一個 ASP.NET Core Web 應用程序的時候,會在 Startup.cs 文件中看到這樣一段代碼:
    public Startup(IHostingEnvironment env)
    {
    .....
    if (env.IsDevelopment())
    {
    builder.AddUserSecrets();
    }
    builder.AddEnvironmentVariables();
    }

    在 project.json 文件中,會看到 User Secrets 相關的一些配置
    {
    "userSecretsId": "aspnet-WebAppCore-e278c40f-15bd-4c19-9662-541514f02f3e"
    ...
    "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0",
    "Microsoft.Extensions.SecretManager.Tools": “1.0.0-preview2-final”
    }

    可以看到builder.AddUserSecrets這行代碼,他是在開發環境才運行的。
    userSecretsId是用來標識項目的User Secrets唯一性的,如果有兩個項目需要使用不同的Secrets ,這就需要有不同的userSecretsId。
    Microsoft.Extensions.SecretManager.Tools 主要是用來設置或者查看secrets的值。
    如何添加用戶機密
    可以在命令行中使用命令來添加:
  • 切換命令行窗口到程序的運行目錄, 輸入 dotnet user-secrets -h ,來查看可以使用的命令

  • 使用 dotnet user-secrets list 列出所有的用戶機密

  • 使用 dotnet user-secrets set WeChatAppKey "X3423FEED2435DD"設置一個用戶機密,其中 WebChatAppKey 為鍵,后面的是值。

  • 然后使用dotnet user-secrets list來查看設置的鍵值對。

  • 然后我又設置了一個數據庫的連接字符串進去。

  • 以上是使用命令行的方式來設置用戶機密,也可以使用 Visual Studio 2015代替命令行來做這項工作。
    Visual Studio中,在Web項目上右鍵,可以看到一個 管理用戶機密 的菜單:
    點擊打開時候,會出現一個secrets.json的文件,里面就是剛剛在命令行設置的鍵值對:
    有些同學可能會問既然是存儲到secrets.json,那么這個文件是在哪里呢?
    secrets.json的存儲位置?
    在非Windows系統中,它的存儲位置在
    ~/.microsoft/usersecrets/<userSecretsId>/secrets.json
    在Windows系統中,它的位置在
    C:\Users\用戶名\AppData\Roaming\Microsoft\UserSecrets\aspnet-WebAppCore-e278c40f-15bd-4c19-9662-541514f02f3e
    可以看到,存儲的上層文件夾就是project.json文件中的 userSecretsId 設定的值。
    在應用程序中使用用戶機密
    要在應用程序中訪問配置的用戶機密,你需要保證project.json文件中存在依賴項:
    Microsoft.Extensions.Configuration.UserSecrets 并且builder.AddUserSecrets()。
    然后在Startup.cs文件中通過 Configuration 對象訪問
    public IConfigurationRoot Configuration { get; }
    public void ConfigureServices(IServiceCollection services)
    {
    var wechatKey = Configuration["WeChatAppKey"]
    }

    你可以使用DI來將用戶機密映射到一個C#類文件,像這樣
    secrets.json
    {
    "SecretsKeys":
    {
    WeCharAppKey:"xxejfwert3045",
    WeboAppKey:"35402345lkefgjlkdfg",
    .....
    }
    }

    SecretsKeysConfig.cs
    public class SecretsKeysConfig
    {
    public string WeCharAppKey { get; set;}
    public string WeboAppKey { get; set;}
    // ......
    }

    Startup.cs
    public void ConfigureServices(IServiceCollection services)
    {
    services.Configure<SecretsKeysConfig>(Configuration.GetSection("SecretsKeys"));
    // 其他代碼
    }

    HomeController.cs
    public class HomeController : Controller
    {
    public SecretsKeysConfig AppConfigs { get; }
    public HomeController(IOptions<SecretsKeysConfig> appkeys)
    {
    AppConfigs = appkeys.Value;
    }
    }

    注意:如果你的appsetting.json文件中有和secrets.json文件中相同節點(沖突)的配置項,那么就會被secrets.json中的設置項給覆蓋掉,因為 builder.AddUserSecrets()晚于 AddJsonFile("appsettings.json")注冊, 那么我們可以利用這個特性來在每個開發人員的機器上重新設置數據庫連接字符串了。


    總結
    以上,或許可以感受到微軟在 ASP.NET Core 中對于開發人員還是非常貼心的,很多小細節都考慮到了,因此在我們構建應用程序的過程中,可以多使用這些小功能(特性)來讓我們的代碼更加的優雅~


    本文地址:http://www.cnblogs.com/savorboard/p/dotnetcore-user-secrets.html
    作者博客:Savorboard
    歡迎轉載,請在明顯位置給出出處及鏈接


    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(405)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:40
    • ASP.NET Core 數據保護(Data Protection 集群場景)【下】


    文章出處
    前言
    接【中篇】,在有一些場景下,我們需要對 ASP.NET Core 的加密方法進行擴展,來適應我們的需求,這個時候就需要使用到了一些 Core 提供的高級的功能。
    本文還列舉了在集群場景下,有時候我們需要實現自己的一些方法來對Data Protection進行分布式配置。
    加密擴展
    IAuthenticatedEncryptor 和 IAuthenticatedEncryptorDescriptor
    IAuthenticatedEncryptor是 Data Protection 在構建其密碼加密系統中的一個基礎的接口。
    一般情況下一個key 對應一個IAuthenticatedEncryptor,IAuthenticatedEncryptor封裝了加密操作中需要使用到的秘鑰材料和必要的加密算法信息等。
    下面是IAuthenticatedEncryptor接口提供的兩個 api方法:
    Decrypt(ArraySegment<byte> ciphertext, ArraySegment<byte> additionalAuthenticatedData) : byte[]
    Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData) : byte[]

    其中接口中的參數additionalAuthenticatedData表示在構建加密的時候提供的一些附屬信息。
    IAuthenticatedEncryptorDescriptor接口提供了一個創建包含類型信息IAuthenticatedEncryptor實例方法。
    CreateEncryptorInstance() : IAuthenticatedEncryptor
    ExportToXml() : XmlSerializedDescriptorInfo

    密鑰管理擴展
    在密鑰系統管理中,提供了一個基礎的接口IKey,它包含以下屬性:
    Activation
    creation
    expiration dates
    Revocation status
    Key identifier (a GUID)

    IKey還提供了一個創建IAuthenticatedEncryptor實例的方法CreateEncryptorInstance。
    IKeyManager接口提供了一系列用來操作Key的方法,包括存儲,檢索操作等。他提供的高級操作有:
  • 創建一個Key 并且持久存儲

  • 從存儲庫中獲取所有的 Key

  • 撤銷保存到存儲中的一個或多個鍵

  • XmlKeyManager
    通常情況下,開發人員不需要去實現IKeyManager來自定義一個 KeyManager。我們可以使用系統默認提供的XmlKeyManager類。
    XMLKeyManager是一個具體實現IKeyManager的類,它提供了一些非常有用的方法。
    public sealed class XmlKeyManager : IKeyManager, IInternalXmlKeyManager
    {
    public XmlKeyManager(IXmlRepository repository, IAuthenticatedEncryptorConfiguration configuration, IServiceProvider services);
    public IKey CreateNewKey(DateTimeOffset activationDate, DateTimeOffset expirationDate);
    public IReadOnlyCollection<IKey> GetAllKeys();
    public CancellationToken GetCacheExpirationToken();
    public void RevokeAllKeys(DateTimeOffset revocationDate, string reason = null);
    public void RevokeKey(Guid keyId, string reason = null);
    }

  • IAuthenticatedEncryptorConfiguration 主要是規定新 Key 使用的算法。

  • IXmlRepository 主要控制 Key 在哪里持久化存儲。

  • IXmlRepository
    IXmlRepository接口主要提供了持久化以及檢索XML的方法,它只要提供了兩個API:
  • GetAllElements() : IReadOnlyCollection

  • StoreElement(XElement element, string friendlyName)

  • 我們可以通過實現IXmlRepository接口的StoreElement方法來定義data protection xml的存儲位置。
    GetAllElements來檢索所有存在的加密的xml文件。

    接口部分寫到這里吧,因為這一篇我想把重點放到下面,更多接口的介紹大家還是去官方文檔看吧~


    集群場景
    上面的API估計看著有點枯燥,那我們就來看看我們需要在集群場景下借助于Data Protection來做點什么吧。
    就像我在【上篇】總結中末尾提到的,在做分布式集群的時候,Data Protection的一些機制我們需要知道,因為如果不了解這些可能會給你的部署帶來一些麻煩,下面我們就來看看吧。
    在做集群的時,我們必須知道并且明白關于 ASP.NET Core Data Protection 的三個東西:
    1、程序識別者
    “Application discriminator”,它是用來標識應用程序的唯一性。
    為什么需要這個東西呢?因為在集群環境中,如果不被具體的硬件機器環境所限制,就要排除運行機器的一些差異,就需要抽象出來一些特定的標識,來標識應用程序本身并且使用該標識來區分不同的應用程序。這個時候,我們可以指定ApplicationDiscriminator。
    在services.AddDataProtection(DataProtectionOptions option)的時候,ApplicationDiscriminator可以作為參數傳遞,來看一下代碼:
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddDataProtection();
    services.AddDataProtection(DataProtectionOptions option);
    }
    //===========擴展方法如下:
    public static class DataProtectionServiceCollectionExtensions
    {
    public static IDataProtectionBuilder AddDataProtection(this IServiceCollection services);
    //具有可傳遞參數的重載,在集群環境中需要使用此項配置
    public static IDataProtectionBuilder AddDataProtection(this IServiceCollection services, Action<DataProtectionOptions> setupAction);
    }
    // DataProtectionOptions 屬性:
    public class DataProtectionOptions
    {
    public string ApplicationDiscriminator { get; set; }
    }

    可以看到這個擴展返回的是一個IDataProtectionBuilder,在IDataProtectionBuilder還有一個擴展方法叫 SetApplicationName ,這個擴展方法在內部還是修改的ApplicationDiscriminator的值。也就說以下寫法是等價的:
    services.AddDataProtection(x => x.ApplicationDiscriminator = "my_app_sample_identity");
    services.AddDataProtection().SetApplicationName("my_app_sample_identity");

    也就是說集群環境下同一應用程序他們需要設定為相同的值(ApplicationName or ApplicationDiscriminator)。
    2、主加密鍵
    “Master encryption key”,主要是用來加密解密的,包括一客戶端服務器在請求的過程中的一些會話數據,狀態等。有幾個可選項可以配置,比如使用證書或者是windows DPAPI或者注冊表等。如果是非windows平臺,注冊表和Windows DPAPI就不能用了。
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddDataProtection()
    //windows dpaip 作為主加密鍵
    .ProtectKeysWithDpapi()
    //如果是 windows 8+ 或者windows server2012+ 可以使用此選項(基于Windows DPAPI-NG)
    .ProtectKeysWithDpapiNG("SID={current account SID}", DpapiNGProtectionDescriptorFlags.None)
    //如果是 windows 8+ 或者windows server2012+ 可以使用此選項(基于證書)
    .ProtectKeysWithDpapiNG("CERTIFICATE=HashId:3BCE558E2AD3E0E34A7743EAB5AEA2A9BD2575A0", DpapiNGProtectionDescriptorFlags.None)
    //使用證書作為主加密鍵,目前只有widnows支持,linux還不支持。
    .ProtectKeysWithCertificate();
    }

    如果在集群環境中,他們需要具有配置相同的主加密鍵。
    3、加密后存儲位置
    在【上篇】的時候說過,默認情況下Data Protection會生成 xml 文件用來存儲session或者是狀態的密鑰文件。這些文件用來加密或者解密session等狀態數據。
    就是上篇中說的那個私鑰存儲位置:

    1、如果程序寄宿在 Microsoft Azure下,存儲在“%HOME%\ASP.NET\DataProtection-Keys” 文件夾。
    2、如果程序寄宿在IIS下,它被保存在HKLM注冊表的ACLed特殊注冊表鍵,并且只有工作進程可以訪問,它使用windows的DPAPI加密。
    3、如果當前用戶可用,即win10或者win7中,它存儲在“%LOCALAPPDATA%\ASP.NET\DataProtection-Keys”文件夾,同樣使用的windows的DPAPI加密。
    4、如果這些都不符合,那么也就是私鑰是沒有被持久化的,也就是說當進程關閉的時候,生成的私鑰就丟失了。


    集群環境下:
    最簡單的方式是通過文件共享、DPAPI或者注冊表,也就是說把加密過后的xml文件都存儲在相同的地方。為什么說最簡單,因為系統已經給封裝好了,不需要寫多余的代碼了,但是要保證文件共享相關的端口是開放的。如下:
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddDataProtection()
    //windows、Linux、macOS 下可以使用此種方式 保存到文件系統
    .PersistKeysToFileSystem(new System.IO.DirectoryInfo("C:\\share_keys\\"))
    //windows 下可以使用此種方式 保存到注冊表
    .PersistKeysToRegistry(Microsoft.Win32.RegistryKey.FromHandle(null))
    }

    你也可以自己擴展方法來自己定義一些存儲,比如使用數據庫或者Redis等。
    不過通常情況下,如果在linux上部署的話,都是需要擴展的。下面來看一下我們想要用redis存儲,該怎么做呢?
    如何擴展加密鍵集合的存儲位置?
    首先,定義個針對IXmlRepository接口的 redis 實現類RedisXmlRepository.cs:
    public class RedisXmlRepository : IXmlRepository, IDisposable
    {
    public static readonly string RedisHashKey = "DataProtectionXmlRepository";
    private IConnectionMultiplexer _connection;
    private bool _disposed = false;
    public RedisXmlRepository(string connectionString, ILogger<RedisXmlRepository> logger)
    : this(ConnectionMultiplexer.Connect(connectionString), logger)
    {
    }
    public RedisXmlRepository(IConnectionMultiplexer connection, ILogger<RedisXmlRepository> logger)
    {
    if (connection == null)
    {
    throw new ArgumentNullException(nameof(connection));
    }
    if (logger == null)
    {
    throw new ArgumentNullException(nameof(logger));
    }
    this._connection = connection;
    this.Logger = logger;
    var configuration = Regex.Replace(this._connection.Configuration, @"password\s*=\s*[^,]*", "password=****", RegexOptions.IgnoreCase);
    this.Logger.LogDebug("Storing data protection keys in Redis: {RedisConfiguration}", configuration);
    }
    public ILogger<RedisXmlRepository> Logger { get; private set; }
    public void Dispose()
    {
    this.Dispose(true);
    }
    public IReadOnlyCollection<XElement> GetAllElements()
    {
    var database = this._connection.GetDatabase();
    var hash = database.HashGetAll(RedisHashKey);
    var elements = new List<XElement>();
    if (hash == null || hash.Length == 0)
    {
    return elements.AsReadOnly();
    }
    foreach (var item in hash.ToStringDictionary())
    {
    elements.Add(XElement.Parse(item.Value));
    }
    this.Logger.LogDebug("Read {XmlElementCount} XML elements from Redis.", elements.Count);
    return elements.AsReadOnly();
    }
    public void StoreElement(XElement element, string friendlyName)
    {
    if (element == null)
    {
    throw new ArgumentNullException(nameof(element));
    }
    if (string.IsNullOrEmpty(friendlyName))
    {
    friendlyName = Guid.NewGuid().ToString();
    }
    this.Logger.LogDebug("Storing XML element with friendly name {XmlElementFriendlyName}.", friendlyName);
    this._connection.GetDatabase().HashSet(RedisHashKey, friendlyName, element.ToString());
    }
    protected virtual void Dispose(bool disposing)
    {
    if (!this._disposed)
    {
    if (disposing)
    {
    if (this._connection != null)
    {
    this._connection.Close();
    this._connection.Dispose();
    }
    }
    this._connection = null;
    this._disposed = true;
    }
    }
    }

    然后任意一個擴展類中先定義一個擴展方法:
    public static IDataProtectionBuilder PersistKeysToRedis(this IDataProtectionBuilder builder, string redisConnectionString)
    {
    if (builder == null)
    {
    throw new ArgumentNullException(nameof(builder));
    }
    if (redisConnectionString == null)
    {
    throw new ArgumentNullException(nameof(redisConnectionString));
    }
    if (redisConnectionString.Length == 0)
    {
    throw new ArgumentException("Redis connection string may not be empty.", nameof(redisConnectionString));
    }
    //因為在services.AddDataProtection()的時候,已經注入了IXmlRepository,所以應該先移除掉
    //此處應該封裝成為一個方法來調用,為了讀者好理解,我就直接寫了
    for (int i = builder.Services.Count - 1; i >= 0; i--)
    {
    if (builder.Services[i]?.ServiceType == descriptor.ServiceType)
    {
    builder.Services.RemoveAt(i);
    }
    }
    var descriptor = ServiceDescriptor.Singleton<IXmlRepository>(services => new RedisXmlRepository(redisConnectionString, services.GetRequiredService<ILogger<RedisXmlRepository>>()))
    builder.Services.Add(descriptor);
    return builder.Use();
    }

    最終Services中關于DataProtection是這樣的:
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddDataProtection()
    // ================以下是唯一標識==============
    //設置應用程序唯一標識
    .SetApplicationName("my_app_sample_identity");
    // =============以下是主加密鍵===============
    //windows dpaip 作為主加密鍵
    .ProtectKeysWithDpapi()
    //如果是 windows 8+ 或者windows server2012+ 可以使用此選項(基于Windows DPAPI-NG)
    .ProtectKeysWithDpapiNG("SID={current account SID}", DpapiNGProtectionDescriptorFlags.None)
    //如果是 windows 8+ 或者windows server2012+ 可以使用此選項(基于證書)
    .ProtectKeysWithDpapiNG("CERTIFICATE=HashId:3BCE558E2AD3E0E34A7743EAB5AEA2A9BD2575A0", DpapiNGProtectionDescriptorFlags.None)
    //使用證書作為主加密鍵,目前只有widnows支持,linux還不支持。
    .ProtectKeysWithCertificate();
    // ==============以下是存儲位置=================
    //windows、Linux、macOS 下可以使用此種方式 保存到文件系統
    .PersistKeysToFileSystem(new System.IO.DirectoryInfo("C:\\share_keys\\"))
    //windows 下可以使用此種方式 保存到注冊表
    .PersistKeysToRegistry(Microsoft.Win32.RegistryKey.FromHandle(null))
    // 存儲到redis
    .PersistKeysToRedis(Configuration.Section["RedisConnection"])
    }

    在上面的配置中,我把所有可以使用的配置都列出來了哦,實際項目中應該視實際情況選擇。
    總結
    關于ASP.NET Core Data Protection 系列終于寫完了,其實這這部分花了蠻多時間的,對于Data Protection來說我也是一個循循漸進的學習過程,希望能幫助到一些人。
    如果您覺得本篇文章對你有用的話,不妨點個【推薦】。


    本文地址:http://www.cnblogs.com/savorboard/p/dotnetcore-data-protected-farm.html
    作者博客:Savorboard
    歡迎轉載,請在明顯位置給出出處及鏈接


    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(25)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:40
    • 免費開源分布式系統日志收集框架 Exceptionless

    image
    文章出處
    前言
    從去年就答應過Eric(Exceptionless的作者之一),在中國會幫助給 Exceptionless 做推廣,但是由于各種原因一直沒有做這件事情,在此對Eric表示歉意。:)
    Exceptionless 簡介
    Exceptionless 是一個開源的實時的日志收集框架,它可以應用在基于 ASP.NET,ASP.NET Core,Web Api,Web Forms,WPF,Console,MVC 等技術棧的應用程序中,并且提供了Rest接口可以應用在 Javascript,Node.js 中。它將日志收集變得簡單易用并且不需要了解太多的相關技術細節及配置。
    在以前,我們做日志收集大多使用 Log4net,Nlog 等框架,在應用程序變得復雜并且集群的時候,可能傳統的方式已經不是很好的適用了,因為收集各個日志并且分析他們將變得麻煩而且浪費時間。
    現在Exceptionless團隊給我們提供了一個更好的框架來做這件事情,我認為這是非常偉大并且有意義的,感謝他們。
    就讓我們一起來看看吧。
    官網:http://exceptionless.com/
    GitHub:https://github.com/exceptionless/Exceptionless
    Getting Started
    一、首先,需要去官網注冊一個帳號(打不開的同學你懂的),注冊完成之后登錄系統。

    ps :Exceptionless 的系統也可以部署到本地服務器哦


    二、按照提示,添加一個你的項目:
    然后可以看到一個下拉菜單,選擇項目的類型,可以看到 Exceptionless支持很多種項目。我們來選擇一個 ASP.NET Core 的項目:
    三、選擇完成之后,會有一個詳細的步驟,說明了如何做項目中使用。
  • 首先,使用 NuGet 添加一個包,名字叫Exceptionless.AspNetCore。

  • 在 ASP.NET Core 項目中,打開startup.cs文件,找到Configure()方法,添加如下:

  • using Exceptionless;
    ......
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
    // xxxxx 處填寫上圖畫紅線部分的key
    app.UseExceptionless("xxxxxxxxxxxxxxxxxxxxxxxxxx");
    app.UseStaticFiles();
    app.UseMvc();
    }

    至此,Exceptionless 已經可以在你的項目中工作了,它會自動記錄項目中的異常情況。
    在 Exceptionless 查看日志
    在 Getting Started 步驟,我們創建了一個 ASP.NET Core 項目,下面我們來運行一下,看看它是怎么工作的吧。
    打開 HomeController.cs文件,修改About的action方法,人為的制造一個異常信息:
    public IActionResult About() {
    throw new Exception("test exception");
    return View();
    }

    接下來,刷新 Exceptionless的頁面,在 Dashboard 主面板中,可以看到關于整個項目的一個異常情況,并且分別以幾種方式列了出來,其中包括分布圖,最頻繁的異常,最近的異常等等。
    這個我們剛才在Abount Action中制造的一個異常,Exceptionless已經記錄了下來,點進去之后可以看一下詳情:
    在上圖中,可以很直觀的看出異常的一些具體信息,除了一些基本的異常類型、時間和堆棧外,還包括訪問者的坐標、IP地址、發生異常的URL地址、瀏覽器信息,操作系統、甚至發生異常時請求的Cookie值。
    向 Exceptionless 發送事件
    除了我們所熟悉的異常信息外,Exceptionless 還可以記錄很多種類的其他信息,這些信息統稱做事件(Event)。
    在Exceptionless 中,有這幾類事件: Log (日志)、Feature Usages(功能用途)、404、Custom Event(自定義事件)。
    Exceptionless 中發送不同類型事件很簡單,代碼如下:
    using Exceptionless;
    // 發送日志
    ExceptionlessClient.Default.SubmitLog("Logging made easy");
    // 你可以指定日志來源,和日志級別。
    // 日志級別有這幾種: Trace, Debug, Info, Warn, Error
    ExceptionlessClient.Default.SubmitLog(typeof(Program).FullName, "This is so easy", "Info");
    ExceptionlessClient.Default.CreateLog(typeof(Program).FullName, "This is so easy", "Info").AddTags("Exceptionless").Submit();
    // 發送 Feature Usages
    ExceptionlessClient.Default.SubmitFeatureUsage("MyFeature");
    ExceptionlessClient.Default.CreateFeatureUsage("MyFeature").AddTags("Exceptionless").Submit();
    // 發送一個 404
    ExceptionlessClient.Default.SubmitNotFound("/somepage");
    ExceptionlessClient.Default.CreateNotFound("/somepage").AddTags("Exceptionless").Submit();
    // 發生一個自定義事件
    ExceptionlessClient.Default.SubmitEvent(new Event { Message = "Low Fuel", Type = "racecar", Source = "Fuel System" });

    手動發送一個已處理的異常
    有時候,我們在程序代碼中顯式的處理一些異常,這個時候可以手動的來將一些異常信息發送到Exceptionless。
    try
    {
    throw new ApplicationException(Guid.NewGuid().ToString());
    }
    catch (Exception ex)
    {
    ex.ToExceptionless().Submit();
    }

    為發送的事件添加額外的標記
    在發送一個事件的時候,可以為當前事件添加額外的上下文信息進行發送,如果添加坐標、標簽、屬性等等。
    try
    {
    throw new ApplicationException("Unable to create order from quote.");
    }
    catch (Exception ex)
    {
    ex.ToExceptionless()
    // 為事件設定一個編號,以便于你搜索
    .SetReferenceId(Guid.NewGuid().ToString("N"))
    // 添加一個不包含CreditCardNumber屬性的對象信息
    .AddObject(order, "Order", excludedPropertyNames: new [] { "CreditCardNumber" }, maxDepth: 2)
    // 設置一個名為"Quote"的編號
    .SetProperty("Quote", 123)
    // 添加一個名為“Order”的標簽
    .AddTags("Order")
    // 標記為關鍵異常
    .MarkAsCritical()
    // 設置一個地理位置坐標
    .SetGeo(43.595089, -88.444602)
    // 設置觸發異常的用戶信息
    .SetUserIdentity(user.Id, user.FullName)
    // 設置觸發用戶的一些描述
    .SetUserDescription(user.EmailAddress, "I tried creating an order from my saved quote.")
    // 發送事件
    .Submit();
    }

    統一處理發送的事件
    你可以默認的為ExceptionlessClient.Default.SubmittingEvent綁定額外的事件,來統一自定義一些處理。
    ExceptionlessClient.Default.SubmittingEvent += OnSubmittingEvent;
    private void OnSubmittingEvent(object sender, EventSubmittingEventArgs e) {
    // 僅處理未被處理過的異常
    if (!e.IsUnhandledError)
    return;
    // 忽略404事件
    if (e.Event.IsNotFound()) {
    e.Cancel = true;
    return;
    }
    // 獲取error對象
    var error = e.Event.GetError();
    if (error == null)
    return;
    // 忽略 401 或 `HttpRequestValidationException`異常
    if (error.Code == "401" || error.Type == "System.Web.HttpRequestValidationException") {
    e.Cancel = true;
    return;
    }
    // 忽略不是指定命名空間代碼拋出的異常
    var handledNamespaces = new List<string> { "Exceptionless" };
    if (!error.StackTrace.Select(s => s.DeclaringNamespace).Distinct().Any(ns => handledNamespaces.Any(ns.Contains))) {
    e.Cancel = true;
    return;
    }
    e.Event.AddObject(order, "Order", excludedPropertyNames: new [] { "CreditCardNumber" }, maxDepth: 2);
    e.Event.Tags.Add("Order");
    e.Event.MarkAsCritical();
    e.Event.SetUserIdentity(user.EmailAddress);
    }

    配合使用 NLog 或 Log4Net
    有時候,程序中需要對日志信息做非常詳細的記錄,比如在開發階段。這個時候可以配合 log4net 或者 nlog 來聯合使用 exceptionless,詳細可以查看這個 示例。
    如果你的程序中有在短時間內生成大量日志的情況,比如一分鐘產生上千的日志。這個時候你需要使用內存存儲(in-memory store)事件,這樣客戶端就不會將事件系列化的磁盤,所以會快很多。這樣就可以使用Log4net 或者 Nlog來將一些事件存儲到磁盤,另外 Exceptionless 事件存儲到內存當中。
    using Exceptionless;
    ExceptionlessClient.Default.Configuration.UseInMemoryStorage();

    總結
    本篇主要是對 Exceptionless 做了一個介紹,然后介紹了 Exceptionless 是怎么發送日志的。
    因為目前 Exceptionless 的站點在國外,并且站點里面調用了一些 google 的 api,所以可能在中國訪問起來有點麻煩,大家可以先科學上網體驗一下功能。畢竟 Exceptionless 是開源免費的并且服務端也是可以本地部署的。
    如果您覺得本文對您有幫助,想讓更多人了解Exceptionless,感謝您幫忙點的【推薦】。
    如果您對 Exceptionless 感興趣或者是想學習 Exceptionless 的代碼,可以加入群。
    Exceptionless QQ群:330316486。


    本文地址:http://www.cnblogs.com/savorboard/p/exceptionless.html
    作者博客:Savorboard
    歡迎轉載,請在明顯位置給出出處及鏈接


    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(96)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:40
    • .NET Core 1.0.1 升級匯總


    文章出處
    ASP.NET Core
    BUG fix:
    ASP.NET Routing
    Port fix for "Request not matching route with defaults" to 1.0.1 (#346)
    ASP.NET Antiforgery
    Antiforgery FIPS-compliant
    https://github.com/aspnet/Antiforgery/issues/95
    How do I modify the defaults for Antiforgery to make it FIPS-compliant
    ASP.NET KestrelHttpServer
    When HttpResponse.Body is replaced, the replacement is used for future requests
    ASP.NET MVC
    MVC doesn't work on FIPS-compliant machines (#5103)
    HTTP Verbs mapping error GET and DELETE (#5038)
    ViewComponentResult does not await the executor.ExecuteAsync method. (#4998)
    EntityFramework Core
    Query: Second level expand not returning correct data (#6366)
    Query: GroupJoin generates LEFT-JOIN and doesn't return all results (#6360)
    ModelBuilder: Confusing exception when mismatched property types are used in a relationship (#6260)
    Migrations: Using name with Script-Migration doesn't work (only ID) (#6228)
    Migrations: Script-Migration doesn't revert -From migration (#6126)
    Query: LoadAsync does not work (#6122)
    SaveChanges: Wrong save order for one to many in combination with multi level inheritance (#6055)
    Query: Wrong data in included navigation when using Skip() method (#5901)
    ModelBuilder: Exception when KeyAttribute used with inheritance (#5898)
    Migrations: RC2 to RTM Regression - Different properties with same name in derived classes cause exceptions (#5894)
    Query: Error using a compare with a nullable boolean (#5877)
    Query: Invalid SQL generated for Guid literals on SQLite (#5801)
    Globalization: Potential bugs due to not specifying culture in string.Format() (#5765)
    Query: Incorrect type mapping chosen for parameter - causes invalid SQL on Postgres (#5717)
    Query: Exception when filter uses subquery and query is executed asynchronously (#5640)
    Query: Exception when projecting navigation property value that could return null, but does not ("Argument Types do not match") (#5522)
    Query: Threading issues cause NullReferenceException in SimpleNullableDependentKeyValueFactory (#5456)
    Query: Exception when filtering on nullable boolean value (through navigation property) (#5454)
    Query: Exception when using OrderBy and navigation ("A column has been specified more than once in the order by list") (#5427)
    Query: Select after complex GroupJoin leads to unpredictable results (#4858)
    Query: Join flattening fails for some cases involving SelectMany (#4539)
    Query: Exception when using the "let" keyword and grouping (#3676)
    .NET Core
    CoreCLR
    5837 - When loading analyzer assemblies on CoreCLR on Mac/Linux PowerShell hits a segmentation fault.
    6016 - Linux kernel 4.6.x seg fault
    JIT
    6460 - In Jitstartup, JIT creates a file descriptor for stdout and unconditionally passes it to setmode, without checking for failures.
    CLI
    3950 - Update cshtml wildcard in publishOptions
    3948 - Update MVC version to 1.0.1
    3789 - Update F# dotnet-new templates for Preview 2
    (繼續閱讀...)
    文章標籤

    AutoPoster 發表在 痞客邦 留言(0) 人氣(5)

    • 個人分類:生活學習
    ▲top
    «1...789230»

    pop-under

    參觀人氣

    • 本日人氣:
    • 累積人氣:

    線上人數

    Marquee

    最新文章

    • 文章列表
    • jvm系列(四):jvm調優-命令大全(jps jstat jmap jhat jstack jinfo)
    • spring boot(一):入門篇
    • jvm系列(一):java類的加載機制
    • jvm系列(三):java GC算法 垃圾收集器
    • spring boot 實戰:我們的第一款開源軟件
    • jvm系列(六):jvm調優-從eclipse開始
    • 混合應用技術選型
    • jvm系列(二):JVM內存結構
    • spring boot(五):spring data jpa的使用

    熱門文章

    • (1,763)jQuery之前端國際化jQuery.i18n.properties
    • (1,001)Oracle Hint
    • (630)技術筆記:Indy控件發送郵件
    • (515)linux下安裝sqlite3
    • (501)學習筆記: Delphi之線程類TThread
    • (242)VC單選按鈕控件(Radio Button)用法(轉)
    • (104)單條件和多條件查詢
    • (51)淺談config文件的使用
    • (22)基于 Asp.Net的 Comet 技術解析
    • (15)Java中的抽象類

    文章分類

    • 生活學習 (2,296)
    • 未分類文章 (1)

    最新留言

    • [20/04/24] 我是女生想約炮 有男生願意給我溫暖的嗎?我賴是woyou58 於文章「(1)從底層設計,探討插件式GIS框架的...」留言:
      我叫黎兒女生最近內心掙扎著要不要約炮我的line:woy...

    文章搜尋

    文章精選

    誰來我家

    Live Traffic Feed