作者:十眠

背景

注册中心作为承担服务注册发现的核心组件,是微服务架构中必不可少的一环。在 CAP 的模型中,注册中心可以牺牲一点点数据一致性(C),即同一时刻每一个节点拿到的服务地址允许短暂的不一致,但必须要保证可用性(A)。因为一旦由于某些问题导致注册中心不可用,或者服务连不上注册中心,那么想要连接他的节点可能会因为无法获取服务地址而对整个系统出现灾难性的打击。

一个真实的案例

全篇从一个真实的案例说起,某客户在阿里云上使用 Kubernetes 集群部署了许多自己的微服务,由于某台 ECS 的网卡发生了异常,虽然网卡异常很快恢复了,但是却出现了大面积持续的服务不可用,业务受损。

我们来看一下这个问题链是如何形成的?

  1. ECS 故障节点上运行着 Kubernetes 集群的核心基础组件 CoreDNS 的所有 Pod,且低版本 Kubernetes 集群缺少 NodeLocal DNSCache 的特性,导致集群 DNS 解析出现问题。
  2. 该客户的服务发现使用了有缺陷的客户端版本(Nacos-client 的 1.4.1 版本),这个版本的缺陷就是跟 DNS 有关——心跳请求在域名解析失败后,会导致进程后续不会再续约心跳,只有重启才能恢复。
  3. 这个缺陷版本实际上是已知问题,阿里云在 5 月份推送了 Nacos-client 1.4.1 存在严重 bug 的公告,但客户研发未收到通知,进而在生产环境中使用了这个版本。

风险环环相扣,缺一不可。

最终导致故障的原因是服务无法调用下游,可用性降低,业务受损。下图示意的是客户端缺陷导致问题的根因:

  1. Provider 客户端在心跳续约时发生 DNS 异常;
  2. 心跳线程未能正确地处理这个 DNS 异常,导致线程意外退出了;
  3. 注册中心的正常机制是,心跳不续约,30 秒后自动下线。由于 CoreDNS 影响的是整个 Kubernetes 集群的 DNS 解析,所以 Provider 的所有实例都遇到相同的问题,整个服务所有实例都被下线;
  4. 在 Consumer 这一侧,收到推送的空列表后,无法找到下游,那么调用它的上游(比如网关)就会发生异常。

回顾整个案例,每一环每个风险看起来发生概率都很小,但是一旦发生就会造成恶劣的影响。服务发现高可用是微服务体系中很重要的一环,当然也是我们时常忽略的点。在阿里内部的故障演练中,这一直是必不可少的一个环节。

面向失败的设计

由于网络环境的抖动比如 CoreDns 的异常,或者是由于某些因素导致我们的注册中心不可用等情况,经常会出现服务批量闪断的情况,但这种情况其实不是业务服务的不可用,如果我们的微服务可以识别到这是一种异常情况(批量闪断或地址变空时),应该采取一种保守的策略,以免误推从而导致全部服务出现"no provider"的问题,会导致所有的微服务不可用的故障,并且持续较长时间难以恢复。

站在微服务角度上考虑,我们如何可以切段以上的问题链呢?以上的案例看起来是 Nacos-client 低版本造成的问题,但是如果我们用的是 zookeeper、eureka 等注册中心呢?我们能拍着胸脯说,不会发生以上的问题吗?面向失败的设计原则告诉我们,如果注册中心挂掉了,或者我们的服务连不上注册中心了,我们需要有一个方式保证我们的服务正常调用,线上的业务持续不断。

本文介绍的是服务发现过程中的高可用的机制,从服务框架层面思考如何彻底解决以上的问题。

服务发现过程中的高可用原理解析

服务发现高可用-推空保护

面向失败的设计告诉我们,服务并不能完全相信注册中心的通知的地址,当注册中心的推送地址为空时候,服务调用肯定会出 no provider 错误,那么我们就忽略此次推送的地址变更。

微服务治理中心提供推空保护能力

  • 默认无侵入支持市面上近五年来的 Spring Cloud 与 Dubbo 框架

  • 无关注册中心实现,无需升级 client 版本

