构建服务引擎

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

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

public static IEngine AddSilkyServices<T>(this IServiceCollection services, IConfiguration configuration,
IHostEnvironment hostEnvironment) where T : StartUpModule
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; // 指定通信管道的加密传输协议
CommonSilkyHelpers.DefaultFileProvider = new SilkyFileProvider(hostEnvironment); // 构建文件服务提供者
services.TryAddSingleton(CommonSilkyHelpers.DefaultFileProvider); // 向services注册单例的文件服务提供者
var engine = EngineContext.Create(); // 创建单例的服务引擎
services.AddOptions<AppSettingsOptions>()
.Bind(configuration.GetSection(AppSettingsOptions.AppSettings)); // 新增AppSettingsOptions配置
var moduleLoader = new ModuleLoader(); // 创建模块加载器
engine.LoadModules<T>(services, moduleLoader); // 加载所有模块
services.TryAddSingleton<IModuleLoader>(moduleLoader); // 注册单例的模块加载器
services.AddHostedService<InitSilkyHostedService>(); // 注册 InitSilkyHostedService 后台任务服务,该服务用于初始化各个模块的任务或是在应用停止时释放模块资源
services.AddSingleton<ICancellationTokenProvider>(NullCancellationTokenProvider.Instance); //注册默认的CancellationTokenProvider
engine.ConfigureServices(services, configuration, hostEnvironment); // 通过服务引擎扫描所有IConfigureService接口的类,其实现类可以通过IServiceCollection对服务进行注册;以及通过各个模块的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注册了必要的核心的对象,如:SilkyFileProviderModuleLoaderNullCancellationTokenProvider等;

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

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

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

提示

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

服务引擎的作用

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

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

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

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

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

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

// SilkyEngine 实现的ConfigureServices注册服务的方法
public void ConfigureServices(IServiceCollection services, IConfiguration configuration,
IHostEnvironment hostEnvironment)
{
_typeFinder = new SilkyAppTypeFinder(); // 创建类型查找器
ServiceProvider = services.BuildServiceProvider();
Configuration = configuration;
HostEnvironment = hostEnvironment;
HostName = Assembly.GetEntryAssembly()?.GetName().Name; // 解析应用服务主机名称 var configureServices = _typeFinder.FindClassesOfType<IConfigureService>(); // 通过查找器查找所有的`IConfigureService`实现类 var instances = configureServices
.Select(configureService => (IConfigureService)Activator.CreateInstance(configureService)); // 通过反射的方式创建`IConfigureService`实现类的实例 foreach (var instance in instances) // 遍历`IConfigureService`的实现类的实例,并通过其实例实现通过IServiceCollection对服务的注册
instance.ConfigureServices(services, configuration);
// configure modules
foreach (var module in Modules) // 遍历各个模块,通过各个模块提供`ConfigureServices`实现服务的注册
module.Instance.ConfigureServices(services, configuration); AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; }

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

A) 创建类型查找器、构建服务提供者以及为配置器、主机环境变量、主机名等赋值;

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

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

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

public static IHostBuilder RegisterSilkyServices<T>(this IHostBuilder builder)
where T : StartUpModule
{
// 其他代码略...
builder
.UseServiceProviderFactory(new AutofacServiceProviderFactory()) // 替换服务提供者工作类
.ConfigureContainer<ContainerBuilder>(builder => // 通过ContainerBuilder实现服务依赖注册
{
engine.RegisterModules(services, builder);
engine.RegisterDependencies(builder);
})
}

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

2.2.1 通过模块注册服务

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

public void RegisterModules(IServiceCollection services, 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);
} 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)等。

提示

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

