catalogue
- Responsibility chain model
- Source code
Responsibility chain model

The handler in the responsibility chain is responsible for processing the request. The customer only needs to send the request to the responsibility chain without paying attention to the processing details of the request and the transmission of the request. Therefore, the responsibility chain decouples the sender of the request from the handler of the request
When to use: filter many channels when processing messages
Usage scenario:
- Multiple objects can process the same request. The specific object to process the request is automatically determined by the runtime
- Submit a request to one of multiple objects without explicitly specifying the recipient
- You can dynamically specify a set of objects to handle requests

Source code
https://github.com/dotnet/aspnetcore/
In ASP Net core source code Kestrel, build KestrelConnection and then transfer it to HttpConnectionMiddleware middleware processing pipeline
In the directory Microsoft AspNetCore. Server. Kestrel. There is a UseHttpServer method in KestrelServerImpl under the core
options.UseHttpServer(ServiceContext, application, options.Protocols, addAltSvcHeader);
In the UseHttpServer method, a HttpConnectionMiddleware is constructed, and then the Use method is called after IConnectionBuilder.
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; }); }
It and ASP can be seen in the implementation class ConnectionBuilder of IConnectionBuilder NET Core's pipes as like as two peas.
There is an IList_ Interface of components
private readonly IList<Func<ConnectionDelegate, ConnectionDelegate>> _components = new List<Func<ConnectionDelegate, ConnectionDelegate>>();
When the Use method is called, it is added to the_ In components
public IConnectionBuilder Use(Func<ConnectionDelegate, ConnectionDelegate> middleware) { _components.Add(middleware); return this; }
Finally, reverse it when building
public ConnectionDelegate Build() { ConnectionDelegate app = features => { return Task.CompletedTask; }; foreach (var component in _components.Reverse()) { app = component(app); } return app; }
The UseHttpServer method of KestrelServerImpl is called by options
options.UseHttpServer(ServiceContext, application, options.Protocols, addAltSvcHeader);
Although it is a ListenOptions, it is actually a ConnectionBuilder
public class ListenOptions : IConnectionBuilder, IMultiplexedConnectionBuilder
It has one_ List of middleware
internal readonly List<Func<ConnectionDelegate, ConnectionDelegate>> _middleware = new List<Func<ConnectionDelegate, ConnectionDelegate>>();
When the Use method is called, all middleware will be added
public IConnectionBuilder Use(Func<ConnectionDelegate, ConnectionDelegate> middleware) { _middleware.Add(middleware); return this; }
Finally, when calling Build, all middleware is connected in series.
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; }
Generate connectionDelegate after Build and pass it in_ 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);
In_ Where the transport manager is bound, you can see that it is passed into the StartAcceptLoop
StartAcceptLoop(new GenericConnectionListener(transport), c => connectionDelegate(c), endpointConfig);
Bind to connectionDispatcher in StartAcceptLoop
var connectionDispatcher = new ConnectionDispatcher<T>(_serviceContext, connectionDelegate, transportConnectionManager); var acceptLoopTask = connectionDispatcher.StartAcceptingConnections(connectionListener);
Listen for requests when the connectionDispatcher starts
var connection = await listener.AcceptAsync();
When a request comes, it will_ connectionDelegate encapsulated into KestrelConnection
var kestrelConnection = new KestrelConnection<T>(id, _serviceContext, _transportConnectionManager, _connectionDelegate, connection, Log);
_ connectionDelegate is an encapsulation of queue requests based on thread pool. Finally, kestrelConnection will be pushed into a request queue for execution
ThreadPool.UnsafeQueueUserWorkItem(kestrelConnection, preferLocal: false);
The main processes executed can be viewed in KestrelConnection, which inherits IThreadPoolWorkItem, which is a queue method
internal class KestrelConnection<T> : KestrelConnection, IThreadPoolWorkItem where T : BaseConnectionContext
Execute at ExecuteAsync_ connectionDelegate
await _connectionDelegate(connectionContext);
This is all the subsequent processing actions after the whole Kestrel receives the network request
Here also follows the opening and closing principle, and the latter responsibility chain mode can be continuously expanded
At the same time, it also reflects the principle of separation of concerns. For the determined part, such as the receiving network, the byte part is handled first, and then the uncertain part is handled through the responsibility chain pipeline