手写Ribbon基本原理
目录
- 概念说明
- 什么是Ribbon
- Ribbon和Nginx负载均衡的区别
- 工作流程
- 代码实现
- RibbonSDK
- 发送请求端
- 引入RibbonSDK和Nacos的依赖
- 配置文件中填写负载均衡策略
- 调用代码
- 接收请求端
- 执行效果
- 发送请求端
- 接收请求端
- 总结提升
概念说明
什么是Ribbon
Ribbon 是一个客户端负载均衡器,它是Spring Cloud Netflix开源的一个组件,用于在分布式系统中实现对服务实例的负载均衡。它可以作为一个独立的组件使用,也可以与 Spring Cloud 等微服务框架集成使用。
Ribbon 的主要功能是根据一定的负载均衡策略,将客户端请求分配到可用的服务实例上,以提高系统的可用性和性能。它通过周期性地从服务注册中心(如 Eureka)获取可用的服务实例列表,并根据配置的负载均衡策略选择合适的实例来处理请求。Ribbon 支持多种负载均衡策略,如轮询、随机、加权随机、加权轮询等。
Ribbon和Nginx负载均衡的区别

工作流程
- 客户端发起请求到 Ribbon。
- Ribbon 从服务注册中心获取可用的服务实例列表。
- 根据配置的负载均衡策略,选择一个合适的服务实例。
- 将请求转发给选中的服务实例进行处理。
- 如果请求失败或超时,Ribbon 会尝试选择其他的服务实例进行重试。

