【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…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