Silky微服务框架之服务引擎的更多相关文章

  1. silky微服务框架的服务治理介绍

    目录 服务治理的概念 服务注册与发现 负载均衡 超时 故障转移(失败重试) 熔断保护(断路器) 限流 RPC限流 HTTP限流 1. 添加配置 2. 注册服务 3.启用 AspNetCoreRateL ...

  2. 使用dubbo分布式服务框架发布服务及消费服务

    什么是DUBBO DUBBO是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案. 准备工作 安装zookeeper ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服 ...

  3. Windows服务框架与服务的编写

    从NT内核开始,服务程序已经变为一种非常重要的系统进程,一般的驻守进程和普通的程序必须在桌面登录的情况下才能运行,而许多系统的基础程序必须在用户登录桌面之前就要运行起来,而利用服务,可以很方便的实现这 ...

  4. 日调度万亿次,微服务框架TSF大规模应用——云+未来峰会开发者专场回顾

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 演讲者:张浩 腾讯云中间件产品负责人 背景:众多开发者中,一定经历类似的甜蜜烦恼,就是当线上业务规模越来越大,系统分支发展越来越多的时候,初 ...

  5. Taurus.MVC 微服务框架 入门开发教程:项目集成:1、服务端:注册中心、网关(提供可运行程序下载)。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...

  6. Dubbo(一) 开始认识Dubbo,分布式服务框架

    引言: 以前的车马很慢,一生只够爱一个人以前的网站人很少,一个单应用服务着一个人--------------------现在,动不动就谈什么高并发,千万级访问.单应用?BOOM!分分钟爆炸.于是,技术 ...

  7. 使用“消息服务框架”(MSF)实现分布式事务的三阶段提交协议(电商创建订单的示例)

    1,示例解决方案介绍 在上一篇 <消息服务框架(MSF)应用实例之分布式事务三阶段提交协议的实现>中,我们分析了分布式事务的三阶段提交协议的原理,现在我们来看看如何使用消息服务框架(MSF ...

  8. 003-读书笔记-企业IT架构转型之道-阿里巴巴中台战略思想与架构实战-分布式服务框架的选择

    3.1.淘宝平台“服务化”历程 大约2007年,淘宝500人团队,维护一个war包,200多个功能模块. 1)项目团队协同成本高,业务响应越来越慢 2)应用复杂度超出人的认知负载. 3)错误难于隔离[ ...

  9. 分布式服务框架选型:面对Dubbo,阿里巴巴为什么选择了HSF?

    转载:http://www.sohu.com/a/141490021_268033 阿里巴巴集团内部使用的分布式服务框架 HSF(High Speed Framework,也有人戏称“好舒服”)已经被 ...

随机推荐

  1. BZOJ3732 (Kruskal重构树)

    Kruskal重构树上\(x\)和\(v\)的\(lca\)的权值即为它们最长路最小值 #include <cstdio> #include <iostream> #inclu ...

  2. Vue3 + Socket.io + Knex + TypeScript 实现可以私聊的聊天室

    前言 下文只在介绍实现的核心代码,没有涉及到具体的实现细节,如果感兴趣可以往下看,在文章最后贴上了仓库地址.项目采用前后端模式,前端使用 Vite + Vue3 + TS:后端使用 Knex + Ex ...

  3. MapReduce核心原理(下)

    MapReduce 中的排序 MapTask 和 ReduceTask 都会对数据按key进行排序.该操作是 Hadoop 的默认行为,任何应用程序不管需不需要都会被排序.默认排序是字典顺序排序,排序 ...

  4. FFT快速傅立叶变换:解析wav波频图、Time Domain、Frequency Domain

    您好,此教程将教大家使用scipy.fft分析wav文件的波频图.Time Domain.Frequency Domain. 实际案例:声音降噪,去除高频. 结果: 波频图: Time Domain:

  5. 状态 :睡眠中,进程ID:13431,yum提示Another app is currently holding the yum lock; waiting for it to exit...

    问题描述: 今天想在虚拟机上重新安装docker然后使用到yum命令报错: 解决办法: [root@localhost ~]# rm -f /var/run/yum.pid 然后重新运行刚才的yum命 ...

  6. [WPF] 使用 HandyControl 的 CirclePanel 画出表盘刻度

    1. 前言 最近需要一个 WPF 的表盘控件,之前 Cyril-hcj 写过一篇不错的博客 <WPF在圆上画出刻度线>,里面介绍了一些原理及详细实现的代码: double radius = ...

  7. KingbaseES TOAST存储方式

    KingbaseES为"大字段"的物理存储提供了TOAST功能,通过合适的配置策略能够减少IO次数和扫描块数,进而提升查询速度. TOAST:The Oversized-Attri ...

  8. KingbaseES 支持列加密

    KINGBASE 列加密支持 sm4 和 rc4 加密算法,具体算法在 initdb 时指定,默认是 sm4.要使用列加密,必须 shared_preload_libraries = 'sysencr ...

  9. 【pkuwc2018】随机算法

    我们考虑用状压dp来解决这一道题 设$f[i][S]$表示当前排列的前i位所构成的最大独立集恰好为S的方案数 我们考虑用$f[i][S]$推出$f[i+1][S']$的值 那么我们有两种扩展的方法,一 ...

  10. MQ的消息丢失/重复/积压的问题解决

    在我们实际的开发过程中,我们肯定会用到MQ中间件,常见的MQ中间件有kafka,RabbitMQ,RocketMQ.在使用的过程中,我们必须要考虑这样一个问题,在使用MQ的时候,我们怎么确保消息100 ...