阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Nginx限速模块初探

433次阅读
没有评论

共计 9137 个字符,预计需要花费 23 分钟才能阅读完成。

Nginx 限速模块分为哪几种?按请求速率限速的 burst 和 nodelay 参数是什么意思?漏桶算法和令牌桶算法究竟有什么不同?本文将带你一探究竟。我们会通过一些简单的示例展示 Nginx 限速模块是如何工作的,然后结合代码讲解其背后的算法和原理。

核心算法

在探究 Nginx 限速模块之前,我们先来看看网络传输中常用两个的流量控制算法:漏桶算法 令牌桶算法。这两只“桶”到底有什么异同呢?

漏桶算法(leaky bucket)

漏桶算法 (leaky bucket) 算法思想如图所示:

Nginx 限速模块初探

一个形象的解释是:

  • 水(请求)从上方倒入水桶,从水桶下方流出(被处理);
  • 来不及流出的水存在水桶中(缓冲),以 固定速率 流出;
  • 水桶满后水溢出(丢弃)。

这个算法的核心是:缓存请求、匀速处理、多余的请求直接丢弃。

令牌桶算法(token bucket)

令牌桶 (token bucket) 算法思想如图所示:

Nginx 限速模块初探

算法思想是:

  • 令牌以固定速率产生,并缓存到令牌桶中;
  • 令牌桶放满时,多余的令牌被丢弃;
  • 请求要消耗等比例的令牌才能被处理;
  • 令牌不够时,请求被缓存。

相比漏桶算法,令牌桶算法不同之处在于它不但有一只“桶”,还有个队列,这个桶是用来存放令牌的,队列才是用来存放请求的。

从作用上来说,漏桶和令牌桶算法最明显的区别就是是否允许 突发流量 (burst) 的处理,漏桶算法能够 强行限制数据的实时传输(处理)速率 ,对突发流量不做额外处理;而令牌桶算法能够在 限制数据的平均传输速率的同时允许某种程度的突发传输

Nginx 按请求速率限速模块使用的是漏桶算法,即能够强行保证请求的实时处理速度不会超过设置的阈值。

Nginx 限速模块

Nginx 主要有两种限速方式:按连接数限速(ngx_http_limit_conn_module)、按请求速率限速(ngx_http_limit_req_module)。我们着重讲解按请求速率限速。

按连接数限速

按连接数限速是指限制单个 IP(或者其他的 key)同时发起的连接数,超出这个限制后,Nginx 将直接拒绝更多的连接。这个模块的配置比较好理解,详见 ngx_http_limit_conn_module 官方文档。

按请求速率限速

按请求速率限速是指限制单个 IP(或者其他的 key)发送请求的速率,超出指定速率后,Nginx 将直接拒绝更多的请求。采用 leaky bucket 算法实现。为深入了解这个模块,我们先从实验现象说起。开始之前我们先简单介绍一下该模块的配置方式,以下面的配置为例:

