PIXNET Logo登入

互聯網 - 大數據

跳到主文

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

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

  • 相簿
  • 部落格
  • 留言
  • 名片
  • 3月 09 週四 201720:40
  • 免費開源的 .NET 分布式組件庫 Exceptionless Foundatio

image
文章出處
前言
在互聯網時代,分布式應用、系統變得越來越多,我們在使用 .Net 技術構建分布式系統的時候,需要使用到一些組件或者是助手庫來幫助我們提高生產力以及應用程序解耦,但是縱觀.Net圈,能夠符合要求的這樣的組件并不是很多,并且沒有一個通用的抽象組件能夠將這些接口集成起來,今天就為大家介紹一款開源的組件庫 Foundatio,他們同樣出自于Exceptionless團隊之手,下面就來看看吧。
目錄
  • Foundatio 介紹

  • Getting Started
    緩存
    隊列
    鎖
    消息
    工作任務
    文件存儲
    度量
    日志

  • 示例程序源碼

  • 總結

  • Foundatio 介紹
    GitHub : https://github.com/exceptionless/Foundatio
    Foundatio 是一個插件式的,松耦合的一套構建分布式應用的程序庫,出自于Exceptionless團隊。
    Foundatio 同時支持 .NET Framework 4.6 和 .NET Core。
    通過 Foundatio 我可以獲得哪些幫助呢?
  • 如果你是面對接口(抽象)構建的程序,你可以很容易的對接口實現進行切換。

  • 具有友好的依賴注入,還在使用 .Net Framework的朋友可以體驗一下,它具有比Autofac,Unity等更簡易的操作和更友好的接口。

  • 可以更加方便的使用緩存了,Foundatio幫助我們封裝了很多緩存的客戶端實現,比如RedisCache、InMemoryCache、ScopedCache等等。

  • 消息總線,你不必自己構建或者使用復雜且昂貴的NServiceBus了,很多時候我們僅僅需要的是一個可以在本地或者云上運行的簡單的消息總線。

  • 存儲,現在你可以很方便的通過一致的接口來使用分布式存儲了,包括內存文件存儲、文件夾文件存儲,Azure文件存儲,AWS S3文件存儲。

  • Foundatio 主要包含以下模塊:
  • 緩存(Caching)

  • 隊列(Queues)

  • 鎖(Locks)

  • 消息(Messaging)

  • 工作任務(Jobs)

  • 文件存儲(File Storage)

  • 度量(Metrics)

  • 日志(Logging)

  • 這些組件都以NuGet包的形式提供出來供我們很方便的使用,
    下面依次來看看每一個組件的用途和使用方法吧。
    Getting Started
    緩存
    緩存是一種空間換時間的技術,你可以通過緩存來快速的獲取一些數據。
    Foundatio Cache 提供了一致的接口ICacheClient 來很容易的存儲或者讀取緩存數據,并且提供了4中不同的緩存客戶端的實現。他們分別是:
    1、InMemoryCacheClient:內存緩存的實現,這種緩存的生命周期為當前進程, 有一個MaxItems屬性,可以設置最多緩存多少條數據。
    2、HybridCacheClient:InMemoryCacheClient 具有相同的實現,但是此接口提供、IMessageBus 可以用來跨線程之間的同步。
    3、RedisCacheClient:一個 Redis 客戶端的實現。
    4、RedisHybridCacheClient:一個RedisCacheClient 、InMemoryCacheClient 的混合實現,通過RedisMessageBus來保持內存緩存跨線程之間的同步。
    注意:如果本地緩存的項已經存在,在調用Redis進行序列化保存的時候可能會有性能問題。
    5、ScopedCacheClient:傳入ICacheClient和scope,scope 可以設置一個字符串,來制定一個緩存鍵前綴,這樣可以很方便的進行批量存儲和刪除。
    例子:
    using Foundatio.Caching;
    ICacheClient cache = new InMemoryCacheClient();
    await cache.SetAsync("test", 1);
    var value = await cache.GetAsync<int>("test");

    隊列
    提供了一個先進,先出的消息管道,Foundatio 提供了一個IQueue接口,并且擁有 4 種不同的隊列實現。
    1、InMemoryQueue:一個內存隊列實現,隊列的生命周期為當前進程。
    2、RedisQueue:一個 Redis 隊列實現。
    3、AzureServiceBusQueue:一個基于Azure的服務消息隊列實現。
    4、AzureStorageQueue:一個基于Azure的存儲隊列實現。
    例子:
    using Foundatio.Queues;
    IQueue<SimpleWorkItem> queue = new InMemoryQueue<SimpleWorkItem>();
    await queue.EnqueueAsync(new SimpleWorkItem {
    Data = "Hello"
    });
    var workItem = await queue.DequeueAsync();

    鎖
    鎖主要是確保無論在什么時候資源只被消費一次,Foundatio 提供了一個ILockProvider接口,并且有兩種不同的鎖機制的實現。
    1、CacheLockProvider:一個緩存鎖實現進程間通訊。
    2、ThrottlingLockProvider:只允許一定數量的請求進入ILockProvider的實現。你可以使用這個api調用外部服務,它將通過這個節氣門鎖來限制所有的請求。

    這里使用了Throttle這個單詞,中文意思是節氣門。為了方便理解給大家科普一下:在汽車的汽油機系統中,節氣門是一個很重要的組件,是用來控制氣體進入引擎的一套可控的閥門。
    流程大概就是 空氣-->節氣門-->氣缸,相當于是汽車發動機的咽喉,車子加速是否靈活,與節氣門的清潔是很有關系的。所以此處用了Throttle這個單詞,非常的形象。


    需要注意的時候,所有的鎖都是基于ICacheClient的,所以要確保你代碼中的鎖是否跨機器的。
    例子:
    using Foundatio.Lock;
    using Foundatio.Messaging;
    ILockProvider locker = new CacheLockProvider(new InMemoryCacheClient(), new InMemoryMessageBus());
    await locker.AcquireAsync("test");
    // ...
    ILockProvider locker = new ThrottlingLockProvider(new InMemoryCacheClient(), 1, TimeSpan.FromMinutes(1));
    ILock locks = await locker.AcquireAsync("test");
    // ...

    消息
    允許通過你的應用程序發布訂閱消息。Foundatio 提供了一個IMessageBus接口,并且有4 種不同的實現。
    1、InMemoryMessageBus:一個內存消息總線實現。這個消息總線的生命周期為當前進程。
    2、RedisMessageBus:一個 Redis 消息總線實現。
    3、RabbitMQMessageBus:一個 RabbitMQ 消息總線實現。
    4、AzureServiceBusMessageBus:一個Azure Service消息總線實現。
    例子
    using Foundatio.Messaging;
    public class Program
    {
    public static void Main(string[] args) {
    MessageBusTest();
    Console.ReadKey();
    }
    public static async void MessageBusTest() {
    IMessageBus messageBus = new InMemoryMessageBus();
    messageBus.Subscribe<SimpleMessageA>(msg => {
    Console.WriteLine(msg.Data);
    });
    await messageBus.PublishAsync(new SimpleMessageA { Data = "Hello" });
    }
    public class SimpleMessageA
    {
    public string Data { get; set; }
    }
    }

    工作任務
    允許你運行一個長時間的任務,并且不用擔心會被終止。Foundatio提供了一下不同的方法來定義一個Job。
    1、Jobs:提供了一、IJob 接口,和一個默認的基類JobBase。直接上代碼吧:
    using Foundatio.Jobs;
    public class HelloWorldJob : JobBase {
    public int RunCount { get; set; }
    protected override Task<JobResult> RunInternalAsync(JobRunContext context) {
    RunCount++;
    return Task.FromResult(JobResult.Success);
    }
    }
    var job = new HelloWorldJob();
    await job.RunAsync(); // job.RunCount = 1;
    await job.RunContinuousAsync(iterationLimit: 2); // job.RunCount = 3;
    await job.RunContinuousAsync(cancellationToken: new CancellationTokenSource(TimeSpan.FromMilliseconds(10)).Token); // job.RunCount > 10;

    2、Queue Processor Jobs:和上面的Jobs差不多,只是這個是基于隊列的。
    using Foundatio.Jobs;
    public class HelloWorldQueueJob : QueueJobBase<HelloWorldQueueItem> {
    public int RunCount { get; set; }
    public HelloWorldQueueJob(IQueue<HelloWorldQueueItem> queue) : base(queue) {}
    protected override Task<JobResult> ProcessQueueEntryAsync(QueueEntryContext<HelloWorldQueueItem> context) {
    RunCount++;
    return Task.FromResult(JobResult.Success);
    }
    }
    public class HelloWorldQueueItem {
    public string Message { get; set; }
    }
    // Register the queue for HelloWorldQueueItem.
    container.RegisterSingleton<IQueue<HelloWorldQueueItem>>(() => new InMemoryQueue<HelloWorldQueueItem>());
    // To trigger the job we need to queue the HelloWorldWorkItem message.
    // This assumes that we injected an instance of IQueue<HelloWorldWorkItem> queue
    var job = new HelloWorldQueueJob();
    await job.RunAsync(); // job.RunCount = 0; The RunCount wasn't incremented because we didn't enqueue any data.
    await queue.EnqueueAsync(new HelloWorldWorkItem { Message = "Hello World" });
    await job.RunAsync(); // job.RunCount = 1;
    await queue.EnqueueAsync(new HelloWorldWorkItem { Message = "Hello World" });
    await queue.EnqueueAsync(new HelloWorldWorkItem { Message = "Hello World" });
    await job.RunUntilEmptyAsync(); // job.RunCount = 3;

    3、Work Item Jobs:這種類型的Job適合于那些不經常發生,但是應該在一個Job(例如:刪除一個實體,有很多子級)。
    using System.Threading.Tasks;
    using Foundatio.Jobs;
    public class HelloWorldWorkItemHandler : WorkItemHandlerBase
    {
    public override async Task HandleItemAsync(WorkItemContext ctx) {
    var workItem = ctx.GetData<HelloWorldWorkItem>();
    // We can report the progress over the message bus easily.
    // To recieve these messages just inject IMessageSubscriber
    // and Subscribe to messages of type WorkItemStatus
    await ctx.ReportProgressAsync(0, "Starting Hello World Job");
    await Task.Delay(TimeSpan.FromSeconds(2.5));
    await ctx.ReportProgressAsync(50, String.Format("Reading value"));
    await Task.Delay(TimeSpan.FromSeconds(.5));
    await ctx.ReportProgressAsync(70, String.Format("Reading value."));
    await Task.Delay(TimeSpan.FromSeconds(.5));
    await ctx.ReportProgressAsync(90, String.Format("Reading value.."));
    await Task.Delay(TimeSpan.FromSeconds(.5));
    await ctx.ReportProgressAsync(100, workItem.Message);
    }
    }
    public class HelloWorldWorkItem
    {
    public string Message { get; set; }
    }
    // Register the shared job.
    var handlers = new WorkItemHandlers();
    handlers.Register<HelloWorldWorkItem, HelloWorldWorkItemHandler>();
    // Register the handlers with dependency injection.
    container.RegisterSingleton(handlers);
    // Register the queue for WorkItemData.
    container.RegisterSingleton<IQueue<WorkItemData>>(() => new InMemoryQueue<WorkItemData>());
    // The job runner will automatically look for and run all registered WorkItemHandlers.
    new JobRunner(container.GetInstance<WorkItemJob>(), instanceCount: 2).RunInBackground();
    await queue.EnqueueAsync(new HelloWorldWorkItem { Message = "Hello World" });

    文件存儲
    Foundatio File Storage 提供了一致的接、IFileStorage 來很容易的存儲或者讀取文件,并且提供了4中不同的文件存儲的實現。他們分別是:
    1、InMemoryFileStorage:基于內存的文件存儲,生命周期為當前進程。
    2、FolderFileStorage:文件夾存儲,存儲到硬盤中。
    3、AzureFileStorage:Azure Blob 存儲的實現。
    4、S3Storage:AWS S3 存儲的實現。
    建議在服務中注入IFileStorage接口的時候,使用單例方式注入。
    例子:
    using Foundatio.Storage;
    IFileStorage storage = new InMemoryFileStorage();
    await storage.SaveFileAsync("test.txt", "test");
    string content = await storage.GetFileContentsAsync("test.txt")

    度量
    關于Metrics的概念,可以參考 這篇博文。
    提供了一個IMetricsClient 接口,并且有 4 中不同的實現:
    1、InMemoryMetricsClient:內存 metrics 的實現。
    2、RedisMetricsClient:Redis metrics 的實現。
    3、StatsDMetricsClient:statsd metrics 的實現。
    4、MetricsNETClient:Metrics.NET 的實現。
    建議在服務中注入IMetricsClient接口的時候,使用單例方式注入。
    例子:
    await metrics.CounterAsync("c1");
    await metrics.GaugeAsync("g1", 2.534);
    await metrics.TimerAsync("t1", 50788);

    日志
    提供了一個流暢型的日志 api, 可用于在應用程序記錄日志消息。并且可以在不需要改變應用程序代碼的情況下,切換各個日志框架。
    例子:
    ILoggerFactory loggerFactory = new LoggerFactory();
    ILogger log = loggerFactory.CreateLogger("Program");
    log.Info("Application starting up"); // OR
    log.Info().Message("Application starting up").Write();
    log.Error(ex, "Writing a captured exception out to the log."); // Or
    log.Error().Exception(ex).Message("Writing a captured exception out to the log.").Write();

    示例程序源碼
    示例程序GitHub:
    https://github.com/exceptionless/Foundatio.Samples
    總結
    感謝 Exceptionless 團隊給我們提供了這么簡單易用的 Foundatio 框架,還在等什么,快在你的項目中用起來吧。
    如果您覺得本文對您有幫助,想讓更多人了解Exceptionless,感謝您幫忙點的【推薦】。
    如果您對 Exceptionless 感興趣或者是想學習 Exceptionless 的代碼,可以加入群
    Exceptionless QQ群:330316486。


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


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

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

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:40
    • ASP.NET Core MVC 配置全局路由前綴


    文章出處
    前言
    大家好,今天給大家介紹一個 ASP.NET Core MVC 的一個新特性,給全局路由添加統一前綴。嚴格說其實不算是新特性,不過是Core MVC特有的。
    應用背景
    不知道大家在做 Web Api 應用程序的時候,有沒有遇到過這種場景,就是所有的接口都是以 /api 開頭的,也就是我們的api 接口請求地址是像這樣的:
    http://www.example.com/api/order/333
    或者是這樣的需求
    http://www.example.com/api/v2/order/333
    在以前,我們如果要實現這種需求,可以在 Controller 中添加一個 [Route("/api/order")] 這樣的特性路由 Attribute,然后MVC 框架就會掃描你的路由表從而可以匹配到 /api/order 這樣的請求。
    但是第二個帶版本號的需求,原本 Controller 的 Route 定義是 [Route("/api/v1/order")],現在要升級到v2,又有上百個接口,這就需要一個一個修改,可能就會懵逼了。
    現在,有一種更加簡便優雅的方式來做這個事情了,你可以統一的來添加一個全局的前綴路由標記,下面就一起來看看吧。
    IApplicationModelConvention 接口
    首先,我們需要使用到 IApplicationModelConvention這個接口,位于 Microsoft.AspNetCore.Mvc.ApplicationModels 命名空間下,我們來看一下接口的定義。
    public interface IApplicationModelConvention
    {
    void Apply(ApplicationModel application);
    }

    我們知道,MVC 框架有一些約定俗成的東西,那么這個接口就是主要是用來自定義一些 MVC 約定的一些東西的,我們可以通過指定 ApplicationModel 對象來添加或者修改一些約定。可以看到接口提供了一個 Apply的方法,這個方法有一個ApplicationModel對象,我們可以利用這個對象來修改我們需要的東西,MVC 框架本身在啟動的時候會注入這個接口到 Services 中,所以我們只需要實現這個接口,然后稍加配置即可。
    那再讓我們看一下ApplicationModel 這個對象都有哪些東西:
    public class ApplicationModel : IPropertyModel, IFilterModel, IApiExplorerModel
    {
    public ApiExplorerModel ApiExplorer { get; set; }
    public IList<ControllerModel> Controllers { get; }
    public IList<IFilterMetadata> Filters { get; }
    public IDictionary<object, object> Properties { get; }
    }

    可以看到有 ApiExplorer,Controllers,Filters,Properties 等屬性。
  • ApiExplorerModel:主要是配置默認MVC Api Explorer的一些東西,包括Api的描述信息,組信息,可見性等。

  • ControllerModel:主要是 Comtroller 默認約定相關的了,這個里面東西就比較多了,就不一一介紹了,我們等下就要配置里面的一個東西。

  • IFilterMetadata :空接口,主要起到標記的作用。

  • 還有一個地方需要告訴大家的是,可以看到上面的 Controllers 屬性它是一個IList<ControllerModel>,也就是說這個列表中記錄了你程序中的所有 Controller 的信息,你可以通過遍歷的方式針對某一部分或某個 Controller 進行設置,包括Controller中的Actions的信息都可以通過此種方式來設置,我們可以利用這個特性來非常靈活的對 MVC 框架進行改造,是不是很炫酷。
    下面,我們就利用這個特性來實現我們今天的主題。謝謝你點的贊~ :)
    添加全局路由統一前綴
    沒有那么多廢話了,直接上代碼,要說的話全在代碼里:
    //定義個類RouteConvention,來實現 IApplicationModelConvention 接口
    public class RouteConvention : IApplicationModelConvention
    {
    private readonly AttributeRouteModel _centralPrefix;
    public RouteConvention(IRouteTemplateProvider routeTemplateProvider)
    {
    _centralPrefix = new AttributeRouteModel(routeTemplateProvider);
    }
    //接口的Apply方法
    public void Apply(ApplicationModel application)
    {
    //遍歷所有的 Controller
    foreach (var controller in application.Controllers)
    {
    // 已經標記了 RouteAttribute 的 Controller
    var matchedSelectors = controller.Selectors.Where(x => x.AttributeRouteModel != null).ToList();
    if (matchedSelectors.Any())
    {
    foreach (var selectorModel in matchedSelectors)
    {
    // 在 當前路由上 再 添加一個 路由前綴
    selectorModel.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(_centralPrefix,
    selectorModel.AttributeRouteModel);
    }
    }
    // 沒有標記 RouteAttribute 的 Controller
    var unmatchedSelectors = controller.Selectors.Where(x => x.AttributeRouteModel == null).ToList();
    if (unmatchedSelectors.Any())
    {
    foreach (var selectorModel in unmatchedSelectors)
    {
    // 添加一個 路由前綴
    selectorModel.AttributeRouteModel = _centralPrefix;
    }
    }
    }
    }
    }

    然后,我們就可以開始使用我們自己定義的這個類了。
    public static class MvcOptionsExtensions
    {
    public static void UseCentralRoutePrefix(this MvcOptions opts, IRouteTemplateProvider routeAttribute)
    {
    // 添加我們自定義 實現IApplicationModelConvention的RouteConvention
    opts.Conventions.Insert(0, new RouteConvention(routeAttribute));
    }
    }

    最后,在 Startup.cs 文件中,添加上面的擴展方法就可以了。
    public class Startup
    {
    public Startup(IHostingEnvironment env)
    {
    //...
    }
    public void ConfigureServices(IServiceCollection services)
    {
    //...
    services.AddMvc(opt =>
    {
    // 路由參數在此處仍然是有效的,比如添加一個版本號
    opt.UseCentralRoutePrefix(new RouteAttribute("api/v{version}"));
    });
    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
    //...
    app.UseMvc();
    }
    }

    其中,opt.UseCentralRoutePrefix 就是上面定義的那個擴展方法,此處路由參數仍然是可以使用的,所以比如你可以給你的接口指定一個版本號之類的東西。這樣之后,你的所有 Controller 的 RoteAttribute 都會添加上了這個前綴,這樣就完美解決了最開始的那個版本號的需求。他們看起來大概是這樣的:

    [Route("order")]
    public class OrderController : Controller
    {
    // 路由地址 : /api/v{version}/order/details/{id}
    [Route("details/{id}")]
    public string GetById(int id, int version)
    {
    //上面是可以接收到版本號的,返回 version 和 id
    return $"other resource: {id}, version: {version}";
    }
    }
    public class ItemController : Controller
    {
    // 路由地址: /api/v{version}/item/{id}
    [Route("item/{id}")]
    public string GetById(int id, int version)
    {
    //上面是可以接收到版本號的,返回 version 和 id
    return $"item: {id}, version: {version}";
    }
    }

    總結
    上面的黑體字,希望大家能夠理解并運用,這個例子只是實際需求中的很小的一個場景,在具體的項目中會有各種各樣正常或者非正常的需求,我們在做一個功能的時候要多多思考,其實 MVC 框架還有很多東西可以去學習,包括它的設計思想,擴展性等東西,都是需要慢慢領悟的。如果大家對 ASP.NET Core 感興趣,可以關注我一下,我會定期在博客中分享我的一些學習成果吧。
    感謝支持,如果你覺得這篇文章對你有幫助,謝謝你的【推薦】,晚安~。


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


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

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

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:40
    • ASP.NET Core 在 JSON 文件中配置依賴注入

    image
    文章出處
    前言
    在上一篇文章中寫了如何在MVC中配置全局路由前綴,今天給大家介紹一下如何在在 json 文件中配置依賴注入。
    在以前的 ASP.NET 4+ (MVC,Web Api,Owin,SingalR等)時候,都是提供了專有的接口以供使用第三方的依賴注入組件,比如我們常用的會使用 Autofac、Untiy、String.Net 等,這些第三放依賴注入組件基本上都提供了一套配置注入或者配置生命周期的方式,除了直接配置到類里面之外,還提供了要么使用 xml 文件,要么使用 json 等,那么在新的 ASP.NET Core 中微軟已經默認的給我們提供了一個依賴注入的功能,我們就不再需要借助于第三方組件來實現依賴注入了,但是有時候我們想在配置文件中來配置依賴注入,微軟本身的 DI 組件并沒有給我們提供一個可供配置的文件,那么我們就需要自己來實現這個配置項的功能。個人覺得其主要使用場景是一些在編譯時不能確定實現的,需要動態修改實現的地方。
    下面就來看看應該如何來做這件事情吧。
    Getting Started
    首先,在應用程序中我們創建一個接口,以供 DI使用:
    public interface IFoo
    {
    string GetInputString(string input);
    }

    然后,添加一個 IFoo 接口的實現 Foo
    public class Foo : IFoo
    {
    public string GetInputString(string input)
    {
    return $"輸入的字符串為:{ input }";
    }
    }

    接下來,我們需要把以上的 IFoo 接口和它的實現添加到 Startup.cs 文件中的ConfigureServices方法中,ConfigureServices 主要是用來配置依賴注入服務的。然后通過該方法提供的ISerciceCollection接口參數注入 Services。
    public void ConfigureServices(IServiceCollection services)
    {
    services.Add(new ServiceDescriptor(serviceType: typeof(IFoo),
    implementationType: typeof(Foo),
    lifetime: ServiceLifetime.Transient));
    }

    這里,我們使用到了 IServiceCollection 里面的 Add 方法,添加一個生命周期為瞬態的 IFoo 的實現。瞬態就是說在每次請求的時候都將創建一個Foo的實例。
    以上是默認微軟為我們提供的添加依賴注入的方法,下面我們來看一下怎么來改造成我們需要的使用 json 文件的方式。
    使用 json 文件配置 DI
    當我們使用json文件配置依賴注入的時候,可以選擇新建一個json文件,也可以直接使用 appsettings.json 文件。現在我們就直接在 appsettings.json 文件中添加關于DI的配置了。
    appsettings.json

    "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
    "Default": "Debug",
    "System": "Information",
    "Microsoft": "Information"
    }
    },
    "DIServices": [
    {
    "serviceType": "[namesapce].IFoo",
    "implementationType": "[namesapce].Foo",
    "lifetime": "Transient"
    }
    ]
    }

    首先,添加一個名為 “DIServices” 的數組節點,數組中包含一個或多個配置service的對象,serviceType代表服務接口的類型,implementationType接口的實現,lifetime 初始化實例的生命周期。
    注意:配置文件中的類型必須為全名稱,即包含命名空間。
    接下來,添加一個和Json文件配置項相對應的一個service類,這里我們需要使用 Newtonsoft 這個json庫。
    using Microsoft.Extensions.DependencyInjection;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Converters;
    public class Service
    {
    public string ServiceType { get; set; }
    public string ImplementationType { get;set; }
    [JsonConverter(typeof(StringEnumConverter))]
    public ServiceLifetime Lifetime { get; set; }
    }

    然后需要改造一下ConfigureServices,在 ConfigureServices 中讀取配置的 json文件即可。
    public void ConfigureServices(IServiceCollection services)
    {
    //services.Add(new ServiceDescriptor(serviceType: typeof(IFoo),
    // implementationType: typeof(Foo),
    // lifetime: ServiceLifetime.Transient));
    var jsonServices = JObject.Parse(File.ReadAllText("appSettings.json"))["DIServices"];
    var requiredServices = JsonConvert.DeserializeObject<List<Service>>(jsonServices.ToString());
    foreach (var service in requiredServices) {
    services.Add(new ServiceDescriptor(serviceType: Type.GetType(service.ServiceType),
    implementationType: Type.GetType(service.ImplementationType),
    lifetime: service.Lifetime));
    }
    }

    然后我們測試一下是否是可用的。
    測試
    打開 HomeController.cs ,添加注入項:
    public class HomeController : Controller
    {
    private readonly IFoo _foo;
    public HomeController(IFoo foo)
    {
    _foo = foo;
    }
    public IActionResult About()
    {
    ViewData["Message"] = _foo.GetInputString("Your application description page.");
    return View();
    }
    }

    在 HomeController的構造函數添加IFoo接口,然后在 About 的Action中使用。
    運行程序,打開頁面,點擊 About標簽
    總結
    以上即為在 ASP.NET Core 中配置依賴注入到json文件中,這只是一個簡單的實例,不要用在生產環境中。在實際的項目中你還需要處理關于讀取配置異常情況,服務是否存在的異常情況,生命周期等等這些問題。


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


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

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

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:40
    • ASP.NET 中的 Async/Await 簡介

    images
    文章出處

    本文轉載自MSDN
    作者:Stephen Cleary
    原文地址:https://msdn.microsoft.com/en-us/magazine/dn802603.aspx


    大多數有關 async/await 的在線資源假定您正在開發客戶端應用程序,但在服務器上有 async 的位置嗎?可以非常肯定地回答“有”。本文是對 ASP.NET 上異步請求的概念性概述,并提供了對最佳在線資源的引用。我不打算介紹 async 或 await 的語法;因為我已經在一篇介紹性的博客文章 (bit.ly/19IkogW) 以及一篇關于 async 最佳做法的文章 (msdn.microsoft.com/magazine/jj991977) 中介紹過了。本文將特別重點介紹 async 在 ASP.NET 上的工作原理。
    對于客戶端應用程序,如 Windows 應用商店、Windows 桌面和 Windows Phone 應用程序,async 的主要優點是出色的響應能力。這些類型的應用程序使用 async 主要是為了保證用戶界面的響應能力。對于服務器應用程序,async 異步的主要優點是不錯的可擴展性。Node.js 可擴展性的關鍵是其固有的異步本質;Open Web Interface for .NET (OWIN) 針對異步進行了全新設計;ASP.NET 也可以是異步的。Async:不僅僅適用于 UI 應用程序!
    同步與異步請求處理
    在深入探討異步請求處理程序之前,我想簡要地回顧同步請求處理程序在 ASP.NET 上的工作原理。在本例中,假設系統中的請求依賴于一些外部資源,如數據庫或 Web API。當收到請求時,ASP.NET 將其中的一個線程池線程分配給該請求。因為它是同步編寫,所以請求處理程序將同步調用該外部資源。這將阻止請求線程,直到返回對外部資源的調用。圖 1 說明了具有兩個線程的線程池,其中有一個線程被阻止,正在等待外部資源。
    圖1 等待同步方式對外部資源
    最后,返回該外部資源的調用,并且請求線程恢復處理該請求。當完成該請求,且準備好發送響應時,請求線程將返回到線程池中。
    這一切好倒是好,但是您的 ASP.NET 服務器獲得的請求總會超出它的線程能夠處理的數量。這時候,額外的請求必須等到有線程可用時才能夠運行。圖 2 說明的仍是該雙線程服務器,此時它收到三個請求。
    圖2 雙線程服務器接收三個請求
    在這種情況下,前兩個請求都被分配到線程池中的線程。每個請求都調用外部資源,于是阻止了它們的線程。第三個請求必須等待有線程可用時,才可以開始進行處理,但該請求已經在系統中。它的計時器一直在工作,它正處于發生 HTTP 錯誤 503(服務不可用)的危險之中。
    但是對此考慮一下:這第三個請求正在等待可用線程,與此同時系統中的另外兩個線程實際上什么都沒做。這些線程受到阻止,都在等待返回外部調用。它們確實沒有做任何實際工作;它們不處于運行狀態,也不占用任何 CPU 時間。這些線程被白白浪費掉,但還有請求處于需要中。以下是異步請求解決的情況。
    異步請求處理程序的操作方式與此不同。當收到請求時,ASP.NET 將其中的一個線程池線程分配給該請求。這一次,請求處理程序將異步調用該外部資源。當返回對外部資源的調用之前,已將此請求線程返回到線程池中。圖 3 說明當請求在異步等待外部資源時具有兩個線程的線程池。
    圖3 異步等待對外部資源
    重要的區別在于,在進行異步調用的過程中,已將請求線程返回到線程池中。當線程處于線程池中時,它不再與該請求相關聯。此時,當返回外部資源調用時,ASP.NET 將其線程池中的一個線程重新分配給該請求。該線程將繼續處理該請求。當請求完成時,該線程再次返回到線程池中。注意,對于同步處理程序,同一線程會用于該請求的整個生命周期;相反,對于異步處理程序,可以將不同的線程分配給同一請求(在不同的時間)。
    現在,如果三個請求都進來,服務器就可以輕松處理了。因為每當請求在等待異步工作時,線程就會被釋放到線程池中,它們可以自由處理新的以及現有的請求。異步請求可以讓數量較少的線程去處理數量較多的請求。因此,ASP.NET 上的異步代碼的主要優點是出色的可擴展性。
    為什么不增加線程池的大小?
    此時,總是會被問到:為什么不增加線程池的大小?答案有兩個:與阻止線程池線程相比,異步代碼擴展得更深層且更快。
    異步代碼的擴展性超過阻止線程,這是因為它使用的內存更少;在現代操作系統上每個線程池線程具有 1MB 的堆棧,外加一個不分頁的內核堆棧。這聽起來好像很多,但當您的服務器上有一大堆線程時,會發現其實不夠用。與此相反,異步操作的內存開銷要小得多。因此,使用異步操作的請求比使用阻止線程的請求面臨更少的內存壓力。異步代碼使您可以將更多的內存用于其他任務(例如緩存)。
    異步代碼在速度上比阻止線程更快,因為線程池的注入速度有限。截至發稿時,該速度為每兩秒鐘一個線程。注入速度有限是件好事;它避免了持續的線程構建和破壞。然而,考慮一下請求蜂擁而至時會發生什么。同步代碼很容易就會陷入癱瘓,因為請求將用光所有可用的線程,其余請求必須等待線程池有新的線程注入。而另一方面,異步代碼不需要有這樣的限制;它是“始終開放”的,可以這么說。異步代碼能夠更出色地響應請求量突然波動。
    請記住,異步代碼不會取代線程池。不應該只有線程池或異步代碼;而是要同時擁有線程池和異步代碼。異步代碼可以讓您的應用程序充分利用線程池。它使用現有的線程池,并把它提高到 11。
    線程執行異步工作怎么樣?
    我一直被人問到這個問題。這意味著,必須有一些線程阻止對外部資源進行 I/O 調用。因此,異步代碼釋放請求線程,但這只能以犧牲系統中另一個線程為代價吧?沒有,一點關系也沒有。
    要了解異步請求為什么擴展,我將跟蹤一個異步 I/O 調用的(簡化)示例。假設有一個請求需要寫入到文件中。請求線程調用異步寫入方法。WriteAsync 由基類庫 (BCL) 實現,并使用其異步 I/O 的完成端口。因此,WriteAsync 調用會作為異步文件寫入傳遞到 OS 中。然后,OS 與驅動程序堆棧進行通信,同時傳遞數據以寫入到 I/O 請求數據包 (IRP) 中。
    現在,有趣的事情發生了:如果設備驅動程序不能立即處理 IRP,就必須異步進行處理。因此,驅動程序告訴磁盤開始寫入,并將“掛起”響應返回到 OS 中。OS 將“掛起”響應傳遞到 BCL,然后 BCL 將一個不完整的任務返回到請求處理代碼。請求處理代碼等待將不完整的任務從該方法等處返回的任務。最后,請求處理代碼最終向 ASP.NET 返回一個不完整的任務,并且請求線程被釋放回線程池中。
    現在,考慮系統的當前狀態。已經分配了各種 I/O 結構(例如,任務實例和 IRP),而且它們都處在掛起/不完整的狀態。然而,沒有任何線程因等待寫入操作完成而受到阻止。ASP.NET、BCL、OS 以及設備驅動程序都沒有專門用于異步工作的線程。
    當磁盤完成寫入數據時,它通過中斷通知其驅動程序。該驅動程序會通知 OS 該 IRP 已經完成,并且 OS 會通過完成端口通知 BCL。線程池線程通過完成從 WriteAsync 返回的任務來響應該通知;這反過來又恢復異步請求代碼。在該完成通知階段中,短期“借用”了一些線程,但實際上沒有線程在寫入過程中受到阻止。
    本示例經過極大地簡化,但是要點突出:真正的異步工作并不需要線程。實際推送字節也無需占用 CPU 時間。還有一個輔助課程要了解。考慮一下在設備驅動程序的世界里,設備驅動程序如何做才能立即或異步處理 IRP。同步處理是不是一個選項。在設備驅動程序級別,所有重要的 I/O 都是異步的。許多開發人員的思維模式都是把用于 I/O 操作的“普通 API”認為是同步的,異步 API 作為一層建立在普通的同步 API 上。然而,這恰恰相反:實際上,普通 API是異步的;使用異步 I/O 實現的是正是同步 API!
    為什么沒有了異步處理程序?
    如果異步請求處理是如此完美,那它為什么還不可用?事實上,異步代碼非常適合擴展,因此從 Microsoft .NET Framework 形成之初到現在,ASP.NET 平臺一直支持異步處理程序和模塊。ASP.NET 2.0 引入了異步網頁,ASP.NET MVC 2 中 MVC 得到了異步控制器。
    然而,最近,異步代碼在編寫上總是有些問題,并且難于維護。許多公司便決定同步開發代碼、支付更大的服務器場或更昂貴的托管,這樣就會簡單一些。現在,出現了逆轉:在 ASP.NET 4.5 中,使用 async 和 await 的異步代碼幾乎與編寫同步代碼一樣簡單。由于大型系統遷移到云托管并要求更加有規模,越來越多的公司開始青睞 ASP.NET 上的 async 和 await。
    異步代碼不是靈丹妙藥
    異步請求處理盡管很強大,但它不會解決所有問題。關于 ASP.NET 上的 async 和 await 可以做什么的問題,存在一些常見的誤解。
    當一些開發人員了解 async 和 await 后,他們認為這是服務器代碼“讓步”于客戶端(例如瀏覽器)的一種方式。然而,ASP.NET 上的 async 和 await 只“讓步”于 ASP.NET 運行時;HTTP 協議保持不變,您對每個請求仍只有一個響應。如果在 async/await 之前您需要 SignalR 或 AJAX 或 UpdatePanel,那么在 async/await 之后仍然需要 SignalR 或 AJAX 或 UpdatePanel。
    使用 async 和 await 的異步請求處理可以幫助擴大您的應用程序規模。然而,這是在一臺服務器上的擴展;您可能仍然需要對擴展進行規劃。如果您確實需要擴展體系結構,將仍然需要考慮無狀態的冪等請求和可靠的隊列。Async/await 多少有所幫助:它們使您能夠充分利用服務器資源,所以您不需要經常進行擴展。但是,如果您確實需要向外擴展,您將需要一個合適的分布式體系結構。
    ASP.NET 上的 async 和 await 都是關于 I/O 的。它們非常適合讀取和寫入文件、數據庫記錄和 REST API。然而,它們不能很好地執行占用大量 CPU 的任務。您可以通過等待 Task.Run 開始一些背景工作,但這樣做沒有任何意義。事實上,通過啟發式干擾 ASP.NET 線程池會損害您的可擴展性。如果您要在 ASP.NET 上執行占用大量 CPU 的工作,最好的辦法是直接在請求線程上執行該工作。通常,不要將工作排隊送到 ASP.NET 上的線程池。
    最后,在整體上考慮系統的可擴展性。十年前,常見的體系結構要有一個可與后端的 SQL Server 數據庫進行通信的 ASP.NET Web 服務器。在這種簡單的體系結構中,通常數據庫服務器是可擴展性的瓶頸,而不是 Web 服務器。讓您的數據庫調用異步可能起不到幫助作用;當然您可以用它們來擴展 Web 服務器,但數據庫服務器將阻止整個系統的擴展。
    Rick Anderson 在他精彩的博客文章中針對異步數據庫調用給出案例,“我的數據庫調用應該是異步的嗎?”(bit.ly/1rw66UB)。以下是兩點支持論據:首先,異步代碼有難度(因而開發人員的時間成本比只是購買較大的服務器要高);其次,如果數據庫后端是瓶頸,那么擴展 Web 服務器沒有什么意義。在寫這篇文章時,這兩方面的論據非常有道理,但隨著時間的推移這兩個論據的意義已經慢慢弱化。首先,使用 async 和 await 編寫異步代碼更加容易了。其次,隨著全球逐步采用云計算,網站的數據后端逐漸得到擴展。諸如 Microsoft Azure SQL 數據庫、NoSQL 以及其他 API 之類的現代后端與單個 SQL Server 相比可以得到更進一步的擴展,從而將瓶頸又推回 Web 服務器。在這種情況下,async/await 可以通過擴展 ASP.NET 帶來巨大的優勢。
    在開始之前
    首先您需要知道只有 ASP.NET 4.5 支持 async 和 await。有一個叫做 Microsoft.Bcl.Async 的 NuGet 程序包可為 .NET Framework 4 啟用 async 和 await,但并不使用它;這將無法正常工作!其原因是,為了能與 async 和 await 更好地一起工作,ASP.NET 本身必須更改其管理異步請求處理的方式;NuGet 程序包中包含編譯器需要的所有類型,但不會修補 ASP.NET 運行時。沒有解決方法;您需要 ASP.NET 4.5 或更高版本。
    接下來,要知道,ASP.NET 4.5 在服務器上引入了“quirks 模式”。如果您創建一個新的 ASP.NET 4.5 項目,則不必擔心。但是,如果要將現有的項目升級到 ASP.NET 4.5,所有 quirk 都將被打開。我建議您​​通過編輯 web.config 并將 httpRuntime.targetFramework 設置為 4.5 把它們全部關閉。如果使用此設置的應用程序失敗(并且您不想花時間去修復它),至少您可以通過為 aspnet:UseTaskFriendlySynchronizationContext 的 appSetting 鍵添加值“true”來獲取 async/await 工作。如果您將 httpRuntime.targetFramework 設置為 4.5,則 appSetting 鍵不必要。Web 開發團隊已在 bit.ly/1pbmnzK 發表一篇關于這一新的“quirks 模式”的詳細信息的博客。提示: 如果您看到出現奇怪的行為或例外情況,并且您的調用堆棧包括 LegacyAspNetSynchronizationContext,那么您的應用程序正在這個“quirks 模式”下運行。LegacyAspNetSynchronizationContext 與異步不兼容;您在 ASP.NET 4.5 上需要常規的 AspNetSynchronizationContext。
    在 ASP.NET 4.5 中,所有的 ASP.NET 設置都針對異步請求設置了很好的默認值,但也有幾個其他設置您可能要更改。首先是 IIS 設置:考慮將 IIS/HTTP.sys 的隊列限制(應用程序池|高級設置|隊列長度)從默認的 1,000 提高到 5,000。另一個是 .NET 運行時設置:ServicePointManager.DefaultConnectionLimit,它的默認值是內核數量的 12 倍。DefaultConnectionLimit 限制到同一主機名的傳出連接數。
    關于中止請求的提示
    當 ASP.NET 同步處理一個請求時,它有一個非常簡單的機制可以中止請求(例如,如果請求超出其超時值):它會中止該請求的工作線程。這是有道理的,因為在同步領域,每個請求從開始到結束都使用同一個工作線程。中止線程對于 AppDomain 的長期穩定性而言尚不完美,因此默認情況下 ASP.NET 將定期回收您的應用程序,以保持干凈。
    對于異步請求,如果要中止請求,ASP.NET 并不會中止工作線程。相反,它會取消使用 CancellationToken 的請求。異步請求處理程序應該接受并遵守取消標記。大多數較新的框架(包括 Web API、MVC 和 SignalR)將構建并直接向您傳遞 CancellationToken;您需要做的就是把它聲明為一個參數。您也可以直接訪問 ASP.NET 標記;例如,HttpRequest.TimedOutToken 是當請求超時時取消的一個 CancellationToken。
    隨著應用程序遷移到云,中止請求就顯得更為重要。基于云的應用程序也越來越依賴于可能占用任意時間量的外部服務。例如,一種標準模式是使用指數回退來重試外部請求;如果您的應用程序依賴于類似這樣的多種服務,對您的請求處理在整體上應用一個超時上限不失為一個好方法。
    Async 支持的現狀
    針對 async 的兼容性問題,已對許多庫進行了更新。在版本 6 中已將 async 支持添加到實體框架(在 EntityFramework NuGet 程序包中)。不過,當以異步方式運行時,您必須要小心操作以避免延遲加載,因為延遲加載總是以同步方式執行。HttpClient(在 Microsoft.Net.Http NuGet 程序包中)是采用 async 理念設計而成的現代 HTTP 客戶端,是調用外部 REST API 的理想選擇;是 HttpWebRequest 和 WebClient 的現代版替代品。在 2.1 版本中,Microsoft Azure 存儲客戶端庫(在 WindowsAzure.Storage NuGet 程序包中)添加了異步支持。
    較新的框架(如 Web API 和 SignalR)對 async 和 await 提供全面的支持。個別 Web API 已圍繞 async 支持建立起整個管道:不僅有異步控制器,還有異步篩選器和處理程序。Web API 和 SignalR 有一個很平凡的異步故事:您可以“放手去做”然后“就會成功”。
    這給我們帶來了一個令人傷感的故事:如今,ASP.NET MVC 只是部分支持 async 和 await。有基本的支持——異步控制器的操作和取消工作正常。ASP.NET 網站上有關于如何使用 ASP.NET MVC 中的異步控制器操作的精彩教程 (bit.ly/1m1LXTx);這對于 MVC 上的 async 入門是絕佳的資源。不幸的是,ASP.NET MVC (目前)不支持異步篩選器 (bit.ly/1oAyHLc) 和異步子操作 (bit.ly/1px47RG)。
    ASP.NET Web 窗體是一個較舊的框架,但它也充分支持 async 和 await。并且,ASP.NET 網站上有關異步 Web 窗體的教程也是入門的絕佳資源 (bit.ly/Ydho7W)。有了 Web 窗體,異步支持可以選擇加入。您必須先將 Page.Async 設置為 true,然后您可以使用 PageAsyncTask 通過該頁面注冊異步工作(或者,您可以使用 async void 事件處理程序)。PageAsyncTask 也支持取消。
    如果您有一個自定義 HTTP 處理程序或 HTTP 模塊,那么 ASP.NET 現在也可以支持它們的異步版本。HTTP 處理程序是通過 HttpTaskAsyncHandler (bit.ly/1nWpWFj) 進行支持的,HTTP 模塊是通過 EventHandlerTaskAsyncHelper (bit.ly/1m1Sn4O) 進行支持的。
    截至發稿時,ASP.NET 團隊正在開發名為 ASP.NET vNext 的一個新項目。在 vNext 中,默認情況下整個管道是異步的。目前,該計劃將 MVC 和 Web API 合并到能夠全面支持 async/await(包括異步篩選器和異步視圖組件)的單一框架中。其他異步就緒框架(如 SignalR)會在 vNext 中找到一個自然的家。當然,未來是 async 的天下。
    尊重安全網
    ASP.NET 4.5 中引入了幾個新的“安全網”,幫助您捕捉應用程序中的異步問題。這些是默認情況下存在的,應當保留。
    當同步處理程序試圖執行異步工作時,您的 InvalidOperationException 會收到這樣的消息,“此時不能開始異步操作”。有兩個主要原因導致出現此異常。第一,Web 窗體頁有異步事件處理程序,但忽略了將 Page.Async 設置為 true。第二,同步代碼調用 async void 方法。這是也是避免 async void 的另一個原因。
    另一個安全網適用于異步處理程序:當異步處理程序完成請求,但 ASP.NET 檢測到異步工作尚未完成時,您的 InvalidOperationException 會收到這樣的消息,“異步模塊或處理程序已完成,但異步操作仍然處于掛起狀態”。這通常是由于異步代碼調用 async void 方法而導致的,但也可能是因為不當使用基于事件的異步模式 (EAP) 組件 (bit.ly/19VdUWu)。
    您還可以使用一個選項來關閉這兩個安全網:HttpContext.AllowAsyncDuringSyncStages(也可以在 web.config 中對它進行設置)。Internet 上的一些頁面建議您在看到這些異常時進行這樣的設置。我完全不同意。說真的,我不知道這怎么可行。禁用安全網是一個可怕的想法。我能夠想到的唯一可能的原因是,您的代碼可能已經進行了一些非常先進的異步處理(遠超我曾經嘗試過的范圍),您是一個多線程處理的天才。所以,如果您已經閱讀了整篇文章,邊打著呵欠邊想,“拜托,我可不是菜鳥”,那么我想你可以考慮禁用安全網。而對于我們中的其他人,這是一個非常危險的選項,除非您完全知曉后果,否則不應進行此設置。
    開始使用
    終于到最后了!準備好開始使用 async 和 await 了嗎?我很欣賞您的耐心。
    首先,查看本文的“異步代碼不是靈丹妙藥”一節以確保 async/await 對您的體系結構是有益的。接下來,將您的應用程序更新到 ASP.NET 4.5 并關閉 quirks 模式(此時若只為確保不發生中斷,運行它也可以)。這時,您便可以開始真正的同步/等待工作了。
    從“葉”開始。想想您的請求如何進行處理并標識任何基于 I/O 的操作,特別是基于網絡的操作。常見的示例是數據庫查詢和命令,以及對其他 Web 服務和 API 的調用。選擇一個來開始,并做一些調查來查找使用 async/await 執行該操作的最佳選擇。.NET Framework 4.5 中有許多內置 BCL 類型目前都已異步就緒;例如,SmtpClient 具有 SendMailAsync 方法。某些類型可以提供異步就緒更換;例如,HttpWebRequest 和 Web 客戶端可以用 HttpClient 來更換。如需要,請升級您的庫版本;例如,EF6 中的實體框架具有異步兼容方法。
    但是,要避免庫中的“假異步”。假異步是這樣一種現象:一個組件中具有一個異步就緒 API,而它只是通過將同步 API 封裝在線程池線程內來實現的。這對于實現 ASP.NET 上的可擴展性適得其反。假異步的一個典型示例就是 Newtonsoft JSON.NET,一個其他方面很出色的庫。最好不調用(假)異步版本來執行 JSON 的序列化;只需換做調用同步版本即可。假異步的一個棘手示例就是 BCL 文件流。當打開一個文件流時,必須以顯式方式打開以便于異步訪問;否則,它會使用假異步,同步阻止文件讀取和寫入操作中的線程池線程。
    選擇一個“葉”之后,就可以開始使用代碼中調用該 API 的方法,使之成為通過等待調用異步就緒 API 的異步方法。如果您調用的 API 支持 CancellationToken,您的方法應該采用 CancellationToken 并將其傳遞給 API 方法。
    只要將一個方法標記為異步,就應該更改其返回類型:void 變為“Task”,非 void 類型 T 變為“Task”。您會發現,所有該方法的調用者都需要變為異步,以使它們能夠等待任務,等等。此外,將 Async 附加到您方法的名稱中,以遵循基于任務的異步模式約定 (bit.ly/1uBKGKR)。


    允許 async/await 模式將您的調用堆棧向“Trunk”進行擴展。在 Trunk 中,您的代碼將與 ASP.NET 框架(MVC、Web 窗體,Web API)相連接。閱讀本文前面所述的“異步支持的現狀”一節中的相關教程,將您的異步代碼與框架進行集成。


    順便找出任何本地線程的狀態。由于異步請求可能會更改線程,所以本地線程狀態(如 ThreadStaticAttribute、ThreadLocal、線程數據插槽和 CallContext.GetData/SetData)將不可用。如果可能的話,請使用 HttpContext.Items 進行更換;或者您可以將不可變的數據存儲在 CallContext.LogicalGetData/LogicalSetData 中。


    以下是我發現的有用技巧:您可以(暫時)復制您的代碼來創建一個垂直分區。利用這種技術,您不用將同步方法更改為異步;可以復制整個同步方法,然后將副本改為異步。然后,您可以讓大多數應用程序保持使用同步方法,只創建異步的一個小垂直片即可。如果您想把異步作為概念證明來進行探索或只針對應用程序的一部分執行負載測試來體驗怎樣擴展您的系統,這會是一個很棒的主意。您可以具有一個完全異步的請求(或頁面),而應用程序的其余部分保持為同步。當然,您不希望對每一個方法都保留副本;最終,所有的 I/O 綁定代碼都將是異步的,可以刪除同步副本。


    總結


    我希望本文能夠幫助您了解 ASP.NET 上的異步請求的基礎概念。使用 async 和 await,可以使編寫能夠最大限度地利用其服務器資源的 Web 應用程序、服務和 API 變得比以往任何時候都更容易。Async 真是太棒了!




    Stephen Cleary 生活在密歇根州北部,他是一位丈夫、父親和程序員。他 16 年來一直從事多線程處理和異步編程的工作,自從第一個社區技術預覽版出現以來,他就一直在 Microsoft .NET Framework 中使用異步支持。他的主頁(包括博客)位于 stephencleary.com。


    衷心感謝以下 Microsoft 技術專家對本文的審閱:James McCaffrey


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

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

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:40
    • 性能測試工具 wrk 安裝與使用

    文章出處
    介紹
    今天給大家介紹一款開源的性能測試工具 wrk,簡單易用,沒有Load Runner那么復雜,他和 apache benchmark(ab)同屬于性能測試工具,但是比 ab 功能更加強大,并且可以支持lua腳本來創建復雜的測試場景。
    wrk 的一個很好的特性就是能用很少的線程壓出很大的并發量, 原因是它使用了一些操作系統特定的高性能 I/O 機制, 比如 select, epoll, kqueue 等。 其實它是復用了 redis 的 ae 異步事件驅動框架. 確切的說 ae 事件驅動框架并不是 redis 發明的, 它來至于 Tcl的解釋器 jim, 這個小巧高效的框架, 因為被 redis 采用而更多的被大家所熟知.
    wrk GitHub 源碼:https://github.com/wg/wrk
    安裝
    wrk只能運行于 Unix 類的系統上,也只能在這些系統上便宜,所以我們需要一個Linux或者macOs。
    不得不說,使用了 Win10之后方便很多。
    必備條件:
  • Win10 RS及以上版本

  • 啟用Ubuntu子系統

  • 1、Win10 系統通過bash命令,切換到Ubuntu子系統。
    然后需要安裝一下編譯工具,通過運行下面命令來安裝工具:
    # 安裝 make 工具
    sudo apt-get install make
    # 安裝 gcc編譯環境
    sudo apt-get install build-essential

    安裝 gcc 編譯環境的時候最好掛一下VPN,速度會快些。
    2、安裝完成之后使用 git 下載 wrk 的源碼到本地:
    https://github.com/wg/wrk.git
    3、切換到git的wrk目錄,然后使用make命令:
    cd /mnt/盤符/wrk目錄
    make

    編譯完成之后,目錄下面會多一個 wrk 的文件。
    測試
    使用以下命令來測試一下:
    ./wrk -c 1 -t 1 -d 1 http://www.baidu.com
    簡單說一下wrk里面各個參數什么意思?
  • -t 需要模擬的線程數

  • -c 需要模擬的連接數

  • --timeout 超時的時間

  • -d 測試的持續時間

  • 結果:
  • Latency:響應時間

  • Req/Sec:每個線程每秒鐘的完成的請求數


  • Avg:平均

  • Max:最大

  • Stdev:標準差

  • +/- Stdev: 正負一個標準差占比


  • 標準差如果太大說明樣本本身離散程度比較高. 有可能系統性能波動很大.


    如果想看響應時間的分布情況可以加上--latency參數
    image


    我們的模擬測試的時候需要注意,一般線程數不宜過多,核數的2到4倍足夠了。 多了反而因為線程切換過多造成效率降低, 因為 wrk 不是使用每個連接一個線程的模型, 而是通過異步網絡 I/O 提升并發量。 所以網絡通信不會阻塞線程執行,這也是 wrk 可以用很少的線程模擬大量網路連接的原因。
    在 wrk 的測試結果中,有一項為Requests/sec,我們一般稱之為QPS(每秒請求數),這是一項壓力測試的性能指標,通過這個參數我們可以看出應用程序的吞吐量。
    總結
    關于 wrk 已經介紹完畢了,之所以寫這篇文章的目的是為了接下來對 ASP.NET Core做一個性能對比測試(Java,NodeJS,Python等)時需要用到該工具,敬請大家期待。


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


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

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

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:40
    • ASP.NET Core 性能對比評測(ASP.NET,Python,Java,NodeJS)

    image
    文章出處
    前言
    性能是我們日常生活中經常接觸到的一個詞語,更好的性能意味著能給我們帶來更好的用戶體檢。比如我們在購買手機、顯卡、CPU等的時候,可能會更加的關注于這樣指標,所以本篇就來做一個性能評測。
    性能也一直是我們開發人員一直追求的一個目標,我們在做語言選擇,平臺選擇,架構選擇的過程中都需要在性能之間做衡量。
    同樣性能對 .NET Core 團隊來說也是至關重要的,一項新技術的誕生,除了對生產力的提高,還有技術團隊對性能的追求。
    今天,我們就來做一個對比測試,來看看微軟的這樣新技術性能到底怎么樣,俗話說的好:“是騾子是馬,拉出來溜溜”。
    下面讓我開始吧。
    目錄
  • 測試目標

  • 測試工具

  • 環境準備

  • 開始測試
    ASP.NET Core Kestrel vs ASP.NET Core IIS
    ASP.NET Core IIS vs ASP.NET IIS
    ASP.NET Core Kestrel vs ASP.NET IIS
    ASP.NET Core vs Python Django
    ASP.NET Core vs Java Servlet
    ASP.NET Core vs NodeJS

  • 總結

  • 測試目標
    在測試之前,我們必須要明確我們本次測試想達到的一個目標。本次測試主要是測試應用程序的一個吞吐量。其中QPS,并發數,響應時間是我們衡量吞吐量的幾個重要指標。
    以下是本次對比測試的任務目標:


    編號
    對比方
    系統環境
    宿主環境
    測試目標




    1
    ASP.NET Core vs ASP.NET Core
    Windows
    Kestrel vs IIS
    相同平臺不同宿主間性能差距


    2
    ASP.NET Core vs ASP.NET
    Windows
    IIS vs IIS
    相同平臺相同宿主不同框架間性能差距


    3
    ASP.NET Core vs ASP.NET
    Windows
    Kestrel vs IIS
    相同平臺不同宿主不同框架間性能差距


    4
    ASP.NET Core vs Python Django
    Linux
    Kestrel vs uwsgi
    相同平臺不同語言不同宿主不同框架間性能差距


    5
    ASP.NET Core vs Java Servlet
    Linux
    Kestrel vs Tomcat
    相同平臺不同語言不同宿主不同框架間性能差距


    6-1
    ASP.NET Core vs NodeJS Express
    Linux
    Kestrel vs self host
    相同平臺不同語言不同宿主不同框架間性能差距


    6-2
    ASP.NET Core vs NodeJS Koa
    Linux
    Kestrel vs self host
    相同平臺不同語言不同宿主不同框架間性能差距


    測試工具
    工欲善其事,必先利其器。
    首先我們需要一個壓力測試工具,本次我們使用 wrk,有關于wrk的介紹和使用,請查看我的 這篇博客。
    然后我們需要一個性能監控工具,因為wrk已經會給我們輸出吞吐量相關指標,所以我們只需要一個監控CPU,內存等的工具即可。本次我們使用 Windows 自帶的性能監視器。

    Windows 性能監視器的打開方式:開始-->運行-->perfmon
    PS: 在下面的監視器圖中如果你發現cpu并沒有100%,那是因為使用的虛擬機占用了一部分cpu,所以計算方式應該是虛擬機的cpu使用量+物理機cpu使用量。


    環境準備
    既然做測試,首先肯定是具有相同的運行環境,以下是本次測試使用到的軟件和硬件環境。
  • 軟硬件環境



  • 名稱
    操作系統
    職責
    CPU
    核心數
    內存




    物理機器1
    Windows 10 RS1
    Web Server && 負載生成
    Intel Core i5-4590
    4
    16G


    虛擬機器2
    Ubuntu Server 16.04
    Web Server
    Intel Core i5-4590
    2
    1G


    虛擬機器3
    Nano Server
    Web Server
    Intel Core i5-4590
    2
    1G


    其中 虛擬機器2 為 “物理機器1” 使用 win 10 的 Hyper-v 技術搭建的一個虛擬機,所以有幾個指標對于本次測試至關重要。
    虛擬機設置為了2個虛擬核心,以便于在壓力測試的過程中利用到多核特性。其中的虛擬機保留百分比,需要設置為100%,來分配兩個物理cpu所有資源給它。占綜系統資源百分比設置為50,也就是說虛擬機最多利用本地50%的CPU資源,虛擬機限制設置為100。
  • 源代碼

  • AspNet 在 GitHub 有一個開源的性能測試項目叫benchmarks,之前新聞中23倍的性能也是出自于本測試項目, 為了客觀,本次測試并不使用該項目,所有項目均我們自己新建,并且使用當前流行的框架,為了排除代碼因素的干擾,我們使用最簡單的 Hello World!。
    如果你覺得本代碼不夠客觀公正,歡迎在GitHub上Fork本項目,修改后給我提交PR,我會重新進行測試,并且更新本博客。
    GitHub: https://github.com/yuleyule66/AspNetCoreBenchmarksCompare
    開始測試
    wkr命令參數:
    wrk -t 2 -c 50 -d 20 --latency http://xxx
    因為已經分配了2個核心給虛擬機使用,所以開的是雙線程。使用這個參數是我經過多次測試,得到的一個最佳的模擬效果。
    1 - ASP.NET Core vs ASP.NET Core(Kestrel vs IIS)
    ASP.NET Core
  • 環境:物理機器1

  • OS:Windows 10 RS 1

  • Host:Kestrel

  • wrk -t 2 -c 50 -d 20 --latency http://localhost:5000
    Running 20s test @ http://localhost:5000
    2 threads and 50 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 5.49ms 21.72ms 358.18ms 98.99%
    Req/Sec 23.28k 1.98k 27.48k 92.13%
    Latency Distribution
    50% 0.00us
    75% 6.87ms
    90% 12.76ms
    99% 28.58ms
    913567 requests in 20.02s, 115.00MB read
    Requests/sec: 45636.43
    Transfer/sec: 5.74MB



    ASP.NET Core
  • 環境:物理機器1

  • OS:Windows 10 RS 1

  • Host:IIS 10.0

  • wrk -t 2 -c 50 -d 20 --latency http://localhost:5001
    Running 20s test @ http://localhost:5001
    2 threads and 50 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 5.30ms 5.81ms 22.24ms 76.75%
    Req/Sec 7.61k 455.21 8.12k 90.00%
    Latency Distribution
    50% 3.14ms
    75% 9.02ms
    90% 15.62ms
    99% 17.17ms
    302880 requests in 20.02s, 44.77MB read
    Requests/sec: 15130.97
    Transfer/sec: 2.24MB



    總結:
    QPS(Kestrel):45636.43
    QPS(IIS):15130.97
    這個結果難免令人詫異,程序部署在IIS上和使用Kestrel竟然差別如此之大,我們知道實際上即便部署在IIS上,實際上內部還是調用的Kestrel,但是測試結果告訴了我們答案。可能是由于IIS進一步的http封裝導致的吧,畢竟IIS提供了那么多的其他功能。
    以下是Windows的性能監視器,兩個的曲線圖差不多我就放一個了:
  • 紅色:CPU使用率

  • 藍色:內存使用率

  • 2 - ASP.NET Core vs ASP.NET(IIS vs IIS)
    ASP.NET Core
  • 環境:物理機器1

  • OS:Windows 10 RS

  • Host:IIS

  • wrk -t 2 -c 50 -d 20 --latency http://localhost:5001
    Running 20s test @ http://localhost:5001
    2 threads and 50 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 5.30ms 5.81ms 22.24ms 76.75%
    Req/Sec 7.61k 455.21 8.12k 90.00%
    Latency Distribution
    50% 3.14ms
    75% 9.02ms
    90% 15.62ms
    99% 17.17ms
    302880 requests in 20.02s, 44.77MB read
    Requests/sec: 15130.97
    Transfer/sec: 2.24MB



    ASP.NET
  • 環境:物理機器1

  • OS:Windows 10 RS

  • Host:IIS

  • .NET Framework 4.6 + MVC5

  • wrk -t 2 -c 50 -d 20 --latency http://localhost:10280
    Running 20s test @ http://localhost:10280
    2 threads and 50 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 4.94ms 5.58ms 22.82ms 80.90%
    Req/Sec 9.10k 444.04 9.42k 95.00%
    Latency Distribution
    50% 3.00ms
    75% 10.10ms
    90% 13.57ms
    99% 16.45ms
    362177 requests in 20.00s, 89.80MB read
    Requests/sec: 18104.50
    Transfer/sec: 4.49MB



    總結:
    QPS(ASP.NET Core + IIS):15130.97
    QPS(ASP.NET + IIS):18104.50
    看到這個結果的時候,其實我還是有一點小驚訝的,不僅僅是因為ASP.NET跑出了1.8K QPS這樣的成績,而是通過Stdev可以看出,ASP.NET 在應對高請求高并發的時候,還是相當的穩定的。這個結果說明了,在同樣Windows+IIS環境中,ASP.NET是具有優勢和競爭力的,可以預見 ASP.NET 應該還不會淘汰的太快。
    Windows性能圖我就不上了,基本上和上面一樣 CPU 100% 的使用率。
    3 - ASP.NET Core vs ASP.NET(Kestrel vs IIS)
    ASP.NET Core
  • 環境:物理機器1

  • OS:Windows 10 RS 1

  • Host:Kestrel

  • wrk -t 2 -c 50 -d 20 --latency http://localhost:5000
    Running 20s test @ http://localhost:5000
    2 threads and 50 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 5.49ms 21.72ms 358.18ms 98.99%
    Req/Sec 23.28k 1.98k 27.48k 92.13%
    Latency Distribution
    50% 0.00us
    75% 6.87ms
    90% 12.76ms
    99% 28.58ms
    913567 requests in 20.02s, 115.00MB read
    Requests/sec: 45636.43
    Transfer/sec: 5.74MB



    ASP.NET
  • 環境:物理機器1

  • OS:Windows 10 RS

  • Host:IIS

  • .NET Framework 4.6 + MVC5

  • wrk -t 2 -c 50 -d 20 --latency http://localhost:10280
    Running 20s test @ http://localhost:10280
    2 threads and 50 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 4.94ms 5.58ms 22.82ms 80.90%
    Req/Sec 9.10k 444.04 9.42k 95.00%
    Latency Distribution
    50% 3.00ms
    75% 10.10ms
    90% 13.57ms
    99% 16.45ms
    362177 requests in 20.00s, 89.80MB read
    Requests/sec: 18104.50
    Transfer/sec: 4.49MB



    總結
    QPS(ASP.NET Core + Kestrel):45636.43
    QPS(ASP.NET + IIS):18104.50
    這個結果應該是在預料之中的,大概是3倍的性能差距吧。但是我覺得和之前微軟宣傳的23倍的性能,是有很大差距的。
    4 - ASP.NET Core vs Python Django
    注意,以下我們開始使用到虛擬機器2了,我們要在Windows性能監控器里面查看CPU使用率,還需要再添加2個計數器。
    物理處理器 \Hyper-V Hypervisor Logical Processor(*) \ %Total Run Time
    虛擬處理器 \Hyper-V Hypervisor Virtual Processor(*) \ %Guest Run Time
    ASP.NET Core
  • 環境:虛擬機器2

  • OS:Linux

  • Host:Kestrel

  • wrk -t 2 -c 50 -d 20 --latency http://192.168.2.48:5000/
    Running 20s test @ http://192.168.2.48:5000/
    2 threads and 50 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 4.39ms 5.33ms 33.05ms 77.20%
    Req/Sec 13.43k 1.32k 17.95k 74.75%
    Latency Distribution
    50% 2.00ms
    75% 8.15ms
    90% 13.75ms
    99% 15.80ms
    534787 requests in 20.01s, 67.32MB read
    Requests/sec: 26730.83
    Transfer/sec: 3.37MB



    Python Django
  • 環境:虛擬機器2

  • OS:Linux

  • Host:uwsgi

  • Python 2.7.12 + Django 1.10.2

  • 服務端宿主運行命令:
    sudo uwsgi --http :8000 --file HelloWorldWebApp/wsgi.py --processes=2 --threads==2 --daemonize=/var/log/django.log
    結果:
    wrk -t 2 -c 50 -d 20 --latency http://192.168.2.48:8000
    Running 20s test @ http://192.168.2.48:8000
    2 threads and 50 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 23.40ms 12.23ms 78.13ms 74.81%
    Req/Sec 792.64 143.13 1.25k 67.10%
    Latency Distribution
    50% 21.16ms
    75% 31.25ms
    90% 38.26ms
    99% 53.75ms
    31591 requests in 20.09s, 3.01MB read
    Socket errors: connect 0, read 31591, write 0, timeout 0
    Requests/sec: 1572.64
    Transfer/sec: 153.67KB



    總結
    QPS(ASP.NET Core + Kestrel):26730.83
    QPS(Python Django + Kestrel ):1572.64
    不知道是我運行的方式不對還是怎么,這個差距還是蠻大的,大概是17倍的差距。看來Python Web 在做針對于做大請求并發情況下,還是弱了一點。
    5 - ASP.NET Core vs Java Servlet
    C# 和 JAVA 一直是兩大陣營的開發人員喜歡討論的話題,為了避免有陣營偏見,JAVA的源代碼是我委托我們一個JAVA同事編寫的,并且委托由他部署的,并且已經交代了他避免使用jsp,由Servlet直接輸出。
    ASP.NET Core
  • 環境:虛擬機器2

  • OS:Linux

  • Host:Kestrel

  • wrk -t 2 -c 50 -d 20 --latency http://192.168.2.48:5000/
    Running 20s test @ http://192.168.2.48:5000/
    2 threads and 50 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 4.39ms 5.33ms 33.05ms 77.20%
    Req/Sec 13.43k 1.32k 17.95k 74.75%
    Latency Distribution
    50% 2.00ms
    75% 8.15ms
    90% 13.75ms
    99% 15.80ms
    534787 requests in 20.01s, 67.32MB read
    Requests/sec: 26730.83
    Transfer/sec: 3.37MB



    Java Servlet
  • 環境:虛擬機器2

  • OS:Linux

  • Host:Tomcat 7.0 + jdk 1.7

  • wrk -t 2 -c 50 -d 20 --latency http://192.168.2.48:8080/j2eeWebApp/hello
    Running 20s test @ http://192.168.2.48:8080/j2eeWebApp/hello
    2 threads and 50 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 4.93ms 6.17ms 68.17ms 81.53%
    Req/Sec 9.22k 1.01k 14.06k 70.50%
    Latency Distribution
    50% 1.75ms
    75% 9.91ms
    90% 14.39ms
    99% 22.10ms
    367733 requests in 20.05s, 93.70MB read
    Requests/sec: 18338.73
    Transfer/sec: 4.67MB



    總結
    QPS(ASP.NET Core + Kestrel):26730.83
    QPS(Java Servlet + Tomcat):18338.73
    通過這個結果我們可以看出,在性能上 ASP.NET Core 已經超越了Java。不說太多了,怕被噴...
    6 - ASP.NET Core vs NodeJS
    ASP.NET Core
  • 環境:虛擬機器2

  • OS:Linux

  • Host:Kestrel

  • wrk -t 2 -c 50 -d 20 --latency http://192.168.2.48:5000/
    Running 20s test @ http://192.168.2.48:5000/
    2 threads and 50 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 4.39ms 5.33ms 33.05ms 77.20%
    Req/Sec 13.43k 1.32k 17.95k 74.75%
    Latency Distribution
    50% 2.00ms
    75% 8.15ms
    90% 13.75ms
    99% 15.80ms
    534787 requests in 20.01s, 67.32MB read
    Requests/sec: 26730.83
    Transfer/sec: 3.37MB



    NodeJS
  • 環境:虛擬機器2

  • OS:Linux

  • Host:self host

  • wrk -t 2 -c 50 -d 20 --latency http://192.168.2.48:1337
    Running 20s test @ http://192.168.2.48:1337
    2 threads and 50 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 4.40ms 5.23ms 31.25ms 79.47%
    Req/Sec 10.32k 0.88k 11.37k 90.25%
    Latency Distribution
    50% 2.08ms
    75% 8.32ms
    90% 13.19ms
    99% 15.93ms
    410902 requests in 20.02s, 61.13MB read
    Requests/sec: 20522.89
    Transfer/sec: 3.05MB

    ***************更新1:NodeJS 添加Web框架*******
    Express框架,cluster模式
    wrk -t 2 -c 30 -d 20 --latency http://192.168.2.48:1337
    Running 20s test @ http://192.168.2.48:1337
    2 threads and 30 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 1.97ms 1.45ms 32.23ms 83.97%
    Req/Sec 7.83k 0.90k 8.82k 91.50%
    Latency Distribution
    50% 2.00ms
    75% 2.50ms
    90% 3.50ms
    99% 6.00ms
    311896 requests in 20.01s, 66.03MB read
    Requests/sec: 15583.99
    Transfer/sec: 3.30MB

    Koa框架,cluster模式
    wrk -t 2 -c 30 -d 20 --latency http://192.168.2.48:1337
    Running 20s test @ http://192.168.2.48:1337
    2 threads and 30 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 1.74ms 0.86ms 13.59ms 86.65%
    Req/Sec 8.79k 804.39 9.98k 87.75%
    Latency Distribution
    50% 1.99ms
    75% 2.00ms
    90% 2.96ms
    99% 4.83ms
    349856 requests in 20.02s, 53.38MB read
    Requests/sec: 17478.73
    Transfer/sec: 2.67MB

    從測試結果可以看出,Koa框架性能略高于Express框架。
    **************End***********


    總結
    QPS(ASP.NET Core + Kestrel):26730.83
    QPS(NodeJS):20522.89 (非cluster模式)
    QPS(NodeJS Express):15583.99 (cluster模式)
    QPS(NodeJS Koa):17478.73 (cluster模式)
    這個結果著實讓我吃了一驚,NodeJS性能竟然如此驚人,比JAVA要快10%。作為一個解釋性語言這個性能可以說達到了極致,雖然在測試之前知道NodeJS采用的是異步IO,但還是被測試結果震驚了。
    ===========更新1=========
    NodeJS 在加入了Web框架之后,性能仍然不弱。


    不知道是不是因為NodeJS沒有經過什么Web框架,直接輸出的結果。所以我需要再加測一個ASP.NET Core 通過中間件直接輸入結果的性能,這次我要使用微軟的測試項目benchmarks。
    wrk -t 2 -c 50 -d 20 --latency http://192.168.2.48:5000/plaintext
    Running 20s test @ http://192.168.2.48:5000/plaintext
    2 threads and 50 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 3.69ms 5.03ms 18.30ms 80.38%
    Req/Sec 25.06k 4.14k 29.19k 83.33%
    Latency Distribution
    50% 806.00us
    75% 6.82ms
    90% 12.62ms
    99% 15.63ms
    1002476 requests in 20.10s, 126.20MB read
    Requests/sec: 49874.57
    Transfer/sec: 6.28MB

    My God !!!
    總結
    以下是測試結果的匯總統計:


    編號
    對比方
    系統環境
    宿主環境
    測試結果(QPS)




    1
    ASP.NET Core vs ASP.NET Core
    Windows
    Kestrel vs IIS
    45.6k vs 15.2k


    2
    ASP.NET Core vs ASP.NET
    Windows
    IIS vs IIS
    15.2k vs 18.2k


    3
    ASP.NET Core vs ASP.NET
    Windows
    Kestrel vs IIS
    45.6k vs 18.2k


    4
    ASP.NET Core vs Python Django
    Linux
    Kestrel vs uwsgi
    26.7k vs 1.57k


    5
    ASP.NET Core vs Java Servlet
    Linux
    Kestrel vs Tomcat
    26.7k vs 18.3k


    6-1
    ASP.NET Core vs NodeJS Express
    Linux
    Kestrel vs self host
    26.7k vs 15.6k


    6-2
    ASP.NET Core vs NodeJS Koa
    Linux
    Kestrel vs self host
    26.7k vs 17.5k


    作為微軟的下一代 ASP.NET 框架,ASP.NET Core沒有讓我們失望,通過本次測試,我們大概對ASP.NET Core的性能心里有底了。一個圈子的良好發展需要社區的共同參與,也希望大家共同為.NET Core社區貢獻自己的力量,同時也希望看到本篇文章的CTOs們以后在平臺和框架選擇的過程中考慮一下ASP.NET Core,因為她真的很優秀。
    如果你覺得本篇博客對您有幫助的話,感謝您的【推薦】,如果你對.NET Core感興趣可以關注我,我會定期在博客分享關于.NET Core的學習心得。


    ========更新1 :2016-10-17 感謝園友“幻天芒” 關于NodeJS的貢獻======
    有園友反應NodeJS項目沒有使用web mvc框架,所以特更新,同時感謝 "幻天芒" 在github向nodeJS項目提交的PR。
    1、添加node 多核cpu cluster 模式
    2、添加node koa框架和express框架測試
    更新測試結果。


    ========更新2 :2016-10-19 添加ASP.NET Core 在Windows Nano Server的測試結果======
    環境:虛擬機器3,和Linux硬件一樣
    wrk -t 2 -c 30 -d 20 --latency http://192.168.2.52:8000
    Running 20s test @ http://192.168.2.52:8000
    2 threads and 30 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 1.08ms 709.98us 31.25ms 77.30%
    Req/Sec 13.98k 1.38k 15.80k 87.75%
    Latency Distribution
    50% 1.00ms
    75% 1.03ms
    90% 2.00ms
    99% 3.45ms
    556354 requests in 20.03s, 70.04MB read
    Requests/sec: 27780.50
    Transfer/sec: 3.50MB

    這個測試結果和微軟的測試結果是一致的,Nano Server大概比在Linux上高出5-10%的性能。


    ========更新3 :2016-12-30 添加 WebListener 測試 ======
    WebListener 是基于 Http.sys 實現的非跨平臺只能運行于 Windows 的一個 web 服務器,其目的我覺得是為了替代iis的性能不足問題。

    引用自QQ群 Lyrics:我的理解是這樣的,Kestrel是跨平臺的,定義了一套通用的 feature,然而目前在windows平臺上,Kestrel所具備的feature 并沒有 http.sys 提供的那么強大,為了使老系統能順利遷移到core上面,微軟不得已搞了一個能支持所有http.sys 的web server,就是weblistener, weblistener 能完整的利用 http.sys 的特性,在windows上功能完整。


    測試結果:
    Windows ASP.NET Core Kestrel :35.5k
    Windows ASP.NET Core WebListener:27.9k
    Kestrl 大概比 WebListener 高出 5-10%的性能。


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


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

    AutoPoster 發表在 痞客邦 留言(0) 人氣(2,252)

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:40
    • 消息隊列 Kafka 的基本知識及 .NET Core 客戶端


    文章出處
    前言
    最新項目中要用到消息隊列來做消息的傳輸,之所以選著 Kafka 是因為要配合其他 java 項目中,所以就對 Kafka 了解了一下,也算是做個筆記吧。
    本篇不談論 Kafka 和其他的一些消息隊列的區別,包括性能及其使用方式。
    簡介
    Kafka 是一個實現了分布式的、具有分區、以及復制的日志的一個服務。它通過一套獨特的設計提供了消息系統中間件的功能。它是一種發布訂閱功能的消息系統。
    一些名詞
    如果要使用 Kafka ,那么在 Kafka 中有一些名詞需要知道,文本不討論這些名詞是否在其他消息隊列中具有相同的含義。所有名詞均是針對于 Kafka。
    Message
    消息,就是要發送的內容,一般包裝成一個消息對象。
    Topic
    通俗來講的話,就是放置“消息”的地方,也就是說消息投遞的一個容器。假如把消息看作是信封的話,那么 Topic 就是一個郵筒,如下圖所示:
    Partition && Log
    Partition 分區,可以理解為一個邏輯上的分區,像是我們電腦的磁盤 C:, D:, E: 盤一樣,
    Kafka 為每個分區維護著一份日志Log文件。
    每個分區是一個有序的,不可修改的,消息組成的隊列。 當消息過來的時候,會被追加到日志文件中,這個追加是根據 commit 命令來執行的。
    分區中的每一條消息都有一個編號,叫做 offset id,這個 id 在當前分區中是唯一的,并且是遞增的。
    日志,就是用來記錄分區中接收到的消息,因為每一個 Topic 可以同時向一個或者多個分區投遞消息,所以實際在存儲日志的時候,每個分區會對應一個日志目錄,其命名規則一般為 <topic_name>-<partition_id>, 目錄中就是一個分區的一份 commit log 日志文件。

    Kafka 集群會保存一個時間段內所有被發布出來的信息,無論這個消息是否已經被消費過,這個時間段是可以配置的。比如日志保存時間段被設置為2天,那么2天以內發布的消息都是可以消費的;而之前的消息為了釋放空間將會拋棄掉。Kafka的性能與數據量不相干,所以保存大量的消息數據不會造成性能問題。


    對日志進行分區主要是為了以下幾個目的:第一、這可以讓log的伸縮能力超過單臺服務器上線,每個獨立的partition的大小受限于單臺服務器的容積,但是一個topic可以有很多partition從而使得它有能力處理任意大小的數據。第二、在并行處理方面這可以作為一個獨立的單元。


    生產者 Producers
    和其他消息隊列一樣,生產者通常都是消息的產生方。
    在 Kafka 中它決定消息發送到指定Topic的哪個分區上。
    消費者 Consumers
    消費者就是消息的使用者,在消費者端也有幾個名詞需要區分一下。
    一般消息隊列有兩種模式的消費方式,分別是 隊列模式 和 訂閱模式。
    隊列模式:一對一,就是一個消息只能被一個消費者消費,不能重復消費。一般情況隊列支持存在多個消費者,但是對于一個消息,只會有一個消費者可以消費它。
    訂閱模式:一對多,一個消息可能被多次消費,消息生產者將消息發布到Topic中,只要是訂閱改Topic的消費者都可以消費。
    Consumer && Subscriber
    Group: 組,是一個消費者的集合,每一組都有一個或者多個消費者,Kafka 中在一個組內,消息只能被消費一次。
    在發布訂閱模式中,消費者是以組的方式進行訂閱的,就是Consumer Group,他們的關系如下圖:
    每個發布到Topic上的消息都會被投遞到每個訂閱了此Topic的消費者組中的某一個消費者,也就是每個組都會被投遞,但是每個組都只會有一個消費者消費這個消息。
    開頭介紹了Kafka 是 發布-訂閱 功能的消息隊列,所以在Kafka中,隊列模式是通過單個消費者組實現的,也就是整個結構中只有一個消費者組,消費者之間負載均衡。
    Kafka 集群
    Borker: Kafka 集群有多個服務器組成,每個服務器稱做一個 Broker。同一個Topic的消息按照一定的key和算法被分區存儲在不同的Broker上。
    上圖引用自:http://blog.csdn.net/lizhitao
    因為 Kafka 的集群它是通過將分區散布到各個Server的實現的,也就是說集群中每個服務器他們都是彼此共享分區的數據和請求,每個分區的日志文件被復制成指定分數,分散在各個集群機器,這樣來實現的故障轉移。

    對于每一個分區都會有一個服務器作為它的 "leader" 并且有零個或者多個服務器作為"followers" 。leader 服務器負責處理關于這個 partition 所有的讀寫請求, followers 服務器則被動的復制 leader 服務器。如果有 leader 服務器失效,那么 followers 服務器將有一臺被自動選舉成為新的 leader 。每個服務器作為某些 partition 的 leader 的同時也作為其它服務器的 follower ,從而實現了集群的負載均衡。


    .NET Core Kafka 客戶端
    在 .NET Core 中,有相對應的開源 kafka sdk 項目,就是 Rdkafka。它同時支持 .NET 4.5,并且支持跨平臺,可以運行于Linux,macOS 和 Windows。
    RdKafka Github :https://github.com/ah-/rdkafka-dotnet
    RdKafka Nuget :Install-Package RdKafka
    生產者 API
    // Producer 接受一個或多個 BrokerList
    using (Producer producer = new Producer("127.0.0.1:9092"))
    //發送到一個名為 testtopic 的Topic,如果沒有就會創建一個
    using (Topic topic = producer.Topic("testtopic")) {
    //將message轉為一個 byte[]
    byte[] data = Encoding.UTF8.GetBytes("Hello RdKafka");
    DeliveryReport deliveryReport = await topic.Produce(data);
    Console.WriteLine($"發送到分區:{deliveryReport.Partition}, Offset 為: {deliveryReport.Offset}");
    }

    消費者 API
    由于 Kafka 是以消費者組的形式進行消費的,所以需要指定一個GroupId。

    在內部實現上,消費者是通過一個輪詢機制來實現的對 Topic 消息的監控,這也是Kafka推薦的方式,在 Rdkafka 中輪詢的間隔為 1 秒鐘。



    //配置消費者組
    var config = new Config() { GroupId = "example-csharp-consumer" };
    using (var consumer = new EventConsumer(config, "127.0.0.1:9092")) {
    //注冊一個事件
    consumer.OnMessage += (obj, msg) =>
    {
    string text = Encoding.UTF8.GetString(msg.Payload, 0, msg.Payload.Length);
    Console.WriteLine($"Topic: {msg.Topic} Partition: {msg.Partition} Offset: {msg.Offset} {text}");
    };
    //訂閱一個或者多個Topic
    consumer.Subscribe(new[] { "testtopic" });
    //啟動
    consumer.Start();
    Console.WriteLine("Started consumer, press enter to stop consuming");
    Console.ReadLine();
    }



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


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

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

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:39
    • ASP.NET Core 中的那些認證中間件及一些重要知識點


    文章出處
    前言
    在讀這篇文章之間,建議先看一下我的 ASP.NET Core 之 Identity 入門系列(一,二,三)奠定一下基礎。
    有關于 Authentication 的知識太廣,所以本篇介紹幾個在 ASP.NET Core 認證中會使用到的中間件,還有Authentication的一些零碎知識點,這些知識點對于 ASP.NET 認證體系的理解至關重要。
    在 Github 中 ASP.NET Core 關于 Authentication 的實現有以下幾個包,那么這幾個包的功能分別是干什么用的呢?我們一一看一下。
  • Microsoft.AspNetCore.Authentication

  • Microsoft.AspNetCore.Authentication.Cookies

  • Microsoft.AspNetCore.Authentication.OAuth

  • Microsoft.AspNetCore.Authentication.OpenIdConnect

  • Microsoft.AspNetCore.Authentication.JwtBearer

  • Microsoft.AspNetCore.Authentication
    Authentication 在 ASP.NET Core 中,對于 Authentication(認證) 的抽象實現,此中間件用來處理或者提供管道中的 HttpContext 里面的 AuthenticationManager 相關功能抽象實現。
    HttpContext 中的 User 相關信息同樣在此中間件中被初始化。
    對于開發人員只需要了解此中間件中的這幾個對象即可:
  • AuthenticationOptions 對象主要是用來提供認證相關中間件配置項,后面的 OpenIdConnect,OAuth,Cookie 等均是繼承于此。

  • AuthenticationHandler 對請求流程中(Pre-Request)中相關認證部分提供的一個抽象處理類,同樣后面的其他幾個中間件均是繼承于此。

  • 在 AuthenticationHandler 中, 有幾個比較重要的方法:
  • HandleAuthenticateAsync :處理認證流程中的一個核心方法,這個方法返回 AuthenticateResult來標記是否認證成功以及返回認證過后的票據(AuthenticationTicket)。

  • HandleUnauthorizedAsync:可以重寫此方法用來處理相應401未授權等問題,修改頭,或者跳轉等。

  • HandleSignInAsync:對齊 HttpContext AuthenticationManager 中的 SignInAsync

  • HandleSignOutAsync:對齊 HttpContext AuthenticationManager 中的 SignOutAsync

  • HandleForbiddenAsync:對齊 HttpContext AuthenticationManager 中的 ForbidAsync,用來處理禁用等結果

  • 以上關于 AuthenticationHandler 我列出來的這些方法都是非常容易理解的,我們在繼承Authentication實現我們自己的一個中間件的時候只需要重寫上面的一個或者多個方法即可。
    還有一個 RemoteAuthenticationHandler 它也是繼承AuthenticationHandler,主要是針對于遠程調用提供出來的一個抽象,什么意思呢?因為很多時候我們的認證是基于OAuth的,也就是說用戶的狀態信息是存儲到Http Header 里面每次進行往來的,而不是cookie等,所以在這個類里面了一個HandleRemoteAuthenticateAsync的函數。
    Microsoft.AspNetCore.Authentication.Cookies
    Cookies 認證是 ASP.NET Core Identity 默認使用的身份認證方式,那么這個中間件主要是干什么的呢
    我們知道,在 ASP.NET Core 中已經沒有了 Forms 認證,取而代之的是一個叫 “個人用戶賬戶” 的一個東西,如下圖,你在新建一個ASP.ENT Core Web 應用程序的時候就會發現它,它實際上就是 Identity。那么Forms認證去哪里了呢?對,就是換了個名字叫 Identity。
    在此中間件中,主要是針對于Forms認證的一個實現,也就是說它通過Cookie把用戶的個人身份信息通過加密的票據存儲的Cookie中去,如果看過我之前Identity系列文章的話,那么應該知道用戶的個人身份信息就是 ClaimsIdentity 相關的東西。
    這個中間件引用了Authentication,CookieAuthenticationHandler 類繼承了 AuthenticationHandler 并重寫了 HandleAuthenticateAsync,HandleSignInAsync,HandleSignOutAsync,HandleForbiddenAsync,HandleUnauthorizedAsync 等方法,就是上一節中列出來的幾個方法。
    我們主要看一下核心方法 HandleAuthenticateAsync 在 Cookie 中間件怎么實現的:
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
    //讀取并解密從瀏覽器獲取的Cookie
    var result = await EnsureCookieTicket();
    if (!result.Succeeded)
    {
    return result;
    }
    // 使用上一步結果構造 CookieValidatePrincipalContext 對象
    // 這個對象是一個包裝類,里面裝著 ClaimsPrincipal、AuthenticationProperties
    var context = new CookieValidatePrincipalContext(Context, result.Ticket, Options);
    // 默認是空的實現,可以重寫來驗證 CookieValidatePrincipalContext 是否有異常
    await Options.Events.ValidatePrincipal(context);
    if (context.Principal == null)
    {
    return AuthenticateResult.Fail("No principal.");
    }
    // 表示是否需要刷新Cookie
    if (context.ShouldRenew)
    {
    RequestRefresh(result.Ticket);
    }
    return AuthenticateResult.Success(new AuthenticationTicket(context.Principal, context.Properties, Options.AuthenticationScheme));
    }

    總結一下就是解密Http請求中的Cookie信息,然后驗證Cookie是否合法,然后提取Cookie中的信息返回結果。
    還有一個方法就是 HandleSignInAsync ,根據名字可以看出主要是處理登入相關操作的,在這個方法里面主要是根據Claims信息生成加入過后的票據,同時會向票據中寫入過期時間,是否持久化等信息。 是否持久化的意思就是用戶在登陸界面是否勾選了 “記住我” 這個操作。
    Microsoft.AspNetCore.Authentication.OAuth
    OAuth 是針對于 OAuth 2.0 標準實現的一個客戶端程序,記住是客戶端,它不具備發放Token或者 Client_id ,Code 等的功能,它的作用是幫你簡化對OAuth2.0服務端程序的調用。 它對應 OAuth 2.0 標準中的 “獲取Access_Token” 這一步驟,如果對騰訊開放平臺QQ授權比較了解的話,就是對應 “使用Authorization_Code獲取Access_Token” 這一步驟。
    點擊 這里 查看圖片詳情。
    OAuth 實現的具體細節就不一一介紹了。
    Microsoft.AspNetCore.Authentication.OpenIdConnect
    獲取 OpenId 是OAuth 授權中的一個步驟,OpenId 它是具體的一個Token Key,不要把他理解成一種授權方式或者和OAuth不同的另外一種東西,他們是一體的。
    代碼上就不詳細說了,和上面的都差不多。主要說一下它們之間的區別或者叫聯系。
    OAuth 它主要是針對于授權(Authorization),而OpenID主要是針對于認證(Authentication),他們之間是互補的。
    那什么叫授權呢? 比如小明是使用我們網站的一個用戶,他現在要在另外一個網站使用在我們網站注冊的賬號,那授權就是代表小明在另外一個網站能夠做什么東西? 比如能夠查看資料,頭像,相冊等等,授權會給用戶發放一個叫 Access_Token 的東西。
    而認證關注的這個用戶是誰,它是用來證明用戶東西。比如小明要訪問它的相冊,那我們網站就需要小明提供一個叫OpenId的一個東西,我們只認這個OpenId。那小明從哪里得到它的這個OpenId呢,對,就是使用上一步的Access_Token 來換取這 個 OpenId ,以后訪問的時候不認 Access_Token ,只認識OpenId這個東西。
    一般情況下,OpenId 是需要客戶端進行持久化的,那么對應在 ASP.NET Core Identity 中,就是存儲在 UsersLogin 表里面的 ProviderKey 字段,懂了吧,懂了給個推薦唄
    Microsoft.AspNetCore.Authentication.JwtBearer
    JwtBearer 這個中間件是依賴于上一步的 OpenIdConnect 中間件的,看到了吧,其實這幾個中間件是環環嵌套的。
    可能很多同學聽說過 Jwt,但是大多數人都有一個誤區,認為JWT是一種認證方式,經常在QQ群里面聽過 前面一個同學在問 實際開發中前后端分離的時候安全怎么做的?,下面一個人回答使用JWT。
    其實JWT 它不是一種認證方式,也不是一種認證的技術,它是一個規范,一個標準。
    Jwt(Json Web Token)的官網是 https://jwt.io,下面是對JWT的一個說明

    JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
    JSON Web Tokens(JWT) 是一個開放的行業標準( RFC 7519),用于在雙方之間傳遞安全的Claims。


    JWT 在身份認證中的應用場景:

    在身份認證場景下,一旦用戶完成了登陸,在接下來的每個請求中包含JWT,可以用來驗證用戶身份以及對路由,服務和資源的訪問權限進行驗證。由于它的開銷非常小,可以輕松的在不同域名的系統中傳遞,所有目前在單點登錄(SSO)中比較廣泛的使用了該技術。


    好了,不過多的說了。 我們還是接著看一下 JwtBearer 中間件,同樣它重寫了 HandleAuthenticateAsync 方法。
    大致步驟如下:
  • 讀取 Http Request Header 中的 Authorization 信息

  • 讀取 Authorization 值里面的 Bearer 信息

  • 驗證 Bearer 是否合法,會得到一個 ClaimsPrincipal

  • 使用 ClaimsPrincipal 構建一個 Ticket(票據)

  • 調用 Options.Events.TokenValidated(context),用戶可以重寫此方法驗證Token合法性

  • 返回驗證成功

  • 其他的知識點
    這幾個中間件對會有對應的 Options 配置項,在這些配置項中,都會有 AuthenticationScheme, AutomaticAuthenticate, AutomaticChallenge 這幾個屬性,那這幾個東西都是干嘛的呢?
    AuthenticationScheme
    我在 《ASP.NET Core 之 Identity 入門(二)》 一文中提到過這個知識點,當時說很重要,這里可以看到了吧,每一種驗證中間件都會使用到這個東西,我比較偏向于把這個翻譯成 “認證方案”。
    我們知道,在 MVC 程序中一般通過在 Controller 或者 Action 上 打標記(Attribute)的方式進行授權,最典型的就是新建一個項目的時候里面的AccountController。
    [Authorize]
    public class AccountController : Controller
    {
    }

    在 Authorize 這個 Attribute 中,有一個屬性叫做 ActiveAuthenticationSchemes 的東西,那么這個東西是干什么用的呢?
    ActiveAuthenticationSchemes 就是對應著中間件Options里面配置的 AuthenticationScheme ,如果你不指定的話,在使用多個身份驗證組件的時候會有問題,會有什么問題呢?往下看
    AutomaticAuthenticate
    AutomaticAuthenticate 很簡單,是一個bool類型的字段,用來配置是否處理 AuthenticationHandler 是否處理請求。或者你可以理解為中間件是不是自動的處理認證相關的業務。
    AutomaticChallenge
    這個重要哦! 當我們使用多個身份驗證中間件的時候,那么就要用到這個配置項了,該配置項是用來設置哪個中間件會是身份驗證流程中的默認中間件,當代碼運行到 Controller 或者 Action 上的 [Authorize] 這個標記的時候,就會觸發身份驗證流程。默認情況下MVC的Filter會自動的觸發[Authorize],當然也有一種手動觸發Authorize的辦法就是使用HttpContext.Authentication.ChallengeAsync()。
    實際上,在驗證中間件的管道流程中,應該只有一個組件被設定為 AutomaticChallenge = true,但其實大多數的中間件這個參數默認都是 true ,這些中間件包括(Identity, Cookie, OAuth, OpenId, IISIntegration, WebListener)等, 這就導致了在整個驗證流程中會觸發多個中間件對其進行相應,這種沖突大部分不是用戶期望的結果。
    不幸的是,目前框架對于這種情況并沒有一個健壯的機制,如果開發人員對于這種機制不是很清楚的話,可能會造成很大的困擾。
    幸運的是,ASP.NET Core 團隊已經意識到了這個問題,他們將在 NET Standard 2.0 中對此重新進行設計,比如手動觸發的時候應該怎么處理,有多個的時候怎么處理,以及會添加一些語法糖。
    目前情況下,當有多個驗證中間件的時候,應該怎么處理呢?比如同時使用 Identity 和 JwtBearer。正確的做法是應該禁用掉除 Identity 以外的其他中間件的 AutomaticChallenge,然后指定調用的AuthenticationScheme。也就是說在Controller或者Action顯式指定 [Authorize(ActiveAuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] ,或者是可以指定一個策略來簡化授權調用 [Authorize("ApiPolicy")]
    services.AddAuthorization(options =>
    {
    options.AddPolicy("ApiPolicy", policy =>
    {
    policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
    policy.RequireAuthenticatedUser();
    });
    });

    而默認不帶參數的 [Authorize] 可以指定AuthorizationPolicie:
    services.AddAuthorization(options =>
    {
    options.DefaultPolicy = new AuthorizationPolicyBuilder("Identity.Application").RequireAuthenticatedUser().Build();
    });

    注意,手動調用 HttpContext.Authentication.ChallengeAsync() 不受 AuthorizationPolicie 影響。
    總結
    本篇介紹了 ASP.NET Core 有關 Authentication 的幾個中間件,然后還有幾個比較重要的知識點,這篇文章內容有點多,對于一些人來說可能需要一點時間消化。
    最后,如果你覺得這篇文章對你有幫助的話,謝謝你的【推薦】。
    如果你對 .NET Core 感興趣可以關注我,我會定期在博客分享關于 .NET Core 的學習心得。


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


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

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

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:39
    • 《C# 并發編程 &#183; 經典實例》讀書筆記


    文章出處
    前言
    最近在看《C# 并發編程 · 經典實例》這本書,這不是一本理論書,反而這是一本主要講述怎么樣更好的使用好目前 C#.NET 為我們提供的這些 API 的一本書,書中絕大部分是一些實例,在日常開發中還是經常會使用到。
    書中一些觀點還是比較贊同,比如作者說目前絕大多數的圖書對關于并發多線程等這些內容放到最后,而缺少一本介紹并發編程的入門指引和參考。另外一個觀點是絕大多數國內的技術人員認為技術越底層就牛逼,而做上層應用的就是“碼農”,作者反對了這一觀點,其實能利用好現有的庫也是一種能力,雖然說理解基礎知識對日常生活仍然有幫助,但最好從更高級的抽象概念來學習。
    異步基礎
    任務暫停,休眠
    異步方式暫停或者休眠任務,可以使用 Task.Delay();
    static async Task<T> DelayResult<T>(T result, TimeSpan delay) {
    await Task.Delay(delay);
    return result;
    }

    異步重試機制
    一個簡單的指數退避策略,重試的時間會逐次增加,在訪問 Web 服務時,一般采用此種策略。

    static async Task<string> DownloadString(string uri) {
    using (var client = new HttpClient()) {
    var nextDealy = TimeSpan.FromSeconds(1);
    for (int i = 0; i != 3; ++i) {
    try {
    return await client.GetStringAsync(uri);
    }
    catch {
    }
    await Task.Delay(nextDealy);
    nextDealy = nextDealy + nextDealy;
    }
    //最后重試一次,拋出出錯信息
    return await client.GetStringAsync(uri);
    }
    }

    報告進度
    異步操作中,經常需要展示操作進度,可以使用 IProcess<T> 和 Process<T>。

    static async Task MyMethodAsync(IProgress<double> progress) {
    double precentComplete = 0;
    bool done = false;
    while (!done) {
    await Task.Delay(100);
    if (progress != null) {
    progress.Report(precentComplete);
    }
    precentComplete++;
    if (precentComplete == 100) {
    done = true;
    }
    }
    }
    public static void Main(string[] args) {
    Console.WriteLine("starting...");
    var progress = new Progress<double>();
    progress.ProgressChanged += (sender, e) => {
    Console.WriteLine(e);
    };
    MyMethodAsync(progress).Wait();
    Console.WriteLine("finished");
    }

    等待一組任務
    同時執行幾個任務,等待他們全部完成
    Task task1 = Task.Delay(TimeSpan.FromSeconds(1));
    Task task2 = Task.Delay(TimeSpan.FromSeconds(2));
    Task task3 = Task.Delay(TimeSpan.FromSeconds(1));
    Task.WhenAll(task1, task2, task3).Wait();

    等待任意一個任務完成
    執行若干任務,只需要對其中一個的完成進行響應。主要用于對一個操作進行多種獨立的嘗試,只要其中一個嘗試完成,任務就算完成。
    static async Task<int> FirstResponseUrlAsync(string urlA, string urlB) {
    var httpClient = new HttpClient();
    Task<byte[]> downloadTaskA = httpClient.GetByteArrayAsync(urlA);
    Task<byte[]> downloadTaskB = httpClient.GetByteArrayAsync(urlB);
    Task<byte[]> completedTask = await Task.WhenAny(downloadTaskA, downloadTaskB);
    byte[] data = await completedTask;
    return data.Length;
    }

    集合
    不可變棧和隊列
    需要一個不會經常修改,可以被多個線程安全訪問的棧和隊列。他們的API和 Stack<T> 和 Queue<T> 非常相似。性能上,不可變棧(LIFO)和隊列(FIFO)與標準的棧和隊列具有相同的時間復雜度。但是在需要頻繁修改的簡單情況下,標準棧和隊列速度更快。
    在內部實現上,當對一個對象進行覆蓋(重新賦值)的時候,不可變集合采用的是返回一個修改過的集合,原始集合引用是不變化的,也就是說如果另外一個變量引用了相同的對象,那么它(另外的變量)是不會變化的。
    ImmutableStack
    var stack = ImmutableStack<int>.Empty;
    stack = stack.Push(11);
    var biggerstack = stack.Push(12);
    foreach (var item in biggerstack) {
    Console.WriteLine(item);
    } // output: 12 11
    int lastItem;
    stack = stack.Pop(out lastItem);
    Console.WriteLine(lastItem); //output: 11

    實際上,兩個棧內部共享了存儲 11 的內存,這種實現方式效率很高,而且每個實例都是線程安全的。
    ImmutableQueue
    var queue = ImmutableQueue<int>.Empty;
    queue = queue.Enqueue(11);
    queue = queue.Enqueue(12);
    foreach (var item in queue) {
    Console.WriteLine(item);
    } // output: 11 12
    int nextItem;
    queue = queue.Dequeue(out nextItem);
    Console.WriteLine(nextItem); //output: 11

    不可變列表和集合
    ImmutableList

    時間復雜度




    操作
    List
    ImmutableList




    Add
    O(1)
    O(log N)


    Insert
    O(log N)
    O(log N)


    RemoveAt
    O(log N)
    O(log N)


    Item[index]
    O(1)
    O(log N)


    有些時候需要這樣一個數據結構:支持索引,不經常修改,可以被多線程安全的訪問。
    var list = ImmutableList<int>.Empty;
    list = list.Insert(0, 11);
    list = list.Insert(0, 12);
    foreach (var item in list) {
    Console.WriteLine(item);
    } // 12 11

    ImmutableList<T> 可以索引,但是注意性能問題,不能用它來簡單的替代 List<T>。它的內部實現是用的二叉樹組織的數據,這么做是為了讓不同的實例之間共享內存。
    ImmutableHashSet
    有些時候需要這樣一個數據結構:不需要存放重復內容,不經常修改,可以被多個線程安全訪問。時間復雜度 O(log N)。
    var set = ImmutableHashSet<int>.Empty;
    set = set.Add(11);
    set = set.Add(12);
    foreach (var item in set) {
    Console.WriteLine(item);
    } // 11 12 順序不定

    線程安全字典
    一個線程安全的鍵值對集合,多個線程讀寫仍然能保持同步。
    ConcurrentDictionary
    混合使用了細粒度的鎖定和無鎖技術,它是最實用的集合類型之一。
    var dictionary = new ConcurrentDictionary<int, string>();
    dictionary.AddOrUpdate(0, key => "Zero", (key, oldValue) => "Zero");

    如果多個線程讀寫一個共享集合,實用 ConcurrentDictionary<TKey,TValue> 是最合適的。如果不會頻繁修改,那么更適合使用 ImmutableDictionary<TKey,TValue> 。
    它最適合用于在需要共享數據的場合,即多個線程共享一個集合,如果一些線程只添加元素一些線程只移除元素,那最好使用 生產者/消費者集合(BlockingCollection<T>)。
    初始化共享資源
    程序多個地方使用一個值,第一次訪問時對它進行初始化。
    static int _simpleVluae;
    static readonly Lazy<Task<int>> shardAsyncInteger =
    new Lazy<Task<int>>(async () => {
    await Task.Delay(2000).ConfigureAwait(false);
    return _simpleVluae++;
    });
    public static void Main(string[] args) {
    int shareValue = shardAsyncInteger.Value.Result;
    Console.WriteLine(shareValue); // 0
    shareValue = shardAsyncInteger.Value.Result;
    Console.WriteLine(shareValue); // 0
    shareValue = shardAsyncInteger.Value.Result;
    Console.WriteLine(shareValue); // 0
    }



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


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

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

    • 個人分類:生活學習
    ▲top
    • 3月 09 週四 201720:39
    • TypeScript 中的 SOLID 原則


    文章出處
    下面的文章解釋了正確使用 TypeScrip的 SOLID原則。

    原文地址:https://samueleresca.net/2016/08/solid-principles-using-typescript/
    作者:Samuele Resca
    翻譯:楊曉東(Savorboard)


    前言
    SOLID 是由 Robert C. Martin 在面向對象設計的(OOD)中提出的五個原則,你可以在這里更一步了解關于@UncleBob,這五個原則(SOLID)就是:
  • 單一職責原則(Single Responsibility Principle):當需要修改某個類的時候原因有且只有一個

  • 開放封閉原則(Open Closed Principle):軟件實體應該是可擴展,而不能可修改的

  • 里氏替換原則(Liskov Substitution Principle):子類的實例應該能夠替換任何其超類的實例

  • 接口分離原則(Interface Segregation Principle):使用多個專門的接口比使用單一的總接口總要好

  • 依賴倒置原則(Dependency Inversion Principle):依賴于抽象不應該依賴于細節

  • 這些原則使得程序員可以輕松地開發易于維護和擴展的軟件。它們還使開發人員的代碼能夠容易地避免壞氣味,輕松重構代碼,并且也是敏捷或自適應軟件開發的一部分。
    單一責任原則(SRP)
    SRP要求類只能有一個更改的原因。遵循這個原則來執行一些特定的相關任務。在考慮SRP時,你不需要將你的思維限制到類。你可以將這個原則應用到方法或者模塊,確保他們僅僅只是做一件事情并且只有一個理由可以修改它們。
    例子 - 錯誤的方式
    這個 Task 類定義了一些于模型相關的屬性,但是它也在一個基本的數據操作上定義了一些保存實體的數據訪問的方法。
    UML
    代碼

    // 這個類沒有遵循 SRP 原則
    class Task {
    private db: Database;
    constructor(private title: string, private deadline: Date) {
    this.db = Database.connect("admin:password@fakedb", ["tasks"]);
    }
    getTitle() {
    return this.title + "(" + this.deadline + ")";
    }
    save() {
    this.db.tasks.save({ title: this.title, date: this.deadline });
    }
    }

    例子 - 正確的方式
    UML
    代碼

    class Task {
    constructor(private title: string, private deadline: Date) {
    }
    getTitle() {
    return this.title + "(" + this.deadline + ")";
    }
    }
    class TaskRepository {
    private db: Database;
    constructor() {
    this.db = Database.connect("admin:password@fakedb", ["tasks"]);
    }
    save(task: Task) {
    this.db.tasks.save(JSON.stringify(task));
    }
    }

    開放封閉原則(OCP)

    軟件實體應該對擴展開放,對修改關閉。


    改變現有類的風險是,你會引入一個無意的行為變化。解決方案是創建另一個類,覆蓋原始類的行為。通過OCP原則,一個組件應盡可能包含可維護并且可重復使用的代碼。
    例子 - 正確的方式
    CreditCard 類描述了一個計算 monthlyDiscount()的方法。這個 monthlyDiscount() 依賴了具體的Card類型,也就是:Silver 或者 Gold。如果要改變月度折扣計算(monthlyDiscount)那么應該建立另外一個類,重寫monthlyDiscount()方法。目前這個的解決方案是新建兩個類:每個類型一個類。
    UML
    代碼

    class CreditCard {
    private Code: String;
    private Expiration: Date;
    protected MonthlyCost: number;
    constructor(code: String, Expiration: Date, MonthlyCost: number) {
    this.Code = code;
    this.Expiration = Expiration;
    this.MonthlyCost = MonthlyCost;
    }
    getCode(): String {
    return this.Code;
    }
    getExpiration(): Date {
    return this.Expiration;
    }
    monthlyDiscount(): number {
    return this.MonthlyCost * 0.02;
    }
    }
    class GoldCreditCard extends CreditCard {
    monthlyDiscount(): number {
    return this.MonthlyCost * 0.05;
    }
    }
    class SilverCreditCard extends CreditCard {
    monthlyDiscount(): number {
    return this.MonthlyCost * 0.03;
    }
    }

    里氏替換原則(LSP)

    子類不應該破壞父類的類型定義


    這一原則的概念是由 Barbara Liskov 在1987年大會上發表,隨后與 Jannette Wing 一起在1994年發表論文。
    就這么簡單,一個子類應當有一種方式覆寫它的父類的方法,但是從客戶的角度來看沒有破壞它的功能。
    例子
    在下面的例子中,ItalyPostalAddress, UKPostalAddress 和 USAPostalAddress 繼承了一個公共的基類:PostalAddress。
    AddressWriter 類有一個引用指向了 PostalAddress 這個基類:也就是說 參數可以被三個不同的之類替換。
    代碼
    abstract class PostalAddress {
    Addressee: string;
    Country: string
    PostalCode: string;
    City: string;
    Street: string
    House: number;
    /*
    * @returns Formatted full address
    */
    abstract WriteAddress(): string;
    }
    class ItalyPostalAddress extends PostalAddress {
    WriteAddress(): string {
    return "Formatted Address Italy" + this.City;
    }
    }
    class UKPostalAddress extends PostalAddress {
    WriteAddress(): string {
    return "Formatted Address UK" + this.City;
    }
    }
    class USAPostalAddress extends PostalAddress {
    WriteAddress(): string {
    return "Formatted Address USA" + this.City;
    }
    }
    class AddressWriter {
    PrintPostalAddress(writer: PostalAddress): string {
    return writer.WriteAddress();
    }
    }

    接口分離原則(ISP)
    有一個很常見的現象就是,在描述一個類的時候,基本上一個接口就把它覆蓋完了,就是一個接口就描述了一整個類。ISP 原則指出,我們應該寫一系列更加小并且具體的接口,交給該類來實現。而每個接口只提供單一的行為。
    示例 - 錯誤的方式
    下面的 Printer 接口,它有一個實現的類 SimplePrinter,該接口具有 Copy 和 Print 的功能。

    interface Printer {
    copyDocument();
    printDocument(document: Document);
    stapleDocument(document: Document, tray: Number);
    }
    class SimplePrinter implements Printer {
    public copyDocument() {
    //...
    }
    public printDocument(document: Document) {
    //...
    }
    public stapleDocument(document: Document, tray: Number) {
    //...
    }
    }

    例子 - 正確的方式
    下面的示例顯示了將方法分組到更加具體的接口和可以被替代的方法,它描述了一些契約,他們可以被一個單獨的 SimplePrinter 類,或 SimpleCopier 類,或 SuperPrinter 類實現。

    interface Printer {
    printDocument(document: Document);
    }
    interface Stapler {
    stapleDocument(document: Document, tray: number);
    }
    interface Copier {
    copyDocument();
    }
    class SimplePrinter implements Printer {
    public printDocument(document: Document) {
    //...
    }
    }
    class SuperPrinter implements Printer, Stapler, Copier {
    public copyDocument() {
    //...
    }
    public printDocument(document: Document) {
    //...
    }
    public stapleDocument(document: Document, tray: number) {
    //...
    }
    }

    依賴倒置原則(DIP)
    DIP 簡單的說就超類不應該依賴于低級的組件,而應該依賴于抽象。
    例子 - 錯誤的方式
    高級的 WindowSwitch 依賴于底層低級的 CarWindow 類。
    UML
    代碼

    class CarWindow {
    open() {
    //...
    }
    close() {
    //...
    }
    }
    class WindowSwitch {
    private isOn = false;
    constructor(private window: CarWindow) {
    }
    onPress() {
    if (this.isOn) {
    this.window.close();
    this.isOn = false;
    } else {
    this.window.open();
    this.isOn = true;
    }
    }
    }

    總結
    TypeScript 可以將所有的OOP原則和實踐帶入到你的軟件中,使用 SOLID 原則來指導你的設計模式吧。
    GitHub 完整的示例代碼。
    (繼續閱讀...)
    文章標籤

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

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

    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