ASP.NET Core使用HostingStartup增强启动操作方法详解

2022-07-26,,,,

概念

在asp.net core中我们可以使用一种机制来增强启动时的操作,它就是hostingstartup。如何叫"增强"操作,相信了解过aop概念的同学应该都非常的熟悉。我们常说aop使用了关注点分离的方式,增强了对现有逻辑的操作。而我们今天要说的hostingstartup就是为了"增强"启动操作,这种"增强"的操作甚至可以对现有的程序可以做到无改动的操作。例如,外部程序集可通过hostingstartup实现为应用提供配置服务、注册服务或中间件管道操作等。

使用方式

hostingstartup属性表示要在运行时激活的承载启动程序集。大致分为两种情况,一种是自动扫描当前web程序集中通过hostingstartup指定的类,另一种是手动添加配置hostingstartupassembles指定外部的程序集中通过hostingstartup指定的类。第一种方式相对简单,但是对web程序本身有入侵,第二种方式稍微复杂一点点,但是可以做到对现有代码无入侵操作,接下来我们分别演示这两种使用方式。

asp.net core中直接定义

首先是在asp.net core程序中直接使用hostingstartup,这种方式比较简单首先在web程序中随便定义一个类,然后实现ihostingstartup接口,最后别忘了在程序集中添加hostingstartupattribute指定要启动的类的类型,具体代码如下所示

仅仅使用上面所示的这些代码,便可在web程序启动的时候去自动执行hostingstartupinweb的configure方法,在这里面我们几乎可以使用所有针对asp.net core程序配置的操作,而且不需要在web程序中额外添加别的代码就可以自动调用hostingstartupinweb的configure方法。

外部程序集引入

我们之前也说过,上面的方式虽然使用起来相对简单一点,仅仅是一点,那就是省去了指定启动程序集的逻辑。但是,上面的方式需要在web程序中添加,这样的话还是会修改代码。而且,可能更多的时候我们是在外部的程序集中编写hostingstartup逻辑,这时候就需要使用另一种方式在将外部程序集中引入hostingstartup。首先我们要在自定义的程序集中至少引入microsoft.aspnetcore.hosting包才能使用hostingstartup

如果你不需要使用注册中间件的逻辑那么仅仅引入microsoft.aspnetcore.hosting.abstractions即可

如果需要使用其他功能包,可以自行在定义的程序集中引入。比如我们定义了一个名为hoststartuplib的standard类库,并创建了名为hoststartuplib的类

然后我们将自定义的hoststartuplib这个standard类库引入web项目中,运行web程序,发现hostingstartupinlib的configure方法并不能被调用。其实我们上面说过了,将hostingstartup从外部程序集引入的话需要手动指定启动程序集的名称。指定启动程序集的方式有两种,一种是指定iwebhostbuilder的扩展usesetting指定

另一种通过添加环境变量aspnetcore_hostingstartupassemblies的方式,可以通过设置launchsettings.json中

可以引入多个包含hostingstartup的程序集,在设置webhostdefaults.hostingstartupassemblieskey或者aspnetcore_hostingstartupassemblies指定多个程序集名称可以使用英文分号(;)隔开程序集名称。虽然是两种形似指定,但是其实本质是一样的那就是设置配置key为hostingstartupassemblie配置的值,下面我们会详细讲解。
通过在程序中设置环境变量的方式等同于window系统中set的方式设置环境变量,或linux系统中export的方式设置环境变量,亦或是直接设置系统环境变量,效果都是一致的。指定完成启动程序集之后,再次运行程序便可以看到hostingstartupinlib的configure方法被调用到了。在这里我们可以看到如果是使用的环境变量的方式去指定启动程序集的话,对现有代码可以做到完全无入侵。

源码探究

在上面我们简单的介绍了hostingstartup的概念及基本的使用方式,基于这些我们产生了几个疑问

  • 首先是关于hostingstartup的基本工作方式是什么
  • 其次是为什么hostingstartup在web程序中不需要配置程序集信息就可以被调用到,而通过外部程序集引入hostingstartup需要手动指定程序集
  • 最后是通过外部程序集引入hostingstartup的指定方式为何只能是usesetting和环境变量的方式
  • 基于以上几个疑问,我们来探索一下hostingstartup的相关源码,来揭开它的神秘面纱。首先废话不多说直接找到源码位置[点击查看源码👈]在genericwebhostbuilder类中的executehostingstartups方法中,关于genericwebhostbuilder类我们在上篇文章深入探究asp.net core startup初始化中主要就是分析这个类,因为这是构建webhost的默认类,而我们接下来要说的executehostingstartups方法也是承载在这个类中,直接贴代码如下所示

通过上面的源码我们就可以很清楚的了解到hostingstartup的基本工作方式。获取的程序集中包含的hostingstartupattribute,通过获取hostingstartupattribute的hostingstartuptype属性得到要执行的ihostingstartup实例,最后执行configure方法,configure方法需要传递iwebhostbuilder的实例,而hostingstartupwebhostbuilder正是实现了iwebhostbuilder接口。
我们了解到了hoststartup的工作方式,接下来我们来探究一下为什么hostingstartup在web程序中不需要配置程序集信息就可以被调用到,而通过外部程序集引入hostingstartup需要手动指定程序集。通过上面的源码我们可以得到一个信息那就是所有需要启动的程序集信息都是来自webhostoptions的getfinalhostingstartupassemblies方法,接下来我们就来查看一下getfinalhostingstartupassemblies方法的实现源码[点击查看源码👈]

