Silky微服务框架之模块
模块的定义
Silky是一个包括多个nuget包构成的模块化的框架,每个模块将程序划分为一个个小的结构,在这个结构中有着自己的逻辑代码和自己的作用域,不会影响到其他的结构。
模块类
一般地,一个模块的定义是通过在该程序集内创建一个派生自 SilkyModule的类,如下所示:
public class RpcModule : SilkyModule
{
}
SilkyModule是一个抽象的类,它定义了模块的基础方法,体现了模块在框架中的作用;
SilkyModule模块定义的核心代码如下所示:
public abstract class SilkyModule : Autofac.Module, ISilkyModule, IDisposable
{
protected SilkyModule()
{
Name = GetType().Name.RemovePostFix(StringComparison.OrdinalIgnoreCase, "Module");
}
protected override void Load([NotNull] ContainerBuilder builder)
{
base.Load(builder);
RegisterServices(builder);
}
public virtual void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
}
protected virtual void RegisterServices([NotNull] ContainerBuilder builder)
{
}
public virtual Task Initialize([NotNull] ApplicationContext applicationContext)
{
return Task.CompletedTask;
}
public virtual Task Shutdown([NotNull] ApplicationContext applicationContext)
{
return Task.CompletedTask;
}
public virtual string Name { get; }
// 其他代码略...
}
通过对SilkyModule模块代码定义的分析我们可以得知,一个Silky模块有如下几个作用:
在
ConfigureServices()方法中,通过IServiceCollection实现服务注册;在
RegisterServices()方法中,通过ContainerBuilder实现服务注册;在应用程序启动时,通过
Initialize()方法实现模块的初始化方法;在应用程序停止时,执行
Shutdown()方法,可以实现模块资源的释放;
关于上述第1、2 点的作用, 我们已经在服务引擎一章中做了详细的解析;关于第3、4点的作用,应用程序是如何在启动时调用Initialize()方法或是在停止时执行Shutdown()方法呢?
在构建服务引擎一章中,我们提到,在构建服务引擎时,我们有一项很重要的工作就是注册了InitSilkyHostedService后台任务。
后台任务InitSilkyHostedService的源码如下所示:
public class InitSilkyHostedService : IHostedService
{
private readonly IModuleManager _moduleManager;
private readonly IHostApplicationLifetime _hostApplicationLifetime;
public InitSilkyHostedService(IServiceProvider serviceProvider,
IModuleManager moduleManager,
IHostApplicationLifetime hostApplicationLifetime)
{
if (EngineContext.Current is SilkyEngine)
{
EngineContext.Current.ServiceProvider = serviceProvider;
}
_moduleManager = moduleManager;
_hostApplicationLifetime = hostApplicationLifetime;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
Console.WriteLine(@"
_____ _ _ _
/ ____|(_)| || |
| (___ _ | || | __ _ _
\___ \ | || || |/ /| | | |
____) || || || < | |_| |
|_____/ |_||_||_|\_\ \__, |
__/ |
|___/
");
var version = Assembly.GetExecutingAssembly().GetName().Version;
var ver = $"{version.Major}.{version.Minor}.{version.Build}";
Console.WriteLine($" :: Silky :: {ver}");
_hostApplicationLifetime.ApplicationStarted.Register(async () =>
{
await _moduleManager.InitializeModules();
});
}
public async Task StopAsync(CancellationToken cancellationToken)
{
_hostApplicationLifetime.ApplicationStopped.Register(async () =>
{
await _moduleManager.ShutdownModules();
});
}
}
在后台任务
StartAsync(),在打印Silky的banner后,在应用启动时注册一个回调方法,通过模块管理器IModuleManager执行初始化模块方法;在后台任务
StopAsync(),在应用停止后注册一个回调方法,通过模块管理器IModuleManager执行关闭模块方法,一般用于各个模块的资源释放;
下面,我们查看模块管理器ModuleManager是如何初始化模块的:
public async Task InitializeModules()
{
foreach (var module in _moduleContainer.Modules)
{
try
{
Logger.LogInformation("Initialize the module {0}", module.Name);
await module.Instance.Initialize(new ApplicationContext(_serviceProvider, _moduleContainer));
}
catch (Exception e)
{
Logger.LogError($"Initializing the {module.Name} module is an error, reason: {e.Message}");
throw;
}
}
}
模块容器_moduleContainer的属性_moduleContainer.Modules是通过模块加载器ModuleLoader加载并通过依赖关系进行排序得到的所有模块的实例,我们看到通过foreach对所有的模块实例进行遍历,并依次执行各个模块的Initialize()方法。
同样的,在应用程序停止时,会调用InitSilkyHostedService任务的StopAsync(),该方法通过调用模块管理器的ShutdownModules()方法,实现对各个模块资源的释放;
public async Task ShutdownModules()
{
foreach (var module in _moduleContainer.Modules)
{
await module.Instance.Shutdown(new ApplicationContext(_serviceProvider, _moduleContainer));
}
}
模块的类型
在Silky框架中,我将模块的类型划分为如下几种类型:
模块的定义
SilkyModule: SilkyModule是一个抽象的模块,用于定义模块的概念;其他业务模块必须要派生自该类;业务模块: 直接派生自
SilkyModule类的非抽象类,Silky框架中,几乎所有的包在通过定义业务模块后从而实现模块化编程的,很多核心的包都是业务模块,如:SilkyModule、ConsulModule、DotNettyModule等等模块都属于业务模块;Http类型的业务模块:该类型的业务模块派生自HttpSilkyModule,相比一般的业务模块,该类型的模块增加了
Configure(IApplicationBuilder application)方法,该类型的模块一般用于通过web主机构建的微服务应用或是网关中,可以在Configure()方法中通过IApplicationBuilder引用http中间件,在silky框架中,诸如:CorsModule、IdentityModule、MiniProfilerModule等均是该类型的模块; 需要特别注意的是,需要http业务模块配置的中间件起效果的话,不要忘记需要在Startup类中的Configure进行如下配置:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.ConfigureSilkyRequestPipeline();
}
- 启动模块:该类型的模块派生自StartUpModule的非抽象类;在模块加载过程中,通过指定启动模块,从而得知模块的依赖关系,模块加载器会通过模块的依赖对模块进行排序,从而影响应用在启动时各个模块的执行的先后顺序;Silky模块预定义了多个启动模块,可以用于不同silky主机的构成:
A)DefaultGeneralHostModule用于构建普通的业务主机,一般用于托管只提供RPC服务的微服务应用;
B)WebSocketHostModule用于构建提供WebSocket服务能力的业务主机;
C)DefaultWebHostModule用于构建能够提供Http服务的业务主机,对外可以提供http服务,也可以用于内部rpc通信;
D)DefaultGatewayHostModule用于构建网关微服务,一般为微服务集群暴露对外部的http访问端口,通过路由机制,将http请求转发到具体某个服务条目,对内通过RPC进行通信;
除此之外,开发者也可以自己的需求,为自己定义需要的启动模块,在构建微服务主机时,指定相应的启动模块。
模块的加载
Silky所有的模块是在什么时候以及如何进行加载和排序的呢?
在之前的构建服务引擎的一章中,我们知道在AddSilkyServices<T>()方法中,我们通过泛型T来指定应用程序的启用模块StartUpModule类型。并构建了模块加载器对象ModuleLoader,并且将模块加载器对象作为服务引擎的LoadModules()方法参数:
public static IEngine AddSilkyServices<T>(this IServiceCollection services, IConfiguration configuration,
IHostEnvironment hostEnvironment) where T : StartUpModule
{
var moduleLoader = new ModuleLoader();
engine.LoadModules<T>(services, moduleLoader);
}
在服务引擎SilkyEngine实现类中,除了实现IEngine接口之外,还需要实现了IModuleContainer接口,IModuleContainer只定义了一个只读属性Modules,要求通过该属性获取所有的模块;在服务引擎中,我们通过模块加载器对象moduleLoader.LoadModules()方法实现对模块的加载与解析,并对属性Modules进行赋值;
internal sealed class SilkyEngine : IEngine, IModuleContainer
{
// 其他代码略...
public void LoadModules<T>(IServiceCollection services, IModuleLoader moduleLoader)
where T : StartUpModule
{
Modules = moduleLoader.LoadModules(services, typeof(T));
}
// 实现IModuleContainer定义的属性
public IReadOnlyList<ISilkyModuleDescriptor> Modules { get; private set; }
}
模块加载器ModuleLoader要求传递两个参数,一个是IServiceCollection的对象services,一个是启动模块StartupModule的的类型typeof(T);下面我们来描述模块加载的过程:
通过
SilkyModuleHelper.FindAllModuleTypes(startupModuleType)查找到启动模块StartupModule类型依赖的所有模块类型;通过反射创建模块的实例,并通过
IServiceCollection注册单例的模块实例,并创建模块描述符SilkyModuleDescriptor;根据模块的依赖关系对模块进行排序;
模块的依赖关系是通过特性DependsOnAttribute指定的,通过DependsOnAttribute在对模块的类进行标注,就可以解析到各个模块的依赖关系,从而实现通过模块的依赖关系进行排序;
提示
熟悉APB框架的小伙伴应该可以看出来,Silky模块的设计主要是借鉴了APB框架的模块设计,在一些细节方面做了调整。
Silky的核心模块
通过上面的介绍, 我们知道一个模块类的最重要的工作主要由两点: 1. 实现服务的注册; 2. 在应用启动时或是停止时执行指定的方法完成初始化任务或是释放资源的任务;
如何判断是否是silky的核心模块呢? 核心模块最重要的一个作用就是在应用启动时,通过Initialize()方法执行该模块的初始化资源的任务;
通过查看源码,我们发现大部分silky模块在应用启动时并没有重写Initialize()方法,也就是说,大部分silky模块在应用启动过程时主要是完成各个模块的服务类的注册并不需要做什么工作。