服务发现高可用-离群实例摘除

心跳续约是注册中心感知实例可用性的基本途径。但是在特定情况下,心跳存续并不能完全等同于服务可用。
因为仍然存在心跳正常,但服务不可用的情况,例如:

  • Request 处理的线程池满

  • 依赖的 RDS 连接异常导致出现大量慢 SQL

  • 某几台机器由于磁盘满,或者是宿主机资源争抢导致 load 很高

此时服务并不能完全相信注册中心的通知的地址,推送的地址中,可能存在一些服务质量低下的服务提供者,因此客户端需要自己根据调用的结果来判断服务地址的可用性与提供服务质量的好坏,来定向忽略某些地址。

微服务治理中心提供离群实例摘除

  • 默认无侵入,支持市面上近五年来的 Spring Cloud 与 Dubbo 框架

  • 无关注册中心实现,无需升级 client 版本

  • 基于异常检测的摘除策略:包含网络异常和网络异常 + 业务异常(HTTP 5xx)

  • 设置异常阈值、QPS 下限、摘除比例下限

  • 摘除事件通知、钉钉群告警

离群实例摘除的能力是一个补充,根据特定接口的调用异常特征,来衡量服务的可用性。

动手实践

前提条件

  • 已创建 Kubernetes 集群,请参见创建 Kubernetes 托管版集群 [1]

  • 已开通 MSE 微服务治理专业版,请参见开通 MSE 微服务治理 [2]

准备工作

开启 MSE 微服务治理

1、开通微服务治理专业版:

  1. 单击开通 MSE 微服务治理 [3]
  2. 微服务治理版本选择专业版,选中服务协议,然后单击立即开通。关于微服务治理的计费详情,请参见价格说明 [4]

2、安装 MSE 微服务治理组件:

  1. 在容器服务控制台 [5] 左侧导航栏中,选择市场 > 应用目录
  2. 应用目录页面搜索框中输入 ack-mse-pilot,单击搜索图标,然后单击组件。
  3. 详情页面选择开通该组件的集群,然后单击创建。安装完成后,在命名空间 mse-pilotmse-pilot-ack-mse-pilot 应用,表示安装成功。

3、为应用开启微服务治理:

  1. 登录 MSE 治理中心控制台 [6]
  2. 在左侧导航栏选择微服务治理中心 > Kubernetes 集群列表
  3. Kubernetes 集群列表页面搜索目标集群,单击搜索图标,然后单击目标集群操作列下方的管理
  4. 集群详情页面命名空间列表区域,单击目标命名空间操作列下方的开启微服务治理
  5. 开启微服务治理对话框中单击确认

部署 Demo 应用程序

  1. 在容器服务控制台 [5] 左侧导航栏中,单击集群
  2. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
  3. 在集群管理页左侧导航栏中,选择工作负载 > 无状态
  4. 无状态页面选择命名空间,然后单击使用 YAML 创建资源
  5. 对模板进行相关配置,完成配置后单击创建。本文示例中部署 sc-consumer、sc-consumer-empty、sc-provider,使用的是开源的 Nacos。

部署示例应用(springcloud)

