第 8 章 服务发现
面对大量服务,为了简化配置和管理工做,咱们须要了解”服务发现“概念git
回顾云原生特性
配置外置
将 URL 和登陆凭证移到配置文件和 C# 代码以外,放到环境变量中github
这样能让代码运行所需的配置参数更明确,而把提供这些配置的责任交给运行环境spring
后端服务
无论程序须要的是二进制存储、数据库、另外一个服务、队列服务,仍是其余类型的依赖,这些设施都应该松耦合,并能从环境变量中配置docker
把资源绑定为后端服务有两种方式:静态绑定和动态绑定数据库
静态绑定指的是,不管是由自动化工具仍是由 DevOps 工程师来分配,服务与资源之间的绑定过程发生在应用启动期间,并且一经绑定,即再也不变化json
动态绑定指资源的绑定过程发生在运行期间,具体来讲,绑定关系并不固定,并能在应用收到的多个请求期间发生变化后端
另外,为避免给应用开发人员增长额外的复杂性,它也要支持松耦合api
Netflix Eureka 简介
GitHub连接:https://github.com/Netflix/eureka服务器
要实现运行时的服务发现,须要用到”服务注册表“设施--一种集中式的服务目录app
Netflix 基础设施主要运行在 Amazon 云服务上
Netflix 自行开发了用于管理服务注册的产品 Eureka,它提供故障转移和负载均衡能力
从一名开发人员的角度看,微服务与 Eureka 服务器的交互方式就是在启动时注册
若是须要发现并消费其余后端服务,可从 Eureka 服务器查找服务目录
微服务还会向 Eureka 服务以必定的时间间隔发送心跳
若是服务在必定时间里没有发送心跳,就会从服务注册表中移除
在服务注册和发现领域, Eureka 也不是惟一的选择
从纯粹的服务注册工具到具备完整注册、发现和容错功能的产品,有不少其余公司和产品可供选用
- etcd:一个底层的分布式键值存储,提供 HTTP 访问
- Consul:一个功能完备的服务发现工具,也提供用于支持配置功能的键值存储
- Marathon:Mesos 和 DC/OS 上一个至关成熟的容器编排系统
- ZooKeeper:源自 Hadoop 项目,是这一体系中最悠久的产品
若是想体验 Eureka,但不想从源代码编译,也不想把它完整地安装到服务器上,你能够直接使用 docker hub 镜像来运行它,命令行以下:
$ docker run -p 8080:8080 --name eureka \ -d netflixoss/eureka:1.3.1
发现和广播 ASP.NET Core 服务
如今分析一些与 Eureka 服务交互的示例代码
在下面的虚构示例中,将开发一套用于支持电子商务的服务
最终的服务将公开一个产品目录,这个目录提供标准的 API 端点,用于访问产品列表和详细信息
此外,有一个库存服务,负责提供物理库存的实时状态
当须要展现产品详细信息时,产品服务将须要调用库存服务获取数据,用于组装最终的完整数据
服务注册
咱们示例项目的第一部分是库存服务,它须要在运行期间动态地被其余服务发现,以提供实时库存状态
GitHub连接:https://github.com/microservices-aspnetcore/ecommerce-inventory
使用 .NET Core 配置系统向 Steeltoe 类库提供一些配置信息
appsettings.json
{ "spring": { "application": { "name": "inventory" } }, "eureka": { "client": { "serviceUrl": "http://localhost:8080/eureka/", "shouldRegisterWithEureka": true, "shouldFetchRegistry": false, "validate_certificates": false }, "instance": { "port": 5000 } }, }
咱们用惯常的方式装配好配置,确保加载了保存着服务发现客户端配置信息的 appsettings.json 文件
var builder = new ConfigurationBuilder() .SetBasePath(System.IO.Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); Configuration = builder.Build();
在 Startup 类的 ConfigureService 方法里调用 Steeltoe 的 AddDiscoveryClient 扩展方法
services.AddDiscoveryClient(Configuration);
最后,只须要在 Configure 方法中添加对 UseDiscoveryClient 方法的调用
app.UseDiscoveryClient();
发现并消费服务
有一个可供发现的服务以后,咱们把注意力转到要开发的下一个服务上:目录服务
这个服务提供产品的目录,并经过查询库存服务来补充产品的详细信息
这一服务与咱们开发过的其余服务之间最重要的区别是,它会在运行期间动态地发现库存服务
GitHub连接:https://github.com/microservices-aspnetcore/ecommerce-catalog
用与配置库存服务时几乎同样的方式配置客户端
appsettings.json
{ "spring": { "application": { "name": "catalog" } }, "eureka":{ "client":{ "serviceUrl":"http://localhost:8080/eureka/", "shouldRegisterWithEureka":false } } }
区别在于目录服务不须要被注册(由于它不须要被其余服务发现),只有获取注册表才能发现库存服务
请看 HttpInventoryClient 类的代码,它负责消费库存服务
using StatlerWaldorfCorp.EcommerceCatalog.Models; using Steeltoe.Discovery.Client; using System.Threading.Tasks; using System.Net.Http; using Newtonsoft.Json; namespace StatlerWaldorfCorp.EcommerceCatalog.InventoryClient { public class HttpInventoryClient : IInventoryClient { private DiscoveryHttpClientHandler handler; private const string STOCKSERVICE_URL_BASE = "http://inventory/api/skustatus/"; public HttpInventoryClient(IDiscoveryClient client) { this.handler = new DiscoveryHttpClientHandler(client); } private HttpClient CreateHttpClient() { return new HttpClient(this.handler, false); } public async Task<StockStatus> GetStockStatusAsync(int sku) { StockStatus stockStatus = null; using (HttpClient client = this.CreateHttpClient()) { var result = await client.GetStringAsync(STOCKSERVICE_URL_BASE + sku.ToString()); stockStatus = JsonConvert.DeserializeObject<StockStatus>(result); } return stockStatus; } } }
.NET Core 的 HttpClient 类的构造函数有一个重载,容许传入一个自定义的 HttpHandler 实例
由 Steeltoe 提供的 DiscoveryHttpClientHandler 负责把 URL 中的服务名称替换成在运行期间发现的 URL
执行以下步骤,可在电脑上同时运行库存服务、目录服务和 Eureka
首先,启动 Eureka 服务
$ docker run -p 8080:8080 -d --name eureka \ -d netflixoss/eureka:1.3.1
而后在 5001 端口运行库存服务
$ cd <inventory service> $ dotnet run --service.urls=http://0.0.0.0:5001
要在 Docker 中运行服务,请使用下面的 docker run 命令:
$ docker run -p 5001:5001 -e PORT=5001 \ -e EURKEA__CLIENT__SERVICEURL=http://192.168.0.33:8080/eureka/ \ dotnetcoreservices/ecommerce-inventory
若是要在这里覆盖配置的值,请确保使用本机的地址
在 Docker 镜像中运行时,指向 localhost 就会有问题
最后,在 5002 端口启动目录服务
$ cd <目录服务> $ dotnet run --service.urls=http://0.0.0.0:5002
如今,能够向产品目录服务 API 发送一些请求,获取产品的列表和详情
GET http://localhost:5002/api/products GET http://localhost:5002/api/products/{id}
获取产品详情信息,期间将调用库存服务,它的 URL 是经过 Eureka 动态发现的
DNS 以及由平台支持的服务发现
在我看来,服务发现、注册,以及失败检测都应该是非功能需求
也就是说,应用代码的任何部分都不该该紧耦合特定服务发现的实现
显然,仍是要优先考虑现实情况,务实地作出决策,并且最终决定还要由你本身来作
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a>
本做品采用<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a>进行许可。
欢迎转载、使用、从新发布,但务必保留文章署名 郑子铭 (包含连接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的做品务必以相同的许可发布。
若有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。