如上图所示,我们看到silky框架定义的模块,由如上几个模块是在应用启动是完成了主机启动时的关键性作业;
我们再根据模块的依赖关系,可以看到主机在应用启动时,通过模块初始化任务的一个执行顺序如下所示:
RpcModule --> DotNettyTcpModule | TransactionModule | WebSocketModule | [RpcMonitorModule]
--> GeneralHostModule(启动模块[StartUpModule])[DefaultGeneralHostModule|WebSocketHostModule|DefaultWebSocketHostModule]
通过上述的依赖关系,我们可以知道:
Rpc模块在应用启动时是最早被执行;
然后依次执行: DotNettyTcpModule | TransactionModule | WebSocketModule | [RpcMonitorModule] 等模块;
最后执行应用启动模块指定的初始化方法;
在上述的过程中,Silky主机在启动时需要完成如下的工作:
实现Rpc消息监听的订阅;
解析应用服务与服务条目;
启动Rpc消息监听;
解析服务主机和注册该主机实例对应的端点;
向服务注册中心更新或是获取服务元数据(应用服务描述信息和服务条目描述信息);
向服务注册中心注册该服务当前实例的端点以及从服务注册中心获取该服务对应的所有实例;
通过心跳的方式从服务注册中心获取最新的服务元数据以及服务实例信息;
在下面的篇章中,我们将着重介绍上述的过程是如何实现的。
Silky微服务框架之模块的更多相关文章
- silky微服务框架的服务治理介绍
目录 服务治理的概念 服务注册与发现 负载均衡 超时 故障转移(失败重试) 熔断保护(断路器) 限流 RPC限流 HTTP限流 1. 添加配置 2. 注册服务 3.启用 AspNetCoreRateL ...
- silky微服务框架服务注册中心介绍
目录 服务注册中心简介 服务元数据 主机名称(hostName) 服务列表(services) 终结点 时间戳 使用Zookeeper作为服务注册中心 使用Nacos作为服务注册中心 使用Consul ...
- Silky微服务框架之服务引擎
构建服务引擎 在注册Silky微服务应用一节中,我们了解到在ConfigureServices阶段,通过IServiceCollection的扩展方法AddSilkyServices<T> ...
- silky微服务业务主机简介
目录 主机的概念 通用主机 web主机 业务主机类型 使用web主机构建微服务应用 使用通用主机构建微服务应用 构建具有websocket能力的微服务应用 构建网关 开源地址 在线文档 主机的概念 s ...
- Taurus.MVC 微服务框架 入门开发教程:项目部署:3、微服务应用程序版本升级:全站升级和局部模块升级。
系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...
- lms微服务框架主机介绍
目录 概念 .net的通用主机 .net的web主机 lms的业务主机类型 用于托管业务应用的普通主机 1. 创建一个应用台程序 2. 安装Silky.Lms.NormHost包 3. 注册LMS服务 ...
- silky微服务简介
代理主机 silky微服务定义了三种类型的代理主机,开发者可以根据需要选择合适的silky代理主机托管微服务应用.代理主机定义了一个Startup模块,该模块给出了使用该种类型主机所必须依赖的模块. ...
- 基于.NET CORE微服务框架 -surging的介绍和简单示例 (开源)
一.前言 至今为止编程开发已经11个年头,从 VB6.0,ASP时代到ASP.NET再到MVC, 从中见证了.NET技术发展,从无畏无知的懵懂少年,到现在的中年大叔,从中的酸甜苦辣也只有本人自知.随着 ...
- 基于.NET CORE微服务框架 -谈谈surging API网关
1.前言 对于最近surging更新的API 网关大家也有所关注,也收到了不少反馈提出是否能介绍下Api网关,那么我们将在此篇文章中剥析下surging的Api 网关 开源地址:https://git ...
随机推荐
- Taurus.MVC 微服务框架 入门开发教程:项目部署:1、微服务应用程序常规部署实现多开,节点扩容。
系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...
- python包合集-shutil
一.简介 shutil是 python 中的高级文件操作模块,与os模块形成互补的关系,os主要提供了文件或文件夹的新建.删除.查看等方法,还提供了对文件以及目录的路径操作.shutil模块提供了移动 ...
- Navicat的使用与python中使用MySQL的基本方法
Navicat的使用与python中使用MySQL的基本方法 Navicat的下载及安装 下载地址 http://www.navicat.com.cn/download/navicat-premium ...
- 「雅礼集训 2017 Day7」跳蚤王国的宰相(树的重心)
题面 来源 「 雅 礼 集 训 2017 D a y 7 」 跳 蚤 王 国 的 宰 相 传 统 2000 m s 1024 M i B {\tt「雅礼集训 2017 Day7」跳蚤王国的 ...
- VS2017Enterprise版本安装ImageWatch 2017问题解决
按照Andrei给的方法并不一定能成功. 方法如下: 1. Download the extension (ImageWatch.vsix) and open it using WinRAR 2. F ...
- 第七十九篇:数组方法(forEach,some,every,reduce)
好家伙,来复习几个数组方法, 1.forEach循环与some循环 代码如下: <script> const arr =['奔驰','宝马','GTR','奥迪'] //forEach循环 ...
- KingbaseES通过sys_waldump解析wal日志
前言 oracle中的redo日志我们无法直接读取,然而对于KingbaseES数据库,我们可以利用sys_waldump工具解析wal日志,查看wal日志记录的信息. 我们可以利用 sys_wald ...
- git rebase合并多个commit总结
git rebase 合并多个commit 方法 在开发过程中,有时一个任务会分几次commit提交,这样可能对于有些分支要cherry pick时会比较麻烦,这是我们可以通过git rebase 将 ...
- Python数据科学手册-Pandas:累计与分组
简单累计功能 Series sum() 返回一个 统计值 DataFrame sum.默认对每列进行统计 设置axis参数,对每一行 进行统计 describe()可以计算每一列的若干常用统计值. 获 ...
- 使用 systemd 定时器代替 cron 作业
转载自:https://mp.weixin.qq.com/s/HpDVp1sNYve8b7OdoHdGNw 创建一个定时器 首先,创建一个运行基础东西的简单的服务,例如 free 命令.举个例子,你可 ...