YAML:

    # 开启推空保护的 sc-consumerapiVersion: apps/v1kind: Deploymentmetadata:name: sc-consumerspec:replicas: 1selector:matchLabels:app: sc-consumertemplate:metadata:annotations:msePilotCreateAppName: sc-consumerlabels:app: sc-consumerspec:containers:- env:- name: JAVA_HOMEvalue: /usr/lib/jvm/java-1.8-openjdk/jre- name: spring.cloud.nacos.discovery.server-addrvalue: nacos-server:8848- name: profiler.micro.service.registry.empty.push.reject.enablevalue: "true"image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/demo:sc-consumer-0.1imagePullPolicy: Alwaysname: sc-consumerports:- containerPort: 18091livenessProbe:tcpSocket:port: 18091initialDelaySeconds: 10periodSeconds: 30# 无推空保护的sc-consumer-empty---apiVersion: apps/v1kind: Deploymentmetadata:name: sc-consumer-emptyspec:replicas: 1selector:matchLabels:app: sc-consumer-emptytemplate:metadata:annotations:msePilotCreateAppName: sc-consumer-emptylabels:app: sc-consumer-emptyspec:containers:- env:- name: JAVA_HOMEvalue: /usr/lib/jvm/java-1.8-openjdk/jre- name: spring.cloud.nacos.discovery.server-addrvalue: nacos-server:8848image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/demo:sc-consumer-0.1imagePullPolicy: Alwaysname: sc-consumer-emptyports:- containerPort: 18091livenessProbe:tcpSocket:port: 18091initialDelaySeconds: 10periodSeconds: 30# sc-provider---apiVersion: apps/v1kind: Deploymentmetadata:name: sc-providerspec:replicas: 1selector:matchLabels:app: sc-providerstrategy:template:metadata:annotations:msePilotCreateAppName: sc-providerlabels:app: sc-providerspec:containers:- env:- name: JAVA_HOMEvalue: /usr/lib/jvm/java-1.8-openjdk/jre- name: spring.cloud.nacos.discovery.server-addrvalue: nacos-server:8848image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/demo:sc-provider-0.3imagePullPolicy: Alwaysname: sc-providerports:- containerPort: 18084livenessProbe:tcpSocket:port: 18084initialDelaySeconds: 10periodSeconds: 30# Nacos Server---apiVersion: apps/v1kind: Deploymentmetadata:name: nacos-serverspec:replicas: 1selector:matchLabels:app: nacos-servertemplate:metadata:labels:app: nacos-serverspec:containers:- env:- name: MODEvalue: standaloneimage: nacos/nacos-server:latestimagePullPolicy: Alwaysname: nacos-serverdnsPolicy: ClusterFirstrestartPolicy: Always# Nacos Server Service 配置---apiVersion: v1kind: Servicemetadata:name: nacos-serverspec:ports:- port: 8848protocol: TCPtargetPort: 8848selector:app: nacos-servertype: ClusterIP

我们只需在 Consumer 增加一个环境变量 profiler.micro.service.registry.empty.push.reject.enable=true,开启注册中心的推空保护(无需升级注册中心的客户端版本,无关注册中心的实现,支持 MSE 的 Nacos、eureka、zookeeper 以及自建的 Nacos、eureka、console、zookeeper 等)

分别给 Consumer 应用增加 SLB 用于公网访问

以下分别使用 {sc-consumer-empty} 代表 sc-consumer-empty 应用的 slb 的公网地址,{sc-consumer} 代表 sc-consumer 应用的 slb 的公网地址。

应用场景

下面通过上述准备的 Demo 来分别实践以下场景

  • 编写测试脚本

vi curl.sh

    while :doresult=`curl $1 -s`if [[ "$result" == *"500"* ]]; thenecho `date +%F-%T` $resultelseecho `date +%F-%T` $resultfisleep 0.1done
  • 测试,分别开两个命令行,执行如下脚本,显示如下

% sh curl.sh {sc-consumer-empty}:18091/user/rest2022-01-19-11:58:12 Hello from [18084]10.116.0.142!2022-01-19-11:58:12 Hello from [18084]10.116.0.142!2022-01-19-11:58:12 Hello from [18084]10.116.0.142!2022-01-19-11:58:13 Hello from [18084]10.116.0.142!2022-01-19-11:58:13 Hello from [18084]10.116.0.142!2022-01-19-11:58:13 Hello from [18084]10.116.0.142!

% sh curl.sh {sc-consumer}:18091/user/rest2022-01-19-11:58:13 Hello from [18084]10.116.0.142!2022-01-19-11:58:13 Hello from [18084]10.116.0.142!2022-01-19-11:58:13 Hello from [18084]10.116.0.142!2022-01-19-11:58:14 Hello from [18084]10.116.0.142!2022-01-19-11:58:14 Hello from [18084]10.116.0.142!2022-01-19-11:58:14 Hello from [18084]10.116.0.142!

