• IceRPC之依赖注入>快乐的RPC


    作者引言

    很高兴啊,我们来到了IceRPC之依赖注入>快乐的RPC,基础引导,打好基础,才能让自已不在迷茫,快乐的畅游世界。

    依赖注入和IceRPC

    了解 IceRPC (C#) 如何为依赖注入(DI)提供支持。

    DI作为可选功能

    DI的第一条规则是:不要引入对DI的依赖。

    IceRPC (C#) 将此规则放首要位置上,即为 DI 提供全方位支持,同时使此支持完全可选。

    IceRPC C# API 被设计为可选 DI 容器。此外,IceRPC 还提供支持代码来帮助大家,在 Microsoft's DI container中使用 IceRPC。.

    DI APIs

    IceRPC 提供的所有与 DI 相关的 API 都在同一个命名空间中, IceRpc.Extensions.DependencyInjection, 但由多个组件实现:

    • IceRpc.dll 提供如IDispatcherBuilderIInvokerBuilder IceRpc.Deadline.dll之类的抽象以及其他拦截器/中间件组件,为IDispatcherBuilderIInvokerBuilder提供了扩展方法。这些扩展方法与 DI 容器无关。
    • IceRpc.Extensions.DependencyInjection.dll 为 Microsoft 的 DI 容器提供支持代码。这包括 IServiceCollection 的各种扩展方法,例如 AddIceRpcServerAddIceRpcClientConnection,以及 IDispatcherBuilderIInvokerBuilder 的实现。
    --- title: 程序集依赖图 --- flowchart BT di([Microsoft.Extensions.DependencyInjection.Abstractions.dll]) logging([Microsoft.Extensions.Logging.Abstractions.dll]) options([Microsoft.Extensions.Options.dll]) IceRpc.dll --> logging IceRpc.Logger.dll --> IceRpc.dll IceRpc.Logger.dll --> logging IceRpc.Deadline.dll --> IceRpc.dll IceRpc.Extensions.DependencyInjection.dll --> di IceRpc.Extensions.DependencyInjection.dll --> options IceRpc.Extensions.DependencyInjection.dll --> IceRpc.dll

    调度管道与 DI

    了解如何使用 DI 容器构建调度管道。

    传统的调度管道

    传统的调度管道相当静态: 创建路由、添加一些中间件、在此路由中映射或安装少量叶片调度程序,然后让服务器将传入的请求调度到此路由。

    叶片调度程序(通常是 Slice 服务)被映射或安装在固定路径(例如/greeter 或/admin/greeter-manager)。 这些调度程序是单例(或类似单例),其使用寿命与路由和服务器相同。

    调度管道中的中间件使用以下features相互通信: 上游中间件设置下游中间件可以检索的features。叶片调度程序还可以使用相同的features与这些中间件进行通信。

    这对于许多应用程序来说都很有效。然而,这并不是使用 DI 时的典型模型。

    DI容器构建调度管道

    DI,调度管道通常更具动态性: 一些基础设施代码为每个调度创建唯一的 DI 范围,并且叶片调度程序是由 DI 容器管理的服务。当该叶片调度器的生命周期是短暂的或范围化的时,该叶片调度器是按需创建的(每个调度)。

    DI 调度管道中的中间件可以像往常一样使用features,与其他中间件和叶片调度器进行通信。然而,更惯用的方法是使用注入服务进行通信。例如:

    • 上游中间件接收范围服务(通过注入),然后填写此服务
    • 下游中间件接收相同的作用域服务(也通过注入)并读取上游中间件填写的信息
    • 叶片调度器的构造函数(作用域或瞬态服务)与该先前填充的作用域服务自动连线

    中间件链本身是静态的: 每个调度不会创建新的中间件实例。中间件通常是由 DI 容器管理的单例。

    使用 Microsoft 的 DI 容器构建调度管道

    IceRpc.Extensions.DependencyInjection组件为 IServiceCollection 提供了多种接受 Action 参数的扩展方法。

    所有这些方法都允许 Microsoft 的 DI 容器构建和配置调度管道。例如:

    // Construct a dispatch pipeline using Microsoft's DI container.
    
    using IceRpc.Extensions.DependencyInjection;
    using Microsoft.Extensions.DependencyInjection;
    ...
    
    // Add a new IDispatcher singleton configured with an action.
    services.AddIceRpcDispatcher(builder => builder.Map());
    

    由此产生的调度程序(调度管道)为每个传入请求创建一个新的 DI 范围,并使用 IServiceProviderFeature 将此范围传输到下游调度程序。

    在 IDispatcherBuilder 中安装标准中间件

    标准中间件是可以与 DI 容器一起使用或不使用 DI 容器的中间件: 它不依赖DI容器注入服务来运行,并且实现了接口 IDispatcher

    IceRPC 附带的所有中间件都是标准中间件: 可以在有或没有 DI 的情况下使用它们,并且它们在调度中使用功能进行通信。

    这些中间件可以安装在路由或 IDispatcherBuilder 中。例如:

    // Construct a dispatch pipeline using Microsoft's DI container.
    
    using IceRpc.Extensions.DependencyInjection;
    using Microsoft.Extensions.DependencyInjection;
    ...
    
    services.AddIceRpcDispatcher(
        builder => builder
            .UseLogger()
            .Map());
    

    这里,UseLoggerIceRpc.Logger 组件提供的扩展方法。此扩展方法适用于实现 IServiceProvider 的任何 DI 容器,例如 Microsoft 的 DI 容器和 Simple Injector 的容器。

    UseLogger 的实现只是从 DI 容器中检索一个记录器实例, 然后用这个实例创建一个新的中间件:

    public static IDispatcherBuilder UseLogger(this IDispatcherBuilder builder) =>
        builder.ServiceProvider.GetService(typeof(ILogger)) is ILogger logger ?
            builder.Use(next => new LoggerMiddleware(next, logger)) :
            throw new InvalidOperationException(
                $"Could not find service of type '{nameof(ILogger)}' in the service container.");
    

    我们建议在创建自己的标准中间件时遵循相同的模式,并为 RouterIDispatcherBuilder 提供使用扩展方法。

    通常不鼓励在运行时调用 DI 容器—它是反模式的服务定位器。在这里,应该将 UseLogger 扩展方法视为不受此规则约束的基础设施代码。

    具有注入服务的中间件

    DI 容器注入的服务来创建与其他中间件和叶片调度器通信的中间件,而不是提供标准的中间件。

    DI 友好的中间件需要实现以下 IMiddleware 接口之一:

    • IMiddleware
    • IMiddleware
    • IMiddleware

    例如,我们希望以更适合 DI 的方式,重新实施截止日期中间件。标准截止日期中间件读取截止日期字段,并创建截止日期功能,以将此截止日期传达给,下游中间件和叶片调度程序。新的 DI 友好截止日期中间件,解码截止日期并将此信息,保存在注入的 scoped 服务中:

    // Configured as a scoped service in the composition root of the application.
    public class DeadlineInformation
    {
        public DateTime Value { get; set; } = DateTime.MinValue; // MinValue means no deadline.
    }
    ...
    
    // New DI-friendly deadline middleware. Note that it does not implement IDispatcher.
    public class DIDeadlineMiddleware : IMiddleware<DeadlineInformation>
    {
        private readonly IDispatcher _next;
    
        // A constructor with an IDispatcher parameter is required for auto-wiring.
        public DIDeadlineMiddleware(IDispatcher next) => _next = next;
    
        // deadlineInfo is a scope service provided by the DI container.
        public ValueTask DispatchAsync(
            IncomingRequest request,
            DeadlineInformation deadlineInfo,
            CancellationToken cancellationToken)
        {
            // Decode the deadline field as usual.
            DateTime deadline = request.Fields.DecodeValue(
                RequestFieldKey.Deadline,
                (ref SliceDecoder decoder) => decoder.DecodeTimeStamp());
    
            if (deadline != DateTime.MinValue)
            {
                // If deadline is not MinValue, store it in deadlineInfo
                deadlineInfo.Value = deadline;
    
                // TODO: enforce deadline while calling _next.DispatchAsync.
            }
            else
            {
                // Call _next.DispatchAsync as usual.
                return _next.DispatchAsync(request, cancellationToken);
            }
        }
    }
    

    如果使用 Microsoft 的 DI 容器,可以使用 IceRpc.Extensions.DependencyInjection 组件提供的 UseMiddleware 扩展方法安装此中间件:

    using IceRpc.Extensions.DependencyInjection;
    using Microsoft.Extensions.DependencyInjection;
    ...
    
    // The DIDeadlineMiddleware is instantiated and managed by the DI container.
    services.AddIceRpcDispatcher(
        builder => builder
            .UseLogger()
            .UseMiddleware()
            .Map());
    
    // To be registered as a transient or scoped service in the DI container.
    [SliceService]
    internal partial class Chatbot : IGreeterService
    {
        // DeadlineInformation is auto-wired by the DI container.
        internal Chatbot(DeadlineInformation deadlineInfo)
        {
            ...
        }
        ...
    }
    

    调用管道与DI

    了解如何使用 DI 容器构建调用管道。

    DI 容器构建调用管道

    与调度管道不同,调用管道在有或没有 DI 容器的情况下几乎相同。 这是因为调用没有自然的 DI 范围: DI 作用域内执行调用,则该作用域来自另一个封闭活动,例如进行此调用的调度。

    IceRPC [C#] 不会为 DI 范围内的调用提供任何特殊支持。特别是 IMiddleware 没有拦截器对应物。

    使用 Microsoft 的 DI 容器构建调用管道

    可以调用 AddIceRpcInvoker 将新的调用器(调用管道)单例添加到您的 DI 容器中。

    例如:

    // Construct an invocation pipeline using Microsoft's DI container.
    
    using IceRpc.Extensions.DependencyInjection;
    using Microsoft.Extensions.DependencyInjection;
    ...
    
    // Add a new IInvoker singleton configured with an action.
    services
        .AddIceRpcClientConnection()
        .AddIceRpcInvoker(builder => builder.Into())
    

    必须使用 Into 方法指定最终调用器。通过此示例,新调用器流入我们之前配置的 ClientConnection 单例。

    IInvokerBuilder 中安装拦截器

    所有使用 IceRPC 发送的拦截器都可以在有或没有 DI 的情况下使用,并使用调用中的通信 features 。 例如,重试拦截器使用 IServerAddressFeature 与连接缓存通信,以协调复制服务器上的重试。

    这些拦截器可以安装在管道或 IInvokerBuilder 中。

    例如:

    // Construct an invocation pipeline using Microsoft's DI container.
    
    using IceRpc.Extensions.DependencyInjection;
    using Microsoft.Extensions.DependencyInjection;
    ...
    
    // Add a new IInvoker singleton configured with an action.
    services
        .AddIceRpcClientConnection()
        .AddIceRpcInvoker(builder =>
            builder
                .UseLogger()
                .Into())
    

    这里,UseLoggerIceRpc.Logger 组件提供的扩展方法。 此扩展方法适用于实现 IServiceProvider 的任何 DI 容器,例如 Microsoft 的 DI 容器和 Simple Injector 的容器。

    UseLogger 方法的实现只是从 DI 容器中检索一个记录器实例, 然后用这个实例创建一个新的拦截器:

    public static IInvokerBuilder UseLogger(this IInvokerBuilder builder) =>
        builder.ServiceProvider.GetService(typeof(ILogger)) is ILogger logger ?
            builder.Use(next => new LoggerInterceptor(next, logger)) :
            throw new InvalidOperationException(
                $"Could not find service of type '{nameof(ILogger)}' in the service container.");
    

    我们建议在创建自己的拦截器时遵循相同的模式,并为 PipelineIInvokerBuilder 提供使用扩展方法。

    通常不鼓励在运行时调用 DI 容器—它是反模式的服务定位器。在这里,应该将 UseLogger 扩展方法视为不受此规则约束的基础设施代码。

    收尾

    最近写的都是基础相关的概念,大家看看就行,以官方为主更为妙哉。

    作者结语

    • 一直做,不停做,才能提升速度
    • 翻译的不好,请手下留情,谢谢
    • 觉得还不错的话,点个
    • 如果对我有点小兴趣,如可加我哦,一起探讨人生,探讨道的世界
      image
  • 相关阅读:
    【Python】13.模块
    CarSim仿真高级进阶(一)---VS 命令行(1)
    (二)k8-集群创建
    React底层实现原理
    [react性能优化]--防止react-re-render: Why Suspense and how ?
    【csdn第十期竞赛】dp专场
    Java实现Excel批量导入数据库
    企业推行OKR的必要条件
    点云 ICP学习-IterativeClosestPoint
    Webpack5 htmlwebpackplugin用法
  • 原文地址:https://www.cnblogs.com/xlgwr/p/18238532