.NET 雲原生架構師訓練營(責任鏈模式)–學習筆記

目錄

  • 責任鏈模式
  • 源碼

責任鏈模式

職責鏈上的處理者負責處理請求,客戶只需要將請求發送到職責鏈上即可,無需關心請求的處理細節和請求的傳遞,所以職責鏈將請求的發送者和請求的處理者解耦了

何時使用:在處理消息的時候以過濾很多道

使用場景:

  • 有多個對象可以處理同一個請求,具體到哪個對象處理該請求由運行時刻自動確定
  • 在不明確指定接收者的情況下,向多個對象中的一個提交一個請求
  • 可動態指定一組對象處理請求

源碼

//github.com/dotnet/aspnetcore/

在 ASP .NET Core 源碼的 Kestrel 當中,構建 KestrelConnection 之後傳送給 HttpConnectionMiddleware 中間件處理管道

在目錄 Microsoft.AspNetCore.Server.Kestrel.Core 下面的 KestrelServerImpl 中有一個 UseHttpServer 方法

options.UseHttpServer(ServiceContext, application, options.Protocols, addAltSvcHeader);

在 UseHttpServer 方法中構造了一個 HttpConnectionMiddleware,構造之後調用了 IConnectionBuilder 的 Use 方法

public static IConnectionBuilder UseHttpServer<TContext>(this IConnectionBuilder builder, ServiceContext serviceContext, IHttpApplication<TContext> application, HttpProtocols protocols, bool addAltSvcHeader) where TContext : notnull
{
    var middleware = new HttpConnectionMiddleware<TContext>(serviceContext, application, protocols, addAltSvcHeader);
    return builder.Use(next =>
    {
        return middleware.OnConnectionAsync;
    });
}

在 IConnectionBuilder 的實現類 ConnectionBuilder 中可以看到它和 ASP .NET Core 的管道一模一樣

有一個 IList 的 _components 的介面

private readonly IList<Func<ConnectionDelegate, ConnectionDelegate>> _components = new List<Func<ConnectionDelegate, ConnectionDelegate>>();

調用 Use 方法的時候就是添加到 _components 中

public IConnectionBuilder Use(Func<ConnectionDelegate, ConnectionDelegate> middleware)
{
    _components.Add(middleware);
    return this;
}

最後 Build 的時候進行一下反轉

public ConnectionDelegate Build()
{
    ConnectionDelegate app = features =>
    {
        return Task.CompletedTask;
    };

    foreach (var component in _components.Reverse())
    {
        app = component(app);
    }

    return app;
}

KestrelServerImpl 的 UseHttpServer 方法由 options 調用

options.UseHttpServer(ServiceContext, application, options.Protocols, addAltSvcHeader);

雖然它是 ListenOptions,但是其實是一個 ConnectionBuilder

public class ListenOptions : IConnectionBuilder, IMultiplexedConnectionBuilder

它有一個 _middleware 的 List

internal readonly List<Func<ConnectionDelegate, ConnectionDelegate>> _middleware = new List<Func<ConnectionDelegate, ConnectionDelegate>>();

調用 Use 方法的時候,所有中間件會被加入進來

public IConnectionBuilder Use(Func<ConnectionDelegate, ConnectionDelegate> middleware)
{
    _middleware.Add(middleware);
    return this;
}

最後調用 Build 的時候,把所有中間件串聯起來

public ConnectionDelegate Build()
{
    ConnectionDelegate app = context =>
    {
        return Task.CompletedTask;
    };

    for (var i = _middleware.Count - 1; i >= 0; i--)
    {
        var component = _middleware[i];
        app = component(app);
    }

    return app;
}

Build 之後生成 connectionDelegate,傳入 _transportManager

options.UseHttpServer(ServiceContext, application, options.Protocols, addAltSvcHeader);

var connectionDelegate = options.Build();

options.EndPoint = await _transportManager.BindAsync(options.EndPoint, connectionDelegate, options.EndpointConfig, onBindCancellationToken).ConfigureAwait(false);

在 _transportManager 綁定的地方可以看到被傳入到 StartAcceptLoop 中

StartAcceptLoop(new GenericConnectionListener(transport), c => connectionDelegate(c), endpointConfig);

在 StartAcceptLoop 中綁定到 connectionDispatcher

var connectionDispatcher = new ConnectionDispatcher<T>(_serviceContext, connectionDelegate, transportConnectionManager);
var acceptLoopTask = connectionDispatcher.StartAcceptingConnections(connectionListener);

在 connectionDispatcher 啟動的時候,監聽請求

var connection = await listener.AcceptAsync();

當有請求過來的時候會將 _connectionDelegate 封裝到 KestrelConnection

var kestrelConnection = new KestrelConnection<T>(id, _serviceContext, _transportConnectionManager, _connectionDelegate, connection, Log);

_connectionDelegate 它是一個基於執行緒池的隊列請求的封裝,最後 kestrelConnection 會被壓入到一個請求隊列之中執行

ThreadPool.UnsafeQueueUserWorkItem(kestrelConnection, preferLocal: false);

執行的主要過程可以在 KestrelConnection 中查看,它繼承了 IThreadPoolWorkItem,這是一個隊列方法

internal class KestrelConnection<T> : KestrelConnection, IThreadPoolWorkItem where T : BaseConnectionContext

在 ExecuteAsync 的時候執行 _connectionDelegate

await _connectionDelegate(connectionContext);

這就是整個 Kestrel 接收到網路請求,後續的全部處理動作

這裡也是遵循開閉原則,後面責任鏈模式可以不斷地擴展

同時也體現了關注點分離的原則,確定的部分比如接收網路,位元組的部分先處理好,然後不確定的部分通過責任鏈管道處理

課程鏈接

//appsqsyiqlk5791.h5.xiaoeknow.com/v1/course/video/v_5f39bdb8e4b01187873136cf?type=2

知識共享許可協議

本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。

歡迎轉載、使用、重新發布,但務必保留文章署名 鄭子銘 (包含鏈接: //www.cnblogs.com/MingsonZheng/ ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發布。

如有任何疑問,請與我聯繫 ([email protected]) 。