详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
🌌上期文章:详解SpringCloud微服务技术栈:认识微服务、服务拆分与远程调用
📚订阅专栏:微服务技术全家桶
希望文章对你们有所帮助

Eureka

  • 提供者与消费者
  • Eureka原理分析
  • 搭建Eureka服务
    • 搭建注册中心
    • 服务注册
    • 服务发现
  • Ribbon
    • 负载均衡原理
    • 源码跟踪原理(强推!)
      • 流程总结
    • 负载均衡策略
    • 饥饿加载

提供者与消费者

服务提供者:一次业务中,被其它微服务调用的服务(提供接口给其他微服务)
服务消费者:一次业务中,调用其它微服务的服务(调用其它微服务提供的接口)

若服务A调用服务B,服务B调用服务C,那么服务B是什么角色?

提供者与消费者的概念是相对的,一个服务既可以是提供者也可以是消费者

Eureka原理分析

上一节内容中服务调用存在问题,服务调用使用http请求,网址直接定死了,如果我们有多个服务集群,亦或是网址在后续开发过程中出现变更,就会产生不方便。如下所示:
在这里插入图片描述

我们需要解决以下三个问题:

服务消费者该如何获取服务提供者的地址信息?
如果有多个服务提供者,消费者该如何选择?
消费者如何得知服务提供者的健康状态?

而Eureka可以解决这个问题,Eureka的架构:

1、eureka-server:注册中心
2、eureka-client:
(1)服务消费者(集群)
(2)服务提供者(集群)

Eureka的作用及工作流程:

1、每个服务启动的时候,都会将注册服务信息记录在注册中心,例如端口号
2、服务信息都记住了,当服务消费者需要信息的时候,无须自己去记录,而是直接去Eureka-server中拉取,这样就可以得到服务提供者的信息
3、得到的服务提供者信息可能是个集群,包含多台服务的信息,这时候要做负载均衡去选取其中一个服务
4、消费者对提供者发起远程调用

这是核心的工作流程,另外,服务的提供者每隔30s就会向Eureka-server发送心跳续约,如果服务宕机了,那么Eureka-server就会将其剔除,这样能够保证服务消费者做远程调用的时候,能调用服务提供者都是健康的。

搭建Eureka服务

要首先Eureka服务,需要实现3点:

1、搭建注册中心(EurekaServer)
2、实现服务注册(将上一篇文章中的user-service、order-service注册到eureka)
3、实现服务发现(在order-service中完成服务拉取,然后通过负载均衡挑选一个服务,实现远程调用)

搭建注册中心

搭建EurekaServer的步骤如下:
1、创建项目,引入EurekaServer依赖:
在这里插入图片描述

	<dependencies>
        <!--Eureka服务端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

2、编写启动类,添加@EnableEurekaServer注解:

@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

3、添加application.yml文件,编写下列配置:

server:
  port: 10086 # 服务端口
spring:
  application:
    name: eurekaserver # 服务名称
eureka:
  client:
    service-url: # Eureka的地址信息
      defaultZone: http://localhost:10086/eureka

Eureka自己也是微服务,所以配置Eureka也需要将Eureka本身给注册。

接着就可以启动Eureka服务,访问端口可以看到其界面信息:
在这里插入图片描述
可以发现注册到Eureka的实例,现在只有它本身。

服务注册

将user-service注册到EurekaServer步骤如下:
1、将user-service项目引入spring-cloud-starter-netflix-eureka-client的依赖:

	<!--Eureka-Client依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

2、在application.yml文件,编写下面的配置:

spring:
  application:
    name: userservice # 服务名称
eureka:
  client:
    service-url: # Eureka的地址信息
      defaultZone: http://localhost:10086/eureka

同样的方式也可以将order-service注册。
刷新Eureka-server端网址:
在这里插入图片描述

如果你出现了java.lang.NoClassDefFoundError的问题,那可能是你的依赖导入错误了,我就不小心把依赖导入成下面这个:
在这里插入图片描述
记得要加上starter,才能被SpringBoot当成启动类。

另外,可以将user-service多次启动,模拟多实例部署,单位了避免端口冲突,需要修改端口设置:
在这里插入图片描述
在这里插入图片描述
将增加的端口也运行一下,工程看到注册了2个实例的实例列表:
在这里插入图片描述
服务注册总体来说分为两步:
1、引入eureka-client依赖
2、在application.yml中配置eureka地址

