构建服务引擎

注册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. SP104 Highways (矩阵树,高斯消元)

    矩阵树定理裸题 //#include <iostream> #include <cstdio> #include <cstring> #include <al ...

  2. 使用three.js(webgl)搭建智慧楼宇、设备检测、数字孪生——第十三课

    老子云:有道无术,术尚可求,有术无道,止于术. 咱开篇引用老子的话术,也没其它意思,只是最近学习中忽有感悟,索性就写了上来. 这句话用现代辩证思维理解,这里的"道" 大抵是指方法论 ...

  3. jQuery 判断父节点下是否有子节点

    查找父节点下是否有子节点有两个情况:(1)查找的是父元素的所有后代节点:(2)仅查找父元素的第一代子节点. <div id="app"> <div> < ...

  4. ByteBuffer数据结构

  5. 技术管理进阶——技术Leader需要数据思维

    原创不易,求分享.求一键三连 假设我长得很漂亮,拥有众多追求者,但是初出闺房的我对这世界上的男人毫无认知,那么该如何选择呢?这真是一个问题! 妈妈说,愿意为我花钱的男人未必爱我,但不愿意为我花钱的男人 ...

  6. C++ Protobuf

    Protobuf protobuf (protocol buffer) 是谷歌内部的混合语言数据标准.通过将结构化的数据进行序列化(串行化),用于通讯协议.数据存储等领域的语言无关.平台无关.可扩展的 ...

  7. 【manim】3b1b的"Almost" Fourier Transform复刻

    最近在做Fourier Transform的内容,记录一下今天下午的成果. 本文代码全部自行编写,需要math and music项目完整工程可以在gayhub上获取.(现在还没弄完,就先不发了.) ...

  8. C# for循环创建多线程

    这里仅讨论Task多线程编程,不讨论其他可以使用多线程的情况,比如Beginxxx,Thread等 一般情况下,如果有多个线程需要同是启动,且每个线程中使用了集合collection中的序号. 比如参 ...

  9. Helm安装ingress-nginx-4.2.3

    Application version 1.3.0 Chart version 4.2.3 获取chart包 helm fetch ingress-nginx/ingress-nginx --vers ...

  10. C语言怎么给函数添加形参的默认值

    以下内容为本人的著作,如需要转载,请声明原文链接微信公众号「englyf」https://www.cnblogs.com/englyf/p/16637890.html 如果不是机缘巧合,当年转到C++ ...