《ASP.NET Core 微服务实战》-- 读书笔记(第9章)
第 9 章 微服務(wù)系統(tǒng)的配置
微服務(wù)系統(tǒng)中的配置需要關(guān)注更多其他方面的因素,包括:
配置值的安全讀寫
值變更的審計能力
配置信息源本身的韌性和可靠性
少量的環(huán)境變量難以承載大型、復(fù)雜的配置信息
應(yīng)用要決定是否支持配置值的在線更新和實時變更,還要決定如何實現(xiàn)
對功能開關(guān)和層級化設(shè)置的支持
對敏感信息以及加密密鑰本身進行存儲和讀取支持
本章首先討論在應(yīng)用中使用環(huán)境變量的機制,并演示 Docker 的支持情況
接著探索一個來自 Netflix OSS 技術(shù)棧的配置服務(wù)器產(chǎn)品
最后將運用 etcd,它是一個常用于配置管理的開源分布式鍵值數(shù)據(jù)庫
在 Docker 中使用環(huán)境變量
為配置提供默認(rèn)值時,還應(yīng)該考慮哪些設(shè)置在應(yīng)用啟動期間需要通過環(huán)境變量進行覆蓋
為配置設(shè)置值時,可使用鍵值對顯示指定,如下所示:
$ sudo docker run -e SOME_VAR='foo' \ -e PASSWORD='foo' \ -e USER='bar' \ -e DB_NAME='mydb' \ -p 3000:3000 \ --name container_name microservices-aspnetcore/image:tag或者,如果不希望在命令行中顯示傳入值,也可以把來自啟動環(huán)境的環(huán)境變量轉(zhuǎn)發(fā)到容器內(nèi)部,只要不傳入包含值的等式即可,例如:
$ docker run -e PORT -e CLIENTSCRET -e CLIENTKEY [...]這一命令將把命令行所在終端中的 PORT、CLIENTSECRET 和 CLIENTKEY 環(huán)境變量的值傳入 Docker 容器中,在這個過程中它們的值不會在命令行文本中公開,以防范潛在的安全漏洞和敏感信息泄露
如果需要向容器傳入大量的環(huán)境變量,可以向 docker 命令指定一個包含鍵值對列表的文件:
$ docker run --env-file ./myenv.file [...]使用 Spring Cloud 配置服務(wù)器
圍繞服務(wù)的配置管理的最大難題之一,并非如何將值注入到環(huán)境變量,而在于這些值本身的日常維護
當(dāng)配置的原始源處的值發(fā)生變更時,我們?nèi)绾蔚玫酵ㄖ?/p>
更進一步,當(dāng)值發(fā)生變更時,我們?nèi)绾位厮莶⒉榭粗暗闹?/p>
你可能發(fā)現(xiàn),這似乎可用使用類似于 Git 倉庫的方法來管理配置值
Spring Cloud 配置服務(wù)器(SCCS)的開發(fā)人員也持相同看法
要在 .NET Core 應(yīng)用中添加 SCCS 客戶端的支持,只需要在項目中添加對 Steeltoe.Extensions.Configuration.ConfigServer NuGet 包的引用
接著,我們需要配置應(yīng)用,讓它從正確的位置獲取設(shè)置信息
我們需要定義一個 Spring 應(yīng)用名稱,并在 appsettings.json 文件中添加配置服務(wù)器的 URL
{"spring": {"application": {"name": "foo"},"cloud": {"config": {"uri": "http://localhost:8888"}}},"Logging": {"IncludeScopes": false,"LogLevel": {"Default": "Debug","System": "Information","Microsoft": "Information"}} }配置完成后,Startup 構(gòu)造方法仍然與其他應(yīng)用幾乎一致
public Startup(IHostingEnvironment env) {var builder = new ConfigurationBuilder().SetBasePath(env.ContentRootPath).AddJsonFile("appsettings.json", optional: true, reloadOnChange: false).AddEnvironmentVariables().AddConfigServer(env);Configuration = builder.Build(); }要添加對配置服務(wù)器的支持,接下來需要修改 ConfigureServices 方法
首先調(diào)用 AddConfigServer 向依賴注入子系統(tǒng)加入配置客戶端
接著指定泛型參數(shù)并調(diào)用 Configure 方法
這一操作能把從配置服務(wù)器獲取的配置信息包裝為一個 IOptionsSnapshot 對象,然后可由控制器和其他代碼使用
public void ConfigureServices(IServiceCollection services) {services.AddConfigServer(Configuration);services.AddMvc();services.Configure<ConfigServerData>(Configuration); }此處,用于表示從配置服務(wù)器獲取的數(shù)據(jù)的數(shù)據(jù)模型,是基于 Spring Cloud 服務(wù)器示例倉庫中的示例配置進行建模的
public class ConfigServerData {public string Bar { get; set; }public string Foo { get; set; }public Info Info { get; set; } }public class Info {public string Description { get; set; }public string Url { get; set; } }然后,在需要時,就可注入這個類的實例,以及配置服務(wù)器的客戶端參數(shù)
public class MyController : MyController {private IOptionsSnapshot<ConfigServerData> MyConfiguration { get; set; }private ConfigServerClientSettingsOptions ConfigServerClientSettingsOptions { get; set; }public MyController(IOptionsSnapShot<ConfigServerData> opts, IOptions<ConfigServerClientSettingsOptions> clientOpts){...}... }上述配備完成后,如果配置服務(wù)器已處于運行狀態(tài),構(gòu)造器中的 opts 變量將包含應(yīng)用所有的相關(guān)配置
啟動配置服務(wù)器最簡單的方法就是直接通過 Docker 鏡像運行以下代碼
$ docker run -p 8888:8888 \ -e SPRING_CLOUD_CONFIG_SERVER_GET_URI=http://github.com/spring-cloud-samples/ \config-repohyness/spring-cloud-config-server如果服務(wù)器運行正確,應(yīng)該能通過以下命令獲取配置信息
curl http://localhost:8888/foo/development在本地用 Docker 鏡像啟動配置服務(wù)器后,使用上面展示的 C# 代碼,就能體驗將外部配置數(shù)據(jù)提供給 .NET Core 微服務(wù)的過程
使用 etcd 配置微服務(wù)
Spring Cloud 配置服務(wù)器的替代品不計其數(shù),etcd 是其中很流行的一個
上一章簡單提到,etcd 是一個輕量級的分布式鍵值數(shù)據(jù)庫
它就是為你存儲分布式系統(tǒng)所需要的最關(guān)鍵信息的位置
etcd 是一個集群產(chǎn)品,其節(jié)點之間的通信是基于 Raft 共識算法實現(xiàn)的
etcd 的一個最常見運用場景就是存儲和檢索配置信息以及功能標(biāo)志
在本章的例子里,我訪問 compose.io 并注冊了一個免費試用的托管 etcd
創(chuàng)建 etcd 配置提供程序
GitHub鏈接:https://github.com/microservices-aspnetcore/etcd-client
創(chuàng)建配置源
using System; using Microsoft.Extensions.Configuration;namespace ConfigClient {public class EtcdConfigurationSource : IConfigurationSource{public EtcdConnectionOptions Options { get; set; }public EtcdConfigurationSource(EtcdConnectionOptions options){this.Options = options;}public IConfigurationProvider Build(IConfigurationBuilder builder){return new EtcdConfigurationProvider(this);}} }創(chuàng)建配置構(gòu)建器
using System; using System.Collections.Generic; using EtcdNet; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Primitives;namespace ConfigClient {public class EtcdConfigurationProvider : ConfigurationProvider{private EtcdConfigurationSource source;public EtcdConfigurationProvider(EtcdConfigurationSource source){this.source = source;}public override void Load(){EtcdClientOpitions options = new EtcdClientOpitions(){Urls = source.Options.Urls,Username = source.Options.Username,Password = source.Options.Password,UseProxy = false,IgnoreCertificateError = true};EtcdClient etcdClient = new EtcdClient(options);try{EtcdResponse resp = etcdClient.GetNodeAsync(source.Options.RootKey,recursive: true, sorted: true).Result;if (resp.Node.Nodes != null){foreach (var node in resp.Node.Nodes){// child nodeData[node.Key] = node.Value;}}}catch (EtcdCommonException.KeyNotFound){// key does notConsole.WriteLine("key not found exception");}}} }借助如下擴展方法
using Microsoft.Extensions.Configuration;namespace ConfigClient {public static class EtcdStaticExtensions{public static IConfigurationBuilder AddEtcdConfiguration(this IConfigurationBuilder builder,EtcdConnectionOptions connectionOptions){return builder.Add(new EtcdConfigurationSource(connectionOptions));}}public class EtcdConnectionOptions{public string[] Urls { get; set; }public string Username { get; set; }public string Password { get; set; }public string RootKey { get; set; }} }便能在 Startup 類中把 etcd 添加為配置源
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging;namespace ConfigClient {public class Startup{public Startup(IHostingEnvironment env){var builder = new ConfigurationBuilder().SetBasePath(env.ContentRootPath).AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true).AddEtcdConfiguration(new EtcdConnectionOptions{Urls = new string[] {"https://portal1934-21.euphoric-etcd-31.capital-one-3.composedb.com:17174","https://portal2016-22.euphoric-etcd-31.capital-one-3.composedb.com:17174"},Username = "root",Password = "changeme",RootKey = "/myapp"}).AddEnvironmentVariables();Configuration = builder.Build();}public static IConfigurationRoot Configuration { get; set; }// This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services){// Add framework services.services.AddMvc();}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory){loggerFactory.AddConsole();loggerFactory.AddDebug();app.UseMvc();}} }使用來自 etcd 的配置值
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using EtcdNet; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Configuration;namespace ConfigClient.Controllers {[Route("api/[controller]")]public class ValuesController : Controller{private ILogger logger;public ValuesController(ILogger<ValuesController> logger){this.logger = logger;}// GET api/values[HttpGet]public IEnumerable<string> Get(){List<string> values = new List<string>();values.Add(Startup.Configuration.GetSection("/myapp/hello").Value);values.Add(Startup.Configuration.GetSection("/myapp/rate").Value);return values;}// GET api/values/5[HttpGet("{id}")]public string Get(int id){return "value";}// POST api/values[HttpPost]public void Post([FromBody]string value){}// PUT api/values/5[HttpPut("{id}")]public void Put(int id, [FromBody]string value){}// DELETE api/values/5[HttpDelete("{id}")]public void Delete(int id){}} }現(xiàn)在訪問 http://localhost:3000/api/values 端點,將返回這些值:
{"world", "12.5"}這些正是本節(jié)前面面向 etcd 服務(wù)器添加的值
只使用了少數(shù)幾行代碼,我們便創(chuàng)建了一個由遠(yuǎn)程配置服務(wù)器支持的、穩(wěn)定而符合標(biāo)準(zhǔn)的 ASP.NET 配置源
總結(jié)
以上是生活随笔為你收集整理的《ASP.NET Core 微服务实战》-- 读书笔记(第9章)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用.NET Core优雅获取并展示最新
- 下一篇: 前端 JS/TS 调用 ASP.NET