无论消费者还是提供者,引入eureka-client依赖,知道了eureka地址后,都可以完成服务注册。

服务发现

服务拉取是基于服务名称获取服务列表,然后对服务列表做负载均衡。
1、修改OrderService代码,修改访问的url路径,用服务名来代替ip、端口:

String url = "http://userservice/user/" + order.getUserId();

2、在order-service项目的启动类OrderApplication中的RestTemplate添加负载均衡注解:

	@Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

重启OrderApplication服务,按顺序访问:
http://localhost:8080/order/101
http://localhost:8080/order/102

可以看到UserApplication与UserApplication2各做了一次数据库查询操作:
在这里插入图片描述
在这里插入图片描述
所以,服务的拉取以及负载均衡已经完成。

服务发现的实现流程总结:
1、引入eureka-client依赖
2、在application.yml中配置eureka地址
3、给RestTemplate添加@LoadBalanced注解
4、给用户提供者的服务名称远程调用

Ribbon

在上面已经实现了服务的拉取,并且验证了负载均衡。那么其原理是怎样的,什么时候做的服务拉取,又是什么时候做的负载均衡,咱们都还不知道。
而其中,负载均衡是由Ribbon组件实现的。

负载均衡原理

首先思考,url已经做了修改:

String url = "http://userservice/user/" + order.getUserId();

然而访问一下http://userservice/user/1,并不能访问到,说明它并不是真实可用的地址。因此,中间肯定有组件拦截了这个请求并且做了处理,这个组件就叫做Ribbon。
整个负载均衡的流程如下:
在这里插入图片描述
而具体到底是什么时候接受这个请求的,又是怎么去做后序工作的,需要跟踪源码来做分析。

源码跟踪原理(强推!)

1、找到LoadBalancerInterceptor类,发现它实现了ClientHttpRequestInterceptor接口:
在这里插入图片描述
2、进入这个接口,查看注释:
在这里插入图片描述

其说明的大致意思是,当客户端有http请求发出的时候,立马就会被这个接口给拦截,那大概就可以知道,当实现类调用这个接口的实现方法的时候,应该要能够实现拦截

3、再回到其实现类,在实现类中打上断点,并debug模式重启OrderApplication:
在这里插入图片描述

4、在浏览器中发起请求:http://localhost:8080/order/101,可以发现请求确实成功被拦截了
在这里插入图片描述
5、跟踪下去,可以看到它解析出了我们要请求的提供者服务端是userservice:
在这里插入图片描述
在这里插入图片描述
6、解析完了就要开始进行拉取了,在这里我们可以看到Ribbon的词眼:
在这里插入图片描述
这是一个RibbonLoadBalancerClient对象,意为Ribbon负载均衡的客户端。
7、跟入execute方法,查看其执行的底层:
在这里插入图片描述
在得到动态服务列表均衡器(DynamicServerListLoadBalancer)后,可以看到这里已经成功拉取到了服务。而getServer方法就肯定是在做负载均衡了。
8、点击进入getServer方法,可以发现这里就是在做服务器的选择了:
在这里插入图片描述
9、跟进,可以看到它在实现父类的方法,可以猜想到,服务器的选择是基于某种规则的:
在这里插入图片描述
10、找到rule的对象,这是一个接口,按住Ctrl+H找到其实现类:
在这里插入图片描述
RoundRobinRule指代轮询负载均衡,RandomRule指代随机负载均衡,这些其实在nginx里面都是接触过的,到这里为止,其实现的机理就不在这里继续跟踪了,想知道的话直接学nginx就可以了。

流程总结

1、order-service(消费者)发起请求,负载均衡拦截器LoadBalancerInterceptorRibbonLoadBalancerClient会接收请求的url,获取url中的服务id
2、RibbonLoadBalancerClient把服务id交给DynamicServerListLoadBalancer
3、DynamicServerListLoadBalancer会去eureka-server中拉取userservice,并返回服务列表
4、DynamicServerListLoadBalancer需要在服务列表中选一个,这个选择即为负载均衡,交给了一个交IRule的接口对象。
5、IRule会选择一个方法在列表中选择服务,把值返回给RibbonLoadBalancerClient
6、RibbonLoadBalancerClient将会修改url,使得其实正确的真实地址,即可在前端成功访问了。

负载均衡策略