http {limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
    ...
    server {
        ...
        location /search/ {limit_req zone=mylimit burst=4 nodelay;
        }

使用 limit_req_zone 关键字,我们定义了一个名为 mylimit 大小为 10MB 的共享内存区域 (zone),用来存放限速相关的统计信息,限速的key 值为二进制的 IP 地址($binary_remote_addr),限速上限 (rate) 为 2r/s;接着我们使用 limit_req 关键字将上述规则作用到 /search/ 上。burstnodelay 的作用稍后解释。

使用上述规则,对于 /search/ 目录的访问,单个 IP 的访问速度被限制在了 2 请求 / 秒,超过这个限制的访问将直接被 Nginx 拒绝。

实验 1——毫秒级统计

我们有如下配置:

...
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / {limit_req zone=mylimit;
    }
}
...

上述规则限制了每个 IP 访问的速度为 2r/s,并将该规则作用于跟目录。如果单个 IP 在非常短的时间内并发发送多个请求,结果会怎样呢?

# 单个 IP 10ms 内并发发送 6 个请求
send 6 requests in parallel, time cost: 2 ms
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 200 OK
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 503 Service Temporarily Unavailable
end, total time cost: 461 ms

我们使用单个 IP 在 10ms 内发并发送了 6 个请求,只有 1 个成功,剩下的 5 个都被拒绝。我们设置的速度是 2r/s,为什么只有 1 个成功呢,是不是 Nginx 限制错了?当然不是,是因为 Nginx 的限流统计是基于毫秒的,我们设置的速度是 2r/s,转换一下就是 500ms 内单个 IP 只允许通过 1 个请求,从 501ms 开始才允许通过第二个请求。

Nginx 限速模块初探

实验 2——burst 允许缓存处理突发请求

实验 1 我们看到,我们短时间内发送了大量请求,Nginx 按照毫秒级精度统计,超出限制的请求直接拒绝。这在实际场景中未免过于苛刻,真实网络环境中请求到来不是匀速的,很可能有请求“突发”的情况,也就是“一股子一股子”的。Nginx 考虑到了这种情况,可以通过 burst 关键字开启对突发请求的缓存处理,而不是直接拒绝。

来看我们的配置:

...
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server {location / {limit_req zone=mylimit burst=4;
    }
}
...

我们加入了 burst=4,意思是每个 key(此处是每个 IP) 最多允许 4 个突发请求的到来。如果单个 IP 在 10ms 内发送 6 个请求,结果会怎样呢?

# 单个 IP 10ms 内发送 6 个请求,设置 burst
send 6 requests in parallel, time cost: 2 ms
HTTP/1.1 200 OK
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
end, total time cost: 2437 ms

相比实验 1 成功数增加了 4 个,这个我们设置的 burst 数目是一致的。具体处理流程是:1 个请求被立即处理,4 个请求被放到 burst 队列里,另外一个请求被拒绝。通过 burst 参数,我们使得 Nginx 限流具备了缓存处理突发流量的能力

但是请注意:burst 的作用是让多余的请求可以先放到队列里,慢慢处理。如果不加 nodelay 参数,队列里的请求 不会立即处理,而是按照 rate 设置的速度,以毫秒级精确的速度慢慢处理。

实验 3——nodelay 降低排队时间

实验 2 中我们看到,通过设置 burst 参数,我们可以允许 Nginx 缓存处理一定程度的突发,多余的请求可以先放到队列里,慢慢处理,这起到了平滑流量的作用。但是如果队列设置的比较大,请求排队的时间就会比较长,用户角度看来就是 RT 变长了,这对用户很不友好。有什么解决办法呢?nodelay参数允许请求在排队的时候就立即被处理,也就是说只要请求能够进入 burst 队列,就会立即被后台 worker 处理 ,请注意,这意味着 burst 设置了 nodelay 时,系统瞬间的 QPS 可能会超过 rate 设置的阈值。nodelay 参数要跟 burst 一起使用才有作用。

延续实验 2 的配置,我们加入 nodelay 选项:

...
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server {location / {limit_req zone=mylimit burst=4 nodelay;
    }
}
...

单个 IP 10ms 内并发发送 6 个请求,结果如下:

# 单个 IP 10ms 内发送 6 个请求
   实验3, 设置 burst 和 nodelay       |  实验2, 只设置 burst
send 6 requests, time cost: 4 ms |  time cost: 2 ms
HTTP/1.1 200 OK                  |  HTTP/1.1 200 OK
HTTP/1.1 200 OK                  |  HTTP/1.1 503 ...
HTTP/1.1 200 OK                  |  HTTP/1.1 200 OK
HTTP/1.1 200 OK                  |  HTTP/1.1 200 OK
HTTP/1.1 503 ...                 |  HTTP/1.1 200 OK
HTTP/1.1 200 OK                  |  HTTP/1.1 200 OK
total time cost: 465 ms          |  total time cost: 2437 ms

跟实验 2 相比,请求成功率没变化,但是 总体耗时变短了 。这怎么解释呢?实验 2 中,有 4 个请求被放到 burst 队列当中,工作进程每隔 500ms(rate=2r/s) 取一个请求进行处理,最后一个请求要排队 2s 才会被处理;实验 3 中,请求放入队列跟实验 2 是一样的,但不同的是,队列中的请求同时具有了被处理的资格,所以实验 3 中的 5 个请求可以说是同时开始被处理的,花费时间自然变短了。

但是请注意,虽然设置 burst 和 nodelay 能够降低突发请求的处理时间,但是长期来看并不会提高吞吐量的上限,长期吞吐量的上限是由 rate 决定的,因为 nodelay 只能保证 burst 的请求被立即处理,但 Nginx 会限制队列元素释放的速度,就像是限制了令牌桶中令牌产生的速度。

看到这里你可能会问,加入了 nodelay 参数之后的限速算法,到底算是哪一个“桶”,是漏桶算法还是令牌桶算法?当然还算是漏桶算法。考虑一种情况,令牌桶算法的 token 为耗尽时会怎么做呢?由于它有一个请求队列,所以会把接下来的请求缓存下来,缓存多少受限于队列大小。但此时缓存这些请求还有意义吗?如果 server 已经过载,缓存队列越来越长,RT 越来越高,即使过了很久请求被处理了,对用户来说也没什么价值了。所以当 token 不够用时,最明智的做法就是直接拒绝用户的请求,这就成了漏桶算法,哈哈~

源码剖析

经过上面的示例,我们队请求限速模块有了一定的认识,现在我们深入剖析代码实现。按请求速率限流模块 ngx_http_limit_req_module 代码位于 src/http/modules/ngx_http_limit_req_module.c,900 多好代码可谓短小精悍。相关代码有两个核心数据结构:

  1. 红黑树:通过红黑树记录每个节点(按照声明时指定的 key)的统计信息,方便查找;
  2. LRU 队列:将红黑树上的节点按照最近访问时间排序,时间近的放在队列头部,以便使用 LRU 队列淘汰旧的节点,避免内存溢出。

这两个关键对象存储在 ngx_http_limit_req_shctx_t 中:

typedef struct {ngx_rbtree_t                  rbtree; /* red-black tree */
    ngx_rbtree_node_t             sentinel; /* the sentinel node of red-black tree */
    ngx_queue_t                   queue; /* used to expire info(LRU algorithm) */
} ngx_http_limit_req_shctx_t;

其中除了 rbtree 和 queue 之外,还有一个叫做 sentinel 的变量,这个变量用作红黑树的 NIL 节点。

该模块的核心逻辑在函数 ngx_http_limit_req_lookup() 中,这个函数主要流程是怎样呢?对于每一个请求:

  1. 从根节点开始查找红黑树,找到 key 对应的节点;
  2. 找到后修改该点在 LRU 队列中的位置,表示该点最近被访问过;
  3. 执行漏桶算法;
  4. 没找到时根据 LRU 淘汰,腾出空间;
  5. 生成并插入新的红黑树节点;
  6. 执行下一条限流规则。

流程很清晰,但是代码中牵涉到红黑树、LRU 队列等高级数据结构,是不是会写得很复杂?好在 Nginx 作者功力深厚,代码写得简洁易懂,哈哈~

// 漏桶算法核心流程
ngx_http_limit_req_lookup(...){while (node != sentinel) {// search rbtree
    if (hash < node->key) {node = node->left; continue;} // 1. 从根节点开始查找红黑树
    if (hash > node->key) {node = node->right; continue;}
    rc = ngx_memn2cmp(key->data, lr->data, key->len, (size_t) lr->len);
    if (rc == 0) {// found
      ngx_queue_remove(&lr->queue); // 2. 修改该点在 LRU 队列中的位置,表示该点最近被访问过
      ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);// 2
      ms = (ngx_msec_int_t) (now - lr->last);
      excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; // 3. 执行漏桶算法
      if (excess < 0) 
        excess = 0;
      if ((ngx_uint_t) excess > limit->burst)
        return NGX_BUSY; // 超过了突发门限,拒绝
      if (account) {// 是否是最后一条规则
        lr->excess = excess;    
        lr->last = now;    
        return NGX_OK; // 未超过限制,通过
      }
      ...
      return NGX_AGAIN; // 6. 执行下一条限流规则
    }
    node = (rc < 0) ? node->left : node->right; // 1
  } // while
  ...
  // not found
  ngx_http_limit_req_expire(ctx, 1); // 4. 根据 LRU 淘汰,腾出空间
  node = ngx_slab_alloc_locked(ctx->shpool, size); // 5. 生成新的红黑树节点
  ngx_rbtree_insert(&ctx->sh->rbtree, node);// 5. 插入该节点,重新平衡红黑树
  ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
  if (account) {    
    lr->last = now; 
    lr->count = 0;
    return NGX_OK;
  }
  ...
  return NGX_AGAIN; // 6. 执行下一条限流规则
}

