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

5大常见高并发限流算法选型浅析

高并发场景下,如何确保系统稳定运行,成为了每一个开发工程师必须面对的挑战。**你是否曾因系统崩溃、请求超时或资源耗尽而头疼不已?**高并发限流算法或许能帮你解决这些难题。

在处理高并发请求时,应该如何选择合适的限流算法呢? 以下,我们将深入分析五种常见的高并发限流算法,帮助你做出更合适的技术选型。

在现代高并发系统中,随着用户访问量的激增和业务需求的不断扩展,限流作为一种至关重要的保护机制,被广泛应用于防止系统过载,确保系统的稳定性和可用性。

本文将深入剖析几种常见的限流算法,探讨它们的原理、优缺点并给出代码实例,帮助读者更好地理解和应用这些算法,从而在实际项目中构建更加高效、稳定的系统。

随着互联网应用的快速发展和用户流量的增加,高并发场景下的限流变得越来越重要。尤其在电商、直播、支付等行业,高并发请求的控制直接决定了用户体验和系统的稳定性。选择合适的限流算法,能有效减少服务器的负载和资源的过度占用,从而保证系统的稳定运行。

01 固定窗口算法(Fixed Window Algorithm)

图片

固定窗口算法将时间划分为固定大小的窗口(如1min),在每个窗口内允许一定数量的请求。每当请求到达时,系统会检查当前窗口内的请求数量,如果未超过限制,则允许请求;否则,拒绝请求。

class FixedWindowRateLimiter {
public:FixedWindowRateLimiter(int max_requests_per_win, std::chrono::seconds window_size): max_requests_per_win_(max_requests_per_win), window_size_(window_size), request_count_(0) {window_start_time_ = std::chrono::steady_clock::now();}bool TryAcquire(int request_size) {std::lock_guard<std::mutex> lock(mtx_);auto now = std::chrono::steady_clock::now();// 如果当前时间在窗口内if (now - window_start_time_ < window_size_) {// 检查请求数量是否超过限制if (request_count_ + request_size <= max_requests_per_win_) {request_count_ += request_size; // 增加请求计数return true; // 允许请求} else {return false; // 超过最大请求数}} else {// 重置窗口window_start_time_ = now;request_count_ = request_size; // 重置请求计数为当前请求数量return true; // 允许请求}}private:int max_requests_per_win_; // 最大请求数std::chrono::seconds window_size_; // 窗口大小int request_count_; // 当前请求计数std::chrono::steady_clock::time_point window_start_time_; // 窗口开始时间std::mutex mtx_; // 互斥锁
};
  • 算法简介:通过设定固定的时间窗口,在窗口内计算请求的数量,如果超出限制,则拒绝请求。每当时间窗口到期,计数器重置。
  • 适用场景:适用于请求速率较稳定、对时间窗口的要求不高的场景。
  • 案例:某银行的API接口,每秒钟限制5次请求,超过5次即拒绝。当一个时间窗口(如1秒)结束时,计数器会重置。

优点

  • 实现简单,非常容易理解。

  • 适用于请求速率相对稳定的场景。

缺点

  • 在短时间流量突发时,将会有大量失败,无法平滑流量。

