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

用Rust和Pingora轻松构建超越Nginx的高效负载均衡器

目录

  1. 什么是Pingora?
  2. 实现过程
    • 初始化项目
    • 编写负载均衡器代码
    • 代码解析
    • 部署
  3. 总结

1. 什么是Pingora?

Pingora 是一个高性能的 Rust 库,用于构建可负载均衡器的代理服务器,它的诞生是为了弥补 Nginx 存在的缺陷。

Pingora 提供了丰富的功能和高度的扩展性,适用于各种网络应用场景。其高效的性能、易于扩展的设计以及 Rust 语言本身的安全性和速度。使得 Pingora 能够处理大量并发请求,确保高可靠性和稳定性。本文将带您一步步使用 Pingora 构建一个基础的负载均衡器。

如果你还不了解 Pingora 的相关背景, 建议先阅读:《一天为用户节省434年握手时间!Rust编写的Pingora凭什么力压Nginx?》

2. 实现过程

2.1 初始化项目

首先,我们需要一个 Rust 项目,并添加必要的依赖项。在项目根目录下的 Cargo.toml 文件中添加以下内容:

[package]
name = "load_balancer"
version = "0.1.0"
edition = "2021"[dependencies]
async-trait = "0.1"
pingora = { version = "0.1", features = ["lb"] }

2.2 编写负载均衡器代码

src/main.rs 中编写负载均衡器的实现代码。以下是完整的代码示例:

use async_trait::async_trait;
use pingora::{prelude::*, services::Service};
use std::sync::Arc;fn main() {// 创建一个服务器实例,传入Some(Opt::default())代表使用默认配置,程序执行时支持接收命令行参数let mut my_server = Server::new(Some(Opt::default())).unwrap();// 初始化服务器my_server.bootstrap();// 创建一个负载均衡器,包含多个上游服务器let mut upstreams = LoadBalancer::try_from_iter(["10.0.0.1:8080", "10.0.0.2:8080", "10.0.0.3:8080"]).unwrap();// 进行健康检查,最终获得到可用的上游服务器let hc = TcpHealthCheck::new();upstreams.set_health_check(hc);upstreams.health_check_frequency = Some(std::time::Duration::from_secs(1));let background = background_service("health check", upstreams);let upstreams = background.task();// 创建一个HTTP代理服务,并传入服务器配置和负载均衡器let mut lb_service: pingora::services::listening::Service<pingora::proxy::HttpProxy<LB>> =http_proxy_service(&my_server.configuration, LB(upstreams));// 添加一个TCP监听地址,监听80端口lb_service.add_tcp("0.0.0.0:80");// 添加一个TLS监听地址,监听443端口println!("The cargo manifest dir is: {}", env!("CARGO_MANIFEST_DIR"));// 在项目目录下新增一个 keys 目录,对应证书文件放在该目录下let cert_path = format!("{}/keys/example.com.crt", env!("CARGO_MANIFEST_DIR"));let key_path = format!("{}/keys/example.com.key", env!("CARGO_MANIFEST_DIR"));let mut tls_settings =pingora::listeners::TlsSettings::intermediate(&cert_path, &key_path).unwrap();tls_settings.enable_h2();lb_service.add_tls_with_settings("0.0.0.0:443", None, tls_settings);// 定义服务列表,这个示例只有一个负载均衡服务,后续有需要可以添加更多,将服务列表添加到服务器中let services: Vec<Box<dyn Service>> = vec![Box::new(lb_service)];my_server.add_services(services);// 运行服务器,进入事件循环my_server.run_forever();
}// 定义一个包含负载均衡器的结构体LB,用于包装Arc指针以实现多线程共享
pub struct LB(Arc<LoadBalancer<RoundRobin>>);// 使用#[async_trait]宏,异步实现ProxyHttp trait。
#[async_trait]
impl ProxyHttp for LB {/// 定义上下文类型,这里使用空元组,对于这个小例子,我们不需要上下文存储type CTX = ();// 创建新的上下文实例,这里返回空元组fn new_ctx(&self) -> () {()}// 选择上游服务器并创建HTTP对等体async fn upstream_peer(&self, _session: &mut Session, _ctx: &mut ()) -> Result<Box<HttpPeer>> {// 使用轮询算法选择上游服务器let upstream = self.0.select(b"", 256) // 对于轮询,哈希不重要.unwrap();println!("上游对等体是:{upstream:?}");// 创建一个新的HTTP对等体,设置SNI为example.comlet peer: Box<HttpPeer> =Box::new(HttpPeer::new(upstream, false, "example.com".to_string()));Ok(peer)}// 在上游请求发送前,执行一些额外操作,例如将某些参数插入请求头,这里的示例是插入Host头部async fn upstream_request_filter(&self,_session: &mut Session,upstream_request: &mut RequestHeader,_ctx: &mut Self::CTX,) -> Result<()> {// 将Host头部设置为example.com,当然,在现实需求中,这一步可能是多余的upstream_request.insert_header("Host", "example.com").unwrap();Ok(())}
}

