Silky微服务框架在线文档Silky微服务框架在线文档
首页
文档
配置
源码解析
博文
github
gitee
  • 简体中文
  • English
首页
文档
配置
源码解析
博文
github
gitee
  • 简体中文
  • English
  • 启动时

    • 主机的构建
    • 服务引擎
    • 模块
    • 服务与服务条目的解析
    • Silky服务主机
    • 依赖注入约定
    • RPC 服务代理
  • 运行时

    • 终结点与路由
    • 执行器与调度
    • 本地执行器与服务端过滤器
    • 远程执行器与 RPC 调用
    • RPC 服务端消息处理
    • 服务治理
    • 缓存拦截器
    • 分布式事务(TCC)
    • HTTP 网关请求管道
    • 过滤器执行管道
    • Polly 弹性策略管道
    • 端点健康监控

构建服务引擎

在注册Silky微服务应用一节中,我们了解到在ConfigureServices阶段,通过IServiceCollection的扩展方法AddSilkyServices<T>()除了注册必要的服务之外,更主要的是构建了服务引擎(IEngine)。

下面,我们学习在IServiceCollection的扩展方法AddSilkyServices<T>()中完成了什么样的工作。如下所示的代码为在包 Silky.Core 的 ServiceCollectionExtensions.cs中提供的扩展方法AddSilkyServices<T>()。

// ServiceCollectionExtensions.cs(Silky.Core)
public static IEngine AddSilkyServices<T>(
    this IServiceCollection services,
    IConfiguration configuration,
    IHostEnvironment hostEnvironment,
    SilkyApplicationCreationOptions options)   // 应用创建选项(插件源、应用名称等)
    where T : SilkyModule                      // 约束:T 必须是 SilkyModule 的子类
{
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
    CommonSilkyHelpers.DefaultFileProvider = new SilkyFileProvider(hostEnvironment); // 构建文件服务提供者
    services.TryAddSingleton(CommonSilkyHelpers.DefaultFileProvider);
    services.TryAddSingleton<IInitLoggerFactory>(new DefaultInitLoggerFactory());
    services.TryAddTransient<IBannerPrinter, BannerPrinter>();
    services.AddHostedService<InitSilkyHostedService>(); // 注册后台启动任务(模块初始化 / 关闭)
    services.AddSingleton<ICancellationTokenProvider>(NullCancellationTokenProvider.Instance);

    var moduleLoader = new ModuleLoader();
    services.TryAddSingleton<IModuleLoader>(moduleLoader);
    services.AddOptions<PlugInSourceOptions>()
        .Bind(configuration.GetSection(PlugInSourceOptions.PlugInSource));

    var engine = EngineContext.Create(); // 创建全局单例的服务引擎
    engine.SetConfiguration(configuration);           // 注入配置
    engine.SetApplicationOptions(options);            // 注入应用选项
    engine.Banner = BannerHelper.BuildBanner(options);
    engine.SetHostEnvironment(hostEnvironment);       // 注入主机环境
    engine.SetTypeFinder(services,                   // 构建类型查找器(扫描所有程序集)
        CommonSilkyHelpers.DefaultFileProvider,
        options.AppServicePlugInSources,
        options.ModulePlugInSources);
    engine.LoadModules(services, typeof(T), moduleLoader, options.ModulePlugInSources); // 解析并排序所有模块
    engine.ConfigureServices(services, configuration); // 遍历 IConfigureService + 各模块 ConfigureServices
    return engine;
}

创建服务引擎的对象方法如下所示,我们可以看出,服务引擎在整个应用的生命周期是全局单例的。

internal static IEngine Create()
{
    return Singleton<IEngine>.Instance ?? (Singleton<IEngine>.Instance = new SilkyEngine()); // 服务引擎在应用的整个生命周期是单例的
}

通过我们对上述代码注释可以看出,在AddSilkyServices<T>()方法中,在该方法中做了如下关键性的工作:

  1. 构建了一个关键性的对象 文件服务提供者(SilkyFileProvider) ,该对象主要用于扫描或是获取指定的文件(例如应用程序集等)以及提供文件夹等帮助方法;

  2. 使用EngineContext创建了服务引擎对象SilkyEngine对象;

  3. 使用IServiceCollection注册了必要的核心的对象,如:SilkyFileProvider、ModuleLoader、NullCancellationTokenProvider等;

  4. 创建模块加载器ModuleLoader对象,并通过服务引擎解析、加载silky模块,需要指出的是,在这里我们需要指定启动模块,系统会根据启动模块指定的依赖关系进行排序;

  5. 注册后台任务服务InitSilkyHostedService,该服务用于初始化各个模块的任务或是在应用停止时释放模块资源;在各个模块的初始化工作中完成了很多核心的工作,例如:对应用服务以及服务条目的解析、服务元数据的注册、服务实例的注册与更新、Rpc消息监听者的启动等等;

  6. 在调用服务引擎的ConfigureServices()方法时,通过服务引擎扫描所有IConfigureService接口的类,通过反射创建实现类的对象,通过IServiceCollection对服务进行注册;以及通过遍历所有的Silky模块实例,通过模块的提供的ConfigureServices()的方法通过IServiceCollection对服务进行注册。

