【SpringCloud】Eureka基于Ribbon负载均衡的调用链路流程分析
文章目录
- 前言
- 1.调用形式
- 2.LoadBalancerInterceptor
- 3.负载均衡流程分析
- 3.1 调用流程图
- 3.2 intercept()方法
- 3.3 execute()方法
- 3.4 getServer()方法
- 3.4 子类的chooseServer()方法
- 3.5 getLoadBalancerStats().getAvailableZones().size() <= 1
- 3.6 父类的chooseServer()方法
- 3.7 IRule接口下的实例
- 3.8 最终的choose()方法—return server
- 3.9 choose()方法内部分析
- 4. 彩蛋
前言
微服务间相互调用的基础上,服务间的调用更多是以调用某多实例服务下的某个实例的形式。而这就需要用到负载均衡技术。对于开发者而言,只要通过@LoadBalance注解就开启了负载均衡。如此简单的操作底层究竟是什么样的,我想你也很想知道。
1.调用形式
在《SpringCloud集成Eureka并实现负载均衡》的基础之上,我们可以进行一个小小的实验,debug运行程序,通过postman发起一个请求,A服务会去远程调用B服务,debug发现发送的url为:http://user-service/user/1,毫无疑问的是,这就是A调用B的途径
同样地,拿到这个url我们去postman里发送请求:
发现请求无法发送出去,路径出了问题。观察路径中的参数user-service发现他是B服务的服务名称,那为什么在A服务里向B服务发送“服务名称-接口路径-参数”形式的请求就能够正常响应?
结合集成负载均衡的过程,这一定是Ribbon在发挥作用
2.LoadBalancerInterceptor
负载均衡的前提不是传递一个具体的url,肯定是Ribbon做了某种解析,通过服务名称得到了服务下的实例列表,从而拉取Eureka-Server中的服务注册表来将请求映射到指定的某个实例上。
结合曾经前后端分离的web开发经验,后端经常会在拦截器中拦截前端发来的请求来对请求做一些操作,比如校验、拼接、鉴权…调用方发送请求和接收方收到的请求并不一致,这其中会不会也是有一个类似于拦截器的东西拦截了请求,并且转换了请求呢?
答案是必然的,那是谁——LoadBalancerInterceptor
可以看到的是,他实现了ClientHttpRequestInterceptor接口,具体用法细节直接去看接口中声明的方法
直观的看出接口中声明了一个intercept()方法并且接受了HttpRequest参数来拦截了客户端的http请求,并且修改的请求体!这么一看URL更改的谜底就在此处揭晓了,那么方法底层具体是怎么实现的呢:
3.负载均衡流程分析
3.1 调用流程图
Debug源码之前先来看一下源码中的调用链路总体流程图(手图):
概括来看则是:拦截请求—读取服务—拉取服务列表—选择规则—返回服务实例
3.2 intercept()方法
下面我们开始Debug:
1.当发送请求使得服务间发生调用关系,调用请求会先传递到拦截器中的intercept方法,可以看到的是目前还和发送是保持一致
2.继续向下执行,开始解析请求,拿到了请求中的URI——通过getHost()方法拿到了主机地址(服务的名称)
3.3 execute()方法
3.Ribbon开始做负载均衡处理
4.两次步入之后进入到execute()方法内部,发现传递进来的服务名称作为服务Id进入到了getLoadBalance()方法,并且得到了一个ILoadbalance接口对象,而在该对象中封装了很多的信息:
这里记住服务实例id的值:host.docker.internal:8084,这就是Eureka客户端接收到的实例信息
3.4 getServer()方法
5.接口对象作为参数传递到了getServer()方法,得到了一个server对象进入到方法内部。发现与此同时传递了一个Object类型的对象用于指定服务器的规则或条件,不过到目前为止,这个参数一直都是null作为传递,即loadBalancer.chooseServer()方法采用的是‘default’的方式进行选择
3.4 子类的chooseServer()方法
6.再次步入到chooseServer()方法,发现是在一个名为BaseLoadBalancer类(这个类是负载均衡器的具体实现后面会具体分析)下重写的父类方法
此时:可以判断的是getLoadBalancerStats().getAvailableZones().size() <= 1为TRUE
3.5 getLoadBalancerStats().getAvailableZones().size() <= 1
对于表达式:getLoadBalancerStats().getAvailableZones().size() <= 1进行分析
发现在BaseLoadBalancer类中通过继承抽象类AbstractLoadBalancer并重写getLoadBalancerStats()抽象方法,获取到了一个loadbalancer统计信息集合LoadBalancerStats
而封装在LoadBalancerStats中的信息里有一个ConcurrentHashMap类型的集合属性,即
volatile Map<String, List<? extends Server>> upServerListZoneMap = new ConcurrentHashMap<String, List<? extends Server>>();
用于存储可用的服务列表,这个集合中的每个条目都代表一个区域,键是区域名称,值是该区域下可用服务器的列表。
后续的.getAvailableZones()方法则是获取这一属性值中所有的键,也就是可用的服务区域,并作为Set集合返回来进行判断
很显然,这里进一步论证getLoadBalancerStats().getAvailableZones().size() <= 1是为true的,后续就会去调用父类的chooseServer()方法
3.6 父类的chooseServer()方法
7.步入到父类的chooseServer()方法中,发现最后返回了一个Server类型的对象,这肯定就是具体的服务实例信息了。
3.7 IRule接口下的实例
去追踪rule变量,发现是一个IRule接口的实例,即为负载均衡提供规则的接口
并且此接口下有大量的规则实现,而默认的规则方式则为轮询调度:
可是看到上图在debug时,rule变量右侧灰色显示的是rule:RandomRule@12045
这是因为我通过配置IRule类型的Bean指定了负载均衡的规则:
@Beanpublic IRule randomRule() {return new RandomRule();}
只要把他注释掉,程序就会继续去采用默认的规则即RoundRobinRule
3.8 最终的choose()方法—return server
8.了解了IRule接口的rule实例,再去看他最终调用的choose()方法。同样地步入进去,由于是默认规则,则按照流程进入到了RoundRobinRule规则实现中的choose方法(其实IRule接口下的每一个规则实现类都有choose方法)
实现了ILoadBalancer
接口的负载均衡器对象作为参数传递到了方法中,与此同时key为default。开始为随机选择预热。
3.9 choose()方法内部分析
9.进入到while循环中,不断选择服务器,直到找到一个可用的服务器。随后会判断线程是否中断,如果中断了,则直接返回null。
这样的情况出现频率还是很高,由此可见,这个小设计会减少很多不必要计算,提升了程序运行的效率。
而后这是分别获取两个服务列表
从列表中选择一个
兜底操作,对选择的做判断
最后成功返回Server实例给chooseServer()方法,服务发起者发送http://user-service/user请求通过Ribbon最后轮询到了localhost:8084服务实例上
4. 彩蛋
现在有很多公司都在用Nacos替换Eureka,因为感知服务列表的变化不够敏感,感知下线服务太过迟钝,就像下面这种情况:
服务实例已经下线,时间大约过了一分钟,却还是把下线的服务加载到了可用服务列表里(upList),其实这并不怪Ribbon,都是Eureka的错
针对这种情况我们留个彩蛋,下次再来talk about~
相关文章:

【SpringCloud】Eureka基于Ribbon负载均衡的调用链路流程分析
文章目录 前言1.调用形式2.LoadBalancerInterceptor3.负载均衡流程分析3.1 调用流程图3.2 intercept()方法3.3 execute()方法3.4 getServer()方法3.4 子类的chooseServer()方法3.5 getLoadBalancerStats().…...
Springboot和Vue+MYSQL项目(基本介绍+前后端结合初步项目)+maven+mybatis
一、基本知识 当我们谈论全栈开发时,通常指的是一个开发者能够处理整个应用程序的开发,包括前端(Front-End)和后端(Back-End)的所有层面。这三个基本的领域是: 前端开发(Front-End …...

基于单片机K型热电偶温度采集报警系统
**单片机设计介绍, 基于单片机K型热电偶温度采集报警系统 文章目录 一 概要简介系统特点系统组成工作原理应用领域 二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 # 基于单片机K型热电偶温度采集报警系统介绍 简介 该系统是基于单片…...

利用OpenCV做个熊猫表情包 二
之前写了一篇 利用OpenCV做个熊猫表情包吧_Leen的博客-CSDN博客 回想起来觉得有点太弱了,意犹未尽,每次使用需要自己去手动截取人脸,清除黑边什么的才能使用demo去合成表情,无奈之前由于安装的vs,opencv版本都比较低…...
华纳云服务器怎么清理cdn缓存?
清理 CDN(内容分发网络)缓存通常需要通过 CDN 提供商的管理界面或 API 进行操作。不同的 CDN 提供商可能有不同的方法和步骤,以下是一个通用的清理 CDN 缓存的一般步骤: 1. 登录到 CDN 提供商的管理界面: 打开你所使用的 CDN 提供商的网站。 …...
python functools.wraps保留被装饰函数属性
作用 普通装饰器 ,会覆盖函数名称,并且 会替换 函数 文档字符串 介绍 functools.wraps(wrapped[, assigned][, updated]) This is a convenience function for invoking partial(update_wrapper, wrappedwrapped, assignedassigned, updatedupdated) …...
【多线程 - 11、死锁】
死锁 1、介绍 在 Java 中使用多线程,就会有可能导致死锁问题。死锁会让程序一直卡住,程序不再往下执行。只能通过中止并重启的方式来让程序重新执行。要尽可能避免死锁的情况发生 2、造成死锁的原因 互斥条件: 同一资源同时只能由一个线程读…...
flask实现session开发
要在Flask应用中实现会话(session)开发,你可以使用Flask内置的session模块。以下是一个示例代码,演示在Flask应用中启用和使用会话功能: from flask import Flask, session, redirect, url_for, requestapp Flask(__…...
paddle dataset
paddle实现图像旋转 import numpy as np from PIL import Image from matplotlib import pyplot as plt from paddle.vision.transforms import functional as F import cv2imagecv2.imread(./1.jpg) imagecv2.cvtColor(image,cv2.COLOR_BGR2RGB)# 图像旋转 opencv # imgR90 …...

接口自动化测试实战:JMeter+Ant+Jenkins+钉钉机器人群通知完美结合
前言 一、本地JAVA环境安装配置,安装JAVA8和JAVA17 二、安装和配置Jmeter 三、安装和配置ant 四、jmeter + ant配置 五、jenkins安装和配置持续构建项目 六、jenkins配置流程 前言 搭建jmeter+ant+jenkins环境有些前提条件,那就是要先配置好java环境,本地java环境…...

HAL库STM32串口开启DMA接收数据
STM32CubeMx的配置 此博客仅仅作为记录,这个像是有bug一样,有时候好使,有时候不好,所以趁现在好使赶紧记录一下,很多地方用到串口接收数据,DMA又是一种非常好的接收方式,可以节约CPU的时间&…...

Web安全研究(五)
Automated WebAssembly Function Purpose Identification With Semantics-Aware Analysis WWW23 文章结构 introbackgroundsystem design abstraction genapplying abstractionsclassifier data collection and handling data acquisitionstatistics of collected datamodule-…...

2023.11.17-hive调优的常见方式
目录 0.设置hive参数 1.数据压缩 2.hive数据存储格式 3.fetch抓取策略 4.本地模式 5.join优化操作 6.SQL优化(列裁剪,分区裁剪,map端聚合,count(distinct),笛卡尔积) 6.1 列裁剪: 6.2 分区裁剪: 6.3 map端聚合(group by): 6.4 count(distinct): 6.5 笛卡尔积: 7…...
ts 联合react 实现ajax的封装,refreshtoken的功能
react ts混合双打,实现ajax的封装,以及401的特殊处理 import axios from axios import {AMDIN_EXPIRES_KEY,AMDIN_KEY,AMDIN_REFRESH_EXPIRES_KEY,AMDIN_REFRESH_KEY,COMMID_KEY,getToken,removeToken } from ../utils/user-token import { showMessage…...
CISP模拟试题(一)
免责声明 文章仅做经验分享用途,利用本文章所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任,一旦造成后果请自行承担!!! 1.下面关于信息安全保障的说法错误的是:C A.信息安全保障的概念是与信息安全的概念同时产生的 …...

轻量封装WebGPU渲染系统示例<35>- HDR环境数据应用到PBR渲染材质
当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/BasePbrMaterialTest.ts 当前示例运行效果: 微调参数之后的效果: 此示例基于此渲染系统实现,当前示例TypeScript源码如下: export class BasePbrMateri…...

春秋云境靶场CVE-2022-28512漏洞复现(sql手工注入)
文章目录 前言一、CVE-2022-28512靶场简述二、找注入点三、CVE-2022-28512漏洞复现1、判断注入点2、爆显位个数3、爆显位位置4 、爆数据库名5、爆数据库表名6、爆数据库列名7、爆数据库数据 总结 前言 此文章只用于学习和反思巩固sql注入知识,禁止用于做非法攻击。…...

数字化文化的守护之星:十八数藏的非遗创新之道
在数字时代的浪潮中,十八数藏犹如一颗璀璨的守护之星,为传统文化注入了新的生命力。这个非遗创新项目以数字化为工具,以守护为使命,开辟了文化传承的新航道。 十八数藏是文化数字守护的引领者,通过数字技术࿰…...

[机缘参悟-119] :反者道之动与阴阳太极
目录 一、阴阳对立、二元对立的规律 1.1 二元对立 1.2 矛盾的对立与统一 二、阴阳互转、阴阳变化、变化无常 》无序变化和有序趋势的规律 三、阴阳合一、佛魔一体、善恶同源 四、看到积极的一面 五、反者道之动 5.1 概述 5.2 "否极泰来" 5.3 “乐极生悲”…...

Docker搭建Redis集群
Docker搭建Redis集群 创建一个专属redis的网络 docker network create redis --subnet 172.38.0.0/16通过shell脚本创建并启动6个redis服务 #通过脚本一次创建6个redis配置 for port in $(seq 1 6); \ do \ mkdir -p /mydata/redis/node-${port}/conf touch /mydata/redis/n…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
git: early EOF
macOS报错: Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...