. NET cloud native architect training camp (responsibility chain mode) -- learning notes

Posted by fwegan on Wed, 05 Jan 2022 14:57:51 +0100

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

Topics: .NET