手写Ribbon基本原理

本文已收录于专栏
《中间件合集》

目录

  • 概念说明
    • 什么是Ribbon
    • Ribbon和Nginx负载均衡的区别
  • 工作流程
  • 代码实现
    • RibbonSDK
    • 发送请求端
      • 引入RibbonSDK和Nacos的依赖
      • 配置文件中填写负载均衡策略
      • 调用代码
    • 接收请求端
    • 执行效果
      • 发送请求端
      • 接收请求端
  • 总结提升

概念说明

什么是Ribbon

  Ribbon 是一个客户端负载均衡器,它是Spring Cloud Netflix开源的一个组件,用于在分布式系统中实现对服务实例的负载均衡。它可以作为一个独立的组件使用,也可以与 Spring Cloud 等微服务框架集成使用。
  Ribbon 的主要功能是根据一定的负载均衡策略,将客户端请求分配到可用的服务实例上,以提高系统的可用性和性能。它通过周期性地从服务注册中心(如 Eureka)获取可用的服务实例列表,并根据配置的负载均衡策略选择合适的实例来处理请求。Ribbon 支持多种负载均衡策略,如轮询、随机、加权随机、加权轮询等。

Ribbon和Nginx负载均衡的区别

在这里插入图片描述

工作流程

  1. 客户端发起请求到 Ribbon。
  2. Ribbon 从服务注册中心获取可用的服务实例列表。
  3. 根据配置的负载均衡策略,选择一个合适的服务实例。
  4. 将请求转发给选中的服务实例进行处理。
  5. 如果请求失败或超时,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 是一个强大的客户端负载均衡器,可以帮助构建可靠和高性能的分布式系统。它通过负载均衡策略将请求分发到多个服务实例上,提供了灵活的配置选项和额外的功能。


🎯 此文章对你有用的话记得留言+点赞+收藏哦🎯

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

序列号读取

1.序列号读取 1.1 应用示例目的与思路 获取光盘的外接圆&#xff1b;然后进行极坐标变换&#xff0c;获取字符所在的区域&#xff1b;最后进行字符分割、识别及其在原图上显示。 1.2 应用示例相关算子介绍 (1) mean_image(Image : ImageMean : MaskWidth, MaskHeight : ) …

uniapp 手机 真机测试 ​ 云打包 要是没申请 可以使用云打包 然后采用 测试权限即可​

uniapp 手机 真机测试 打开手机 找到手机的 版本号 点击 知道提示 &#xff08;启动开发者模式&#xff09; 然后 在进行usb的连接打开 运行uniapp 到手机基台 手机确认 即可 四&#xff0c; 云打包 要是没申请 可以使用云打包 然后采用 测试权限即可

每日刷题|回溯法解决全排列问题第二弹之解决字符串、字母大小排列问题

食用指南&#xff1a;本文为作者刷题中认为有必要记录的题目 前置知识&#xff1a;回溯法经典问题之全排列 ♈️今日夜电波&#xff1a;带我去找夜生活—告五人 0:49 ━━━━━━️&#x1f49f;──────── 4:59 …

2023国赛数学建模C题思路代码 - 蔬菜类商品的自动定价与补货决策

# 1 赛题 在生鲜商超中&#xff0c;一般蔬菜类商品的保鲜期都比较短&#xff0c;且品相随销售时间的增加而变差&#xff0c; 大部分品种如当日未售出&#xff0c;隔日就无法再售。因此&#xff0c; 商超通常会根据各商品的历史销售和需 求情况每天进行补货。 由于商超销售的蔬菜…

QUIC协议科普导入(一)

一&#xff1a;QUIC协议导入 QUIC是一个通用的传输层网络协议&#xff0c;最初由Google的Jim Roskind设计&#xff0c;2012年实现并部署&#xff0c;2013年随着实验范围的扩大而公开发布&#xff0c;并向IETF描述。虽然长期处于互联网草案阶段&#xff0c;但在从Chrome浏览器到…

vue3中通过ref获取子组件实例,取值为undefined

也就是Vue3如何通过 ref 获取子组件实例(子组件中的DOM结构、数据、及方法)&#xff0c;今天写index.vue(父组件&#xff09;时想获取子组件的数据和方法&#xff0c;通过给子组件绑定ref&#xff0c;打印子组件的数据为undefined&#xff1b;百度搜索常用方法为&#xff1a; …

【C++】继承基础知识一遍过

目录 一&#xff0c;概念 二&#xff0c;继承定义 1. 继承格式 2. 访问限定符与继承方式的关系 3. 继承父类成员访问方式的变化 小结&#xff1a; 三. 父类与子类对象赋值转化 四&#xff0c;继承作用域 1.特点 2. 测试题 五&#xff0c;派生类不一样的默认成员函…

基于深度学习的三维重建从入门实战教程 原理讲解 源码解析 实操教程课件下载

传统的重建方法是使用光度一致性等来计算稠密的三维信息。虽然这些方法在理想的Lambertian场景下,精度已经很高。 但传统的局限性,例如弱纹理,高反光和重复纹理等,使得重建困难或重建的结果不完整。 基于学习的方法可以引入比如镜面先验和反射先验等全局语义信息,使匹配…

