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 ...
随机推荐
- LuoguP2953 [USACO09OPEN]牛的数字游戏Cow Digit Game(博弈论)
1~9显然,后面平\(A\)过去 #include <iostream> #include <cstdio> #include <cstring> #include ...
- 基于.NetCore开发博客项目 StarBlog - (17) 自动下载文章里的外部图片
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...
- FormData 和表单元素(form)的区别
Form 元素 <form>元素表示文档中的一个区域,此区域包含交互控件,用于向 Web 服务器提交信息(文件.字符).下面称之为表单元素或表单. 要向 Web 服务器提交信息,我们必须要 ...
- 使用 Vue.js 框架后的感想
前言 用 Vue 已经有段时间了,把自己的所想所悟写下来,每一个想法都是非常宝贵的,记录成为生活,记录成为习惯. 简化开发 Vue 是可以辅助前端工程师开发 Web App 的一种框架,它节省很多时间 ...
- XYX错误集
(频数递减) # 数据范围:没开Long Long (*inf^2) # while 打成了 if ,if 打成了 while(*inf^2) # 换根DP:两个dfs调用错误 (*inf) # ZK ...
- springboot配置(yami配置文件,JSR303数据校验,多环境配置)
yami配置文件 YAML是 "YAML Ain't a Markup Language" (YAML不是一种标记语言)的递归缩写.在开发的这种语言时,YAML 的意思其实是:&q ...
- helm安装csi-driver-nfs-v4.1.0
Application version v4.1.0 Chart version v4.1.0 获取chart包 helm repo add csi-driver-nfs https://raw.gi ...
- Django 使用VScode 创建工程
一.VSCode 创建Django 工程 VSCode 官方: https://code.visualstudio.com 1 mysite(项目名),创建Django 项目,可以和虚拟环境放在同一目 ...
- 【BotR】CLR类型系统
.NET运行时之书(Book of the Runtime,简称BotR)是一系列描述.NET运行时的文档,2007年左右在微软内部创建,最初目的是为了帮助其新员工快速上手.NET运行时:随着.NET ...
- Redis基本数据结构ZipList
为什么要有ziplist 有两点原因: 普通的双向链表,会有两个指针,在存储数据很小的情况下,我们存储的实际数据的大小可能还没有指针占用的内存大,是不是有点得不偿失?而且Redis是基于内存的,而且是 ...