当前位置: 首页 > 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;直到所有的结点…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...