Ribbon负载均衡+Nacos服务搭建

Ribbon负载均衡 流程 首先通过RibbonLoadBalanceerClient获取服务名&#xff0c;并传给DynamicServerListLoadBalancer——>通过EureKa-server获取服务名对应服务列表(也就是被注册到EureKa中的服务&#xff0c;可能包括不同端口的)&#xff0c;然后我们会根据IRule中的服务…

【python自动化】playwright长截图切换标签页JS注入实战

前言 当前教程使用的playwright版本为1.37.0,selenium版本为3.141.0 官方文档&#xff1a;https://playwright.dev/python/docs/screenshots 本教程目录如下 文章目录 前言playwright各类截图源码阅读ElementHandle类下的截图Page类下的截图Locator类下的截图 Playwright快速…

官方项目《内容示例》中Common UI部分笔记:Common UI 分场景使用教程

文章目录 前言0. 通用设置0.1 开启插件0.2 设置Viewport 1. 分场景教程1. 1 在仅使用鼠标控制的场景下Common Activatable StackCommon Activatable Widget 1.2 当焦点落到一个按钮时显示默认确认&#xff08;Click/Accept&#xff09;按键图标Common Input Action DataBaseInp…

【Mysql】数据库第二讲(数据库中数据类型的介绍)

数据类型 1.数据类型分类2.数值类型介绍2.1tinyint类型2.2bit类型介绍2.3小数类型介绍2.3.1 float2.3.2decimal 3.字符串类型介绍3.1char3.2varchar面试&#xff1a;char和varchar的区别 4.日期和时间类型5.enum和set 1.数据类型分类 2.数值类型介绍 2.1tinyint类型 数值越界测…

项目(智慧教室)第三部分,人机交互在stm32上的实现

一。使用软件 1.stm32cubemx中针对汉字提供的软件 2.对数据进行处理 2.上面点击ok--》这里选择确定 3.这里选择保存即可由字符库&#xff0c;但是需要占用内存太大&#xff0c;需35M&#xff0c;但是stm32只有几百k&#xff0c;所以需要自己删减。 生成中文字符&#xff08;用…

QTday3(QT实现文件对话框保存操作、实现键盘触发事件【WASD控制小球的移动】)

1.实现文件对话框保存操作 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }void Widget::on_fontBtn_clicked() {//调用QFo…

dll文件反编译源代码 C#反编译 dotpeek反编译dll文件后export

目录 背景下载安装dotpeek导入dll文件export导出文件参考 背景 项目合作的时候&#xff0c;使用前人的或者其他部门dll文件直接在机台运行&#xff0c;会出现很多问题&#xff0c;逻辑&#xff0c;效率等等&#xff0c;此时我们可以选择对他们的代码进行反编译和重构&#xff…

redisson分布式锁

RLock官网解释 基于Redis的Java分布式可重入锁对象&#xff0c;实现了锁接口。 如果获得锁的Redisson实例崩溃&#xff0c;那么这种锁可能永远挂起在获得状态。为了避免这种情况&#xff0c;Redisson维护了锁看门狗&#xff0c;它在锁持有者Redisson实例活着的时候延长锁过期时…

算法笔记:哈夫曼树、哈夫曼编码

1 字符的机内表示 2 前缀编码 字符只放在叶结点中字符编码可以有不同的长度由于字符只放在叶结点中&#xff0c;所以每个字符的编码都不可能是其他字符编码的前缀前缀编码可被惟一解码 3 哈夫曼树 哈夫曼树是一棵最小代价的二叉树&#xff0c;在这棵树上&#xff0c;所有的字…

SpotBugs(是FindBugs的继任者)安装、使用

SpotBugs介绍 SpotBugs和FindBugs的关系 SpotBugs是FindBugs的继任者&#xff0c;从SpotBugs停止的地方继续。 备注&#xff1a;FindBugs项目已经停止了&#xff0c;从2015年发布3.0.1版本以后再没有新的版本。 SpotBugs通过静态分析寻找java代码中的bug&#xff0c;通过发现…

lv3 嵌入式开发-9 linux TFTP服务器搭建及使用

目录 1 TFTP服务器的介绍 2 TFTP文件传输的特点 3 TFTP服务器的适用场景 4 配置介绍 4.1 配置步骤 4.2 使用 5 常见错误 1 TFTP服务器的介绍 TFTP&#xff08;Trivial File Transfer Protocol&#xff09;即简单文件传输协议 是TCP/IP协议族中的一个用来在客户机与服务器…

手机也可以搭建个人博客?安卓Termux+Hexo搭建属于你自己的博客网站【cpolar实现公网访问】

文章目录 前言 1.安装 Hexo2.安装cpolar3.远程访问4.固定公网地址 前言 Hexo 是一个用 Nodejs 编写的快速、简洁且高效的博客框架。Hexo 使用 Markdown 解析文章&#xff0c;在几秒内&#xff0c;即可利用靓丽的主题生成静态网页。 下面介绍在Termux中安装个人hexo博客并结合…