第 9 章 微服务系统的配置

微服务系统中的配置需要关注更多其他方面的因素,包括:

  • 配置值的安全读写

  • 值变更的审计能力

  • 配置信息源本身的韧性和可靠性

  • 少量的环境变量难以承载大型、复杂的配置信息

  • 应用要决定是否支持配置值的在线更新和实时变更,还要决定如何实现

  • 对功能开关和层级化设置的支持

  • 对敏感信息以及加密密钥本身进行存储和读取支持

本章首先讨论在应用中使用环境变量的机制,并演示 Docker 的支持情况

接着探索一个来自 Netflix OSS 技术栈的配置服务器产品

最后将运用 etcd,它是一个常用于配置管理的开源分布式键值数据库

在 Docker 中使用环境变量

为配置提供默认值时,还应该考虑哪些设置在应用启动期间需要通过环境变量进行覆盖

为配置设置值时,可使用键值对显示指定,如下所示:

$ 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

或者,如果不希望在命令行中显示传入值,也可以把来自启动环境的环境变量转发到容器内部,只要不传入包含值的等式即可,例如:

$ docker run -e PORT -e CLIENTSCRET -e CLIENTKEY [...]

这一命令将把命令行所在终端中的 PORT、CLIENTSECRET 和 CLIENTKEY 环境变量的值传入 Docker 容器中,在这个过程中它们的值不会在命令行文本中公开,以防范潜在的安全漏洞和敏感信息泄露

如果需要向容器传入大量的环境变量,可以向 docker 命令指定一个包含键值对列表的文件:

$ docker run --env-file ./myenv.file [...]

使用 Spring Cloud 配置服务器

围绕服务的配置管理的最大难题之一,并非如何将值注入到环境变量,而在于这些值本身的日常维护

当配置的原始源处的值发生变更时,我们如何得到通知

更进一步,当值发生变更时,我们如何回溯并查看之前的值

你可能发现,这似乎可用使用类似于 Git 仓库的方法来管理配置值

Spring Cloud 配置服务器(SCCS)的开发人员也持相同看法

要在 .NET Core 应用中添加 SCCS 客户端的支持,只需要在项目中添加对 Steeltoe.Extensions.Configuration.ConfigServer NuGet 包的引用

接着,我们需要配置应用,让它从正确的位置获取设置信息

我们需要定义一个 Spring 应用名称,并在 appsettings.json 文件中添加配置服务器的 URL

{"spring": {"application": {"name": "foo"},"cloud": {"config": {"uri": "http://localhost:8888"}}},"Logging": {"IncludeScopes": false,"LogLevel": {"Default": "Debug","System": "Information","Microsoft": "Information"}}
}

配置完成后,Startup 构造方法仍然与其他应用几乎一致

public Startup(IHostingEnvironment env)
{var builder = new ConfigurationBuilder().SetBasePath(env.ContentRootPath).AddJsonFile("appsettings.json", optional: true, reloadOnChange: false).AddEnvironmentVariables().AddConfigServer(env);Configuration = builder.Build();
}

要添加对配置服务器的支持,接下来需要修改 ConfigureServices 方法

首先调用 AddConfigServer 向依赖注入子系统加入配置客户端

接着指定泛型参数并调用 Configure 方法

这一操作能把从配置服务器获取的配置信息包装为一个 IOptionsSnapshot 对象,然后可由控制器和其他代码使用

public void ConfigureServices(IServiceCollection services)
{services.AddConfigServer(Configuration);services.AddMvc();services.Configure<ConfigServerData>(Configuration);
}

此处,用于表示从配置服务器获取的数据的数据模型,是基于 Spring Cloud 服务器示例仓库中的示例配置进行建模的

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; }
}

然后,在需要时,就可注入这个类的实例,以及配置服务器的客户端参数

public class MyController : MyController
{private IOptionsSnapshot<ConfigServerData> MyConfiguration { get; set; }private ConfigServerClientSettingsOptions ConfigServerClientSettingsOptions { get; set; }public MyController(IOptionsSnapShot<ConfigServerData> opts, IOptions<ConfigServerClientSettingsOptions> clientOpts){...}...
}

上述配备完成后,如果配置服务器已处于运行状态,构造器中的 opts 变量将包含应用所有的相关配置

启动配置服务器最简单的方法就是直接通过 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

如果服务器运行正确,应该能通过以下命令获取配置信息

curl http://localhost:8888/foo/development

在本地用 Docker 镜像启动配置服务器后,使用上面展示的 C# 代码,就能体验将外部配置数据提供给 .NET Core 微服务的过程

使用 etcd 配置微服务

Spring Cloud 配置服务器的替代品不计其数,etcd 是其中很流行的一个

上一章简单提到,etcd 是一个轻量级的分布式键值数据库

它就是为你存储分布式系统所需要的最关键信息的位置

etcd 是一个集群产品,其节点之间的通信是基于 Raft 共识算法实现的

etcd 的一个最常见运用场景就是存储和检索配置信息以及功能标志

在本章的例子里,我访问 compose.io 并注册了一个免费试用的托管 etcd

创建 etcd 配置提供程序

GitHub链接:https://github.com/microservices-aspnetcore/etcd-client

创建配置源

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);}}
}

创建配置构建器

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){}}
}

现在访问 http://localhost:3000/api/values 端点,将返回这些值:

{"world", "12.5"}

这些正是本节前面面向 etcd 服务器添加的值

