什么是服务发现
首先我们先思考一个问题,当我们在浏览器中输入一个域名比如baidu.com,然后发生了什么才能让我们访问到百度的网页?简单来说,浏览器会首先从主机的hosts文件中查看是否有baidu.com对应ip的映射,如果有就直接用hosts文件得到的ip来请求数据,如果没有那么就需要去DNS服务器来请求ip地址,Dns服务器在自己数据库中查找域名对应的ip,如果有多个ip那么需要用DNS负载均衡器根据策略返回一个ip。DNS服务器简单来看就是提供了一个域名和服务器映射关系的注册和查询的东西,这个其实就是服务发现。所以我们可以说DNS就是实现了服务发现,当然DNS有两点局限性使得我们无法直接应用到微服务中。
- DNS服务器不支持动态变更。我在前面的文章中有提到过微服务有按需虚拟化的概念,我们的节点可能会根据需要在虚拟化平台中动态的增加,减少,丢弃,DNS服务器就不能支撑我们微服务的动态变化
- DNS服务器也没办法了解到每个实例的情况,因此无法实现真正的负载均衡
为什么有服务发现
以前我们服务一般运行在物理机器上,服务实例的地址相对于固定,我们只需要在调用方用配置的方式获取服务实例的网络地址就行,但是对于现在基于云端,容器化的微服务来说服务实例的网络地址一般都是动态变化的,在实例升级,扩展,离线的时候都会经常改变,所以才有了服务发现。服务发现提供了服务注册,服务目录,服务查询等功能,并且会动态的更新服务目录来保证调用方获取的服务列表是最新可用的。
常用服务发现工具
这个就直接贴网上的图片了
本篇文章就选用Consul来进行Demo的测试了。
服务发现均衡器
当我们从服务发现工具中获取一个实例集群的地址,应该会是多个(当然这个集群也可以直接暴露的是一个负债均衡的地址),客户端可以自己根据策略来进行负载均衡处理,但是这样额外增加了每个客户端工作,所以我们可以结合现在的HA均衡器和服务发现工具来实现这个这个功能,比如 Nginx + Consul Template。大概流程如图
搭建Consul测试环境
Consul的介绍网上一大堆,我就直接贴以下他的特性然后直接进入到测试阶段。
- 服务发现(Service Discovery):客户端通过 Consul 提供服务,其他客户端可以通过 Consul 利用 dns 或者 http 发现依赖服务
- 健康检查(Health Checking): Consul 提供任务的健康检查,可以用来操作或者监控集群的健康,也可以在服务发现时去除失效的服务
- 键值对存储(Key/Value Store): 存储层级键值对
- 多数据中心(Multi Datacenter): Consul 支持开箱即用的多数据中心
因为我只有一台ubuntu和一台windows,准备再ubuntu.docker,windows 分别运行一个Node,docker上的node就只是用来满足三个服务节点,本次测试不会在这个节点上配置服务。
安装
Linux(Ubuntu 18.04)上安装Consul并检查安装结果
到官网根据系统版本下载压缩包https://www.consul.io/downloads.html
解压之后就是一个consul的可执行文件,将其添加到系统的环境变量中,并检查是否可运行
unzip consul_1.4.4_linux_amd64.zip
sudo cp -a consul /usr/bin
consul -v
Ubutu启动Agent
consul必须启动agent才能使用,有两种启动模式server和client,还有一个官方自带的ui。server用与持久化服务信息,集群官方建议3或5个节点。client只用与于server交互。ui可以查看集群情况的。
先启动一个Server
consul agent -bootstrap-expect 3 -server -data-dir /path/to/data-dir -node=linuxnode1 -bind=YourIP -config-dir /path/to/config-dir -enable-script-checks=true -datacenter=dc1 -ui
参数解释:
-bootstrap-expect:集群期望的节点数,只有节点数量达到这个值才会选举leader。
-server: 运行在server模式
-data-dir:指定数据目录,其他的节点对于这个目录必须有读的权限
-node:指定节点的名称
-bind:为该节点绑定一个地址
-config-dir:指定配置文件,定义服务的,默认所有一.json结尾的文件都会读
-enable-script-checks=true:设置检查服务为可用
-datacenter: 数据中心没名称,
-ui: 使用自带的UI
这些命令行参数 也可以直接在配置文件中进行设置,如下所示
{
"datacenter": "dc1,
"data_dir": "/your/data/path,
"log_level": "INFO",
"node_name": "foobar",
"server": true,
"watches": [
{
"type": "checks",
"handler": "/usr/bin/health-check-handler.sh"
}
]
}
*其中Watch节点是用于服务告警
因为我们集群期望节点数是3 所以现在的consul agent 是报错状态
docker启动Agent
docker run -d -p 18500:8500 -p 18300:8300 -p 18301:8301 --name consulserver consul agent -server -node=dockernode1 -client 0.0.0.0 -datacenter=dc1 -retry-join={KnowCluserIP}
windows启动Agent
windows跟linux操作一样下载压缩包 解压之后将consul 可执行文件加入系统的环境变量
打开cmd 执行命令
consul agent -bootstrap-expect 2 -server -data-dir /path/to/data-dir -node=cn1 -bind=YourIP -config-dir /path/to/config-dir -enable-script-checks=true -datacenter=dc1 -retry-join=KnowClusterIp
参数解释
-retry-join:加入到已有的集群中支持重试
再执行 docker members 可以看到我们三个Server已经启动并加入了相同的集群中了
通过ip:8500 访问自带的Web管理器
也可以很直观的看到我们的三个node,我现在停掉dockernode1的容器
可以看到dockernode1处于异常状态了
OK,到现在为止我们consul的测试环境是搭建起来了,但是我们的三个节点中都没有服务,接下来我们会向这几个节点中都加入一些服务。
服务注册
创建一个ASP.NET CORE WebApi程序
打开VS CODE 并运行
dotnet new webapi
一个简单的api程序就建立好了,我们添加一点简单的代码用于健康检查
[Route("api/Health")]
public class HealthController : Controller
{
[HttpGet]
public IActionResult Get() => Ok("ok");
}
使用AntDepoly(https://github.com/yuzd/AntDeployAgent)发布到windows服务器上
编写dockerfile
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
WORKDIR /app
# copy csproj and restore as distinct layers
COPY *.csproj ./aspnetapi/
WORKDIR /app/aspnetapi
RUN dotnet restore
# copy everything else and build app
COPY . ./aspnetapi/
WORKDIR /app/aspnetapi
RUN dotnet publish -c Release -o out
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS runtime
WORKDIR /app
COPY --from=build /app/aspnetapi/out ./
ENTRYPOINT ["dotnet", "aspnet-healthcheck.dll"]
直接去构建镜像 或者直接用我放到hub中的镜像来运行两个服务
docker run -it --rm -p 8001:80 -d --name ubuntunode1 yhx1990126/aspnetcore-healthcheck:1.0
docker run -it --rm -p 8002:80 -d --name ubuntunode2 yhx1990126/aspnetcore-healthcheck:1.0
OK,现在我们就已经IIS和docker中分别部署了两套服务,接着把这四个服务注册到Consul中
注册服务
在前面运行Consul Agent配置的文件夹中建立一个service.json的配置文件(当指定了config-dir后,consul会到目录中根据词典顺序加载配置文件),service.json的内容如下
{
"services": [{
"id": "ubuntuservice01",
"name": "ubuntuservice",
"tags": ["ubuntu"],
"address": "192.168.0.149",
"port": 8001,
"checks": [{
"http": "http://192.168.0.149:8001/api/Health",
"interval": "10s"
}]
},
{
"id": "ubuntuservice02",
"name": "ubuntuservice",
"tags": ["ubuntu"],
"address": "192.168.0.149",
"port": 8002,
"checks": [{
"http": "http://192.168.0.149:8001/api/Health",
"interval": "10s"
}]
}
]
}
两个服务用的同一个name,当后面从Consul获取IP的时候就会拿到这两个服务的IP,Json文件中的Checks节点是用来进行健康检查的,更多管service的配置可以参照官方文档https://www.consul.io/docs/agent/services.html
在windows上同样建立相同的Json文件,修改一下name和IP就行,最后运行命令
consul reload
这个命令是Consul用来重新加载配置的,支持重新加载的配置如下
- Log level
- Checks
- Services
- Watches
- HTTP Client Address
- TLS Configuration
- Please be aware that this is currently limited to reload a configuration that is already TLS enabled. You cannot enable or disable TLS only with reloading.
- Node Metadata
- Metric Prefix Filter
- Discard Check Output
- RPC rate limiting
OK,骚等几秒,直接去web界面看结果
可以看到我们的服务已经注册成功了,调用接口来获取服务地址试试
http://192.168.0.149:8500/v1/catalog/service/ubuntuservice
可以看到我们返回了我们注册的两个地址。
我们现在是用配置文件的方式注册服务的,当然也可以在asp.net core 程序中进行注册,但是这样对代码有入侵所以还是推荐配置文件的方式来注册。有关其他的配置比如 watch,check可以参考官方,这里就不进行实验了。
服务消费
本来计划接下来是讲解在asp.net core 程序中怎么消费服务的,但是想了下其实没有什么好说的,无外乎就是利用Consul提供的接口来进行服务的寻址,然后利用获取到的接口进行调用,这一块内容就打算放到后期与网关结合那一块去实现。
小结
今天了解了服务发现在微服务中的作用,了解了Consul的基本使用,但是基本没有跟.net core相关的东西,
我之所以还是把这篇文章放到微服务之路的系列文章里是因为服务发现在微服务中地位太重要了,所以还是决定要单独写一篇来介绍服务发现。
最后应该会发现,在微服务的体系中,如果用Consul类似的工具来实现服务发现基础设施,为了保证高可用性,无疑会增加系统的复杂性,所以我们也可以选择一些部署环境内置了服务发现的工具 比如k8s来减少我们自己搭建环境的复杂性和成本。