提示

如果熟悉 nopCommerce 框架的小伙伴们应该注意到,SilkyEngine服务引擎的作用与构建与该框架的设计基本是一致的。

服务引擎的作用

服务引擎的SilkyEngine的作用主要由如下几点:

  1. 通过模块加载器ModuleLoader解析和加载模块,关于模块如何解析和加载,请查看下一节模块内容;

  2. 实现服务的依赖注入,本质上来说要么通过IServiceCollection服务实现服务注册,要么通过Autofac提供的ContainerBuilder实现服务注册;

服务引擎实现服务的依赖注入主要由如下几种方式实现:

2.1 通过扫描所有IConfigureService接口的类,并通过反射的方式构建实现类的对象,然后可以通过IServiceCollection对服务进行注册;以及通过遍历所有的Silky模块实例,通过模块的提供的ConfigureServices()的方法通过IServiceCollection对服务进行注册。

如下代码为服务引擎提供的ConfigureServices()方法源码:

// SilkyEngine 实现的 ConfigureServices 方法
void IEngine.ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
    // 通过类型查找器查找所有 IConfigureService 实现类,并通过反射创建实例
    var configureServices = _typeFinder.FindClassesOfType<IConfigureService>();
    var instances = configureServices
        .Select(configureService => (IConfigureService)Activator.CreateInstance(configureService));

    foreach (var instance in instances)            // 遍历 IConfigureService 实现,完成服务注册
        instance.ConfigureServices(services, Configuration);

    foreach (var module in Modules)                // 遍历所有模块,通过各模块的 ConfigureServices 注册
        module.Instance.ConfigureServices(services, Configuration);

    ServiceProvider = services.BuildServiceProvider(); // 构建初始服务提供者(用于早期解析)
}

在上述代码中,我们可以看到在该方法体内主要完成如下工作:

A) 使用类型查找器查找到所有IConfigureService实现类,并通过反射的方式创建其实例,遍历其实例,其实例通过IServiceCollection实现对服务的注册;

B) 遍历所有的模块,通过模块的实例提供的ConfigureServices()方法,通过IServiceCollection实现对服务的注册;

C) 调用 services.BuildServiceProvider() 构建初始服务提供者,供框架早期阶段解析服务使用(Autofac 容器构建完成后会替换它);

2.2 在上一章注册silky微服务应用中有指出, 执行ContainerBuilder方法时,主要通过Autofac的ContainerBuilder实现服务的依赖注册。

// HostBuilderExtensions.cs(Silky.Core)
public static IHostBuilder RegisterSilkyServices<T>(
    this IHostBuilder builder,
    Action<SilkyApplicationCreationOptions> optionsAction = null)
    where T : SilkyModule   // 注意:约束是 SilkyModule,不是 StartUpModule
{
    // 其他代码略...
    builder
        .UseServiceProviderFactory(new AutofacServiceProviderFactory()) // 替换为 Autofac 容器
        .ConfigureContainer<ContainerBuilder>(containerBuilder =>
        {
            engine.RegisterModules(containerBuilder);      // 通过 Autofac Module 注册模块服务
            engine.RegisterDependencies(containerBuilder); // 通过约定接口(ISingletonDependency 等)注册服务
        });
    // ...
}

我们看到,如何通过ContainerBuilder实现服务注册,也是通过服务引擎巧妙的实现:一种方式是通过模块,另外一种方式是通过约定的依赖方式。

2.2.1 通过模块注册服务

在SilkyModule的定义中,我们看到模块的基类是Autofac.Module,我们在遍历所有的模块实例的过程中,通过ContainerBuilder提供的RegisterModule()方法实现模块指定的服务的注册。换句话说,就是在在执行RegisterModule()的方法过程中,Autofac会调用模块的提供的RegisterServices(ContainerBuilder builder)实现具体的服务注册。

