<address id="fvpxj"><progress id="fvpxj"><font id="fvpxj"></font></progress></address>
    <address id="fvpxj"></address>

      <address id="fvpxj"><progress id="fvpxj"></progress></address>
      <sub id="fvpxj"></sub>

      <address id="fvpxj"></address>

        <track id="fvpxj"><big id="fvpxj"></big></track>

          <address id="fvpxj"><big id="fvpxj"><font id="fvpxj"></font></big></address>

          <dl id="fvpxj"><em id="fvpxj"><form id="fvpxj"></form></em></dl>

            Jackyfei

            解读大内老A的《.NET Core框架本质》

            老A说的一句话让我很受启发,想要深入了解框架,你要把精力聚焦在架构设计的层面来思考问题。而透彻了解底层原理,最好的笨办法就是根据原理对框架核心进行重建或者说再造。看起来没有捷径,也是最快的捷径。

            题外话

              相信很多读者已经看过老A写的这篇文章《200行代码,7个对象——让你了解ASP.NET Core框架的本质》,这是一篇模仿和重建的典范。重建说白了就是模仿,模仿有一个前置条件就是你对底层原理要烂熟于心。否则画虎难画骨,原本要画虎,最后出来的是只猫。

            要理解原理就要去阅读源码,就像新人学开车,如?#38382;?#29992;尚且磕磕碰碰,更何况让你去了解汽车的构造和引擎。

              所以老A是引路人,我像个门外汉一样对前辈的文章解读不下5遍。我有几个疑问,1.为什么是7个对象?2.这些对象如何分类,如何排序?3.这些对象发明的那个“无”是什么?

              在我深入学习和解读的时候,我越加感觉到老A的这篇文章很值得去深入解读,所谓知其然,知其所以然,这样在编码过程才会游刃有余,以下开始我个?#35828;?#35299;读。

            知识准备

            • 委托
            • 构建模式
            • 适配器模式

            引子

            public class Program
            {
                public static void Main()
                => new WebHostBuilder()
                    .UseKestrel()
                    .Configure(app => app.Use(context => context.Response.WriteAsync("Hello World!")))
                    .Build()
                    .Run();
            }
            

              以上是原文的代码,我们可以看到WebHostBuilder、Server(即Kestrel)、ApplicationBuilder(即app)三大重要的对象,如下图所示:

              WebHostBuilder这个父亲生出WebHost这个孩子,WebHost又生成整个ASP.NET Core最核心的内容,即由Server和中间件(Middleware)构成的管道Pipeline。我们看下Pipeline的放大图:

              继续把Pipeline拆开,有个很重要的ApplicationBuilder对象,里面包含MiddlewareRequestDelegate。至于HttpContext是独立共享的对象,贯穿在整个管道中间,至此7大对象全部出场完毕。

            Configure是个什么玩意?看下代码:

            public IWebHostBuilder Configure(Action<IApplicationBuilder> configure)
            {
                _configures.Add(configure);
                return this;
            }
            

              我们看到他是一个接受IApplicationBuilder的委托!继续刨根问底,IApplicationBuilder是什么玩意?看下源码:

            public interface IApplicationBuilder
            {
                IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);
                RequestDelegate Build();
            }
            

              他是一个注册中间件和生成Application的容器,那么Application是什么呢?源码没有这个对象,但是看代码(如下所示)我们可以知道他是真正的委托执行者(Handler),执行是一个动作可以理解为app,我猜想这是取名为ApplicationBuilder的原因。

            public RequestDelegate Build()
            {
                _middlewares.Reverse();
                return httpContext =>
                {
                  //_是一个?#34892;?#30340;标识符,因此它可以用作?#38382;?#21517;称
                    RequestDelegate next = _ => { _.Response.StatusCode = 404; return Task.CompletedTask; };
                
                    foreach (var middleware in _middlewares)
                    {
                        next = middleware(next);
                    }
                    return next(httpContext);
                };
            }
            

              

              更详细的过程可以参考下面这张图(?#35745;?#26469;源),

              WebHostBuilder开始Build的那一刻开始,WebHost被构造,Server被指定,Middlewares被指定,等WebHost真正启动的时候,Server开始监听,收到请求后,Middleware开始执行。

            到此,一个完整的ASP.NET Core的流程就简单的走完了。接下来,我们跟着老A一个一个对象的详细介绍。

            7个对象解读

            1.HttpContext

            这个对象应该是最容易理解的,也是我们在编程时候遇到的最多的,最重要的(没有之一)对象。请看这个对象的简要代码:

            public class HttpContext
            {           
                public  HttpRequest Request { get; }
                public  HttpResponse Response { get; }
            }
            public class HttpRequest
            {
                public  Uri Url { get; }
                public  NameValueCollection Headers { get; }
                public  Stream Body { get; }
            }
            public class HttpResponse
            {
                public  NameValueCollection Headers { get; }
                public  Stream Body { get; }
                public int StatusCode { get; set;}
            }
            

              我们知道一个Http事务包括最核心的Request(输入)和Response(输出),所以HttpContext包含这两个核心的东西。

              老A建议大家从管道的角度来理解该对象的作用,管道和HTTP请求流程一脉相承。在Server接收到请求后,HttpContext被创建。

              在服务器和中间件,中间件之间通过什么来传递信息?就是共享上下文,这个上下文就是HttpContext。可以说HttpContext是根据HTTP请求原理包裹的在管道之间的共享的一个上下文对象。

              为什么这里要把HttpContext放在第一个来介绍,因为这是一个最基础的对象。这7大对象的讲解顺序,我感觉是从底层基础开始讲起,再层层往上,最后到WebHostBuilder。

             

            2.RequestDelegate

              这个委托太重要了,和HttpContext一样,老A建议大家从管道的角度来理解这个委托。我们再复习一下管道的含义,如图所示:

             

              这里的管道:Pipeline = Server + Middlewares

              还能更简单一点吗?可以的:如下图所示

             

              这里的管道:Pipeline =Server + HttpHandler

              多个Middlewares构成一个HttpHandler对象,这是整个管道的核心,那么应该如何用代码来表示呢?

              老A讲到:“既然针对当前请求的所有输入和输出都通过HttpContext来表示,那么HttpHandler就可以表示成一个Action<HttpContext>对象”。

              但是由于ASP.NET Core推崇异步编程,所以你应该想得到Task对象,那么HttpHandler自然就可以表示为一个Func<HttpContext,Task>对象。由于这个委托对象实在太重要了,所以我们将它定义成一个独立的类型。下图展示的就是整个RequestDelegate的设计思路

            public delegate Task RequestDelegate(HttpContext context);
            

              这就是委托的由来!

            • 为什么是委托,而不是别的函数?

              委托是架构设计的底层技术,非常常见。因为委托可以承载约定的函数,遵循开闭原则,能很好的把扩展对外进行开放,保证?#35828;?#23618;架构的稳定性。

            3.Middleware

              这个对象比较费解。根据源码我们知道Middleware也是一个委托对象(代码如下所示),中间件其实就是一个Func<RequestDelegate, RequestDelegate>对象:

            private readonly List<Func<RequestDelegate, RequestDelegate>> _middlewares = new List<Func<RequestDelegate, RequestDelegate>>();
            

              该对象的输入和输入都是RequestDelegate,为什么要这么设计呢?我们想一下,当前中间件处理完成后需要将请求分发给后续中间件进行处理,他如何让后续的中间件参与当前的请求呢?所以他必须要拿到代表后续中间件管道构成的那个Handler。

            如下图所示,也就是说,后续三个中间件构成的管道就是一个输入,执行完毕后,当前中间件也将被“融入”这个管道(此时该新管道就会由四个中间件构成的一个委托链),然后再输出给你由所有的中间件构成的新管道。如下图所示:

            4.ApplicationBuilder

              这又是一个builder,可见builder模式在ASP.NET Core有非常广泛的应用。但是该Builder构建的不是Application,?#28966;?#24314;什么内容呢?#30475;?#19979;面代码声明我们可以看到他有两个功能。

              从Use的使用来看,第一个功能是注册器,他把一个个中间件串联成一个管道。

            public interface  IApplicationBuilder
            {
                IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);
                RequestDelegate Build();
            }
            

              第二个功能是Build,如下所示:

            public class ApplicationBuilder : IApplicationBuilder
            {
                private readonly List<Func<RequestDelegate, RequestDelegate>> _middlewares = new List<Func<RequestDelegate, RequestDelegate>>();
                public RequestDelegate Build()
                {
                    _middlewares.Reverse();
                    return httpContext =>
                    {
                        RequestDelegate next = _ => { _.Response.StatusCode = 404; return Task.CompletedTask; };
                        foreach (var middleware in _middlewares)
                        {
                            next = middleware(next);
                        }
                        return next(httpContext);
                    };
                }
            
            
                public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
                {
                    _middlewares.Add(middleware);
                    return this;
                }
            }
            

              Build真正做的事情是循环组装中间件,最后把组装好的委托链进行返回。从_middlewares.Reverse();我们又可以知道,对于委托链来说,中间件的注册顺序和执行顺序是相反的,这里需要进行反转,然后才能保证先注册的中间件先执行。

            5.Server

            Server对象相对比较简单,我们看下他的接口定义:

            public interface IServer
            { 
                Task StartAsync(RequestDelegate handler);
            }
            

              我们可以看到Server有个启动函数StartAsync,StartAsync内部封装了RequestDelegate中间件,同时内部也会new一个HttpContext(features),这样Server、RequestDelegate、HttpContext三者就全部聚齐了。

            public class HttpListenerServer : IServer
            {
                private readonly HttpListener _httpListener;
                private readonly string[] _urls;
                public HttpListenerServer(params string[] urls)
                {
                    _httpListener = new HttpListener();
                    //绑定默认监听地址(默认端口为5000)
                    _urls = urls.Any()?urls: new string[] { "http://localhost:5000/"};
                }
            
                public async Task StartAsync(RequestDelegate handler)
                {
                    Array.ForEach(_urls, url => _httpListener.Prefixes.Add(url));    
                    _httpListener.Start();
                    Console.WriteLine("Server started and is listening on: {0}", string.Join(';', _urls));
                    while (true)
                    {
                    //该方法将阻塞进程(这里使用了await),等待传入的请求,直到收到请求
                        var listenerContext = await _httpListener.GetContextAsync(); 
                    //打印状态行: 请求方法, URL, 协议版本
                     Console.WriteLine("{0} {1} HTTP/{2}", 
                                listenerContext.Request.HttpMethod, 
                                listenerContext.Request.RawUrl, 
                                listenerContext.Request.ProtocolVersion);
                                
                      // 获取抽象封装后的HttpListenerFeature
                            var feature = new HttpListenerFeature(listenerContext);
                            
                            // 获取封装后的Feature集合
                            var features = new FeatureCollection()
                                .Set<IHttpRequestFeature>(feature)
                                .Set<IHttpResponseFeature>(feature);
                                
                            // 创建HttpContext
                            var httpContext = new HttpContext(features);
                            Console.WriteLine("[Info]: Server process one HTTP request start.");
                            
                            // 开始?#26469;?#25191;行中间件
                            await handler(httpContext);
                            Console.WriteLine("[Info]: Server process one HTTP request end.");
                            
                            // 关闭响应
                            listenerContext.Response.Close();
                    }
                }
            }
            public static partial class Extensions
            {
                public static IWebHostBuilder UseHttpListener(this IWebHostBuilder builder, params string[] urls)
                => builder.UseServer(new HttpListenerServer(urls));
            

              通过以上代码分析,我们可以画个图做总结:

            • 适配器模式

            由于ASP.NET Core可以支持不同的WebServer,比如Kestrel和IIS,不同的WebServer返回的HttpContext各不相同,所以这里又增加了一个中间层进行适配。这个中间层是什么呢?如下图所示,就是IRequestFeature和IResponseFeature。这一层是典型的适配器模式。

              这里重点讲解的7大对象,这个适配器模式的实现细节暂且略过。

             6.WebHost

            public interface IWebHost
            {
                Task StartAsync();
            }
            

              根据这段定义,我们只能知道简单知道WebHost只要是用来启动什么对象用的,具体什么对象似乎都可以。直到我们看了实现,如下代码所示:

            public class WebHost : IWebHost
            {
                private readonly IServer _server;
                private readonly RequestDelegate _handler; 
                public WebHost(IServer server, RequestDelegate handler)
                {
                    _server = server;
                    _handler = handler;
                } 
                public Task StartAsync() => _server.StartAsync(_handler);
            }
            

              通过StartAsync,我们知道WebHost是用来启动管道的中间件的,管道是在作为应用宿主的WebHost对象启动的时候被构建出来的。

              而WebHost是如何被创建的呢?接下来就要讲他的父亲WebHostBuilder

            7.WebHostBuilder

            public class WebHostBuilder : IWebHostBuilder
            {
                private IServer _server;
                private readonly List<Action<IApplicationBuilder>> _configures = new List<Action<IApplicationBuilder>>();   
                public IWebHostBuilder Configure(Action<IApplicationBuilder> configure)
                {
                    _configures.Add(configure);
                    return this;
                }
                public IWebHostBuilder UseServer(IServer server)
                {
                    _server = server;
                    return this;
                }   
                public IWebHost Build()
                {
                    var builder = new ApplicationBuilder();
                    foreach (var configure in _configures)
                    {
                        configure(builder);
                    }
                    return new WebHost(_server, builder.Build());
                }
            }
            

              我们看到该对象有个Build方法,内部返回一个WebHost对象,这就是父亲的职责,负责生娃,他的娃就是WebHost。生出来的时候,给孩子一个ApplicationBuilder作为食物。而这个食物其实是包裹起来的,展开来看就是一个个RequestDelegate委托?#30784;?/p>

            public interface IWebHostBuilder
            {
                IWebHostBuilder UseServer(IServer server);
                IWebHostBuilder Configure(Action<IApplicationBuilder> configure);
                IWebHost Build();
            }
            

              父亲除了创建WebHost之外,他还提供了注册服务器的UseServer方法和用来注册中间件的Configure方法。说到Configure方法,我们一定还记得ApplicationBuilder方法的Use也是一个注册器。这两个注册器有何不同呢?我们对比一下代码:

            • WebHostBuilder
            public IWebHostBuilder Configure(Action<IApplicationBuilder> configure)
            {
                _configures.Add(configure);
                return this;
            }
            
            • ApplicationBuilder
            public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
            {
                _middlewares.Add(middleware);
                return this;
            }
            

              其中Use只是增加一个中间件,Configure输入的是中间件构成的委托?#30784;?#25105;们看下入口函数的代码就知道了:

            public static async Task Main()
            {
                await new WebHostBuilder()
                    .UseHttpListener()
                    .Configure(app => app
                        .Use(FooMiddleware)
                        .Use(BarMiddleware)
                        .Use(BazMiddleware))
                    .Build()
                    .StartAsync();
            }

            参加文章:

            posted @ 2019-05-10 09:45 张飞洪 阅读(...) 评论(...) 编辑 收藏
            加拿大app

              <address id="fvpxj"><progress id="fvpxj"><font id="fvpxj"></font></progress></address>
              <address id="fvpxj"></address>

                <address id="fvpxj"><progress id="fvpxj"></progress></address>
                <sub id="fvpxj"></sub>

                <address id="fvpxj"></address>

                  <track id="fvpxj"><big id="fvpxj"></big></track>

                    <address id="fvpxj"><big id="fvpxj"><font id="fvpxj"></font></big></address>

                    <dl id="fvpxj"><em id="fvpxj"><form id="fvpxj"></form></em></dl>

                        <address id="fvpxj"><progress id="fvpxj"><font id="fvpxj"></font></progress></address>
                        <address id="fvpxj"></address>

                          <address id="fvpxj"><progress id="fvpxj"></progress></address>
                          <sub id="fvpxj"></sub>

                          <address id="fvpxj"></address>

                            <track id="fvpxj"><big id="fvpxj"></big></track>

                              <address id="fvpxj"><big id="fvpxj"><font id="fvpxj"></font></big></address>

                              <dl id="fvpxj"><em id="fvpxj"><form id="fvpxj"></form></em></dl>

                                北京28开奖官方网站 江西快乐十分开奖结果 二分彩算不算违法 澳门21点游戏规则 怎么看大乐透走势图 围棋俱乐部 广西十一选五基本走图 浙江11选五走势图 北京市快三开奖号码 京东彩票合买 足球混合过关即时比分 陕西快乐10分 pc蛋蛋的牛人怎么玩的 重庆时时彩下载版 澳洲幸运10开奖网