Asp.Net Core 轻松学-在.Net Core 中使用钩子
前言
host startup hook,是2.2中提供的一项新的功能,通过使用主机启动钩子,允许开发人员在不修改代码的情况下,在服务启动之前注入代码;通过使用钩子,可以对已部署好的服务在服务启动期间自定义托管程序的行为;通过使用钩子,可以对服务进行跟踪或者遥测,也可以在服务启动前对托管环境进行健康检查;还可以通过钩子动态加载程序集进行依赖注入等功能。
什么是钩子
钩子的作用原理是通过设置环境变量 dotnet_startup_hooks 的值将钩子程序挂载到托管程序之中,在托管程序启动的时候,coreclr 将按照钩子列表顺序进行检查,初始化后执行每个钩子程序,当钩子列表中的钩子程序被逐一执行完成后,托管程序将返回到程序主入口 main 方法,进入一系列的启动,钩子程序可以是任何 .net core 版本的类库项目,在项目内必须包含类 startuphook 这是固定命名,且 startuphook 必须是一个没有命名空间的内部类,包含默认的静态方法 initialize(),符合此规范即可作为钩子程序进行托管挂载
使用钩子
1.首先创建一个控制台项目 ron.hooksdemo ,作为托管主机,用于挂载钩子程序 ron.init
ron.hooksdemo 的代码非常简单,仅仅输出一句话
class program { static void main(string[] args) { console.writeline("\n程序已启动"); console.readkey(); } }
2. 创建钩子程序,ron.init
2.1 按照钩子程序的规范,创建一个无命名空间的内部类 startuphook ,且包含默认静态方法 initialize()
internal class startuphook { public static void initialize() { console.writeline("程序集:ron.init.dll"); console.writeline("正在获取服务器信息....."); string[] drives = environment.getlogicaldrives(); console.writeline("machinename:{0},\nosversion:{1},\nversion:{2},\nusername:{3},\ncurrentdirectory:{4}\ncore count:{5}\nworkset:{6}\ndrives:{7}", environment.machinename, environment.osversion, environment.version, environment.username, environment.currentdirectory, environment.processorcount, environment.workingset, string.join(",", drives)); console.writeline("\n\n正在获取网络配置....."); var hostname = dns.gethostname(); console.writeline("hostname:{0}", hostname); var addresses = dns.gethostaddresses(hostname); foreach (var item in addresses) { ipaddress ip = item.maptoipv4(); console.writeline("addressfamily:{0} \taddress:{1}", ip.addressfamily, ip); } console.writeline("\n\n正在上报启动信息....."); console.writeline("=========== ron.init.dll 结束 ==========="); } }
上面的代码即表示一个标准的钩子程序,在 initialize() 内部,进行托管主机检查,获取网络配置等行为,最好,还打印一条上报到遥测服务器的信息,这里是模拟上报检查报告,最后输出结束信息
代码非常检查,现在打开 ron.hooksdemo 项目属性页进行钩子挂载
上图添加环境变量 dotnet_startup_hooks ,并设置其值为 c:\users\administrator\source\repos\ron.hooksdemo\ron.init\bin\debug\netcoreapp2.2\ron.init.dll,这是本次示例的钩子程序绝对路径
注意:该环境变量的值不支持相对路径,如果尝试使用相对路径,托管主机将抛出 argumentexception 异常
2.2 运行程序,看看是否正确挂载了钩子程序 ron.init
上图红色部分输出信息表示钩子程序挂载成功,蓝色部分表示托管主机已启动,可以看到,托管主机启动是在挂载钩子之后运行的
一定要注意,钩子是在托管程序的 main 方法之前运行的
3. 挂载多个钩子
3.1 一个托管程序可以挂载多个钩子
挂载多个钩子的方法是设置环境变量 dotnet_startup_hooks 的值,多个钩子按顺序执行,其中 windows 和 unix 挂载多个钩子的方式基本相同,这其中,有一点微小的区别
- windows 平台挂载方式
dotnet_startup_hooks = c:\hooks_1.dll;c:\hooks_2.dll
- unix 平台挂载方式
dotnet_startup_hooks =/data/hooks_1.dll:/data/hooks_2.dll
以上 dotnet_startup_hooks 变量的值包含两个钩子程序,其中 windows 平台的值为使用分号(;)进行分隔,unix 平台使用冒号(:)进行分隔,这于传统使用方式一致
3.2 运行挂载了多个钩子的托管程序
-
下面把两个钩子挂载到 ron.hooksdemo 项目后,他们分别是:ron.init 和 ron.license
ron.init 钩子输出的是检查服务器信息,这个信息在之前已经演示,这里不再重复,下面看 ron.license 代码
public static void initialize() { console.writeline("\n\n程序集:ron.license.dll"); console.writeline("作者:ron.liang"); console.writeline("博客地址:https://www.cnblogs.com/viter/\n\n"); console.writeline("=========== ron.license.dll 结束 ==========="); }
- 钩子程序的 ron.license 代码也非常简单,结构和 ron.init 钩子程序一致,只是简单的输出版权信息
3.3 运行 ron.hooksdemo 程序,看下图输出结果
红色部分是 ron.init 钩子输出信息,黄色部分是 ron.license 输出信息,蓝色部分是托管主机 ron.hooksdemo 输出信息
可以看到,钩子上安装挂载的顺序执行的
4. 在钩子中加载额外的程序集
我们应该这么理解,钩子程序也是一个普通的应用程序集;所以一个普通的程序集能做到事情,钩子也一样可以
4.1 在 ron.license 加载一个程序集 ron.service,ron.service 中定义了一个类 userservice,继承自并实现 idisposable 接口
public class userservice : idisposable { public void dispose() { console.writeline("程序集:ron.service.dll"); console.writeline("动态加载程序集,执行清理任务已完成\n\n"); console.writeline("=========== ron.service.dll 结束 ==========="); } }
4.2 在 ron.license 的钩子方法中加载 ron.service 程序集,创建 idisposable 的实现,并调用 dispose() 方法
internal class startuphook { public static void initialize() { console.writeline("\n\n程序集:ron.license.dll"); console.writeline("作者:ron.liang"); console.writeline("博客地址:https://www.cnblogs.com/viter/\n\n"); string path = @"c:\users\administrator\source\repos\ron.hooksdemo\ron.service\bin\debug\netcoreapp2.2\ron.service.dll"; var assembly = assemblyloadcontext.default.loadfromassemblypath(path); dynamic obj = assembly.createinstance("ron.service.userservice"); obj.dispose(); console.writeline("=========== ron.license.dll 结束 ==========="); } }
4.3 运行程序 ron.hooksdemo
从输出结果看到,ron.service 程序集已被成功加载并调用,控制台红色输出信息部分表示加载成功
5. 在 asp.net web api 项目中使用钩子
web api 项目挂载钩子的方式和控制台方式相同,首先我们还是创建一个 web api 项目 ron.hooksdemo.web
接着挂载钩子
"dotnet_startup_hooks": "c:\\users\\administrator\\source\\repos\\ron.hooksdemo\\ron.init\\bin\\debug\\netcoreapp2.2\\ron.init.dll;c:\\users\\administrator\\source\\repos\\ron.hooksdemo\\ron.license\\bin\\debug\\netcoreapp2.2\\ron.license.dll"
5.1 运行 web api 项目 ron.hooksdemo.web
红色输出部分表示 web api 程序的 main 方法在钩子列表执行完成之后成功启动,这表示在 .net core 中,挂载钩子的方式是一致的,其行为也相同
结束语
使用钩子程序注意事项
- 钩子程序不能依赖于托管主机的tpa列表之外的任何程序集,否则会抛出 filenotfoundexception 的异常
- 不要挂载过多的钩子程序,这可能会出现兼容性问题,如果要使用多个钩子,必须确保每个钩子程序的行为都是独立的,互不干扰的,如果一定要使用,建议修改托管主机的代码,使用依赖注入的方式而不是钩子
- startuphook 类应该是 internal 类型的,如果是使用 public 进行修饰,还是可以正常加载钩子程序
演示代码下载
https://files.cnblogs.com/files/viter/ron.hooksdemo.zip
上一篇: Linux系统优化脚本
下一篇: Jenkins节点配置实现原理及过程解析
推荐阅读
-
在Windows系统中构建还原ASP.NET Core 源码
-
在ASP.NET Core中使用托管启动(hosting startup)程序集,实现批量注册service
-
在.NET Core 3.0中的WPF中使用IOC图文教程
-
在 ASP.NET Core 项目中使用 npm 管理你的前端组件包
-
ASP.NET Core在支付宝小程序中使用signalR
-
在ASP.NET Core中显示自定义的错误页面
-
在ASP.NET Core中实现自动注入、批量注入
-
学习ASP.NET Core Razor 编程系列十二——在页面中增加校验
-
Asp.Net Core 轻松学-利用日志监视进行服务遥测
-
ASP.NET Core - 在ActionFilter中使用依赖注入