当前位置: 首页 > news >正文

18. 从零用Rust编写正反向代理, 主动式健康检查源码实现

wmproxy

wmproxy是由Rust编写,已实现http/https代理,socks5代理, 反向代理,静态文件服务器,内网穿透,配置热更新等, 后续将实现websocket代理等,同时会将实现过程分享出来, 感兴趣的可以一起造个轮子法

项目地址

gite: https://gitee.com/tickbh/wmproxy

github: https://github.com/tickbh/wmproxy

为什么我们需要主动

  主动可以让我们掌握好系统的稳定性,假设我们有一条连接不可达,连接超时的判定是5秒,需要检测失败3次才认定为失败,那么此时从我们开始检测,到判定失败需要耗时15秒。

  如果此时我们是个高并发的系统,每秒的QPS是1000,我们有三个地址判定,那么此时我们有1/3的失败概率。那么在15秒内,我们会收到15000个请求,会造成5000个请求失败,如果是重要的数据,我们会丢失很多重要数据。

  如果此时客户端拥有重试机制,那么客户端在失败的时候会发起重试,而且系统可能会反复的分配到那台不可达的系统,将会造成短时间内请求数激增,可能引发系统的雪崩。

  所以此时我们主动知道目标端的系统稳定性极其重要。

网络访问示意图

以下是没有主动健康检查

客户端 代理服务器 后端1 后端2 请求数据(0.5s) 连接并请求数据(5s)失败 机器宕机不可达 返回失败0.5s(总耗时6s) 重新请求数据(0.5s) 请求数据成功(0.2s) 返回数据成功(0.2s) 返回数据成功0.5s(总耗时1.4s) 客户端 代理服务器 后端1 后端2

如果出错的时候,一个请求的平均时长可能会达到(1.4s + 5s) / 2 = (3.2s),比正常访问多了(3.2 - 1.4) = 1.8s,节点的宕机会对系统的稳定性产生较大的影响

以下是主动健康检查,它保证了访问后端服务器组均是正常的状态

客户端 代理服务器 服务器组(只访问1) 请求数据(0.5s) 定时请求,保证存活,1检查成功,2检查失败 loop [健康检查] 处理客户端数据 请求数据(0.2s) 返回数据成功(0.2s) 返回数据成功(0.5s)(总耗时1.4s) 客户端 代理服务器 服务器组(只访问1)

服务器2出错的时候,主动检查已经检查出服务器2不可用,负载均衡的时候选择已经把服务器2摘除,所以系统的平均耗时1.4s,系统依然保持稳定

健康检查的种类

在目前的系统中有以下两分类:

  • HTTP 请求特定的方法及路径,判断返回是否得到预期的status或者body
  • TCP 仅只能测试连通性,如果能连接表示正常,会出现能连接但无服务的情况

健康检查的准备

我们需要从配置中读出所有的需要健康检查的类型,即需要去重,把同一个指向的地址过滤掉
配置有可能被重新加载,所以我们需要预留发送配置的方式(或者后续类似nginx用新开进程的方式则不需要),此处做一个预留。

  • 如何去重
    像这种简单级别的去重通常用HashSet复杂度为O(1)或者用简单的Vec复杂度为O(n),以SocketAddr的为键值,判断是否有重复的数据。

  • 如何保证不影响主线程
    把健康请求的方法移到异步函数,用tokio::spawn中处理,在健康检查的情况下保证不影响其它数据处理

  • 如果同时处理多个地址的健康检查
    每一次健康检查都会在一个异步函数中执行,在我们调用完请求后,我们会对当前该异步进行tokio::time::sleep以让出当前CPU。

  • 如何按指定间隔时间请求
    因为每一次健康请求都是在异步函数中,我们不确认之前的异步是否完成,所以我们在每次请求前都记录last_request,我们在请求前调用HealthCheck::check_can_request判断当前是否可以发送请求来保证间隔时间内不多次请求造成服务器的压力。

  • 超时连接判定处理
    利用tokio::time::timeoutfuture做组合,等超时的时候直接按错误处理

部分实现源码

主要源码定义在check/active.rs中,主要的定义两个类

/// 单项健康检查
#[derive(Debug, Clone)]
pub struct OneHealth {/// 主动检查地址pub addr: SocketAddr,/// 主动检查方法, 有http/https/tcp等pub method: String,/// 每次检查间隔pub interval: Duration,/// 最后一次记录时间pub last_record: Instant,
}
/// 主动式健康检查
pub struct ActiveHealth {/// 所有的健康列表pub healths: Vec<OneHealth>,/// 接收健康列表,当配置变更时重新载入pub receiver: Receiver<Vec<OneHealth>>,
}

我们在配置的时候获取所有需要主动检查的数据