并保持脚本一直在调用,观察 MSE 控制台分别看到如下情况


  • 将 coredns 组件缩容至数量 0,模拟 DNS 网络解析异常场景。

发现实例与 Nacos 的连接断开且服务列表为空。

  • 模拟 DNS 服务恢复,将其扩容回数量 2。

结果验证

在以上过程中保持持续的业务流量,我们发现 sc-consumer-empty 服务出现大量且持续的报错

2022-01-19-12:02:37 {“timestamp”:“2022-01-19T04:02:37.597+0000”,“status”:500,“error”:“Internal Server Error”,“message”:“com.netflix.client.ClientException: Load balancer does not have available server for client: mse-service-provider”,“path”:"/user/feign"}2022-01-19-12:02:37 {“timestamp”:“2022-01-19T04:02:37.799+0000”,“status”:500,“error”:“Internal Server Error”,“message”:“com.netflix.client.ClientException: Load balancer does not have available server for client: mse-service-provider”,“path”:"/user/feign"}2022-01-19-12:02:37 {“timestamp”:“2022-01-19T04:02:37.993+0000”,“status”:500,“error”:“Internal Server Error”,“message”:“com.netflix.client.ClientException: Load balancer does not have available server for client: mse-service-provider”,“path”:"/user/feign"}

相比之下,sc-consumer 应用全流程没有任何报错

  • 只有重启了 Provider,sc-consumer-empty 才恢复正常

相比之下,sc-consumer 应用全流程没有任何报错

后续

我们当发生推空保护后,我们会上报事件、告警至钉钉群,同时建议配合离群实例摘除使用,推空保护可能会导致 Consumer 持有过多的 Provider 地址,当 Provider 地址为无效地址时,离群实例摘除可以对其进行逻辑隔离,保证业务的高可用。

保障云上业务的永远在线,是 MSE 一直在追求的目标,本文通过面向失败设计的服务发现高可用能力的分享,以及 MSE 的服务治理能力快速构建起服务发现高可用能力的演示,模拟了线上不可预期的服务发现相关异常发生时的影响以及我们如何预防的手段,展示了一个简单的开源微服务应用应该如何构建起服务发现高可用能力。

相关链接

[1] 创建 Kubernetes 托管版集群

https://help.aliyun.com/document_detail/95108.htm#task-skz-qwk-qfb

[2] 开通 MSE 微服务治理

https://help.aliyun.com/document_detail/347625.htm#task-2140253

[3] 开通 MSE 微服务治理

https://common-buy.aliyun.com/?commodityCode=mse_basic_public_cn

[4] 价格说明

​https://help.aliyun.com/document_detail/170443.htm#concept-2519524​

[5] 容器服务控制台

​https://cs.console.aliyun.com​

[6] MSE 治理中心控制台

​https://mse.console.aliyun.com​

​点击​​​此处​​,前往 MSE 官网查看更多!​

