• 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("
    程序已启动");
                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},
    OSVersion:{1},
    version:{2},
    userName:{3},
    CurrentDirectory:{4}
    Core Count:{5}
    WorkSet:{6}
    Drives:{7}",
                Environment.MachineName,
                Environment.OSVersion,
                Environment.Version,
                Environment.UserName,
                Environment.CurrentDirectory,
                Environment.ProcessorCount,
                Environment.WorkingSet,
                string.Join(",", drives));
    
            Console.WriteLine("
    
    正在获取网络配置.....");
            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} 	Address:{1}", ip.AddressFamily, ip);
            }
    
            Console.WriteLine("
    
    正在上报启动信息.....");
            Console.WriteLine("=========== Ron.Init.dll 结束 ===========");
        }
    }

    上面的代码即表示一个标准的钩子程序,在 Initialize() 内部,进行托管主机检查,获取网络配置等行为,最好,还打印一条上报到遥测服务器的信息,这里是模拟上报检查报告,最后输出结束信息
    代码非常检查,现在打开 Ron.HooksDemo 项目属性页进行钩子挂载

    上图添加环境变量 DOTNET_STARTUP_HOOKS ,并设置其值为 C:UsersAdministratorSourceReposRon.HooksDemoRon.InitinDebug etcoreapp2.2Ron.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("
    
    程序集:Ron.License.dll");
            Console.WriteLine("作者:Ron.liang");
            Console.WriteLine("博客地址:https://www.cnblogs.com/viter/
    
    ");
            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("动态加载程序集,执行清理任务已完成
    
    ");
                Console.WriteLine("=========== Ron.Service.dll 结束 ===========");
            }
        }
    4.2 在 Ron.License 的钩子方法中加载 Ron.Service 程序集,创建 IDisposable 的实现,并调用 Dispose() 方法
    internal class StartupHook
    {
        public static void Initialize()
        {
            Console.WriteLine("
    
    程序集:Ron.License.dll");
            Console.WriteLine("作者:Ron.liang");
            Console.WriteLine("博客地址:https://www.cnblogs.com/viter/
    
    ");
    
            string path = @"C:UsersAdministratorSourceReposRon.HooksDemoRon.ServiceinDebug
    etcoreapp2.2Ron.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 中,挂载钩子的方式是一致的,其行为也相同

    结束语

    使用钩子程序注意事项

    1. 钩子程序不能依赖于托管主机的TPA列表之外的任何程序集,否则会抛出 FileNotFoundException 的异常
    2. 不要挂载过多的钩子程序,这可能会出现兼容性问题,如果要使用多个钩子,必须确保每个钩子程序的行为都是独立的,互不干扰的,如果一定要使用,建议修改托管主机的代码,使用依赖注入的方式而不是钩子
    3. StartupHook 类应该是 internal 类型的,如果是使用 public 进行修饰,还是可以正常加载钩子程序

    演示代码下载

    https://files.cnblogs.com/files/viter/Ron.HooksDemo.zip

    • 您随手点赞是我不断书写的动力,如有错误,欢迎指正
    • 出处:http://www.cnblogs.com/viter/
    • 推荐一个快速开发脚手架,基于 .netcore+pgsql,GitHub地址: https://github.com/lianggx/mystaging
    • 本文版权归作者和博客园共有,欢迎个人转载,必须保留此段声明;商业转载请联系授权,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
     
     
  • 相关阅读:
    uva 10127
    POJ 3280 Cheapest Palindrome
    看图学英语
    看图学英语
    算法 Tricks(四)—— 获取一个数二进制形式第一个不为 0 的位置
    算法 Tricks(四)—— 获取一个数二进制形式第一个不为 0 的位置
    Opencv Surf算子中keyPoints,描述子Mat矩阵,配对向量DMatch里都包含了哪些好玩的东东?
    强大的 function adapters
    强大的 function adapters
    transform、accumulate —— C++ 下的 MapReduce
  • 原文地址:https://www.cnblogs.com/yanglang/p/10142818.html
Copyright © 2020-2023  润新知