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

MingsonZheng發表於2022-01-05

目錄

  • 責任鏈模式
  • 原始碼

責任鏈模式

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

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

使用場景:

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

原始碼

https://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 接收到網路請求,後續的全部處理動作

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

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

課程連結

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

知識共享許可協議

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

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

如有任何疑問,請與我聯絡 (MingsonZheng@outlook.com) 。

相關文章