/// 获取所有待健康检查的列表
pub fn get_health_check(&self) -> Vec<OneHealth> {let mut result = vec![];let mut already: HashSet<SocketAddr> = HashSet::new();if let Some(proxy) = &self.proxy {// ...}if let Some(http) = &self.http {// ...}result
}

主要的检查源码,所有的最终信息都落在HealthCheck中的静态变量里:

pub async fn do_check(&self) -> ProxyResult<()> {// 防止短时间内健康检查的连接过多, 做一定的超时处理, 或者等上一条消息处理完毕if !HealthCheck::check_can_request(&self.addr, self.interval) {return Ok(())}if self.method.eq_ignore_ascii_case("http") {match tokio::time::timeout(self.interval + Duration::from_secs(1), self.connect_http()).await {Ok(r) => match r {Ok(r) => {if r.status().is_server_error() {log::trace!("主动健康检查:HTTP:{}, 返回失败:{}", self.addr, r.status());HealthCheck::add_fall_down(self.addr);} else {HealthCheck::add_rise_up(self.addr);}}Err(e) => {log::trace!("主动健康检查:HTTP:{}, 发生错误:{:?}", self.addr, e);HealthCheck::add_fall_down(self.addr);}},Err(e) => {log::trace!("主动健康检查:HTTP:{}, 发生超时:{:?}", self.addr, e);HealthCheck::add_fall_down(self.addr);},}} else {match tokio::time::timeout(Duration::from_secs(3), self.connect_http()).await {Ok(r) => {match r {Ok(_) => {HealthCheck::add_rise_up(self.addr);}Err(e) => {log::trace!("主动健康检查:TCP:{}, 发生错误:{:?}", self.addr, e);HealthCheck::add_fall_down(self.addr);}}}Err(e) => {log::trace!("主动健康检查:TCP:{}, 发生超时:{:?}", self.addr, e);HealthCheck::add_fall_down(self.addr);}}}Ok(())
}

结语

主动检查可以及时的更早的发现系统中不稳定的因素,是系统稳定性的基石,也可以通过更早的发现因素来通知运维介入,我们的目的是使系统更稳定,更健壮,处理延时更少。

点击 [关注][在看][点赞] 是对作者最大的支持

相关文章:

18. 从零用Rust编写正反向代理, 主动式健康检查源码实现

wmproxy wmproxy是由Rust编写&#xff0c;已实现http/https代理&#xff0c;socks5代理&#xff0c; 反向代理&#xff0c;静态文件服务器&#xff0c;内网穿透&#xff0c;配置热更新等&#xff0c; 后续将实现websocket代理等&#xff0c;同时会将实现过程分享出来&#xff…...

[DM8] 达梦8配置兼容Oracle

查看版本信息 select *&#xff0c;id_code from v$version; 查询解释&#xff1a; DM Database Server 64 V8 1-1-190-21.03.12-136419-ENT 64 版本位数标识&#xff0c;64表示为64位版本&#xff0c;无64则表示为32位版本 V8 大版本号&#xff0c;目前主要是V7、V8 1-1-190…...

【Pytorch简介】1.Introduction 简介

Introduction 简介 大多数机器学习工作流涉及处理数据、创建模型、使用超参数优化模型&#xff0c;以及保存&#xff0c;然后推理已训练的模型。 本模块介绍在 PyTorch&#xff08;一种常用的 Python ML 框架&#xff09;中实现的完整机器学习 (ML) 工作流。 我们使用 Fashio…...

什么是Session以及如何在 NestJS 项目中的优雅管理 Session

前言 Web开发中一个常见的问题是用户身份的管理和状态保持。Session 就是处理这个问题的一个传统技术。在这篇文章中&#xff0c;我们将探讨Session是什么&#xff0c;为什么我们需要Session&#xff0c;以及在NestJS项目中如何优雅地管理Session。 什么是Session 众所周知&…...

高级分布式系统-第6讲 分布式系统的容错性--故障/错误/失效/异常

分布式系统容错性的概念 分布式系统的容错性&#xff1a; 当发生故障时&#xff0c; 分布式系统应当在进行恢复的同时继续以可接受的方式进行操作&#xff0c; 并且可以从部分失效中自动恢复&#xff0c; 且不会严重影响整体性能。 具体包括以下4个方面的内容&#xff1a; 可…...

网络多线程开发小项目--QQ登陆聊天功能(服务端推送新闻、离线留言和文件)

9.1.5、QQ登陆聊天功能&#xff08;服务端推送新闻、离线留言和文件&#xff09; 9.1.5.1、服务端推送新闻 1、需求分析 2、思路分析 3、代码实现 QQServer&#xff1a; 1&#xff09;cn.com.agree.qqserver.service.SendNewsToAllClient package cn.com.agree.qqserver.s…...

Jtti:有哪些方法可以提升Tomcat的性能?

提升 Tomcat 性能是确保 Web 应用程序快速响应并能够处理高并发请求的关键任务。以下是一些提升 Tomcat 性能的常见方法&#xff1a; 1. 调整JVM参数&#xff1a; a. 内存分配&#xff1a; 增加 JVM 的堆内存(Heap Memory)以提高应用程序的内存容量。使用 -Xmx 和 -Xms 参数设置…...

LeetCode 2085. 统计出现过一次的公共字符串

目录 一、题目 1、题目描述 2、接口描述 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 C代码 ​Python3代码 一、题目 1、题目描述 给你两个字符串数组 words1 和 words2 &#xff0c;请你返回在两个字符串数组中 都恰好出现一次 的字符串的数目。 2…...

130基于MATLAB并结合IBD算法的盲迭代反卷积法进行图像复原

基于MATLAB并结合IBD算法的盲迭代反卷积法进行图像复原 ,输出复原前后图像&#xff0c;PSF频谱结果。程序已调通&#xff0c;可直接运行。 130 matlab盲迭代反卷积IBD (xiaohongshu.com)...

Flying HTML生成PDF添加水印

HTML转PDF并添加水印 <!-- 用于生成PDF --> <dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-pdf</artifactId><version>9.1.20</version> </dependency>import java.io.File; import jav…...

MySQL 8.0 InnoDB Tablespaces之Temporary Tablespaces(临时表空间)

文章目录 MySQL 8.0 InnoDB Tablespaces之Temporary Tablespaces&#xff08;临时表空间&#xff09;会话临时表空间会话临时表空间的磁盘分配和回收会话临时表空间的创建创建临时表和查看临时表信息会话临时表空间相关的设置参数innodb_temp_tablespaces_dir 全局临时表空间查…...

轴承滚珠故障的理论推导与计算(修订中...)

1.缘起 轴承故障故障位置在高频&#xff0c;如果不即时处理&#xff0c;恶化后&#xff0c;滚珠会有单颗故障迅速恶化到多颗&#xff0c;如果此时电机承载较大负载&#xff0c;轴承的恶化&#xff0c;会牵连到相关的动力轴。是一个不可不进行监测的项目。 2.频谱特征 轴承的…...

NVMe系统内存结构 - PRP与PRP List

NVMe系统内存结构 - PRP与PRP List 1 为什么需要PRP2 PRP3 PRP List4 PRP寻址算法4.1 仅PRP1指向数据4.2 PRP1指向数据&#xff0c;PRP2指向数据4.3 PRP1指向数据&#xff0c;PRP2指向PRP List 本文属于《 NVMe协议基础系列教程》之一&#xff0c;欢迎查看其它文章。 1 为什么…...

系列二、Spring Security中的核心类

一、Spring Security中的核心类 1.1、自动配置类 UserDetailsServiceAutoConfiguration 1.2、密码加密器 1.2.1、概述 Spring Security 提供了多种密码加密方案&#xff0c;官方推荐使用 BCryptPasswordEncoder&#xff0c;BCryptPasswordEncoder 使用 BCrypt 强哈希函数&a…...

基于多反应堆的高并发服务器【C/C++/Reactor】(中)HttpRequest 提取请求行、解析请求行和优化 以及解析请求头并存储

### 知识点1&#xff1a;读取网络数据 客户端发送给服务器的通信数据通过封装的bufferSocketRead函数读取读取的数据存储在struct Buffer结构体实例中&#xff0c;可将该实例作为参数传递给解析函数 回顾Buffer.c中的bufferSocketRead函数 // 写内存 2.接收套接字数据 int b…...

数据结构-测试1

一、判断题 1.队列中允许插入的一端叫队头&#xff0c;允许删除的一端叫队尾&#xff08;F&#xff09; 队列中允许删除的一端叫队头&#xff08;front&#xff09;,允许插入的一端叫队尾&#xff08;rear&#xff09; 2. 完全二叉树中&#xff0c;若一个结点没有左孩子&#…...

【设计模式】01-前言

23 Design Patterns implemented by C. 从本文开始&#xff0c;一系列的文章将揭开设计模式的神秘面纱。本篇博文是参考了《设计模式-可复用面向对象软件的基础》这本书&#xff0c;由于该书的引言 写的太好了&#xff0c;所以本文基本是对原书的摘抄。 0.前言 评估一个面向对…...

SpringBoot源码分析

一&#xff1a;简介 由Pivotal团队提供的全新框架其设计目的是用来简化新Spring应用的初始搭建以及开发过程使用了特定的方式来进行配置快速应用开发领域 二&#xff1a;运行原理以及特点 运行原理&#xff1a; SpringBoot为我们做的自动配置&#xff0c;确实方便快捷&#…...

约数个数和约数之和算法总结

知识概览 约数个数 基于算数基本定理&#xff0c;假设N分解质因数的结果为 可得对于N的任何一个约数d&#xff0c;有 因为N的每一个约数和~的一种选法是一一对应的&#xff0c;根据乘法原理可得&#xff0c; 一个数的约数个数为 约数之和 一个数的约数之和公式为 多项式乘积的…...

数据结构-怀化学院期末题(322)

图的深度优先搜索 题目描述&#xff1a; 图的深度优先搜索类似于树的先根遍历&#xff0c;是树的先根遍历的推广。即从某个结点开始&#xff0c;先访问该结点&#xff0c;然后深度访问该结点的第一棵子树&#xff0c;依次为第二顶子树。如此进行下去&#xff0c;直到所有的结点…...

小手也能用的高性能鼠标,自定义空间还挺高,雷柏VT9Pro mini上手

今年搭载PAW3395传感器的电竞鼠标很受欢迎&#xff0c;雷柏就出了不少型号&#xff0c;满足各种喜好的玩家选择&#xff0c;像是近期新出的搭载3395高定版的VT9Pro和VT9Pro mini&#xff0c;就在轻量化的基础上&#xff0c;满足了各种手型的玩家的使用需要&#xff0c;而且价格…...

CDN加速原理详解

一、CDN加速是什么意思 CDN是Content Delivery Network&#xff09;英文首字母的缩写&#xff0c;中文翻译为内容分发网络&#xff0c;由于CDN是为加快网络访问速度而被优化的网络覆盖层&#xff0c;因此被形象地称为”网络加速器”&#xff0c;即CDN加速。CDN加速是通过将网站…...

sqlachemy orm create or delete table

sqlacehmy one to one ------detial to descript 关于uselist的使用。如果你使用orm直接创建表关系,实际上在数据库中是可以创建成多对多的关系,如果加上uselistFalse 你会发现你的orm只能查询出来一个&#xff0c;如果不要这个参数orm查询的就是多个&#xff0c;一对多的…...

科普小米手机、华为手机、红米手机、oppo手机、vivo手机、荣耀手机、一加手机、realme手机如何设置充电提示音

用空空鱼就可以设置&#xff0c;上面还有很多提示音素材还可以设置满电和低电提醒...

zookeeper应用场景之分布式的ID生成器

1. 分布式ID生成器的使用场景 在分布式系统中&#xff0c;分布式ID生成器的使用场景非常之多&#xff1a; 大量的数据记录&#xff0c;需要分布式ID。大量的系统消息&#xff0c;需要分布式ID。大量的请求日志&#xff0c;如restful的操作记录&#xff0c;需要唯一标识&#x…...

Java--Spring项目生成雪花算法数字(Twitter SnowFlake)

文章目录 前言步骤查看结果 前言 分布式系统常需要全局唯一的数字作为id&#xff0c;且该id要求有序&#xff0c;twitter的SnowFlake解决了这种需求&#xff0c;生成了符合条件的这种数字&#xff0c;本文将提供一个接口获取雪花算法数字。以下为代码。 步骤 SnowFlakeUtils …...

紫光展锐M6780丨画质增强——更炫的视觉体验

智能显示被认为是推动数字化转型和创新的重要技术之一。研究机构数据显示&#xff0c;预计到2035年底&#xff0c;全球智能显示市场规模将达到1368.6亿美元&#xff0c;2023-2035年符合年增长率为36.4%。 随着消费者对高品质视觉体验的需求不断增加&#xff0c;智能手机、平板…...

控制el-table的列显示隐藏

控制el-table的列显示隐藏&#xff0c;一般的话可以通过循环来实现&#xff0c;但是假如业务及页面比较复杂的话&#xff0c;list数组循环并不好用。 在我们的页面中el-table-column是固定的&#xff0c;因为现在是对现有的进行维护和迭代更新。 对需要控制列显示隐藏的页面进…...

2024上海国际冶金及材料分析测试仪器设备展览会

2024上海国际冶金及材料分析测试仪器设备展览会 时间&#xff1a;2024年12月18&#xff5e;20日 地点&#xff1a;上海新国际博览中心 ◆ 》》》组织机构&#xff1a; 主办单位&#xff1a;全联冶金商会、中国宝武钢铁集团有限公司、上海市金属学会 支持单位&#xff…...

商业定位,1元平价商业咨询:豪威尔咨询!平价咨询。

在做生意之前&#xff0c;就需要对企业整体进行一完整的商业定位&#xff0c;才能让商业定位带动企业进行飞速发展。 所以&#xff0c;包含商业定位的有效工作内容就显得极为重要&#xff0c;今天&#xff0c;小编特地为大家整理出了商业定位所需要的筹备的工作&#xff0c;如下…...