Ribbon的负载均衡规则是一个叫做IRule的接口来定义的,每个子接口都是一种规则:
在这里插入图片描述
其中,ZoneAvoidanceRule是默认方式,其父类的父类是基于轮询的,因此也可以推测ZoneAvoidanceRule是基于轮询的。
ZoneAvoidanceRule:以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。(Zone可以配置,没配置就默认所有服务都在一个Zone内)。

通过定义IRule可以修改负载均衡的规则,有2种方式:
1、代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:

	@Bean
    public IRule randomRule(){
        return new RandomRule();
    }

2、配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:

userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则

测试的话大家自己多访问几次网址,看控制台就可以知道是不是随机负载均衡了。

饥饿加载

这部分看不懂就去回顾一下设计模式吧。
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:

ribbon:
  eager-load:
    enabled: true # 开启饥饿加载
    clients: # 指定饥饿加载的服务名称
      - userservice

总结:

1、Ribbon负载均衡规则
(1)规则接口是IRule
(2)默认实现是ZoneAvoidanceRule,根据zone选择服务列表,然后轮询
2、负载均衡自定义方式
(1)代码方式:配置灵活,但修改时需要重新打包发布
(2)配置方式:直观,方便,无需重新打包发布,但是无法做全局配置
3、饥饿加载
(1)开启饥饿加载
(2)指定饥饿加载的微服务名称

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/321448.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

01 SpringMVC的快速理解

1.1 如图所示&#xff0c;SpringMVC负责表述层&#xff08;控制层Controller&#xff09;实现简化&#xff01; SpringMVC的作用主要覆盖的是表述层&#xff0c;例如&#xff1a; 请求映射、数据输入、视图界面、请求分发、表单回显、会话控制、过滤拦截、异步交互、文件上传…

adb 常用命令汇总

目录 adb 常用命令 1、显示已连接的设备列表 2、进入设备 3、安装 APK 文件到设备 4、卸载指定包名的应用 5、从设备中复制文件到本地 6、将本地文件复制到设备 7、查看设备日志信息 8、重启设备 9、截取设备屏幕截图 10、屏幕分辨率 11、屏幕密度 12、显示设备的…

PyTorch损失函数(二)

损失函数 5、nn.L1Loss nn.L1Loss是一个用于计算输入和目标之间差异的损失函数&#xff0c;它计算输入和目标之间的绝对值差异。 主要参数&#xff1a; reduction&#xff1a;计算模式&#xff0c;可以是none、sum或mean。 none&#xff1a;逐个元素计算损失&#xff0c;返…

书生·浦语大模型实战营笔记(四)

Finetune模型微调 直接使用现成的大语言模型&#xff0c;在某些场景下效果不好&#xff0c;需要根据具体场景进行微调 增量预训练&#xff1a;投喂垂类领域知识 陈述形式&#xff0c;无问答&#xff0c;即只有assistant 指令跟随&#xff1a;system-user-assistant XTuner …

树莓派4B-Python-使用PCA9685控制舵机云台+跟随人脸转动

系列文章 树莓派4B-Python-控制舵机树莓派-Pico控制舵机树莓派4B-Python-使用PCA9685控制舵机云台跟随人脸转动&#xff08;本文章&#xff09; 目录 系列文章前言一、SG90s舵机是什么&#xff1f;二、PCA9685与舵机信号线的接线图三、控制SG90s云台&#xff08;也可用来测试舵…

YOLOv5改进 | 主干篇 | 12月最新成果UniRepLknet特征提取网络(附对比试验效果图)

一、本文介绍 本文给大家带来的改进机制是特征提取网络UniRepLknet,其也是发表于今年12月份的最新特征提取网络,该网络结构的重点在于使用Dilated Reparam Block和大核心指导原则,强调了高效的结构进行通道间通讯和空间聚合,以及使用带扩张的小核心进行重新参数化,该网络…

C++输入输出和文件

文章目录 一. 流, 缓冲区和iostream文件二. 使用cout进行输出1. 用cout进行格式化2. 刷新输出缓冲区 三. 使用cin进行输入1. cin>>如何检查输入2. 流状态3. 其他istream类方法 四. 文件输入和输出1. 简单的文件I/O2. 文件模式3. 随机存取4. 内核格式化 To be continue...…

使用docker搭建LNMP架构