如何快速构建服务发现的高可用能力相关推荐

  1. 唯品会、滴滴、沪江架构师,关于微服务粒度、高可用、持续交互的实践分享交流(下)...

    架构师小组交流会:每期选择一个时下最热门的技术话题进行实践经验分享. 本期小组交流会邀请到了沪江黄凯.唯品会郑明华.滴滴赵伟.七牛云肖勤,对微服务粒度.高可用.持续交互展开了交流. 本期接着上期唯品会 ...

  2. 实现Kubernetes跨集群服务应用的高可用

    我们在进行生产环境部署时得到的一个明确的需求,是Kubernetes用户希望服务部署能够zone.跨区域.跨集群甚至跨云边界(译者:如跨云供应商).相比单集群多zone部署,跨集群服务提供按地域分布, ...

  3. Eureka的初理解【服务注册与发现、高可用集群、自我保护机制、与Zookeeper的比较】

    Eureka的初理解 什么是Eureka? Eureka[读音要知道怎么读] Netflix 在设计 Eureka 时,遵循的就是AP原则(CAP文章下面有介绍). Eureka是Netflix的一个 ...

  4. 容器云平台、灰度发布系统、微服务网关的高可用实践

    http://www.sohu.com/a/227223771_355140 系统高可用是互联网企业系统架构的基础要求之一,一个好的高可用架构可以以最低的成本.更灵活的方式,满足企业用户需求.相反,糟 ...

  5. Nacos发布 v0.2 版本,无缝支持 Spring Cloud 微服务生态及高可用集群模式

    2019独角兽企业重金招聘Python工程师标准>>> 近日,阿里巴巴新开源项目Nacos 发布了 v0.2 版本,该版本开始支持完整的Spring生态技术栈,这包括 Spring ...

  6. 构建在线教育弹性高可用视频处理架构实战

    简介: 对于负责建设视频处理系统的技术团队而言,这样的业务场景就留给了他们一系列的挑战. 前言 近些年,在线教育行业飞速发展,为整个社会的知识传播提供了前所未有的便利性.通过多种形式的在线教育平台,学 ...

  7. linux7自带haprox版本,CentOS7.4—构建最新版haproxy高可用群集

    CentOS7.4-构建haproxy高可用群集 目录 第一部分 实验环境 第二部分 搭建配置web服务器 第三部分 安装配置haproxy服务器 第四部分 测试验证 第五部分 haproxy配置相关 ...

  8. pacemaker+nginx+iscsi实现的nginx服务远程储存高可用

    文章目录 scsi iscsi iscsi储存配置 添加虚拟磁盘(服务端) targetcli的安装(服务端) 配置iscsi共享存储输出(服务端) 客户端的配置 装包: 添加认证名称 进行扫描 注册 ...

  9. 构建MFS+Keepalived双机高可用热备方案`

    一.简介 MooseFS(即Moose File System,简称MFS)是一个具有容错性的网络分布式文件系统,它将数据分散存放在多个物理服务器或单独磁盘或分区上,确保一份数据有多个备份副本,对于访 ...

最新文章

  1. react 错误边界_React with GraphQL和错误边界中的自定义错误页面
  2. python中矩阵与向量的区别
  3. (五)Docker镜像管理2之nginx镜像制作
  4. 如何处理异常? catch Exception OR catch Throwable
  5. 【Uva 10934】Dropping water balloons
  6. linux禁用用户账号,技术|在 Linux 系统中禁用与解禁用户的账号
  7. android 双向滑动 seekbar
  8. 数据库选型入门必读:如何在眼花缭乱的产品中挑出最适合业务的?
  9. dubbo通信协议之对比
  10. return 输出为空php,thinkphp5 返回json数据的方法---以及返回json为空的原因
  11. Windows Azure系列公开课 - 第二课:为什么选择Windows Azure(下)
  12. Wireshark安装提示缺少KB3118401和KB2999226文件
  13. Nginx反向代理RabbitMQ出现交换机和队列出现Not found The object you clicked on was not found; it may have been。。。
  14. H264编码-码率控制原理以及JM代码分析
  15. Python爬取电影天堂最新发布电影下载地址
  16. K - Tourists' Tour(图染色)
  17. 大数据之Hadoop3简单入门(一)(通俗易懂)
  18. 列表页详情页html源码,UI布局欣赏:文章列表与内容详情页设计
  19. 华为的隐藏功能,你们知道多少?
  20. 如何正确使用TreeView的HitTest方法?

热门文章

  1. 【Zabbix】配置 腾讯企业邮箱 发送报警邮件
  2. nodejs模块笔记
  3. 7、CSS 属性选择器
  4. 2017年蓝桥杯省赛
  5. CSS之【字体/文本样式】
  6. 799. 最长连续不重复子序列 【双指针经典板子题】
  7. Liunx文件的属性(权限) 超详细解析
  8. Linux之磁盘概述
  9. XML Schema简介
  10. 计算发送延时与传播延迟_消息队列——延时消息应用解析及实践