public void RegisterModules(ContainerBuilder containerBuilder)
{
    containerBuilder.RegisterInstance(this).As<IModuleContainer>().SingleInstance();
    var assemblyNames = ((AppDomainTypeFinder)_typeFinder).AssemblyNames;
    foreach (var module in Modules)
    {
        if (!assemblyNames.Contains(module.Assembly.FullName))
        {
            ((AppDomainTypeFinder)_typeFinder).AssemblyNames.Add(module.Assembly.FullName);
        }
        // Autofac 内部会调用 module.Load() -> RegisterServices(builder)
        containerBuilder.RegisterModule((SilkyModule)module.Instance);
    }
}

所以在Silky模块的定义SilkyModule中,提供了如下虚方法(RegisterServices),实际上是Autofac的基类Autofac.Module的一个基础方法,在调用containerBuilder.RegisterModule((SilkyModule)module.Instance)时,底层会通过调用模块的Load()实现模块的具体服务的注册。在Load()方法中,每个模块会调用RegisterServices(builder)实现通过ContainerBuilder对服务进行注册。

protected override void Load([NotNull] ContainerBuilder builder)
{
    base.Load(builder);
    RegisterServices(builder);
}

所以,Silky具体的模块可以通过重写RegisterServices([NotNull] ContainerBuilder builder)实现该模块使用ContainerBuilder实现服务的依赖注册。

protected virtual void RegisterServices([NotNull] ContainerBuilder builder)
{
}

提示

使用ContainerBuilder实现服务的注册和通过IServiceCollection实现服务的注册的效果是一致的;使用ContainerBuilder实现服务的注册的优势在于支持命名服务的注册。也就是在服务注册的过程中,可以给服务起个名字,在服务解析的过程中,通过名称去解析到指定名称的接口的实现的对象。

2.2.2 通过约定注册服务

服务引擎SilkyEngine通过调用RegisterDependencies()方法,使用ContainerBuilder实现对约定的规范的服务进行注册。

 public void RegisterDependencies(ContainerBuilder containerBuilder)
{
    containerBuilder.RegisterInstance(this).As<IEngine>().SingleInstance();
    containerBuilder.RegisterInstance(_typeFinder).As<ITypeFinder>().SingleInstance();

    var dependencyRegistrars = _typeFinder.FindClassesOfType<IDependencyRegistrar>();
    var instances = dependencyRegistrars
        .Select(dependencyRegistrar => (IDependencyRegistrar)Activator.CreateInstance(dependencyRegistrar))
        .OrderBy(dependencyRegistrar => dependencyRegistrar.Order);
    foreach (var dependencyRegistrar in instances)
        dependencyRegistrar.Register(containerBuilder, _typeFinder);
}

在上面的代码中,我们看到通过构建约定注册器(IDependencyRegistrar)的实例,通过约定注册器实现指定服务的注册。系统存在两个默认的约定注册器:

(1) DefaultDependencyRegistrar,该服务注册器可以实现对标识接口的服务注册;

A) 对继承ISingletonDependency的类注册为单例; B) 对继承ITransientDependency的类注册为瞬态; C) 对继承IScopedDependency的类注册为范围;

(2) NamedServiceDependencyRegistrar 实现了对命名服务的注册;在某个类继承上述标识接口时,如果通过InjectNamedAttribute特性对服务进行命名,那么该服务的将会被命名为该名称的服务,在解析该服务的时候,可以通过名称进行解析。 例如:

// 该服务将会被注册为范围的,并被命名为:DemoService,在服务解析过程中可以通过服务名 DemoService 解析到
[InjectNamed("DemoService")]
public class DemoService : IScopedDependency
{

}

  1. 服务引擎提供了多种判断服务是否注册以及服务解析方法;

  2. 服务引擎提供了获取指定的配置项的方法;

  3. 可以通过服务引擎获取类型查找器(TypeFinder)、服务配置器(Configuration)、主机环境变量提供者(IHostEnvironment)、以及主机名(HostName)等信息。

获取和使用服务引擎

在开发过程中,可以通过EngineContext.Current获取服务引擎,并使用服务引擎提供的各个方法,例如:判断服务是否注册、解析服务、获取配置类、获取当前原因的主机名称、或是使用类型查找器(TypeFinder)、服务配置器(Configuration)、主机环境变量提供者(IHostEnvironment)等。

提示

在开发过程中,使用服务引擎的大部分场景是,在不方便实现对某个服务进行构造注入的场景下,通过服务引擎实现对某个服务解析,从而得到该服务的实例。

编辑当前页
Prev
主机的构建
Next
模块