3. 代码解析

3.1 对等体健康检查

为了使我们的负载均衡器更可靠,我们添加了健康检查功能到我们的上游对等体。这样,如果有一个对等体已经出现异常,就可以快速停止将流量路由到该对等体。如下代码

fn main() {// ...// 以下对等体中包含一个异常的对等体let upstreams =LoadBalancer::try_from_iter(["10.0.0.1:8080", "10.0.0.2:8080", "10.0.0.3:8080"]).unwrap();// ...
}

现在如果我们再次运行我们的负载均衡器 cargo run,并用以下命令测试它:

curl http://127.0.0.1 -svo /dev/null

如果去掉健康检查的代码片段,我们发现会出现 502: Bad Gateway 的失败情况,这是因为我们的对等体选择严格遵循我们给出的 RoundRobin 选择模式,而没有考虑该对等体是否健康。通过引入一个健康检查的功能来解决这个问题,进而排除掉不健康对等体。关键代码如下

fn main() {// ...// 健康检查let hc = TcpHealthCheck::new();upstreams.set_health_check(hc);upstreams.health_check_frequency = Some(std::time::Duration::from_secs(1));let background = background_service("health check", upstreams);let upstreams = background.task();// ...
}
3.2 接收命令行参数

在创建 pingora 服务时,传入了一个 Some(Opt::default())

参数,pingora 将会捕获我们运行的命令行参数,并使用这些参数来配置 pingora 服务。代码变更如下

fn main() {// ...let mut my_server = Server::new(Some(Opt::default())).unwrap();// ...
}

我们可以通过以下命令来看 pingora 负载均衡器的参数说明

cargo run -- -h

这时我们可以了解到 pingora 相关参数提供的功能,后续可以为我们的服务器实现更多的功能。

4. 部署

4.1 后台运行

通过传递 -d 或者 --daemon 参数,可以将 pingora 运行在后台。如果要优雅的停止 pingora,可以使用 pkill 命令并且传递 SIGTERM 信号,那么在关闭的过程中,服务将停止接收新的请求,但是仍然会处理完当前请求再退出。命令如下

# 后台运行,我们使用release模式,因为debug模式下会生成调试信息,会影响性能
cargo run --release -- -d
# 优雅的停止
pkill -SIGTERM load_balancer
4.2 配置

Pingora 配置文件可以定义 Pingora 如何运行,以下定义了 Pingora 的版本、线程数、pid文件、错误日志文件、升级套接字文件的配置,文件名称命名为conf.yaml

---
version: 1
threads: 2
pid_file: /tmp/load_balancer.pid
error_log: /tmp/load_balancer_err.log
upgrade_sock: /tmp/load_balancer.sock

加载配置文件运行如下:

# 设置日志级别
RUST_LOG=INFO
# 启用
cargo run --release -- -c conf.yaml -d
4.3 优雅地升级

假设我们更改了负载均衡器的代码并重新编译了二进制文件,现在我们希望将正在后台运行的服务升级到这个新版本。如果我们简单地停止旧服务,然后启动新服务,那么在中间到达的一些请求可能会丢失。幸运的是,Pingora 提供了一种优雅的方式来升级服务。

首先,我们通过SIGQUIT停止正在运行的服务,然后使用-u或者--upgrade参数来启动全新的程序,如下命令

pkill -SIGQUIT load_balancer && RUST_LOG=INFO cargo run --release -- -c conf.yaml -d -u

在升级过程中,Pingora 将会自动将请求路由到新的服务,而不会丢失任何请求。从客户端的角度来看,用户感觉不到任何变化。

5. 总结

到此为止,我们已经拥有了一个功能完备的负载均衡器。通过这个简单的示例,相信大家已经对 Pingora 有了一个初步的了解。不过,这是一个非常基础的负载均衡器。在实际应用中,负载均衡器的配置和功能可能会更加复杂,我们还需要根据实际需求来进行扩展和优化。

在后续,我也会分享一些关于 Pingora 以及新兴热门技术的更多内容,欢迎继续关注!

本文完整示例代码:https://github.com/phyuany/simple-pingora-reverse-proxy

相关文章:

用Rust和Pingora轻松构建超越Nginx的高效负载均衡器

目录 什么是Pingora&#xff1f;实现过程 初始化项目编写负载均衡器代码代码解析部署 总结 1. 什么是Pingora&#xff1f; Pingora 是一个高性能的 Rust 库&#xff0c;用于构建可负载均衡器的代理服务器&#xff0c;它的诞生是为了弥补 Nginx 存在的缺陷。 Pingora 提供了…...

华为云与AWS负载均衡服务深度对比:性能、成本与可用性

随着云计算的迅速发展&#xff0c;企业对于云服务提供商的选择变得越来越关键。在选择云服务提供商时&#xff0c;负载均衡服务是企业关注的重点之一。我们九河云将深入比较两大知名云服务提供商华为云和AWS的负载均衡服务&#xff0c;从性能、成本和可用性等方面进行对比。 AW…...

Vue65-组件之间的传值

1、收数据 2、传数据 3、批量的数据替换 若是info里面有四个数据&#xff0c;传过来的dataObj里面有三个数据&#xff0c;则info里面也只有三个数据了 解决方式&#xff1a; 该写法还有一个优势&#xff1a;传参的时候&#xff0c;顺序可以随意&#xff01;...

Java零基础之多线程篇:线程生命周期

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…...

技术差异,应用场景;虚拟机可以当作云服务器吗

虚拟机和云服务器是现在市面上常见的两种计算资源提供方式&#xff0c;很多人把这两者看成可以相互转换或者替代的物品&#xff0c;实则不然&#xff0c;这两种资源提供方式有许多相似之处&#xff0c;但是也有不少区别&#xff0c;一篇文章教你识别两者的技术差异&#xff0c;…...

Qt Quick 教程(一)

文章目录 1.Qt Quick2.QML3.Day01 案例main.qml退出按钮&#xff0c;基于上面代码添加 4.使用Qt Design StudioQt Design Studio简介Qt Design Studio工具使用版本信息 1.Qt Quick Qt Quick 是一种现代的用户界面技术&#xff0c;将声明性用户界面设计和命令性编程逻辑分开。 …...

react钩子函数用法(useCallback、useMemo)

