var configBuilder = new ConfigurationBuilder() .AddInMemoryCollection(); if (!options.SuppressEnvironmentConfiguration) { //添加环境变量 configBuilder.AddEnvironmentVariables(prefix: "ASPNETCORE_"); } //构建了_config实例 private readonly IConfiguration _config = configBuilder.Build();
也就可以解释为何我们可以通过环境变量去配置HostingStartup,然后我们再来看UseSetting方法的逻辑
public IWebHostBuilder UseSetting(string key, string value) { _config[key] = value; return this; }
原来UseSetting也是给_config实例设置值,所以无论通过UseSetting或环境环境变量的方式去配置,本质都是在操作_config这个配置实例,到此为止所有谜团均以解开。
在SkyAPM中的使用
我们上面说了HostingStartup可以增强启动时候的操作,可以通过对现有代码无入侵的方式增强程序功能。而SkyAPM-dotnet也正是使用了这个功能,实现了无入侵启动APM监控。我们来回顾一下SkyAPM-dotnet的使用方式
首先是使用Nuget添加SkyAPM.Agent.AspNetCore程序集引用。
using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using SkyApm.Agent.AspNetCore; using SkyApm.AspNetCore.Diagnostics; [assembly: HostingStartup(typeof(SkyApmHostingStartup))] namespace SkyApm.Agent.AspNetCore { internal class SkyApmHostingStartup : IHostingStartup { public void Configure(IWebHostBuilder builder) { builder.ConfigureServices(services => services.AddSkyAPM(ext => ext.AddAspNetCoreHosting())); } } }
通过这个我们可以看出确实如此,当然也是等同于我们通过UseSetting(WebHostDefaults.HostingStartupAssembliesKey, "SkyApm.Agent.AspNetCore")去配置,我们甚至可使用如下的方式去使用SkyAPM-dotnet
public void ConfigureServices(IServiceCollection services) { services.AddSkyAPM(ext => ext.AddAspNetCoreHosting()) }
这些写法其实是完全等价的,但是通过环境变量的方式配置HostingStartup启动程序集的方式无疑是最优雅的。所以我们在日常的学习开发中,最好还是通过这种方式去操作。
改造Zipkin使用
我们在之前的文章ASP.NET Core整合Zipkin链路跟踪中曾演示过基于诊断日志DiagnosticSource改进Zipkin的集成方式,通过本篇文章讲述的HostingStartup我们可以进步一改进Zipkin的集成方式,可以让它使用起来和SkyAPM-dotnet类似的方式,我们基于之前的示例中的ZipkinExtensions程序集中添加一个ZipkinHostingStartup类,用于承载集成Zipkin的操作,代码如下
using System; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; namespace ZipkinExtensions { public class ZipkinHostingStartup: IHostingStartup { public void Configure(IWebHostBuilder builder) { builder.ConfigureServices(services=> { services.AddZipkin(); services.AddSingleton<ITraceDiagnosticListener, HttpDiagnosticListener>(); }); builder.Configure(app=> { IHostApplicationLifetime lifetime = app.ApplicationServices.GetService<IHostApplicationLifetime>(); ILoggerFactory loggerFactory = app.ApplicationServices.GetService<ILoggerFactory>(); IConfiguration configuration = app.ApplicationServices.GetService<IConfiguration>(); string serivceName = configuration.GetValue<string>("ServiceName"); string zipKinUrl = configuration.GetValue<string>("ASPNETCORE_ZIPKINADDRESS"); app.UseZipkin(lifetime, loggerFactory, serivceName, zipKinUrl); }); } } }
然后在每个项目的launchSettings.json文件中添加如下所示的配置即可,这样的话就可以做到对现有业务代码无任何入侵。
"environmentVariables": { "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "ZipkinExtensions", "ASPNETCORE_ZIPKINADDRESS": "http://localhost:9411/" }
总结
本文介绍了HostingStartup的基本概念,基础使用以及对其源码的分析和在SkyAPM-dotnet中的应用,最后我们改造了Zipkin的集成方式。HostingStartup在一些集成APM或者链路跟踪的类似场景还是非常实用的,或者如果我们有集成一些基础组件或者三方的组件,但是我们的代码中并不需要直接的使用这些组件中的类或者直接的代码关系,均可以使用HostingStartup的方式去集成,为我们实现对现有代码提供无入侵增强提供了强大的支持。关于HostingStartup我也是在看源码中无意发现的,后来发现微软ASP.NET Core官方文档
一文中有讲解,然后联想到自己使用过的SkyAPM-dotnet正是使用了HostingStartup+诊断日志DiagnosticSource的方式实现了对代码无入侵的方式进行监控和链路跟踪。于是决定深入研究一下,可谓收获满满,便写下这篇文章希望更多的人能够了解使用这个功能。