线上SpringCloud网关调用微服务跨机房了,咋整?
1、前言
公司内考虑到服务器资源成本的问题,目前业务上还在进行服务的容器化改造和迁移,计划将容器化后的服务,以及一些中间件(MQ、DB、ES、Redis等)尽量都迁移到其他机房。
那你们为什么不用阿里云啊,腾讯云啊,还用自己的机房?
的确是这样,公司内部目前还是有专门的运维团队。也是因为历史原因,当时业务发展比较迅猛,考虑到数据的安全性也是自建机房的。对于中小型公司这样做,显然成本太高了,所以一般都用阿里云。对于中大型企业或者对数据安全性要求高的公司,自建机房维护的也不再少数。
对于中间件来说,比如 Redis 缓存,有的业务也是因为历史原因,当时上线后都是单独申请,并部署的一套集群,但是量并不是很大,所以类似这种情况的,可以考虑跟其他项目使用的集群合并为一个,这样就可能节省了一部分服务器资源。
现在大多数企业都已经微服务化,容器化了。
所以,将非容器化的业务要求都迁移到容器中,这里的容器基本都是指 Kubernetes
平台了,通过容器发布调度服务,对于运维来说,维护变得更加便捷,高效。
对于研发来说,业务需要部署服务,不再需要重新提 JIRA
工单,走一系列审核流程,最后给到你的可能还是一台虚拟机,依赖的软件单独安装部署。用了容器,只要在 集装箱
中提前安装好所需软件环境,按照发布规范打好镜像,发布服务的过程一路就是 点点点...
。
2、线上业务场景介绍
继续来说今天的主题。
有一个项目是 SpringCloud
架构的,其中使用到了 网关 Zuul
,并且也使用了到了 Eureka
作为注册中心。
因为该项目提前已经迁移到北京机房节点部署的容器环境,我们最终目标是迁移到其他机房(如:天津机房)。
北京有两个机房:A机房、B机房,因为都在北京,所以两个机房之间的 网络延时
是可以接受的。
微服务也同样在这两个机房之间都有部署。
此时,如果只是将微服务部署到 天津机房
,会变成如下图所示的关系:
问题很明显,就是网关服务只有北京的,而微服务新增了天津机房的,此时会导致 跨机房调用
,即北京网关调用到了天津微服务。
尽管北京到天津 ping
的网络延时仅有 3 毫秒
之差,但是服务与服务之间的调用,可就不止这 3 毫秒了。
其中包括服务器与服务器之间 TCP连接的建立、数据传输的网络开销,如果数据包过大,跨机房访问耗时就会很明显了。
所以呢,尽量避免跨机房访问,当然要将网关也要迁移到天津机房。
但是,大家看 粉红色粗体
的线条,仍然存在跨机房调用,天津网关调用到北京微服务。
对于线上并发访问量稍微大点,或者有些接口响应体大的,又或者网络抖动等场景下,可能就会导致接口响应时间变长了。
如何解决呢?
因大部分业务都部署到天津,可以将天津机房的服务权重调高
SLB配置 (类Nginx):
upstream {
server 北京机房网关IP 20;
server 天津机房网关IP 80;
}
网关与微服务之间,都是通过 Eureka
注册中心媒介来沟通,即 注册服务
拉取服务
。
仅仅在网关层配置好权重还不够,此时还会存在天津网关路由到北京微服务上。
Eureka 内部是基于 Ribbon
实现负载均衡的,自行实现按权重的负载均衡策略,Eureka做一点改造,界面上支持权重的修改。
下图截图了部分示例:
IP后面的就是权重值,可以在界面上输入权重值进行调整。
我们可以将北京微服务权重调低,天津微服务权重调高。
相当于网关以及微服务两侧都是通过基于 权重
的负载均衡算法来尽量减少
跨机房调用的,但是无法避免
跨机房调用。
使用 Eureka 的分区改进
上面描述的方案对于 20%
的流量仍然存在跨机房访问,我们能不能做到先访问同一机房的服务,如果同一机房的服务都不可用了,再访问其他机房的呢?
答案是 可以的
。
我们可以借助于 Eureka
注册中心里提供了 region
和 zone
的概念来实现。
region
和 zone
两个概念均来自亚马逊的 AWS:
region
:简单理解为地理上的分区,比如亚洲地区,或者华北地区等等,没有具体大小的限制。根据项目情况,自行合理划分 region。
zone
:简单理解为 region
内的具体机房,比如说 zone
划分为北京、天津,且北京有两个机房,就可以在 region 内划分为三个zone,北京划分为zone1、zone2,天津为zone3。
结合上面的示例,假设仅设置一个 region
为京津地区。
然后我们给这个区域下的网关服务、微服务打上 zone
机房标签,在系统运维上将机房也称作 IDC
数据中心。
网关服务打上zone标签:
微服务打上zone标签:
这个功能都是在 Eureka注册中心
上实现的,在给服务配置 zone
前,调用路径如下所示:
给服务配置 zone
之后,框架内部的路由机制
的实现下,调用路径如下所示:
当前使用的 Eureka
是部署在北京,如果想让服务在注册
、续约
、拉取
动作时也能实现 就近机房访问
,部署架构就变成如下这个样子:
北京区域不同机房假设认为网络延时小,所以北京两个机房可以使用同一个 Eureka
集群;天津可以单独再部署一套 Eureka
集群,这样就可以实现优先路由到同机房访问。
服务注册的关键配置
基本原理就是这样,贴上一段 Eureka
使用 region
和 zone
的配置供大家参考:
spring:
application:
name: mananger
server:
port: ${EUREKA_SERVER_PORT:8011}
eureka:
instance:
# 全网服务实例唯一标识
instance-id: ${EUREKA_SERVER_IP:127.0.0.1}:${server.port}
# 服务实例的meta数据键值对集合,可由注册中心进行服务实例间传递
metadata-map:
# [HA-P配置]-当前服务实例的zone
zone: ${EUREKA_SERVER_ZONE:tz-1}
profiles: ${spring.profiles.active}
# 开启ip,默认为false=》hostname
prefer-ip-address: true
ip-address: ${EUREKA_SERVER_IP:127.0.0.1}
# [HA-P配置]-当前服务实例的region
client:
region: ${EUREKA_SERVER_REGION:cn-bj}
# [HA-P配置]-开启当前服务实例优先发现同zone的注册中心,默认为true
prefer-same-zone-eureka: true
# [服务注册]-允许当前服务实例注册,默认为true
register-with-eureka: true
# [服务续约]-允许当前服务实例获取注册信息,默认为true
fetch-registry: true
# [HA-P配置]-可用region下zone集合
availability-zones:
cn-bj: ${eureka.instance.metadata-map.zone},zone-bj,zone-tj
service-url:
# [HA-P配置]-各zone下注册中心地址列表
zone-bj: http://BJIP1:8011/eureka,http://BJIP2:8012/eureka
zone-tj: http://TJIP1:8013/eureka,http://TJIP2:8014/eureka
prefer-same-zone-eureka
:
默认就为true,首先会通过 region
找到 availability-zones
内的第一个 zone
,然后通过这个 zone
找到 service-url
对应该机房的注册中心地址列表,并向该列表内的 第一个URL
地址发起注册和心跳,不会再向其它的URL地址发起操作。只有当第一个URL地址注册失败的情况下,才会依次向其它的URL发起操作,重试一定次数仍然失败,会间隔一段心跳时间继续重试。
eureka.instance.metadata-map.zone
:
服务提供者和消费者都要配置该参数,表示自己属于哪一个机房的。网关服务也属于消费者,从注册中心拉取到注册表之后会根据这个参数中指定的 zone
进行过滤,过滤后向同 zone
内的服务会有多个实例 ,通过 Ribbon
来实现负载均衡调用。如果同一 zone
内的所有服务都不可用时,会其他 zone
的服务发起调用。
另外注意一点 availability-zones
下 region 的配置是 ${eureka.instance.metadata-map.zone},...
这样配置的好处是,你只要指定好了 eureka.instance.metadata-map.zone
,优先会将这个参数放到可用分区下作为第一个 zone
来访问。
Zuul 网关路由分区源码分析
网关使用的 zuul
,其内部也是通过 ribbon
和 eureka
的结合来实现服务之间的调用,因为网关实际也是个服务消费者,同样会注册到 eureka
上,被网关拉取过来的注册表里的服务,作为服务提供者,同样会注册到eureka
上。
通过一张图把控整个请求的大致脉络:
上述图示中部分核心源码如下所示:
PollServerListUpdater#start(final UpdateAction action)
启动后会每隔30秒(默认)去Eureka注册中心拉取一次注册表信息,更新本地缓存的数据结构。
调用到了DyamicServerListLoadBalancer匿名实现类中。
通过DyamicServerListLoadBalancer类调用了 updateListOfServer()
方法更新服务列表,serverListImpl的实现是DiscoveryEnabledNIWSServerList类
在DiscoveryEnabledNIWSServerList类内部会调用 obtainServersViaDiscovery()
方法,其内部通过 EurekaClient
来实现从 Eureka 注册中心拉取服务列表。
过滤器内部获取同一机房(zone)的服务列表,先后会调用 ZonePreferenceServerListFilter
和 ZoneAffinityServerListFilter
两个过滤器实现 zone
的过滤。
最开始获取的Servers一共是有4条记录,根据调试的代码看,我们是为了获取 zone
为2的服务,所以得到的结果是一条,即 zone = "2"
,说明找到了同 zone 服务。
请求接口后会调用到 LoadBalancerContext#getServerFromLoadBalancer(...)
,内部会调用到ILoadBalancer
具体实现的 chooseServer()
方法,最终会获取到 zone="2"
里的一个Server。
那么这里是如何选择的Server呢?
本地调试时,只配置了已给可用的zone,所以这里条件满足会直接调用 super.chooseServer(key)
父类的方法:
BaseLoadBalancer#chooseServer(...) 父类的选择Server的方法,其内部通过 IRule#choose(key)
会调用到具体的负载均衡器的实现:
上述截图中,能看到 MetadataWeightedRule
,这个类是我们自行基于权重
负载均衡实现。
该实现类是继承了 ZoneAviodanceRule
,目的就是利用了 zone
的概念,所重写的 choose(Object key)
方法,调用了 this.getPredicate().getEligibleServers(...)
会走同样的过滤规则获取到同一机房(zone)下的所有服务列表,然后在基于每个服务配置的权重筛选一个Server。
获取到 Server
后,拼接接口的URI请求地址 http://IP:PORT/api/.../xxx.json
,通过底层的 OkHttp
实现完成 Http 接口的调用过程。
好了,到此基本就分析完了,从网关请求,通过 ribbon
组件从 eureka
注册中心拉取服务列表,如何基于 zone
分区来实现多数据中心的访问。
对于 服务注册
,要保证服务能注册到同一个 zone
内的注册中心,如果跨 zone
注册,会导致网络延时较大,出现拉取注册表,心跳超时等问题。
对于 服务调用
,要保证优先调用同一个 zone
内的服务,当无法找到同 zone
或者 同 zone
内的服务不可用时,才会转向调用其他 zone
里的服务。
本文提到的只是网关到微服务之间的调用,实际项目中,微服务还会调用其他第三方的服务,也要同时考虑到跨机房调用的问题,尽量都让各服务之间在同机房调用,减少网络延时,提高服务的稳定性。
欢迎关注我的公众号,扫二维码关注获得更多精彩原创文章,与你一同成长~
线上SpringCloud网关调用微服务跨机房了,咋整?的更多相关文章
- SpringCloud系列二:Restful 基础架构(搭建项目环境、创建 Dept 微服务、客户端调用微服务)
1.概念:Restful 基础架构 2.具体内容 对于 Rest 基础架构实现处理是 SpringCloud 核心所在,其基本操作形式在 SpringBoot 之中已经有了明确的讲解,那么本次为 了清 ...
- springcloud与docker微服务架构实战--笔记
看了<微服务那些事>之后,Spring boot和Spring Cloud的关系理清楚了,Spring cloud各个模块的作用也了解了. 但是,Spring cloud 与Docker的 ...
- java框架之SpringCloud(2)-Rest微服务案例
在上一章节已经对微服务与 SpringCloud 做了介绍,为方便后面学习,下面以 Dept 部门模块为例做一个微服务通用 Demo —— Consumer 消费者(Client) 通过 REST 调 ...
- 微服务系列(二):使用 API 网关构建微服务
编者的话|本文来自 Nginx 官方博客,是微服务系列文章的第二篇,本文将探讨:微服务架构是如何影响客户端到服务端的通信,并提出一种使用 API 网关的方法. 作者介绍:Chris Richardso ...
- SpringCloud与Docker微服务架构实战笔记
一 微服务架构概述 1. 单体应用架构存在的问题 结合:https://www.cnblogs.com/jialanshun/p/10637454.html一起看,在该篇博客中搜索“单块架构的优缺点 ...
- 0102-使用 API 网关构建微服务
一.移动客户端如何访问这些服务 1.1.客户端与微服务直接通信[很少使用] 从理论上讲,客户端可以直接向每个微服务发送请求.每个微服务都有一个公开的端点(https ://.api.company.n ...
- 怎么用API网关构建微服务
选择将应用程序构建为微服务时,需要确定应用程序客户端如何与微服务交互.在单体应用程序中,只有一组端点.而在微服务架构中,每个微服务都会暴露一组通常是细粒度的端点.在本文中,我们将讨论一下这对客户端与应 ...
- 使用 API 网关构建微服务-2
「Chris Richardson 微服务系列」使用 API 网关构建微服务 Posted on 2016年5月12日 编者的话|本文来自 Nginx 官方博客,是微服务系列文章的第二篇,本文将探讨: ...
- .net MVC, webAPI,webForm集成steeltoe+springcloud实现调用服务中心服务的总结
开始之前,如果没接触过Autofac的,可以移步到Autofac官方示例学习一下怎么使用:https://github.com/autofac/Examples .net 下集成steeltoe进行微 ...
随机推荐
- Spring 连接MySQL报错java.sql.SQLException: Unknown system variable 'tx_isolation'
先是报错255,这个时候需要把 jdbc:mysql://localhost:3306/projUse 写成 jdbc:mysql://localhost:3306/projUse?useUnicod ...
- elementUI的select全选
elementUI中的el-select全选 <template> <el-select class="handle-select" size="min ...
- 图像的手绘效果(Python)
PIL库,Python Image Library PIL库是一个具有强大图像处理能力的第三方库 在命令行下的安装方法:pip install pillow from PIL import Image ...
- PyTorch基础——使用神经网络识别文字中的情感信息
一.介绍 知识点 使用 Python 从网络上爬取信息的基本方法 处理语料"洗数据"的基本方法 词袋模型搭建方法 简单 RNN 的搭建方法 简单 LSTM 的搭建方法 二.从网络中 ...
- 68.26-95.44-99.74 rule|empirical rule
6.3 Working with Normally Distributed Variables As illustrated in the previous example, the 68.26-95 ...
- 头次接触wamp服务器、xampp,初次单独使用tomcat部署
刚刚经过了近两天的接触wamp.xampp.tomcat的时光,真的爽 导师有个网站打不开了,就让我去弄,还有一个网站的后台密码忘了,让我帮忙找回来.我第一感觉就是第一个活不简单,第二个还不简单吗?打 ...
- Create Access Point on Archlinux
Create Access Point on Archlinux */--> Create Access Point on Archlinux 1 Solution Download creat ...
- OLED带来全新视觉体验
2013年1月,在国际消费电子展(CES)上世界首款曲面OLED电视--LG OLED电视亮相,LG Display将曲面与OLED完美结合的面板技术让显示设备的外观和品质都达到了一个全新的高度,惊艳 ...
- 在CMD中操作mysql数据库出现中文乱码解决方案
百度了一下..有说将cmd字符编码用chcp命令改为65001(utf8字符编码),可这样之后根本无法输入中文,查询出的中问结果依旧乱码 其实,只要保证cmd客户端和MySQL两者编码一致即可. 但现 ...
- 荼菜的iOS笔记--UIView的几个Block动画
前言:我的第一篇文章荼菜的iOS笔记–Core Animation 核心动画算是比较详细讲了核心动画的用法,但是如你上篇看到的,有时我们只是想实现一些很小的动画,这时再用coreAnimation就会 ...