从这里我们可以看出程序集信息来自于hostingstartupassemblies属性,而且还要排除掉hostingstartupexcludeassemblies包含的程序集。我们找到他们初始化的相关逻辑大致如下

首先,通过hostingstartupassemblies的初始化逻辑我们可以得出,默认会是有两个数据来源,一个是当前的applicationname,另一个是通过hostingstartupassemblieskey配置的程序集信息。这也解答了我们上面说过的为什么hostingstartup在web程序中不需要配置程序集信息就可以被调用到,而通过外部程序集引入hostingstartup需要手动指定程序集。其次,我们可以了解到通过配置hostingstartupexcludeassemblies信息排除你不想启动的hostingstartup程序集,而且还可以通过配置preventhostingstartup值来禁止使用hostingstartup的功能。
通过上面的代码我们还了解到这三个属性的来源的配置名称都是来自webhostdefaults这个常量类,接下来我们查看一下这三个属性对应的配置名称

也就是说,我们可以可以通过配置这三个名称的配置,来完成hostingstartup相关的功能比如

或通过环境变量的方式去操作

其实这两种配置方式是完全等价的,为什么这么说呢?首先是在configuration中获取配置是忽略大小写的,其实是使用configurewebhostdefaults配置webhost相关信息的时候会添加configbuilder.addenvironmentvariables(prefix: "aspnetcore_")逻辑这样的话获取环境变量的时候可以忽略aspnetcore_前缀。
那么到目前为止,还有一个疑问尚未解决,那就是为何只能通过usesetting和环境变量的方式去配置hostingstartup相关配置,解铃还须系铃人,我们在上面的executehostingstartups方法中看到了这个逻辑

我们可以看到传递了配置configuration的实例_config,我们到初始化_config地方有如下逻辑

也就可以解释为何我们可以通过环境变量去配置hostingstartup,然后我们再来看usesetting方法的逻辑

原来usesetting也是给_config实例设置值,所以无论通过usesetting或环境环境变量的方式去配置,本质都是在操作_config这个配置实例,到此为止所有谜团均以解开。

在skyapm中的使用

我们上面说了hostingstartup可以增强启动时候的操作,可以通过对现有代码无入侵的方式增强程序功能。而skyapm-dotnet也正是使用了这个功能,实现了无入侵启动apm监控。我们来回顾一下skyapm-dotnet的使用方式

首先是使用nuget添加skyapm.agent.aspnetcore程序集引用。

  • 其次是在launchsettings.json文件中添加aspnetcore_hostingstartupassemblies:"skyapm.agent.aspnetcore"环境变量配置(等同于set aspnetcore_hostingstartupassemblies=skyapm.agent.aspnetcore或export aspnetcore_hostingstartupassemblies=skyapm.agent.aspnetcore
  • 的方式,本质都是在配置环境变量)
  • 最后通过skywalking__servicename设置程序名称
  • 这里我们通过需要配置aspnetcore_hostingstartupassemblies名称可以看出确实是使用了hostingstartup功能,而通过hostingstartup增强的操作入口肯定就在skyapm.agent.aspnetcore程序集中,我们找到skyapm.agent.aspnetcore程序集的源码[点击查看源码👈]看到了skyapmhostingstartup类实现如下

通过这个我们可以看出确实如此,当然也是等同于我们通过usesetting(webhostdefaults.hostingstartupassemblieskey, "skyapm.agent.aspnetcore")去配置,我们甚至可使用如下的方式去使用skyapm-dotnet

这些写法其实是完全等价的,但是通过环境变量的方式配置hostingstartup启动程序集的方式无疑是最优雅的。所以我们在日常的学习开发中,最好还是通过这种方式去操作。

改造zipkin使用

我们在之前的文章asp.net core整合zipkin链路跟踪中曾演示过基于诊断日志diagnosticsource改进zipkin的集成方式,通过本篇文章讲述的hostingstartup我们可以进步一改进zipkin的集成方式,可以让它使用起来和skyapm-dotnet类似的方式,我们基于之前的示例中的zipkinextensions程序集中添加一个zipkinhostingstartup类,用于承载集成zipkin的操作,代码如下

然后在每个项目的launchsettings.json文件中添加如下所示的配置即可,这样的话就可以做到对现有业务代码无任何入侵。

总结

本文介绍了hostingstartup的基本概念,基础使用以及对其源码的分析和在skyapm-dotnet中的应用,最后我们改造了zipkin的集成方式。hostingstartup在一些集成apm或者链路跟踪的类似场景还是非常实用的,或者如果我们有集成一些基础组件或者三方的组件,但是我们的代码中并不需要直接的使用这些组件中的类或者直接的代码关系,均可以使用hostingstartup的方式去集成,为我们实现对现有代码提供无入侵增强提供了强大的支持。关于hostingstartup我也是在看源码中无意发现的,后来发现微软asp.net core官方文档
use hosting startup assemblies in asp.net core一文中有讲解,然后联想到自己使用过的skyapm-dotnet正是使用了hostingstartup+诊断日志diagnosticsource的方式实现了对代码无入侵的方式进行监控和链路跟踪。于是决定深入研究一下,可谓收获满满,便写下这篇文章希望更多的人能够了解使用这个功能。

到此这篇关于asp.net core使用hostingstartup增强启动操作的文章就介绍到这了,更多相关asp.net core使用hostingstartup增强启动操作内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

《ASP.NET Core使用HostingStartup增强启动操作方法详解.doc》

下载本文的Word格式文档,以方便收藏与打印。