只使用了少数几行代码,我们便创建了一个由远程配置服务器支持的、稳定而符合标准的 ASP.NET 配置源

《ASP.NET Core 微服务实战》-- 读书笔记(第9章)相关推荐

  1. 《ASP.NET Core 微服务实战》译者序

    最近,我将<ASP.NET Core 微服务实战>一书由英文翻译为中文.这本书是由清华大学出版社引进的,目前还处于最后的排版校对过程中,现将该书的译者序发表于此. 以下为译者译全文: &q ...

  2. 《ASP.NET Core 微服务实战》送书结果公告

    如何构建基于.NET Core和云环境下的微服务技术体系?的送书抽奖结果已经出来了: 当前只有一位同学填写了地址.其他几位同学抓紧填写,3/9 日还没有完成填写将作废,奖品可是热门的<ASP.N ...

  3. 《ASP.NET Core 微服务实战》-- 读书笔记(第10章)

    第 10 章 应用和微服务安全 云应用意味着应用运行所在的基础设施无法掌控,因此安全不能再等到事后再考虑,也不能只是检查清单上毫无意义的复选框 由于安全与云原生应用密切相关,本章将讨论安全话题,并用示 ...

  4. 《ASP.NET Core 微服务实战》-- 读书笔记(第7章)

    第 7 章 开发 ASP.NET Core Web 应用 ASP.NET Core 基础 在本章,我们将从一个命令行应用开始,并且在不借助任何模板,脚手架和向导的情况下,最终得到一个功能完整的 Web ...

  5. 《ASP.NET Core 微服务实战》-- 读书笔记(第3章)

    第 3 章 使用 ASP.NET Core 开发微服务 微服务定义 微服务是一个支持特定业务场景的独立部署单元.它借助语义化版本管理.定义良好的 API 与其他后端服务交互.它的天然特点就是严格遵守单 ...

  6. 《ASP.NET Core 微服务实战》-- 读书笔记(第1章 、第2章)

    译者序 微服务设计方法清晰定义了各个开发团队的业务边界,微服务框架以不同方式实现了服务之间的协作与集成. .NET Core 作为全新的 .NET 技术,它不仅完全开源.跨平台,更面向云原生开发进行了 ...

  7. 《ASP.NET Core 微服务实战》-- 读书笔记(第12章)

    第 12 章 设计汇总 微服务开发并不是要学习 C#.Java 或者 Go 编程--而是要学习如何开发应用以适应并充分利用弹性伸缩环境的优势,它们对托管环境没有偏好,并能瞬间启停 换句话说,我们要学习 ...

  8. 《ASP.NET Core 微服务实战》-- 读书笔记(第11章)

    第 11 章 开发实时应用和服务 在本章,我们将讨论"实时"的准确含义,以及在大部分消费者看来应该属于这一范畴的应用类型 接着,我们将探讨 WebSocket,并分析为什么传统的 ...

  9. 《ASP.NET Core 微服务实战》-- 读书笔记(第6章)

    第 6 章 事件溯源与 CQRS 在本章,我们来了解一下随着云平台一同出现的设计模式 我们先探讨事件溯源和命令查询职责分离(CQRS)背后的动机与哲学 事件溯源简介 事实由事件溯源而来 我们大脑就是一 ...

最新文章

  1. ACM 模板--邻接表 有向图 搜索算法
  2. ProMesh.Net基本使用说明!
  3. 在asp.net 2.0中使用SqlBulkCopy类迁移数据
  4. linux两台服务器 同一个地址_【网工玩Linux】搭建开源多运营商(ISP)链路负载均衡器...
  5. ELK下Elasticsearch如何关掉服务
  6. git SSL certificate problem: unable to get local issuer certificate
  7. 武器系统仿真技术(一):系统误差分析的蒙特卡洛算法
  8. 《Arduino奇妙之旅:智能车趣味制作天龙八步》一第2章
  9. matlab中firrcos,DMR数字集群关键技术的应用研究
  10. ECSHOP获取当前分类下商品的品牌列表
  11. nmap扫描端口 python
  12. 下列选项中不是具体的python序列类型的是_以下选项中,不是具体的Python序列类型的是...
  13. 二分查找边界问题总结
  14. 怎样实现iMessage群发
  15. 雷军与小米:上扬的微笑与下行的隐忧
  16. 有些钱,即便不脏,但也有毒。
  17. 32线镭神雷达跑LeGO-LOAM:3D 激光SLAM
  18. crh寄存器_牛人的STM32学习笔记(寄存器版本).doc
  19. 骄傲自满的联发科在中高端市场沦落,技术还是不如高通啊
  20. 竹炭纤维集成墙面板装修的缺点是什么,有哪些弊端

热门文章

  1. DataForm 中通过外面的按钮进行提交时,出错
  2. 遍历DataTable内存数据的三种方法性能对比
  3. 指针04 - 零基础入门学习C语言44
  4. 火狐打印预览_将打印和打印预览命令添加到Firefox的上下文菜单
  5. 手机主题随手机壳改变_无线充电可以与手机壳一起使用吗?
  6. java复合赋值运算符_Java 之复合赋值运算符
  7. jqGrid('setSelection',rowid)报Cannot read property 'multiple' of undefined
  8. 避免活跃性危险(第十章)
  9. 修改GIT的user.name和user.email
  10. JavaScript匿名函数以及在循环中的匿名函数