代码有三种返回值,它们的意思是:

  • NGX_BUSY 超过了突发门限,拒绝
  • NGX_OK 未超过限制,通过
  • NGX_AGAIN 未超过限制,但是还有规则未执行,需执行下一条限流规则

上述代码不难理解,但我们还有几个问题:

  1. LRU 是如何实现的?
  2. 漏桶算法是如何实现的?
  3. 每个 key 相关的 burst 队列在哪里?

LRU 是如何实现的

LRU 算法的实现很简单,如果一个节点被访问了,那么就把它移到队列的头部,当空间不足需要淘汰节点时,就选出队列尾部的节点淘汰掉,主要体现在如下代码中:

ngx_queue_remove(&lr->queue); // 2. 修改该点在 LRU 队列中的位置,表示该点最近被访问过
ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);// 2
...
ngx_http_limit_req_expire(ctx, 1); // 4. 根据 LRU 淘汰,腾出空间

漏桶算法是如何实现的

漏桶算法的实现也比我们想象的简单,其核心是这一行公式 excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000,这样代码的意思是:excess 表示当前 key 上遗留的请求数, 本次遗留的请求数 = 上次遗留的请求数 – 预设速率 X 过去的时间 + 1。这个 1 表示当前这个请求,由于 Nginx 内部表示将单位缩小了 1000 倍,所以 1 个请求要转换成 1000。

excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; // 3. 执行漏桶算法
if (excess < 0) 
    excess = 0;
if ((ngx_uint_t) excess > limit->burst)
    return NGX_BUSY; // 超过了突发门限,拒绝
if (account) {// 是否是最后一条规则
    lr->excess = excess;    
    lr->last = now;    
    return NGX_OK; // 未超过限制,通过
}
...
return NGX_AGAIN; // 6. 执行下一条限流规则

上述代码受限算出当前 key 上遗留的请求数,如果超过了 burst,就直接拒绝;由于 Nginx 允许多条限速规则同时起作用,如果已是最后一条规则,则允许通过,否则执行下一条规则。

单个 key 相关的 burst 队列在哪里

没有单个 key 相关的 burst 队列。上面代码中我们看到当到达最后一条规则时,只要 excess<limit->burst 限速模块就会返回 NGX_OK,并没有把多余请求放入队列的操作,这是因为 Nginx 是基于 timer 来管理请求的,当限速模块返回 NGX_OK 时,调度函数会计算一个延迟处理的时间,同时把这个请求放入到共享的 timer 队列中(一棵按等待时间从小到大排序的红黑树)。

ngx_http_limit_req_handler(ngx_http_request_t *r)
{
    ...
    for (n = 0; n < lrcf->limits.nelts; n++) {
        ...
        ngx_shmtx_lock(&ctx->shpool->mutex);// 获取锁
        rc = ngx_http_limit_req_lookup(limit, hash, &key, &excess, // 执行漏桶算法
                                       (n == lrcf->limits.nelts - 1));
        ngx_shmtx_unlock(&ctx->shpool->mutex);// 释放锁
        ...
        if (rc != NGX_AGAIN)
            break;
    }
    ...
    delay = ngx_http_limit_req_account(limits, n, &excess, &limit);// 计算当前请求需要的延迟时间
    if (!delay) {return NGX_DECLINED;// 不需要延迟,交给后续的 handler 进行处理
    }
    ...
    ngx_add_timer(r->connection->write, delay);// 否则将请求放到定时器队列里
    return NGX_AGAIN; // the request has been successfully processed, the request must be suspended until some event. http://www.nginxguts.com/2011/01/phases/
}

我们看到 ngx_http_limit_req_handler() 调用了函数 ngx_http_limit_req_lookup(),并根据其返回值决定如何操作:或是拒绝,或是交给下一个 handler 处理,或是将请求放入定期器队列。当限速规则都通过后,该 hanlder 通过调用函数ngx_http_limit_req_account() 得出当前请求需要的延迟时间,如果不需要延迟,就将请求交给后续的 handler 进行处理,否则将请求放到定时器队列里。注意这个定时器队列是共享的,并没有为单独的 key(比如,每个 IP 地址)设置队列。关于 handler 模块背景知识的介绍,可参考 Tengine 团队撰写的 Nginx 开发从入门到精通

结尾

本文主要讲解了 Nginx 按请求速率限速模块的用法和原理,其中 burst 和 nodelay 参数是容易引起误解的,虽然可通过 burst 允许缓存处理突发请求,结合 nodelay 能够降低突发请求的处理时间,但是长期来看他们并不会提高吞吐量的上限,长期吞吐量的上限是由 rate 决定的。需要特别注意的是,burst 设置了 nodelay 时,系统瞬间的 QPS 可能会超过 rate 设置的阈值。

本文只是对 Nginx 管中窥豹,更多关于 Nginx 介绍的文章,可参考 Tengine 团队撰写的 Nginx 开发从入门到精通。

下面关于 Nginx 的文章您也可能喜欢,不妨参考下:

Nginx 403 forbidden 的解决办法  http://www.linuxidc.com/Linux/2017-08/146084.htm

CentOS 7 下 Nginx 服务器的安装配置  http://www.linuxidc.com/Linux/2017-04/142986.htm

CentOS 上安装 Nginx 服务器实现虚拟主机和域名重定向  http://www.linuxidc.com/Linux/2017-04/142642.htm

CentOS 6.8 安装 LNMP 环境(Linux+Nginx+MySQL+PHP)http://www.linuxidc.com/Linux/2017-04/142880.htm