目录 环境准备 下载安装包 服务器环境 任务分析 nginx部分 建立工作目录 编写 Dockerfile 脚本 准备 nginx.conf 配置文件 生成镜像 创建自定义网络 启动镜像容器 验证nginx MySQL部分 建立工作目录 编写 Dockerfile 准备 my.cnf 配置文件 生成镜像 启动镜像…

C语言基础内容(七)——第07章_结构体与共同体

文章目录 第07章_结构体与共用体本章专题脉络1、结构体(struct)类型的基本使用1.1 为什么需要结构体?1.2 结构体的理解1.3 声明结构体1.4 声明结构体变量并调用成员1.5 举例1.6 小 结2、进一步认识结构体2.1 结构体嵌套2.2 结构体占用空间2.3 结构体变量的赋值操作3、结构体数…

JDK8-JDK17版本升级

局部变量类型推断 switch表达式 文本块 Records 记录Records是添加到 Java 14 的一项新功能。它允许你创建用于存储数据的类。它类似于 POJO 类&#xff0c;但代码少得多&#xff1b;大多数开发人员使用 Lombok 生成 POJO 类&#xff0c;但是有了记录&#xff0c;你就不需要使…

保卫战小游戏

欢迎来到程序小院 保卫战 玩法&#xff1a;当鬼子进入射击范围内点击鼠标左键射击&#xff0c;不要让鬼子越过炮台哦&#xff0c;快去杀鬼子去吧^^。开始游戏https://www.ormcc.com/play/gameStart/249 html <div style"position: relative;" id"gameDiv&q…

K 个一组翻转链表(链表反转,固定长度反转)(困难)

优质博文&#xff1a;IT-BLOG-CN 一、题目 给你链表的头节点head&#xff0c;每k个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是k的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。…

Spring Boot - Application Events 的发布顺序_ApplicationContextInitializedEvent

文章目录 Pre概述Code源码分析 Pre Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent 概述 Spring Boot 的广播机制是基于观察者模式实现的&#xff0c…

Spring Boot - 利用Resilience4j-RateLimiter进行流量控制和服务降级

文章目录 Resilience4j概述Resilience4j官方地址Resilience4j-RateLimiter微服务演示Payment processorPOM配置文件ServiceController Payment servicePOMModelServiceRestConfigController配置验证 探究 Rate Limiting请求三次 &#xff0c;观察等待15秒连续访问6次 Resilienc…

安装nodejs出现问题

Error: EPERM: operation not permitted, mkdir… 全局安装express模块进行测试时&#xff1a; npm install express -g出现&#xff1a; 表示nodejs的安装目录无权限&#xff0c;根据错误日志的信息&#xff0c;定位到安装目录下&#xff1a; 点击属性&#xff1a; 点击编…

优先级队列(Priority Queue)

文章目录 优先级队列&#xff08;Priority Queue&#xff09;实现方式基于数组实现基于堆实现方法实现offer(E value)poll()peek()isEmpty()isFull() 优先级队列的实现细节 优先级队列&#xff08;Priority Queue&#xff09; 优先级队列是一种特殊的队列&#xff0c;其中的元素…

Jsqlparser简单学习

文章目录 学习链接模块访问者模式parser模块statement模块Expression模块deparser模块 测试TestDropTestSelectTestSelectVisitor 学习链接 java设计模式&#xff1a;访问者模式 github使用示例参考 测试 JSqlParser使用示例 JSqlParse&#xff08;一&#xff09;基本增删改…

第02章_变量与运算符拓展练习

文章目录 第02章_变量与运算符拓展练习1、辨别标识符2、数据类型转换简答3、判断如下代码的运行结果(难)4、判断如下程序的运行结果5、判断如下程序的运行结果6、Java的基本数据类型有哪些&#xff1f;String是基本数据类型吗&#xff1f;7、语法判断8、char型变量中是否可以存…

LeeCode前端算法基础100题(20)找出字符串中第一个匹配项的下标

一、问题详情: 给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。 示例 1: 输入:haystack = "sadbutsad", needle = "s…

【现代密码学】笔记 补充7-- CCA安全与认证加密《introduction to modern cryphtography》

【现代密码学】笔记7-- CCA安全与认证加密《introduction to modern cryphtography》 写在最前面7 CCA安全与认证加密 写在最前面 主要在 哈工大密码学课程 张宇老师课件 的基础上学习记录笔记。 内容补充&#xff1a;骆婷老师的PPT 《introduction to modern cryphtography》…