useMemo import { useMemo } from react; function MyComponent({ a, b }) { const memoizedValue useMemo(() > { // 进行一些昂贵的计算 return a b; }, [a, b]); // 当 a 或 b 发生变化时&#xff0c;memoizedValue 将被重新计算 return <div>{memoizedVa…...

linux配置Vnc Server给Windows连接

1. linux 安装必要vnc server和桌面组件 sudo apt -y install tightvncserversudo apt install xfce4 xfce4-goodies2. linux 配置vncserver密码 #bash vncserver参考: https://cn.linux-console.net/?p21846#google_vignette 3. 将启动桌面命令写入.vnc/xstartup # .vnc/x…...

Android中的KeyEvent详解

介绍 在Android中&#xff0c;KeyEvent 是用来表示按键事件的类&#xff0c;可根据对应的事件来处理按键输入&#xff0c;具体包含了关于按键事件的信息&#xff0c;例如按键的代码、动作&#xff08;按下或释放&#xff09;以及事件的时间戳&#xff0c;KeyEvent 对象通常在用…...

移植案例与原理 - HDF驱动框架-驱动配置(2)

1.2.7 节点复制 节点复制可以实现在节点定义时从另一个节点先复制内容&#xff0c;用于定义内容相似的节点。语法如下&#xff0c;表示在定义"node"节点时将另一个节点"source_node"的属性复制过来。 node : source_node示例如下&#xff0c;编译后bar节点…...

年终奖发放没几天,提离职领导指责我不厚道,我该怎么办?

“年终奖都发了&#xff0c;你还跳槽&#xff1f;太不厚道了吧&#xff01;” “拿完年终奖就走人&#xff0c;这不是典型的‘骑驴找马’吗&#xff1f;” 每到岁末年初&#xff0c;关于“拿到年终奖后是否应该立即辞职”的话题总会引发热议。支持者认为&#xff0c;这是个人…...

多处理系统结构

目录 统一内存访问&#xff08;UMA&#xff09;多处理器系统结构 优点 缺点 应用场景 UMA 结构的架构示例 解决方案和改进 非统一内存访问&#xff08;NUMA&#xff09;多处理系统结构 概述 NUMA的优点 NUMA的缺点 NUMA系统的工作原理 NUMA优化策略 结论 现代计算…...

创建进程的常用方式

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在Python中有多个模块可以创建进程&#xff0c;比较常用的有os.fork()函数、multiprocessing模块和Pool进程池。由于os.fork()函数只适用于Unix/Linu…...

李宏毅2023机器学习作业HW06解析和代码分享

ML2023Spring - HW6 相关信息&#xff1a; 课程主页 课程视频 Sample code HW06 视频 HW06 PDF 个人完整代码分享: GitHub | Gitee | GitCode P.S. HW06 是在 Judgeboi 上提交的&#xff0c;出于学习目的这里会自定义两个度量的函数&#xff0c;不用深究&#xff0c;遵循 Sugge…...

专业技能篇--算法

文章目录 前言经典算法思想总结一、贪心算法二、动态规划三、回溯算法四、分治算法 前言 这篇简单理解一些常见的算法。如果面试的时候问到相关的算法&#xff0c;能够应付一二。 经典算法思想总结 一、贪心算法 思想&#xff1a;贪心算法是一种在每一步选择中都采取在当前状…...

Vue中CSS动态样式绑定

Vue中CSS动态样式绑定与注意事项_vue css动态绑定-CSDN博客 在 Vue 中&#xff0c;你不能直接在 CSS 中直接绑定 data 中的数据&#xff0c;因为 CSS 不是响应式的。但是&#xff0c;有几种方法可以实现根据 Vue 实例中的数据来动态地改变样式&#xff1a; 内联样式绑定&…...

【漏洞复现】契约锁电子签章平台 add 远程命令执行漏洞(XVE-2023-23720)

0x01 产品简介 契约锁电子签章平台是上海亘岩网络科技有限公司推出的一套数字签章解决方案。契约锁为中大型组织提供“数字身份、电子签章、印章管控以及数据存证服务”于一体的数字可信基础解决方案,可无缝集成各类系统,让其具有电子化签署的能力,实现组织全程数字化办公。通…...

计算机专业是否仍是“万金油”?

身份角度一&#xff1a;一名曾经的计算机专业学生  随着高考的结束&#xff0c;我站在了人生的分岔路口&#xff0c;面临着大学专业的选择。在众多的选择中&#xff0c;计算机专业一直是我深思熟虑后的一个重要选项。然而&#xff0c;我并不清楚自己是否真的适合这个专业&…...

Spring 启动顺序

在 Spring 框架中&#xff0c;应用启动过程涉及多个步骤和组件的初始化。理解 Spring 启动顺序不仅有助于优化应用性能&#xff0c;还能帮助开发人员排查启动过程中可能出现的问题。本文将详细介绍 Spring 启动过程中的关键步骤和顺序。 1. Spring 启动过程概述 Spring 应用的…...

2024.06.19 刷题日记

41. 缺失的第一个正数 这个题目的通过率很低&#xff0c;是一道难题&#xff0c;类似于脑筋急转弯&#xff0c;确实不好想。实际上&#xff0c;对于一个长度为 N 的数组&#xff0c;其中没有出现的最小正整数只能在 [1,N1] 中。 这个结论并不好想&#xff0c;举个例子&#x…...

linux系统中,pwd获取当前路径,dirname获取上一层路径;不使用 ../获取上一层路径

在实际项目中&#xff0c;我们通常可以使用 pwd 来获取当前路径&#xff0c;但是如果需要获取上一层路径&#xff0c;有不想使用 …/ 的方式&#xff0c;可以尝试使用 dirname指令 测试shell脚本 #!/bin/bash# 获取当前路径 CURRENT_PATH$PWD echo "CURRENT_PATH$CURREN…...

DeepSpeed Monitoring Comm. Logging

Monitoring 支持多种后端&#xff1a;Tensorboard、WandB、Comet、CSV文件&#xff1b; TensorBoard例子&#xff1a; 自动监控&#xff1a;DeepSpeed自动把重要metric记录下来。只需在配置文件里enable相应的看板后端即可&#xff1a; {"tensorboard": {"enabl…...

关于INCA的几个实用功能

01--VUI窗口设计 这个可以按照自己的想法设计INCA观测或标定窗口 首先进入到INCA的环境内&#xff0c;点击实验→加载VUI窗口 选择空的窗口 打开后如下所示&#xff1a; 点击UI开发模式&#xff0c;如下图 如下&#xff1a; 添加标定量、观测量、示波器 窗口的大小需要在开发…...

Mamaba3--RNN、状态方程、勒让德多项式

Mamaba3–RNN、状态方程、勒让德多项式 一、简单回顾 在Mamba1和Mamba2中分别介绍了RNN和状态方程。 下面从两个图和两个公式出发&#xff0c;对RNN和状态方程做简单的回顾&#xff1a; R N N : s t W s t − 1 U x t &#xff1b; O t V s t RNN: s_t Ws_{t-1}Ux_t&…...

PLC模拟量和数字量到底有什么区别?

PLC模拟量和数字量的区别 在工业自动化领域&#xff0c;可编程逻辑控制器&#xff08;PLC&#xff09;是控制各种机械设备和生产过程的核心组件。PLC通过处理模拟量和数字量来实现对工业过程的精确控制。了解模拟量和数字量的区别对于设计高效、可靠的自动化系统至关重要。 1. …...

html中如何写一个提示框,css画一个提示框

在HTML中&#xff0c;提示框通常使用<div>元素来创建&#xff0c;然后使用CSS进行样式化。以下是一个示例&#xff0c;展示如何在HTML中写一个提示框&#xff0c;并使用CSS来设计其外观。 HTML 首先&#xff0c;创建一个HTML文件&#xff0c;其中包含一个提示框的结构&…...

ExoPlayer 学习笔记

https://www.51cto.com/article/777840.html ExoPlayer支持多种媒体格式和流媒体协议的播放器 播放视频&#xff1a;player.play()暂停视频&#xff1a;player.pause()停止播放&#xff1a;player.stop() Media3 ExoPlayer | Android media | Android Developers implem…...

汽车IVI中控开发入门及进阶(二十七):车载摄像头vehicle camera

前言: 在车载IVI、智能座舱系统中,有一个重要的应用场景就是视频。视频应用又可分为三种,一种是直接解码U盘、SD卡里面的视频文件进行播放,一种是手机投屏,就是把手机投屏软件已视频方式投屏到显示屏上显示,另外一种就是对视频采集设备(主要就是摄像头Camera)的视频源…...

Transformer模型:未来的改进方向与潜在影响

Transformer模型&#xff1a;未来的改进方向与潜在影响 自从2017年Google的研究者们首次提出Transformer模型以来&#xff0c;它已经彻底改变了自然语言处理&#xff08;NLP&#xff09;领域的面貌。Transformer的核心优势在于其“自注意力&#xff08;Self-Attention&#xf…...

ROS 激光雷达

ROS 激光雷达 基本工作原理 激光雷达&#xff08;LIDAR&#xff0c;Light Detection and Ranging&#xff09;是一种用于测量距离的远程感应技术。它通过向目标发射激光并分析反射回来的光来测量目标与激光发射源之间的距离。激光雷达广泛应用于多种领域&#xff0c;包括地理…...

建网站的公司不肯签合同/优化大师免费版

对于vs2013来讲&#xff0c; 步骤&#xff1a; 1.VS2013 中找到&#xff08;安装盘符以C盘为例&#xff09;C:\ProgramFiles(x86)\Microsoft VisualStudio12.0\Common7\IDE\ItemTemplatesCache\CSharp\Code\2052\Class Code文件夹指类代码&#xff0c; 2052文件夹下的每个文件的…...

php教育学校网站源码/上海网站搜索引擎优化

关于mysql效率优化一般通过以下两种方式定位执行效率较低的sql语句。通过慢查询日志定位那些执行效率较低的 SQL 语句&#xff0c;用 --log-slow-queries[file_name] 选项启动时&#xff0c; mysqld 会 写一个包含所有执行时间超过 long_query_time 秒的 SQL 语句的日志文件&am…...

设计师一般放作品的网站/网站推广哪家好

引入jquery博主的链接 本文转载&#xff0c;谢谢博主...

贵州做网站/百度广告点击软件源码

extjs panel layoutconfig属性 分类&#xff1a; extjs2012-06-23 23:27 3790人阅读 评论(0) 收藏 举报extjslayouthtmlobjectgo目录(?)[] layoutConfig: Object 这是一个包含指定布局详细属性的对象 (与layout 配置值结合使用)。 对于...这是一个包含指定布局详细属性的对象 …...

辽宁建设厅网站监管处/西安seo王尘宇

List接口中&#xff0c;有一个方法addAll&#xff0c;可以实现合并list。 List<String> anew ArrayList<String>(); List<String> bnew ArrayList<String>(); b.addAll(a); 合并出来的List就是b&#xff0c;而不是一个新的List . 如果创建新的可以&…...

5个不好的网站/百度推广官网网站

2017-9入学&#xff0c;在实验室采购电子元器件&#xff0c;简单记录下 普通贴片电容、钽电容标号的解读&#xff08;后面单位是PF&#xff09; 103 10*10^3 pF 铝电解电容符号的解读&#xff1a;铝电解电容上面写多少&#xff0c;就是多少uF 电感标号的解读&#xff08;后…...