Linux 下安装 PHP 环境并配置 Nginx 支持 php-fpm 模块  http://www.linuxidc.com/Linux/2017-05/144333.htm

Nginx 服务的 SSL 认证和 htpasswd 认证  http://www.linuxidc.com/Linux/2017-04/142478.htm

Ubuntu 16.04 上启用加密安全的 Nginx Web 服务器  http://www.linuxidc.com/Linux/2017-07/145522.htm

Linux 中安装配置 Nginx 及参数详解  http://www.linuxidc.com/Linux/2017-05/143853.htm

Nginx 日志过滤 使用 ngx_log_if 不记录特定日志 http://www.linuxidc.com/Linux/2014-07/104686.htm

CentOS 7.2 下 Nginx+PHP+MySQL+Memcache 缓存服务器安装配置  http://www.linuxidc.com/Linux/2017-03/142168.htm

CentOS6.9 编译安装 Nginx1.4.7  http://www.linuxidc.com/Linux/2017-06/144473.htm

Nginx 的详细介绍:请点这里
Nginx 的下载地址:请点这里

本文永久更新链接地址:http://www.linuxidc.com/Linux/2017-12/149688.htm

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-21发表,共计9137字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19351
评论数
4
阅读量
7998725
文章搜索
热门文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛 NAS 中安装 Navidrome 音乐文件中文标签乱码问题解决、安装 FntermX 终端 问题背景 ...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
免费领取huggingface的2核16G云服务器,超简单教程

免费领取huggingface的2核16G云服务器,超简单教程

免费领取 huggingface 的 2 核 16G 云服务器,超简单教程 前言 HuggingFace.co...
150元打造低成本NAS小钢炮,捡一块3865U工控板

150元打造低成本NAS小钢炮,捡一块3865U工控板

150 元打造低成本 NAS 小钢炮,捡一块 3865U 工控板 一块二手的熊猫 B3 工控板 3865U,搭...
零成本上线!用 Hugging Face免费服务器+Docker 快速部署HertzBeat 监控平台

零成本上线!用 Hugging Face免费服务器+Docker 快速部署HertzBeat 监控平台

零成本上线!用 Hugging Face 免费服务器 +Docker 快速部署 HertzBeat 监控平台 ...
星哥带你玩飞牛NAS-12:开源笔记的进化之路,效率玩家的新选择

星哥带你玩飞牛NAS-12:开源笔记的进化之路,效率玩家的新选择

星哥带你玩飞牛 NAS-12:开源笔记的进化之路,效率玩家的新选择 前言 如何高效管理知识与笔记,已经成为技术...
让微信公众号成为 AI 智能体:从内容沉淀到智能问答的一次升级

让微信公众号成为 AI 智能体:从内容沉淀到智能问答的一次升级

让微信公众号成为 AI 智能体:从内容沉淀到智能问答的一次升级 大家好,我是星哥,之前写了一篇文章 自己手撸一...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
支付宝、淘宝、闲鱼又双叕崩了,Cloudflare也瘫了连监控都挂,根因藏在哪?

支付宝、淘宝、闲鱼又双叕崩了,Cloudflare也瘫了连监控都挂,根因藏在哪?

支付宝、淘宝、闲鱼又双叕崩了,Cloudflare 也瘫了连监控都挂,根因藏在哪? 最近两天的互联网堪称“故障...
星哥带你玩飞牛NAS-11:咪咕视频订阅部署全攻略

星哥带你玩飞牛NAS-11:咪咕视频订阅部署全攻略

星哥带你玩飞牛 NAS-11:咪咕视频订阅部署全攻略 前言 在家庭影音系统里,NAS 不仅是存储中心,更是内容...
免费无广告!这款跨平台AI RSS阅读器,拯救你的信息焦虑

免费无广告!这款跨平台AI RSS阅读器,拯救你的信息焦虑

  免费无广告!这款跨平台 AI RSS 阅读器,拯救你的信息焦虑 在算法推荐主导信息流的时代,我们...
星哥带你玩飞牛NAS-14:解锁公网自由!Lucky功能工具安装使用保姆级教程

星哥带你玩飞牛NAS-14:解锁公网自由!Lucky功能工具安装使用保姆级教程

星哥带你玩飞牛 NAS-14:解锁公网自由!Lucky 功能工具安装使用保姆级教程 作为 NAS 玩家,咱们最...
Prometheus:监控系统的部署与指标收集

Prometheus:监控系统的部署与指标收集

Prometheus:监控系统的部署与指标收集 在云原生体系中,Prometheus 已成为最主流的监控与报警...