目錄
- 責任鏈模式
- 原始碼
責任鏈模式
職責鏈上的處理者負責處理請求,客戶只需要將請求傳送到職責鏈上即可,無需關心請求的處理細節和請求的傳遞,所以職責鏈將請求的傳送者和請求的處理者解耦了
何時使用:在處理訊息的時候以過濾很多道
使用場景:
- 有多個物件可以處理同一個請求,具體到哪個物件處理該請求由執行時刻自動確定
- 在不明確指定接收者的情況下,向多個物件中的一個提交一個請求
- 可動態指定一組物件處理請求
原始碼
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) 。