当前位置: 首页 > 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…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

iview框架主题色的应用

1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题&#xff0c;无需引入&#xff0c;直接可…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...

GraphQL 实战篇:Apollo Client 配置与缓存

GraphQL 实战篇&#xff1a;Apollo Client 配置与缓存 上一篇&#xff1a;GraphQL 入门篇&#xff1a;基础查询语法 依旧和上一篇的笔记一样&#xff0c;主实操&#xff0c;没啥过多的细节讲解&#xff0c;代码具体在&#xff1a; https://github.com/GoldenaArcher/graphql…...

CMS内容管理系统的设计与实现:多站点模式的实现

在一套内容管理系统中&#xff0c;其实有很多站点&#xff0c;比如企业门户网站&#xff0c;产品手册&#xff0c;知识帮助手册等&#xff0c;因此会需要多个站点&#xff0c;甚至PC、mobile、ipad各有一个站点。 每个站点关联的有站点所在目录及所属的域名。 一、站点表设计…...