  • 有窗口边际效应:在窗口切换时,可能会出现短时间内请求激增的情况,导致系统过载。

02 滑动窗口算法(Sliding Window Algorithm)

图片

滑动窗口算法是对固定窗口算法的改进,它将时间窗口划分为多个小桶,并为每个小桶维护一个独立的请求计数器。当请求到达时,算法会根据请求的时间戳将其放入相应的小桶中,并检查整个滑动窗口内的请求总数是否超过限制。随着时间的推移,滑动窗口会不断向右滑动,丢弃最旧的小桶并添加新的小桶。

class SlidingWindowRateLimiter {
public:SlidingWindowRateLimiter(int max_requests_per_win, std::chrono::seconds bucket_size, int num_buckets): max_requests_per_win_(max_requests_per_win), bucket_size_(bucket_size), num_buckets_(num_buckets) {request_counts_.resize(num_buckets, 0);last_bucket_time_ = std::chrono::steady_clock::now();}bool TryAcquire(int request_size) {std::lock_guard<std::mutex> lock(mtx_);auto now = std::chrono::steady_clock::now();UpdateBuckets_(now);int total_requests = 0;for (int count : request_counts_) {total_requests += count;}if (total_requests + request_size <= max_requests_per_win_) {request_counts_[current_bucket_index_] += request_size; // 增加当前桶的请求计数return true; // 允许请求} else {return false; // 超过最大请求数}}private:void UpdateBuckets_(std::chrono::steady_clock::time_point now) {auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - last_bucket_time_);int buckets_to_update = static_cast<int>(elapsed / bucket_size_);if (buckets_to_update > 0) {for (int i = 0; i < std::min(num_buckets_, buckets_to_update); ++i) {auto next_bucket_index = (current_bucket_index_ + 1) % num_buckets_;request_counts_[next_bucket_index] = 0; // 移除最旧的桶current_bucket_index_ = next_bucket_index; // 移动到下一个桶}last_bucket_time_ += buckets_to_update * bucket_size_; // 更新最后桶的时间}}int max_requests_per_win_; // 最大请求数std::chrono::seconds bucket_size_; // 每个小桶的大小int num_buckets_; // 小桶的数量std::vector<int> request_counts_; // 每个小桶的请求计数std::mutex mtx_; // 互斥锁std::chrono::steady_clock::time_point last_bucket_time_; // 上一个桶的时间int current_bucket_index_ = 0; // 当前桶的索引
};

  • 算法简介:滑动窗口计数器算法是对固定窗口计数器算法的改进。它将时间窗口滑动分段,每个小段都进行计数,精确度更高。相比固定窗口,它更加平滑,不会出现突如其来的拒绝。
  • 适用场景:适用于对请求的实时性和稳定性要求较高的场景。
  • 案例:某社交平台在API访问上使用了滑动窗口算法,可以避免短时间内请求量突然增大时的系统压力,使请求处理更加平稳。

优点

  • 相比固定窗口算法可以更细粒度地控制流量。

  • 减缓了固定窗口算法中的窗口边际效应。

缺点

  • 在短时间流量突发时,将会有大量失败,无法平滑流量。

03 滑动日志算法(Sliding Log Algorithm)

图片

滑动日志算法通过记录每个请求的时间戳来控制请求速率。当一个请求到达时,系统会检查最近一段时间内的请求记录,计算请求速率是否超过限制。如果超过,则拒绝请求;否则,处理请求并记录当前请求的时间戳。

class SlidingLogRateLimiter {
public:SlidingLogRateLimiter(int max_requests_per_win, std::chrono::seconds window_size): max_requests_per_win_(max_requests_per_win), window_size_(window_size) {}bool TryAcquire(int request_size) {std::lock_guard<std::mutex> lock(mtx_);auto now = std::chrono::steady_clock::now();CleanUp_(now);int total_requests = 0;for (const auto& timestamp : request_log_) {total_requests += timestamp.second;}if (total_requests + request_size <= max_requests_per_win_) {request_log_.emplace_back(now, request_size); // 记录当前请求return true; // 允许请求} else {return false; // 超过最大请求数}}private:void CleanUp_(std::chrono::steady_clock::time_point now) {auto expiration_time = now - window_size_;while (!request_log_.empty() && request_log_.front().first < expiration_time) {request_log_.pop_front(); // 移除过期的请求记录}}int max_requests_per_win_; // 最大请求数std::chrono::seconds window_size_; // 窗口大小std::deque<std::pair<std::chrono::steady_clock::time_point, int>> request_log_; // 请求日志std::mutex mtx_; // 互斥锁
};

优点

  • 可以非常精确地控制请求速率。

缺点

  • 在短时间流量突发时,将会有大量失败,无法平滑流量。

  • 由于每一次成功请求都要被记录,所以会有较大额外的内存开销。

04 漏桶算法(Leaky Bucket Algorithm)

图片

漏桶算法将请求看作水滴,将请求处理过程看作水从漏桶底部中流出。系统以恒定速率处理请求(即漏桶的漏水速率),当一个请求到达时,如果漏桶未满,则请求被放入漏桶中等待处理;如果漏桶已满,则请求被拒绝。

class Task {
public:virtual int GetLoad() = 0;virtual void Run() = 0;
}class LeakyBucketRateLimiter {
public:LeakyBucketRateLimiter(int capacity, int leak_rate): capacity_(capacity), leak_rate_(leak_rate), current_water_(0) {last_leak_time_ = std::chrono::steady_clock::now();}bool TryRun(const TaskPtr& task) {std::lock_guard<std::mutex> lock(mtx_);Leak();if (current_water_ + task.GetLoad() <= capacity_) {current_water_ += task.GetLoad(); // 将请求放入漏桶heap_timer_.Schedule(task, current_water_ / leak_rate); // 定时器在该任务的水漏完后将会执行task的Run方法return true; // 允许请求} else {return false; // 漏桶已满,请求被拒绝}}private:void Leak() {auto now = std::chrono::steady_clock::now();auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - last_leak_time_);int leaked_water = static_cast<int>(elapsed.count()) * leak_rate_;if (leaked_water > 0) {current_water_ = std::max(0, current_water_ - leaked_water); // 减少水量last_leak_time_ = now; // 更新最后漏水时间}}int capacity_; // 漏桶的容量int leak_rate_; // 漏水速率(每秒漏掉的水量)int current_water_; // 当前水量HeapTimer heap_timer_; // 一个任务执行的定时器std::mutex mtx_; // 互斥锁std::chrono::steady_clock::time_point last_leak_time_; // 上次漏水的时间
};

​​​​​​​

  • 算法简介:漏桶算法和令牌桶算法类似,但它更加严格。漏桶算法通过固定的出水速率来处理请求,桶满后会丢弃新请求。适合用来处理请求的平滑速率控制。
  • 适用场景:适用于有稳定速率处理的场景,比如实时数据处理,确保处理流量的一致性。
  • 案例:一个短视频平台可以使用漏桶算法来平稳处理视频上传流量,确保不会因为突发的高并发导致服务器崩溃。

优点

  • 能提供非常平稳的流量。

  • 削峰填谷,有一定的应对流量突发能力(桶的大小)。

缺点

  • 控制比较刻板,弹性能力较弱。

  • 在并发时候会产生额外的延迟等待开销(比如限制流量为1qps,两个请求同时到达,必然有其中一个请求需要等1s后才能服务)。

05 令牌桶算法(Token Bucket Algorithm)

图片

令牌桶算法使用一个桶来存储令牌,以固定速率向桶中添加令牌。每当请求到达时,会先检查桶中是否有令牌,如果有,则允许请求并消耗相应令牌;如果没有,则拒绝请求。

class TokenBucketRateLimiter {
public:TokenBucketRateLimiter(int capacity, int refill_rate): capacity_(capacity), refill_rate_(refill_rate), current_tokens_(0) {last_refill_time_ = std::chrono::steady_clock::now();}bool TryAcquire(int request_size) {std::lock_guard<std::mutex> lock(mtx_);Refill_();if (current_tokens_ >= request_size) {current_tokens_ -= request_size; // 消耗令牌return true; // 允许请求} else {return false; // 令牌不足,请求被拒绝}}private:void Refill_() {auto now = std::chrono::steady_clock::now();auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - last_refill_time_);current_tokens_ = std::min(capacity_, current_tokens_ + static_cast<int>(elapsed.count()) * refill_rate_); // 更新令牌数量last_refill_time_ = now; // 更新最后补充时间}int capacity_; // 令牌桶的容量int refill_rate_; // 令牌补充速率(每秒补充的令牌数量)int current_tokens_; // 当前令牌数量std::mutex mtx_; // 互斥锁std::chrono::steady_clock::time_point last_refill_time_; // 上次补充令牌的时间
};

​​​​​​​

  • 算法简介:令牌桶算法是一种典型的限流算法,通过控制令牌的生成速率和桶的容量来限制请求数量。当令牌不足时,请求会被延迟或拒绝。
  • 适用场景:适合流量突发性较强的场景。能够平滑处理高峰流量,避免瞬时请求量过高导致系统崩溃。
  • 案例:例如,一家电商平台的促销活动中,用户请求流量很难控制,使用令牌桶算法,能够保证每秒有一定数量的请求被允许进入系统,避免流量瞬间激增时导致系统崩溃。

优点

  • 能够处理突发流量,避免系统瞬间过载。

  • 灵活性高,可以通过调整令牌生成速率和桶容量来控制流量。

缺点

  • 实现相对复杂。

总结

想要在高并发系统中使用这些算法来优化系统性能?使用一些优秀的云平台服务可以帮助你实现灵活的限流与调度,如阿里云的云数据库API Gateway,为你的系统提供强有力的支撑

每种限流算法都有其适用的场景和优缺点。在选择限流算法时,需要根据具体的业务需求和系统特性进行权衡。通过合理选择和组合这些算法,可以有效地保护系统免受过载的影响

在面对高并发压力时,选择合适的限流算法至关重要。不同的业务场景需要根据流量特点选择最佳的限流策略,以确保系统高效、稳定地运行。希望本文的分析能帮助你在面对复杂的流量控制时做出明智的选择。

“高并发的世界里,限流算法不仅是保障系统稳定的利器,更是确保用户体验不被破坏的守护者。”

相关文章:

5大常见高并发限流算法选型浅析

高并发场景下&#xff0c;如何确保系统稳定运行&#xff0c;成为了每一个开发工程师必须面对的挑战。**你是否曾因系统崩溃、请求超时或资源耗尽而头疼不已&#xff1f;**高并发限流算法或许能帮你解决这些难题。 在处理高并发请求时&#xff0c;应该如何选择合适的限流算法呢…...

深入刨析数据结构之排序(下)

目录 1.内部排序 1.5选择排序 1.5.1简单选择排序 1.5.2树形选择排序 1.6堆排序 1.7归并排序 1.7.1递归归并 1.7.2非递归归并 1.8计数排序 1.9基数排序 常见内部排序的总结&#xff1a; 1.内部排序 1.5选择排序 选择排序&#xff08;Selection Sort&#xff09;的基…...

特殊数据类型的深度分析:JSON、数组和 HSTORE 的实用价值

title: 特殊数据类型的深度分析:JSON、数组和 HSTORE 的实用价值 date: 2025/1/4 updated: 2025/1/4 author: cmdragon excerpt: 随着数据管理需求的多样化,许多现代数据库系统开始支持特殊数据类型,以满足更多复杂应用场景的需求。在 PostgreSQL 中,JSON、数组和 HSTOR…...

PCA降维算法详细推导

关于一个小小的PCA的推导 文章目录 关于一个小小的PCA的推导1 谱分解 (spectral decomposition)2 奇异矩阵(singular matrix)3 酉相似(unitary similarity)4 酉矩阵5 共轭变换6 酉等价7 矩阵的迹的计算以及PCA算法推导8 幂等矩阵(idempotent matrix)9 Von Neumanns 迹不等式 [w…...

NS4861 单灯指示独立耳锂电池充放电管理 IC

1 特性  最大 500mA 线性充电电流&#xff0c;外部可调节  内部预设 4.2V 充电浮充电压  支持 0V 电池充电激活  支持充满 / 再充功能  内置同步升压放电模块&#xff0c;输出电压 5.1V  同步升压 VOUT 最大输出电流 500mA  VOL/OR 独…...

编写可复用性的模块

在生活中&#xff0c;重复的机械劳动会消耗我们的时间和精力&#xff0c;提高生产成本&#xff0c;降低工作效率。同样&#xff0c;在代码世界中&#xff0c;编写重复的代码会导致代码的冗余&#xff0c;页面性能的下降以及后期维护成本的增加。由此可见将重复的事情复用起来是…...

2025年1月4日CSDN的Markdown编辑器

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…...

广域网连接PPP

广域网连接PPP PPP协议是一种应用广泛的点到点链路协议&#xff0c;主要用于点到点连接的路由器间的通信。PPP协议既可以用于同步通信&#xff0c;也可以用于异步通信&#xff0c;本部分只讨论同步接口上的PPP配置。 锐捷路由器的同步串行口默认封装Cisco HDLC&#xff0c;所…...

【pyqt】(四)Designer布局

布局 之前我们利用鼠标拖动的控件的时候&#xff0c;发现一些部件很难完成对齐这些工作&#xff0c;pyqt为我们提供的多种布局功能不仅可以让排版更加美观&#xff0c;还能够让界面自适应窗口大小的变化&#xff0c;使得布局美观合理。最常使用的三种布局就是垂直河子布局、水…...

【从零开始入门unity游戏开发之——C#篇40】C#特性(Attributes)和自定义特性

文章目录 前言一、特性&#xff08;Attributes&#xff09;基本概念二、自定义特性1、自定义特性代码示例&#xff1a;2、应用自定义特性&#xff1a;3、解释3.1 **AttributeUsage 特性**3.2 特性的命名3.3 **构造函数**&#xff1a;3.4 **属性**&#xff1a; 4、使用反射获取特…...

DES密码的安全性分析(简化版本)

DES仍是世界上使用最广的&#xff08;DES发行后20年&#xff0c;互联网的兴起&#xff0c;人们开始觉得DES不安全了&#xff0c;但DES的实现成本也越来越低&#xff09; 宏观分析&#xff1a; 密钥空间方面&#xff1a; 密钥长度&#xff1a;DES 算法使用 56 位的密钥对数据…...

引入三方jar包命令

mvn install:install-file \ -Dfile本地磁盘路径 \ -DgroupId组织名称 \ -DartifactId项目名称 \ -Dversion版本号 \ -Dpackagingjar 例如 假设你的 JAR 文件路径是 /home/user/common-pojo-1.0-SNAPSHOT.jar&#xff0c;组织名称是 com.example&#xff0c;项目名…...

机器学习基础-机器学习的常用学习方法

半监督学习的概念 少量有标签样本和大量有标签样本进行学习&#xff1b;这种方法旨在利用未标注数据中的结构信息来提高模型性能&#xff0c;尤其是在标注数据获取成本高昂或困难的情况下。 规则学习的概念 基本概念 机器学习里的规则 若......则...... 解释&#xff1a;如果…...

在控制领域中如何区分有效性、优越性、稳定性和鲁棒性?

在控制领域中&#xff0c;区分有效性、优越性、稳定性和鲁棒性可以通过具体的控制器设计实例来更好地理解。以下以经典的质量-弹簧-阻尼系统的PID控制器设计为例&#xff0c;展示如何区分这四个性能指标。 经典质量-弹簧-阻尼系统的PID控制器设计 质量-弹簧-阻尼系统模型 考…...

美国宏观经济基础框架梳理

玩转币圈和美股&#xff0c;最关键的是理解美国宏观经济。以下是核心逻辑&#xff1a;美国经济数据→政策调整→资金流动→资产价格变化。掌握这些因素的关系&#xff0c;才能在市场中立于不败之地。 一、核心变量及其意义 1. GDP&#xff08;国内生产总值&#xff09; • …...

装饰器模式详解

装饰器模式&#xff08;Decorator Pattern&#xff09;是一种设计模式&#xff0c;属于结构型模式之一。它允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其结构。这种模式创建了一个装饰类&#xff0c;用来包装原有类的一个实例&#xff0c;从而扩展该实例的功能。…...

[最新] SIM卡取出后还能找到我的iPhone吗?

您是否曾在任何地方丢失过 SIM 卡&#xff1f;或者您是否已移除 SIM 卡&#xff0c;现在无法在任何地方找到您的 iPhone&#xff1f;在这篇博客中&#xff0c;您将了解即使 SIM 卡被移除&#xff0c;“查找我的 iPhone”也能正常工作。 在某些情况下&#xff0c;您必须取出 SIM…...

数据分析思维(六):分析方法——相关分析方法

数据分析并非只是简单的数据分析工具三板斧——Excel、SQL、Python&#xff0c;更重要的是数据分析思维。没有数据分析思维和业务知识&#xff0c;就算拿到一堆数据&#xff0c;也不知道如何下手。 推荐书本《数据分析思维——分析方法和业务知识》&#xff0c;本文内容就是提取…...

谷歌2025年AI战略与产品线布局

在2024年12月的战略会议上,谷歌高层向员工描绘了2025年的宏伟蓝图,特别是在人工智能(AI)领域。这一年被定位为AI发展的关键转折点,谷歌计划通过一系列新产品和创新来巩固其在全球科技领域的领导地位。本文将深入探讨谷歌的2025年AI战略、重点产品以及竞争策略。 一、整体…...

登录的几种方式

使用Session完成登录 1. 手机号发送验证码 逻辑步骤&#xff1a; 校验手机号格式是否正确。生成验证码&#xff08;例如使用Hutool工具类&#xff09;。将手机号和验证码存入Session。返回验证码发送成功的响应。 2. 用户登录逻辑 逻辑步骤&#xff1a; 从Session中获取存…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

《Offer来了:Java面试核心知识点精讲》大纲

文章目录 一、《Offer来了:Java面试核心知识点精讲》的典型大纲框架Java基础并发编程JVM原理数据库与缓存分布式架构系统设计二、《Offer来了:Java面试核心知识点精讲(原理篇)》技术文章大纲核心主题:Java基础原理与面试高频考点Java虚拟机(JVM)原理Java并发编程原理Jav…...

深入解析 ReentrantLock:原理、公平锁与非公平锁的较量

ReentrantLock 是 Java 中 java.util.concurrent.locks 包下的一个重要类,用于实现线程同步,支持可重入性,并且可以选择公平锁或非公平锁的实现方式。下面将详细介绍 ReentrantLock 的实现原理以及公平锁和非公平锁的区别。 ReentrantLock 实现原理 基本架构 ReentrantLo…...

第22节 Node.js JXcore 打包

Node.js是一个开放源代码、跨平台的、用于服务器端和网络应用的运行环境。 JXcore是一个支持多线程的 Node.js 发行版本&#xff0c;基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线程运行。 本文主要介绍JXcore的打包功能。 JXcore 安装 下载JXcore安装包&a…...

GB/T 43887-2024 核级柔性石墨板材检测

核级柔性石墨板材是指以可膨胀石墨为原料、未经改性和增强、用于核工业的核级柔性石墨板材。 GB/T 43887-2024核级柔性石墨板材检测检测指标&#xff1a; 测试项目 测试标准 外观 GB/T 43887 尺寸偏差 GB/T 43887 化学成分 GB/T 43887 密度偏差 GB/T 43887 拉伸强度…...

20250609在荣品的PRO-RK3566开发板的Android13下解决串口可以执行命令但是脚本执行命令异常的问题

20250609在荣品的PRO-RK3566开发板的Android13下解决串口可以执行命令但是脚本执行命令异常的问题 2025/6/9 20:54 缘起&#xff0c;为了跨网段推流&#xff0c;千辛万苦配置好了网络参数。 但是命令iptables -t filter -F tetherctrl_FORWARD可以在调试串口/DEBUG口正确执行。…...

【threejs】每天一个小案例讲解:创建基本的3D场景

代码仓 GitHub - TiffanyHoo/three_practices: Learning three.js together! 可自行clone&#xff0c;无需安装依赖&#xff0c;直接liver-server运行/直接打开chapter01中的html文件 运行效果图 知识要点 核心三要素 场景&#xff08;Scene&#xff09; 使用 THREE.Scene(…...