代码实现
RibbonSDK
sdk是每个使用ribbon的服务中需要引入的jar包,需要借助jar包中的功能来完成ribbon的使用。
package com.example.ribbonsdk.config.test;import com.example.client.Controller.SDKController;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.http.*;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;/*** @BelongsProject: ribbonDemo* @BelongsPackage: com.example.ribbonsdk.config* @Author: Wuzilong* @Description: RibbonSDK* @CreateTime: 2023-07-31 22:47* @Version: 1.0*/
@Component
public class RequestInterceptor implements ClientHttpRequestInterceptor, ApplicationContextAware {public static ApplicationContext applicationContext;int index = 0;// 目前是写死的,应该放到注册中心中去,动态的添加注册服务和权重public Map<String,Integer> serverList = new HashMap<>(){{put("localhost:9002",7); // 权重值为7put("localhost:9005",3); // 权重值为3}};@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {if (this.applicationContext == null) {this.applicationContext = applicationContext;}}/*** @Author:Wuzilong* @Description: 手动注入AnnotationConfigApplicationContext用于判断* @CreateTime: 2023/6/19 17:36* @param:* @return:**/@Beanpublic AnnotationConfigApplicationContext annotationConfigApplicationContext() {return new AnnotationConfigApplicationContext();}@Overridepublic ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {System.out.println("拦截器拦截进来了,拦截的地址是:"+request.getURI());RestTemplate restTemplate = new RestTemplate();//获取服务名String serveName = request.getURI().getAuthority();String newAuthority = null;Environment environment = applicationContext.getBean(Environment.class);String loadBalanceName = environment.getProperty("ribbon.loadBalanceName");if (loadBalanceName.equals("polling")){newAuthority = this.polling(serveName);System.out.println("采用的是负载均衡策略————轮询");}else if (loadBalanceName.equals("weight")){newAuthority = this.weight();System.out.println("采用的是负载均衡策略————权重");}String newHost= newAuthority.split(":")[0];String newPort= newAuthority.split(":")[1];URI newUri = UriComponentsBuilder.fromUri(request.getURI()).host(newHost).port(newPort).build().toUri();RequestEntity tRequestEntity = new RequestEntity(HttpMethod.GET, newUri);ResponseEntity<String> exchange = restTemplate.exchange(tRequestEntity, String.class);System.out.println("请求的服务是"+exchange.getBody());// 创建一个ClientHttpResponse对象,并将实际的响应内容传递给它ClientHttpResponse response = new ClientHttpResponse() {@Overridepublic HttpStatus getStatusCode() {return exchange.getStatusCode();}@Overridepublic int getRawStatusCode() {return exchange.getStatusCodeValue();}@Overridepublic String getStatusText() {return exchange.getBody();}@Overridepublic void close() {}@Overridepublic InputStream getBody() {return new ByteArrayInputStream(exchange.getBody().getBytes());}@Overridepublic HttpHeaders getHeaders() {return exchange.getHeaders();}};return response;}//轮询获取服务的IP地址public String polling(String serverName){List<String> pollingList = applicationContext.getBean(SDKController.class).getList(serverName);String ipContext = pollingList.get(index);index=(index+1)%pollingList.size();return ipContext;}//权重获取服务的IP地址public String weight() {int totalWeight = serverList.values().stream().mapToInt(Integer::intValue).sum();int randomWeight = new Random().nextInt(totalWeight); // 生成一个随机权重值int cumulativeWeight = 0; // 累计权重值for (Map.Entry<String,Integer> server : serverList.entrySet()) {cumulativeWeight += server.getValue();if (randomWeight < cumulativeWeight) {return server.getKey();}}return null; // 没有找到合适的服务器}}
RequestInterceptor 类实现了两个接口,一个是ClientHttpRequestInterceptor用来重写intercept方法,也就是说重写了拦截器中的业务逻辑,我们可以把拦截到的请求进行处理,处理的过程可以写到intercept方法中,另一个是ApplicationContextAware这个接口是用来获取bean容器中对象的。
发送请求端
引入RibbonSDK和Nacos的依赖
<dependency><groupId>com.example</groupId><artifactId>RibbonSDK</artifactId><version>1.0-SNAPSHOT</version></dependency>
<!-- 手写nacos的sdk,用来获取注册列表--><dependency><groupId>com.example</groupId><artifactId>Client</artifactId><version>2.5-20230615.123611-1</version></dependency>
Nacos的其他配置可参考:手写Naocs注册中心基本原理 手写Nacos配置中心基本原理
配置文件中填写负载均衡策略
ribbon:loadBalanceName: polling
调用代码
import com.example.ribbonsdk.config.test.RequestInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;/*** @BelongsProject: ribbonDemo* @BelongsPackage: com.example.ribbonsdk.service* @Author: Wuzilong* @Description: 请求端* @CreateTime: 2023-08-28 08:20* @Version: 1.0*/
@Service
public class ServiceA {@Autowiredprivate RequestInterceptor requestInterceptor;public void getServiceInfo(){String url = "http://"+"localhost"+"/B/receiveMessage/";RestTemplate restTemplate=new RestTemplateBuilder().build();restTemplate.getInterceptors().add(requestInterceptor);ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);if (forEntity.getStatusCode() == HttpStatus.OK) {System.out.println("调用B服务成功!");}}
}
import com.example.ribbonsdk.service.ServiceA;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;/*** @BelongsProject: ribbonDemo* @BelongsPackage: com.example.ribbonsdk.Controller* @Author: Wuzilong* @Description: 描述什么人干什么事儿* @CreateTime: 2023-07-31 22:54* @Version: 1.0*/
@RestController
@RequestMapping("/ribbonsdk")
public class ServiceAController {@Autowiredprivate ServiceA serviceA;@RequestMapping(value="getInfo",method= RequestMethod.GET)public void getInfo(){serviceA.getServiceInfo();}
}
接收请求端
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.net.InetAddress;
import java.net.UnknownHostException;/*** @BelongsProject: ServiceB* @BelongsPackage: com.example.serviceb.Controller* @Author: Wuzilong* @Description: B服务* @CreateTime: 2023-06-07 19:08* @Version: 1.0*/
@RestController
@RequestMapping("/B")
public class ServiceBController {@Value("${server.port}")private String serverPort;@GetMapping("/receiveMessage")public String receiveMessage() throws UnknownHostException {System.out.println("B:我被调用了");//返回的内容是ip地址和端口号return InetAddress.getLocalHost().getHostAddress()+":"+serverPort;}
}
执行效果
发送请求端

接收请求端

总结提升
Ribbon 是一个强大的客户端负载均衡器,可以帮助构建可靠和高性能的分布式系统。它通过负载均衡策略将请求分发到多个服务实例上,提供了灵活的配置选项和额外的功能。
相关文章:
手写Ribbon基本原理
本文已收录于专栏 《中间件合集》 目录 概念说明什么是RibbonRibbon和Nginx负载均衡的区别 工作流程代码实现RibbonSDK发送请求端引入RibbonSDK和Nacos的依赖配置文件中填写负载均衡策略调用代码 接收请求端执行效果发送请求端接收请求端 总结提升 概念说明 什么是Ribbon Ribb…...
k8s集群中ETCD备份和恢复
文章目录 [toc]一、etcd 概述二、安装etcdctl工具三、kubeadm部署方式部署1)备份2)恢复四、定时备份 五、二进制部署备份1)备份2)恢复1、停止apiserver和etcd2、etcd_1恢复3、etcd_2恢复4、etcd_3恢复5、启动etcd和apiserver6、检…...
node版本问题
服务器下载下来的vue项目启动出现下列问题 npm ERR! path E:\vueEnv\app\node_modules\node-sass npm ERR! command failed npm ERR! command C:\Windows\system32\cmd.exe /d /s /c node scripts/build.js npm ERR! Building: C:\Program Files\nodejs\node.exe E:\vueEnv\ap…...
四)Stable Diffussion使用教程:图生图
这一篇来说说图生图。 除了文生图之外,SD常用的还有图生图模式。 图生图,顾名思义就是使用一张图去让AI生成自己喜欢的另一张图。 有时候我们有一张喜欢的图,但是希望换一种颜色方案,这时就可以通过图生图的方式去实现了&#…...
yolov7简化yaml配置文件
yolov7代码结构简单,效果还好,但是动辄超过70几个模块的配置文件对于想要对网络进行魔改的朋友还是不怎么友好的,使用最小的tiny也有77个模块 代码的整体结构简单,直接将ELAN结构化写成一个类就能像yolov5一样仅仅只有20几个模块&…...
pprof火焰图性能优化
pprof火焰图性能优化 火焰图(flame graph)是性能分析的利器,在go1.1之前的版本我们需要借助go-torch生成,在go1.1后go tool pprof集成了此功能,今天就来说说如何使用其进行性能优化 在你启动http server的地方直接加入导入: _ “net/http/pprof” 获取…...
Greenplum 查找数据目录占用最大的表
背景 社区中某同学提出问题: 某环境磁盘占用空间较大,于是想找到数据目录占用最大的表。使用常规查询找不出来,于是到数据目录下分析filenode,找到3个filenode占了400G。然而根据filenode从pg_class中确找不到对应的relfilenode。…...
Java 基于 SpringBoot 的酒店管理系统,附源码和数据库
博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W,Csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 一、前言介绍二、系统结构三、系统详细实现3.1用户信息管理3.2会员信息管理3.3客房信息管理3.4收藏…...
LinkedList(4):多线程LinkedList 不安全情况
多线程不安全演示,线程越多,现象越明显,这边只启了四个线程。 package com.example.demo;import java.util.LinkedList; import java.util.UUID;public class LInkedListThread {public static void main(String[] args) {final LinkedList&…...
3D印刷电路板在线渲染查看工具
从概念上讲,这是有道理的,因为PCB印制电路板上的走线从一个连接到下一个连接的路线基本上是平面的。 然而,我们生活在一个 3 维世界中,能够以这种方式可视化电路以及相应的组件,对于设计过程很有帮助。本文将介绍KiCad…...
【mysql】出现 slow sql 问题及建议
文章目录 1. SQL 执行什么情况下会变慢?2. 影响 SQL 语句执行效率的主要因素有哪些?3. 慢 SQL 是如何拖垮数据库的?4. 最佳实践建议 1. SQL 执行什么情况下会变慢? ● 数据量增加:数据库中的数据量可能会逐渐增加&…...
element树形筛选
<el-inputv-model"projectName"placeholder"请输入名称"clearablemaxlength"10"clear"clearTree" /> <el-divider /> <el-treeref"tree"class"filter-tree":data"treeList":props"…...
打字侠:一款专业的中文打字网站
打字侠第一个正式版发布啦!!! 虽然离期望的样子还有一段路要走,不过能看到它正式发布,我还是很激动哟! 打字侠是一款面向中学生和大学生的在线打字软件,它通过合理的课程设计和精美的图形界面帮…...
C++ std::default_random_engine的使用
使用std::default_random_engine可生成不同分布的随机数,下面使用实例来说明其使用。 随机生成0-1间的实数 //利用当前时间生成的种子,可保证每次生成的值都不一样 unsigned seed std::chrono::system_clock::now().time_since_epoch().count(); std:…...
软件设计模式(二):工厂、门面、调停者和装饰器模式
前言 在这篇文章中,荔枝将会梳理软件设计模式中的四种:工厂模式、Facade模式、Mediator模式和装饰器Decorator模式。其中比较重要的就是工厂模式和装饰器模式,工厂模式在开发中使用的频数比较高。希望荔枝的这篇文章能讲清楚哈哈哈哈…...
pdf文件签名的问题解决
今天解决冲突的jar,结果出现下面的问题 java.lang.IllegalAccessError: tried to access method org.bouncycastle.asn1.DERNull.<init>()V from class com.itextpdf.text.pdf.security.PdfPKCS7at com.itextpdf.text.pdf.security.PdfPKCS7.getEncodedPKCS7…...
Node.js安装使用
目录 一、安装 Node.js二、环境变量配置三、npm常用命令 Node.js 是一个强大的运行时环境,它使您能够在服务器端运行 JavaScript 代码。它非常流行,用于构建 Web 应用程序、API 和各种后端服务。 一、安装 Node.js 1、访问 Node.js 官方网站。 在主页上…...
sql:SQL优化知识点记录(七)
(1)索引优化5 (2)索引优化6 (3)索引优化7 查询*, 百分号加右边,否则索引会失效 没建立索引之前都是全表扫描 没建立索引 建立索引: 建立索引 id是主键,他也…...
机器学习:基于梯度下降算法的线性拟合实现和原理解析
机器学习:基于梯度下降算法的线性拟合实现和原理解析 线性拟合梯度下降算法步骤算法实现数据可视化(动态展示)应用示例 当我们需要寻找数据中的趋势、模式或关系时,线性拟合和梯度下降是两个强大的工具。这两个概念在统计学、机器…...
关键点数据增强
1.关键点数据增强 # 关键点数据增强 from PIL import Image, ImageDraw import random import json from pathlib import Path# 创建一个黑色背景图像 width, height 5000, 5000 # 图像宽度和高度 background_color (0, 0, 0) # 黑色填充# 随机分布图像 num_images 1 # …...
XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...
