Silky微服务框架之服务引擎
构建服务引擎
在注册Silky微服务应用一节中,我们了解到在ConfigureServices
阶段,通过IServiceCollection
的扩展方法AddSilkyServices<T>()
除了注册必要的服务之外,更主要的是构建了服务引擎(IEngine
)。
下面,我们学习在IServiceCollection
的扩展方法AddSilkyServices<T>()
中完成了什么样的工作。如下所示的代码为在包 Silky.Core 的 ServiceCollectionExtensions.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>()
方法中,在该方法中做了如下关键性的工作:
构建了一个关键性的对象 文件服务提供者(
SilkyFileProvider
) ,该对象主要用于扫描或是获取指定的文件(例如应用程序集等)以及提供文件夹等帮助方法;使用
EngineContext
创建了服务引擎对象SilkyEngine
对象;使用
IServiceCollection
注册了必要的核心的对象,如:SilkyFileProvider
、ModuleLoader
、NullCancellationTokenProvider
等;创建模块加载器
ModuleLoader
对象,并通过服务引擎解析、加载silky模块,需要指出的是,在这里我们需要指定启动模块,系统会根据启动模块指定的依赖关系进行排序;注册后台任务服务
InitSilkyHostedService
,该服务用于初始化各个模块的任务或是在应用停止时释放模块资源;在各个模块的初始化工作中完成了很多核心的工作,例如:对应用服务以及服务条目的解析、服务元数据的注册、服务实例的注册与更新、Rpc消息监听者的启动等等;在调用服务引擎的
ConfigureServices()
方法时,通过服务引擎扫描所有IConfigureService
接口的类,通过反射创建实现类的对象,通过IServiceCollection
对服务进行注册;以及通过遍历所有的Silky模块实例,通过模块的提供的ConfigureServices()
的方法通过IServiceCollection
对服务进行注册。
提示
如果熟悉 nopCommerce 框架的小伙伴们应该注意到,
SilkyEngine
服务引擎的作用与构建与该框架的设计基本是一致的。
服务引擎的作用
服务引擎的SilkyEngine
的作用主要由如下几点:
通过模块加载器
ModuleLoader
解析和加载模块,关于模块如何解析和加载,请查看下一节模块内容;实现服务的依赖注入,本质上来说要么通过
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
方法时,主要通过Autofac
的ContainerBuilder
实现服务的依赖注册。
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
{
}
服务引擎提供了多种判断服务是否注册以及服务解析方法;
服务引擎提供了获取指定的配置项的方法;
可以通过服务引擎获取类型查找器(
TypeFinder
)、服务配置器(Configuration
)、主机环境变量提供者(IHostEnvironment
)、以及主机名(HostName
)等信息。
获取和使用服务引擎
在开发过程中,可以通过EngineContext.Current
获取服务引擎,并使用服务引擎提供的各个方法,例如:判断服务是否注册、解析服务、获取配置类、获取当前原因的主机名称、或是使用类型查找器(TypeFinder
)、服务配置器(Configuration
)、主机环境变量提供者(IHostEnvironment
)等。
提示
在开发过程中,使用服务引擎的大部分场景是,在不方便实现对某个服务进行构造注入的场景下,通过服务引擎实现对某个服务解析,从而得到该服务的实例。
Silky微服务框架之服务引擎的更多相关文章
- silky微服务框架的服务治理介绍
目录 服务治理的概念 服务注册与发现 负载均衡 超时 故障转移(失败重试) 熔断保护(断路器) 限流 RPC限流 HTTP限流 1. 添加配置 2. 注册服务 3.启用 AspNetCoreRateL ...
- 使用dubbo分布式服务框架发布服务及消费服务
什么是DUBBO DUBBO是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案. 准备工作 安装zookeeper ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服 ...
- Windows服务框架与服务的编写
从NT内核开始,服务程序已经变为一种非常重要的系统进程,一般的驻守进程和普通的程序必须在桌面登录的情况下才能运行,而许多系统的基础程序必须在用户登录桌面之前就要运行起来,而利用服务,可以很方便的实现这 ...
- 日调度万亿次,微服务框架TSF大规模应用——云+未来峰会开发者专场回顾
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 演讲者:张浩 腾讯云中间件产品负责人 背景:众多开发者中,一定经历类似的甜蜜烦恼,就是当线上业务规模越来越大,系统分支发展越来越多的时候,初 ...
- Taurus.MVC 微服务框架 入门开发教程:项目集成:1、服务端:注册中心、网关(提供可运行程序下载)。
系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...
- Dubbo(一) 开始认识Dubbo,分布式服务框架
引言: 以前的车马很慢,一生只够爱一个人以前的网站人很少,一个单应用服务着一个人--------------------现在,动不动就谈什么高并发,千万级访问.单应用?BOOM!分分钟爆炸.于是,技术 ...
- 使用“消息服务框架”(MSF)实现分布式事务的三阶段提交协议(电商创建订单的示例)
1,示例解决方案介绍 在上一篇 <消息服务框架(MSF)应用实例之分布式事务三阶段提交协议的实现>中,我们分析了分布式事务的三阶段提交协议的原理,现在我们来看看如何使用消息服务框架(MSF ...
- 003-读书笔记-企业IT架构转型之道-阿里巴巴中台战略思想与架构实战-分布式服务框架的选择
3.1.淘宝平台“服务化”历程 大约2007年,淘宝500人团队,维护一个war包,200多个功能模块. 1)项目团队协同成本高,业务响应越来越慢 2)应用复杂度超出人的认知负载. 3)错误难于隔离[ ...
- 分布式服务框架选型:面对Dubbo,阿里巴巴为什么选择了HSF?
转载:http://www.sohu.com/a/141490021_268033 阿里巴巴集团内部使用的分布式服务框架 HSF(High Speed Framework,也有人戏称“好舒服”)已经被 ...
随机推荐
- BZOJ3732 (Kruskal重构树)
Kruskal重构树上\(x\)和\(v\)的\(lca\)的权值即为它们最长路最小值 #include <cstdio> #include <iostream> #inclu ...
- Vue3 + Socket.io + Knex + TypeScript 实现可以私聊的聊天室
前言 下文只在介绍实现的核心代码,没有涉及到具体的实现细节,如果感兴趣可以往下看,在文章最后贴上了仓库地址.项目采用前后端模式,前端使用 Vite + Vue3 + TS:后端使用 Knex + Ex ...
- MapReduce核心原理(下)
MapReduce 中的排序 MapTask 和 ReduceTask 都会对数据按key进行排序.该操作是 Hadoop 的默认行为,任何应用程序不管需不需要都会被排序.默认排序是字典顺序排序,排序 ...
- FFT快速傅立叶变换:解析wav波频图、Time Domain、Frequency Domain
您好,此教程将教大家使用scipy.fft分析wav文件的波频图.Time Domain.Frequency Domain. 实际案例:声音降噪,去除高频. 结果: 波频图: Time Domain:
- 状态 :睡眠中,进程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命 ...
- [WPF] 使用 HandyControl 的 CirclePanel 画出表盘刻度
1. 前言 最近需要一个 WPF 的表盘控件,之前 Cyril-hcj 写过一篇不错的博客 <WPF在圆上画出刻度线>,里面介绍了一些原理及详细实现的代码: double radius = ...
- KingbaseES TOAST存储方式
KingbaseES为"大字段"的物理存储提供了TOAST功能,通过合适的配置策略能够减少IO次数和扫描块数,进而提升查询速度. TOAST:The Oversized-Attri ...
- KingbaseES 支持列加密
KINGBASE 列加密支持 sm4 和 rc4 加密算法,具体算法在 initdb 时指定,默认是 sm4.要使用列加密,必须 shared_preload_libraries = 'sysencr ...
- 【pkuwc2018】随机算法
我们考虑用状压dp来解决这一道题 设$f[i][S]$表示当前排列的前i位所构成的最大独立集恰好为S的方案数 我们考虑用$f[i][S]$推出$f[i+1][S']$的值 那么我们有两种扩展的方法,一 ...
- MQ的消息丢失/重复/积压的问题解决
在我们实际的开发过程中,我们肯定会用到MQ中间件,常见的MQ中间件有kafka,RabbitMQ,RocketMQ.在使用的过程中,我们必须要考虑这样一个问题,在使用MQ的时候,我们怎么确保消息100 ...