跳转至

nchan: 可扩展、灵活的现代网络发布/订阅服务器

安装

您可以在任何基于 RHEL 的发行版中安装此模块,包括但不限于:

  • RedHat Enterprise Linux 7、8、9 和 10
  • CentOS 7、8、9
  • AlmaLinux 8、9
  • Rocky Linux 8、9
  • Amazon Linux 2 和 Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-nchan
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-nchan

通过在 /etc/nginx/nginx.conf 顶部添加以下内容来启用模块:

load_module modules/ngx_nchan_module.so;

本文档描述了 nginx-module-nchan v1.3.7,于 2024 年 9 月 20 日发布。


https://nchan.io

Nchan 是一个可扩展、灵活的现代网络发布/订阅服务器,作为 Nginx 网络服务器的模块构建。它可以配置为独立服务器,或作为您的应用程序与数百、数千或数百万个实时订阅者之间的适配器。它可以在内存中、磁盘上或通过 Redis 缓冲消息。所有连接都是异步处理的,并在任意数量的工作进程之间分配。它还可以扩展到多个 Nginx 服务器,使用 Redis

消息通过 HTTP POST 请求或 Websocket 发布 到频道,并通过 Websocket长轮询事件源 (SSE)、老式的 间隔轮询更多 方式 订阅

在 Web 浏览器中,您可以原生使用 Websocket 或 EventSource,或使用 NchanSubscriber.js 包装库。它支持长轮询、事件源和可恢复的 Websockets,并有一些其他附加的便利选项。它也可以在 NPM 上找到。

特性

它能扩展吗?

benchmarking internal subscriber response times

是的,它可以。像 Nginx 一样,Nchan 可以轻松处理您能施加的任何流量。我尝试对其进行基准测试,但我的基准测试工具比 Nchan 慢得多。我收集的数据是 Nchan 在发布消息后对每个订阅者的响应时间——这不包括 TCP 握手时间和内部 HTTP 请求解析时间。基本上,它测量 Nchan 的扩展性,假设所有其他组件已经针对可扩展性进行了调整。图表数据是 5 次运行 50 字节消息的平均值。

在普通服务器硬件上,经过良好调优的操作系统和网络堆栈,预计每秒可以处理超过 30 万个并发订阅者,CPU 负载极小。Nchan 还可以通过使用 Redis 存储引擎 扩展到多个 Nginx 实例,并且也可以通过使用 Redis Cluster 进一步扩展,超越单点故障。

入门

一旦您构建并安装了 Nchan,开始使用非常简单。向您的 nginx 配置添加两个位置:

#...
http {
  server {
    #...

    location = /sub {
      nchan_subscriber;
      nchan_channel_id $arg_id;
    }

    location = /pub {
      nchan_publisher;
      nchan_channel_id $arg_id;
    }
  }
}

您现在可以通过向 /pub?id=channel_id 发送数据 POST 消息到频道,并通过将 Websocket、EventSource 或 NchanSubscriber.js 指向 sub/?id=channel_id 进行订阅。就是这么简单。

但是 Nchan 非常灵活且高度可配置。因此,当然,它可以变得更加复杂...

概念概述

大多数发布/订阅解决方案的基本单元是消息 频道。Nchan 也不例外。发布者通过特定的 频道 ID 向频道发送消息,订阅该频道的订阅者接收这些消息。在频道的消息缓冲区中,某些消息可能会被缓冲一段时间,然后被删除。相当简单,对吧?

好吧... 问题在于 nginx 配置并不处理频道、发布者和订阅者。相反,它有几个部分用于匹配传入请求的 服务器位置 部分。Nchan 配置指令将服务器和位置映射到频道发布和订阅端点

#非常基本的 nchan 配置
worker_processes 5;

http {
  server {
    listen       80;

    location = /sub {
      nchan_subscriber;
      nchan_channel_id foobar;
    }

    location = /pub {
      nchan_publisher;
      nchan_channel_id foobar;
    }
  }
}

上述内容将对 URI /sub 的请求映射到频道 foobar订阅者端点,同样 /pub 映射到频道 foobar发布者端点

发布者端点

发布者端点是带有 nchan_publisher 指令的 Nginx 配置 位置

可以通过将消息内容发送到 发布者端点 位置的 HTTP POST 请求来发布消息到频道。您还可以通过与同一位置的 Websocket 连接发布消息。

  location /pub {
    #示例发布者位置
    nchan_publisher;
    nchan_channel_id foo;
    nchan_channel_group test;
    nchan_message_buffer_length 50;
    nchan_message_timeout 5m;
  }

发布消息

请求和 websocket 消息的响应将包含有关频道的信息,发布时间。以下是使用 curl 发布的示例:

>  curl --request POST --data "test message" http://127.0.0.1:80/pub

 queued messages: 5
 last requested: 18 sec. ago
 active subscribers: 0
 last message id: 1450755280:0

响应可以是纯文本(如上所示)、JSON 或 XML,具体取决于请求的 Accept 头:

> curl --request POST --data "test message" -H "Accept: text/json" http://127.0.0.2:80/pub

 {"messages": 5, "requested": 18, "subscribers": 0, "last_message_id": "1450755280:0" }

Websocket 发布者在发布时也会收到相同的响应,编码由握手期间存在的 Accept 头决定。

HTTP 请求的响应代码是 202 Accepted,如果在发布时没有订阅者存在,或者 201 Created,如果至少有 1 个订阅者存在。

在使用 HTTP POST 请求发布时,可以向消息添加元数据。Content-Type 头将与消息的内容类型相关联(并输出到长轮询、间隔轮询和多部分/混合订阅者)。X-EventSource-Event 头也可以用于将 EventSource event: 行值与消息关联。

其他发布者端点操作

HTTP GET 请求返回频道信息,而不发布消息。如果频道存在,则响应代码为 200,否则为 404

> curl --request POST --data "test message" http://127.0.0.2:80/pub
  ...

> curl -v --request GET -H "Accept: text/json" http://127.0.0.2:80/pub

 {"messages": 1, "requested": 7, "subscribers": 0, "last_message_id": "1450755421:0" }

HTTP DELETE 请求删除频道并结束所有订阅者连接。与 GET 请求一样,如果频道存在,则返回 200 状态响应,并返回频道信息,否则返回 404

频道设置的工作原理

频道的配置设置为其最后使用的发布位置的配置。 因此,如果您希望频道表现一致,并希望从多个位置发布到它,请确保这些位置具有相同的配置

您还可以使用配置不同的发布者位置动态更新频道的消息缓冲区设置。这可以用于擦除消息或根据需要扩展现有频道的消息缓冲区。

订阅者端点

订阅者端点是带有 nchan_subscriber 指令的 Nginx 配置 位置

Nchan 支持多种不同类型的订阅者接收消息:WebsocketEventSource (服务器发送事件)、长轮询间隔轮询HTTP 分块传输HTTP 多部分/混合

  location /sub {
    #示例订阅者位置
    nchan_subscriber;
    nchan_channel_id foo;
    nchan_channel_group test;
    nchan_subscriber_first_message oldest;
  }
  • 长轮询

    经过验证的服务器推送方法,所有浏览器都支持。 通过向频道订阅者端点发送 HTTP GET 请求启动。 长轮询订阅者通过 HTTP 客户端的内置缓存机制遍历频道的消息队列,即使用 "Last-Modified" 和 "Etag" 头。明确地,要接收给定长轮询订阅者响应的下一个消息,请发送请求,If-Modified-Since 头设置为上一个响应的 "Last-Modified" 头,并且 "If-None-Match" 同样设置为上一个响应的 "Etag" 头。 发送没有 "If-Modified-Since" 或 "If-None-Match" 头的请求将返回频道消息队列中的最旧消息,或者根据 nchan_subscriber_first_message 配置指令的值等待下一个发布的消息。 消息的相关内容类型(如果存在)将通过 Content-Type 头发送给此订阅者。

  • 间隔轮询

    工作方式与长轮询相同,只是如果请求的消息尚不可用,则立即响应 304 Not Modified。 Nchan 无法自动区分长轮询和间隔轮询订阅者请求,因此如果希望使用间隔轮询,则必须禁用订阅者位置的长轮询。

  • Websocket

    双向通信,适用于 Web 浏览器。属于 HTML5 规范。Nchan 支持最新的协议版本 13 (RFC 6455)。 通过向所需的订阅者端点位置发送 websocket 握手来启动。 如果服务器关闭 websocket 连接,则 close 帧将包含描述关闭连接原因的 HTTP 响应代码和状态行。可以使用 nchan_websocket_ping_interval 配置指令配置服务器发起的保持活动 ping。 消息以 text websocket 帧的形式传递给订阅者,除非消息的 content-type 为 "application/octet-stream"——则以 binary 帧传递。
    Websocket 订阅者可以使用自定义的 ws+meta.nchan 子协议接收消息元数据,使 websocket 连接可恢复。使用此子协议接收的消息格式为

      id: message_id
      content-type: message_content_type
      \n
      message_data
      

content-type: 行可以省略。
#### Websocket 发布者 通过 websocket 连接发布的消息可以使用 nchan_publisher_upstream_request 配置指令转发到上游应用程序。 在二进制帧中发布的消息自动赋予 content-type "application/octet-stream"。 #### Permessage-deflate Nchan 版本 1.1.8 及以上支持 permessage-deflate 协议扩展。消息在发布时被压缩一次,然后可以广播到任意数量的兼容 websocket 订阅者。通过在发布者位置设置 nchan_deflate_message_for_websocket on; 指令来启用消息压缩。
压缩后的数据与原始消息一起存储在内存中,或者如果足够大,则存储在磁盘上。这意味着使用 nchan_deflate_message_for_websocket 时需要更多的 共享内存
压缩参数(速度、内存使用、策略等)可以使用 nchan_permessage_deflate_compression_windownchan_permessage_deflate_compression_levelnchan_permessage_deflate_compression_strategynchan_permessage_deflate_compression_window 设置进行调整。
Nchan 还支持(已弃用的)perframe-deflate 扩展,Safari 仍然使用 x-webkit-perframe-deflate

  • 事件源

    也称为 服务器发送事件 或 SSE,它在 HTML5 规范 中早于 Websockets,是一种 非常简单的协议。 通过向频道订阅者端点发送带有 "Accept: text/event-stream" 头的 HTTP GET 请求来启动。 每条消息的 data: 段前面将加上消息的 id:。 要从最后接收到的消息恢复已关闭的 EventSource 连接,应该以 "Last-Event-ID" 头设置为最后一条消息的 id 开始连接。 不幸的是,浏览器 不支持设置 此头用于 EventSource 对象,因此默认情况下,最后消息 ID 是从 "Last-Event-Id" 头或 last_event_id URL 查询字符串参数设置的。 此行为可以通过 nchan_subscriber_last_message_id 配置进行配置。 消息的 content-type 不会被 EventSource 订阅者接收,因为该协议没有为此元数据提供规定。 消息的相关 event 类型(如果存在)将通过 event: 行发送给此订阅者。

  • HTTP multipart/mixed

    multipart/mixed MIME 类型是为电子邮件设计的,但为什么不将其用于 HTTP 呢?它易于解析,并为每条消息包含元数据。 通过包含 Accept: multipart/mixed 头启动。 响应头和响应体中未使用的“前言”部分会立即发送,边界字符串为每个订阅者随机生成。每条后续消息将作为多部分消息的一部分发送,并将包括消息时间和标签(Last-ModifiedEtag)以及可选的 Content-Type 头。 每条消息以下一个多部分消息的边界 没有尾随换行符 结束。虽然这符合多部分规范,但不寻常,因为多部分消息被定义为 以边界开始,而不是以边界结束。 消息的相关内容类型(如果存在)将通过 Content-Type 头发送给此订阅者。

  • HTTP 原始流

    一种简单的订阅方法,类似于 Nginx HTTP Push Stream Module流订阅者。消息附加到响应体中,用换行符分隔,或通过 nchan_subscriber_http_raw_stream_separator 配置。

  • HTTP 分块传输

    此订阅方法使用 chunked Transfer-Encoding 接收消息。 通过在 TE 中明确包含 chunked 启动: TE: chunked(或 TE: chunked;q=??,其中 qval > 0) 响应头立即发送,每条消息将作为单独的块发送。请注意,由于零长度块终止传输,因此 零长度消息将不会发送 给订阅者。 与其他订阅者类型不同,chunked 订阅者不能与 http/2 一起使用,因为它不允许分块编码。

发布/订阅端点

发布/订阅端点是带有 nchan_pubsub 指令的 Nginx 配置 位置

发布者和订阅者端点的组合,此位置将所有 HTTP GET 请求视为订阅者,所有 HTTP POST 请求视为发布者。无法通过 HTTP DELETE 请求删除频道。

一个简单的用例是回声服务器:

  location = /pubsub {
    nchan_pubsub;
    nchan_channel_id foo;
    nchan_channel_group test;
  }

一个更有趣的设置可能设置不同的发布者和订阅者频道 ID:

  location = /pubsub {
    nchan_pubsub;
    nchan_publisher_channel_id foo;
    nchan_subscriber_channel_id bar;
    nchan_channel_group test;
  }

在这里,订阅者将监听频道 foo 上的消息,发布者将消息发布到频道 bar。这在设置 Websocket 代理时非常有用,代理在 Web 客户端和您的应用程序之间。

频道 ID

到目前为止,示例使用了静态频道 ID,这不是很有用。在实践中,频道 ID 可以设置为任何 nginx 变量,例如查询字符串参数、头值或位置 URL 的一部分:

  location = /sub_by_ip {
    #频道 ID 是订阅者的 IP 地址
    nchan_subscriber;
    nchan_channel_id $remote_addr;
  }

  location /sub_by_querystring {
    #频道 ID 是查询字符串参数 chanid
    # GET /sub/sub_by_querystring?foo=bar&chanid=baz 将使频道 ID 设置为 'baz'
    nchan_subscriber;
    nchan_channel_id $arg_chanid;
  }

  location ~ /sub/(\w+)$ {
    #频道 ID 是 /sub/ 后的单词
    # GET /sub/foobar_baz 将使频道 ID 设置为 'foobar_baz'
    # 我希望你知道你的正则表达式...
    nchan_subscriber;
    nchan_channel_id $1; #位置匹配的第一个捕获
  }

我建议使用最后一个选项,即通过正则表达式从请求 URL 派生的频道 ID。这使事情变得简单且符合 RESTful 风格。

频道复用

通过频道复用,订阅者可以每个连接订阅多达 255 个频道。发布到所有指定频道的消息将按顺序传递给订阅者。启用复用有两种方法:

最多可以为 nchan_channel_idnchan_channel_subscriber_id 配置指令指定 7 个频道 ID:

  location ~ /multisub/(\w+)/(\w+)$ {
    nchan_subscriber;
    nchan_channel_id "$1" "$2" "common_channel";
    #GET /multisub/foo/bar 将订阅:
    # 频道 'foo'、'bar' 和 'common_channel',
    #并将接收来自上述所有频道的消息。
  }

对于超过 7 个频道,可以使用 nchan_channel_id_split_delimiternchan_channel_idnchan_channel_subscriber_id 拆分为多达 255 个单独的频道 ID:

  location ~ /multisub-split/(.*)$ {
    nchan_subscriber;
    nchan_channel_id "$1";
    nchan_channel_id_split_delimiter ",";
    #GET /multisub-split/foo,bar,baz,a 将订阅:
    # 频道 'foo'、'bar'、'baz' 和 'a'
    #并将接收来自上述所有频道的消息。
  }

也可以通过单个请求发布到多个频道,以及通过类似的配置删除多个频道:

  location ~ /multipub/(\w+)/(\w+)$ {
    nchan_publisher;
    nchan_channel_id "$1" "$2" "another_channel";
    #POST /multipub/foo/bar 将发布到:
    # 频道 'foo'、'bar' 和 'another_channel'
    #DELETE /multipub/foo/bar 将删除:
    # 频道 'foo'、'bar' 和 'another_channel'
  }

当频道被删除时,所有消息都会被删除,所有订阅者的连接都会关闭——包括通过复用位置订阅的连接。例如,假设一个订阅者通过单个复用连接订阅了频道 "foo" 和 "bar"。如果 "foo" 被删除,则连接会关闭,因此订阅者也会失去对 "bar" 的订阅。

有关使用良好 ID 和保持私有频道安全的信息,请参见 频道安全 部分。

频道组

频道可以与组关联,以避免频道 ID 冲突:

  location /test_pubsub {
    nchan_pubsub;
    nchan_channel_group "test";
    nchan_channel_id "foo";
  }

  location /pubsub {
    nchan_pubsub;
    nchan_channel_group "production";
    nchan_channel_id "foo";
    #相同的频道 ID,不同的频道组。因此,不同的频道。
  }

  location /flexgroup_pubsub {
    nchan_pubsub;
    nchan_channel_group $arg_group;
    nchan_channel_id "foo";
    #组也可以通过请求变量设置
  }

限制和统计

组可以用于跟踪汇总的频道使用情况,并设置频道、订阅者、存储消息、内存使用等的上限:

  #启用组统计
  nchan_channel_group_accounting on;

  location ~ /pubsub/(\w+)$ {
    nchan_pubsub;
    nchan_channel_group "limited";
    nchan_channel_id $1;
  }

  location ~ /prelimited_pubsub/(\w+)$ {
    nchan_pubsub;
    nchan_channel_group "limited";
    nchan_channel_id $1;
    nchan_group_max_subscribers 100;
    nchan_group_max_messages_memory 50M;
  }

  location /group {
    nchan_channel_group limited;
    nchan_group_location;
    nchan_group_max_channels $arg_max_channels;
    nchan_group_max_messages $arg_max_messages;
    nchan_group_max_messages_memory $arg_max_messages_mem;
    nchan_group_max_messages_disk $arg_max_messages_disk;
    nchan_group_max_subscribers $arg_max_subs;
  }

在这里,/group 是一个 nchan_group_location,用于访问和修改组数据。要获取组数据,请向 nchan_group_location 发送 GET 请求:

>  curl http://localhost/group

channels: 10
subscribers: 0
messages: 219
shared memory used by messages: 42362 bytes
disk space used by messages: 0 bytes
limits:
  max channels: 0
  max subscribers: 0
  max messages: 0
  max messages shared memory: 0
  max messages disk space: 0

默认情况下,数据以人类可读的纯文本格式返回,但也可以格式化为 JSON、XML 或 YAML:

>  curl -H "Accept: text/json" http://localhost/group

{
  "channels": 21,
  "subscribers": 40,
  "messages": 53,
  "messages_memory": 19941,
  "messages_disk": 0,
  "limits": {
    "channels": 0,
    "subscribers": 0,
    "messages": 0,
    "messages_memory": 0,
    "messages_disk": 0
  }
}

响应中的数据仅适用于单个 Nchan 实例,无论是否使用 Redis。限制为 0 意味着“无限制”。

限制可以按位置设置,如上面的 /prelimited_pubsub/... 位置,或通过向 nchan_group_location 发送 POST 请求:

>  curl -X POST "http://localhost/group?max_channels=15&max_subs=1000&max_messages_disk=0.5G"

channels: 0
subscribers: 0
messages: 0
shared memory used by messages: 0 bytes
disk space used by messages: 0 bytes
limits:
  max channels: 15
  max subscribers: 1000
  max messages: 0
  max messages shared memory: 0
  max messages disk space: 536870912

限制仅在本地应用,无论 Redis 是否启用。 如果发布者或订阅者请求超过组限制,Nchan 将以 403 Forbidden 响应。

钩子和回调

请求授权

此功能通过 nchan_authorize_request 配置,行为与 Nginx http_auth_request 模块 相同。

考虑以下配置:

  upstream my_app {
    server 127.0.0.1:8080;
  }
  location = /auth {
    proxy_pass http://my_app/pubsub_authorize;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Subscriber-Type $nchan_subscriber_type;
    proxy_set_header X-Publisher-Type $nchan_publisher_type;
    proxy_set_header X-Prev-Message-Id $nchan_prev_message_id;
    proxy_set_header X-Channel-Id $nchan_channel_id;
    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header X-Forwarded-For $remote_addr;
  }

  location ~ /pubsub/auth/(\w+)$ {
    nchan_channel_id $1;
    nchan_authorize_request /auth;
    nchan_pubsub;
    nchan_channel_group test;
  }

在这里,任何对位置 /pubsub/auth/<...> 的请求都需要您的应用程序(my_app)进行授权。Nginx 将生成一个 GET /pubsub_authorize 请求到应用程序,并设置额外的头部,通过 proxy_set_header 指令。请注意,Nchan 特定的变量可用于此授权请求。一旦您的应用程序收到此请求,它应该决定是否授权订阅者。这可以基于转发的会话 cookie、IP 地址或您选择的任何参数集。如果被授权,它应该以空的 200 OK 响应进行响应。 所有非 2xx 响应代码(如 403 Forbidden)都被解释为授权失败。在这种情况下,失败的响应将被代理到客户端。

请注意,Websocket 和 EventSource 客户端仅会在初始握手请求期间尝试授权,而长轮询和间隔轮询订阅者在每次请求下一个消息时都需要进行授权,这可能会使您的应用程序面临过多的授权请求。

订阅者存在

订阅者可以在订阅和取消订阅频道时通知应用程序,使用 nchan_subscribe_requestnchan_unsubscribe_request 设置。 这些应指向配置为将请求转发到上游代理(您的应用程序)的 Nginx 位置:

  location ~ /sub/(\w+)$ {
    nchan_channel_id $1;
    nchan_subscribe_request /upstream/sub;
    nchan_unsubscribe_request /upstream/unsub;
    nchan_subscriber;
    nchan_channel_group test;
  }

  location = /upstream/unsub {
    proxy_pass http://127.0.0.1:9292/unsub;
    proxy_ignore_client_abort on;  #!!!重要!!!!
    proxy_set_header X-Subscriber-Type $nchan_subscriber_type;
    proxy_set_header X-Channel-Id $nchan_channel_id;
    proxy_set_header X-Original-URI $request_uri;
  }
  location = /upstream/sub {
    proxy_pass http://127.0.0.1:9292/sub;
    proxy_set_header X-Subscriber-Type $nchan_subscriber_type;
    proxy_set_header X-Message-Id $nchan_message_id;
    proxy_set_header X-Channel-Id $nchan_channel_id;
    proxy_set_header X-Original-URI $request_uri;
  }

为了使 nchan_unsubscribe_request 正常工作,它指向的位置必须具有 proxy_ignore_client_abort on;。否则,突然中止的订阅者可能不会触发取消订阅请求。

请注意,订阅/取消订阅钩子对长轮询和间隔轮询客户端是 禁用的,因为它们会在每次接收消息时触发这些钩子。

消息转发

可以使用 nchan_publisher_upstream_request 设置在发布之前将消息转发到上游应用程序:

  location ~ /pub/(\w+)$ {
    #发布者端点
    nchan_channel_id $1;
    nchan_pubsub;
    nchan_publisher_upstream_request /upstream_pub;
  }

  location = /upstream_pub {
    proxy_pass http://127.0.0.1:9292/pub;
    proxy_set_header X-Publisher-Type $nchan_publisher_type;
    proxy_set_header X-Prev-Message-Id $nchan_prev_message_id;
    proxy_set_header X-Channel-Id $nchan_channel_id;
    proxy_set_header X-Original-URI $request_uri;
  }
使用此配置,传入的消息首先会 POSThttp://127.0.0.1:9292/pub。 上游响应代码决定发布将如何进行: - 304 Not Modified 将按原样发布消息。 - 204 No Content 将丢弃消息。 - 200 OK 将用于修改消息。将发布来自上游响应主体的消息,而不是原始传入消息。

nchan_publisher_upstream_request 有两个主要用例:将来自 Websocket 发布者的传入数据转发到应用程序,以及在发布到频道之前修改传入消息。

存储

Nchan 可以将消息存储在内存中、磁盘上或通过 Redis。内存存储速度更快,而 Redis 由于额外的开销在发布消息时速度较慢,但为具有远多于发布者的订阅者的广播用例提供近乎无限的可扩展性。

内存存储

此默认存储方法使用共享内存的一部分来存储消息和频道数据。由 Nginx 的缓存层确定的大型消息存储在磁盘上。内存段的大小通过 nchan_shared_memory_size 配置。存储在这里的数据不是持久的,如果 Nginx 被重启或重新加载,则会丢失。

Redis

Redis 可用于为您的 Nchan 设置添加 数据持久性水平可扩展性故障转移高可用性

连接到 Redis 服务器

要连接到单个 Redis 主服务器,请使用带有 nchan_redis_servernchan_redis_pass 设置的 upstream

http {
  upstream my_redis_server {
    nchan_redis_server 127.0.0.1;
  }
  server {
    listen 80;

    location ~ /redis_sub/(\w+)$ {
      nchan_subscriber;
      nchan_channel_id $1;
      nchan_redis_pass my_redis_server;
    }
    location ~ /redis_pub/(\w+)$ {
      nchan_redis_pass my_redis_server;
      nchan_publisher;
      nchan_channel_id $1;
    }
  }
}

所有使用上述配置连接到同一 Redis 服务器的服务器共享频道和消息数据。

不使用 Redis 的频道可以与 Redis 支持的频道并排配置,前提是端点永远不会重叠。(如上所述,通过设置单独的 nchan_channel_group 可以确保这一点。)不同的位置也可以连接到不同的 Redis 服务器。

Nchan 可以与单个 Redis 主服务器一起工作。它还可以自动发现并使用 Redis 从服务器来平衡 PUBSUB 流量。

Redis 集群

Nchan 还支持使用 Redis 集群,通过在集群节点之间分片频道来增加可扩展性。Redis 集群还提供 自动故障转移高可用性,并消除了单个共享 Redis 服务器的单点故障。其配置和使用如下:

http {
  upstream redis_cluster {
    nchan_redis_server redis://127.0.0.1:7000;
    nchan_redis_server redis://127.0.0.1:7001;
    nchan_redis_server redis://127.0.0.1:7002;
    # 您不需要指定所有节点,它们将被自动发现
    # 但是,建议您确实指定至少几个主节点。
  }
  server {
    listen 80;

    location ~ /sub/(\w+)$ {
      nchan_subscriber;
      nchan_channel_id $1;
      nchan_redis_pass redis_cluster;
    }
    location ~ /pub/(\w+)$ {
      nchan_publisher;
      nchan_channel_id $1;
      nchan_redis_pass redis_cluster;
    }
  }
}
高可用性

Redis 集群连接旨在具有弹性,并尝试从错误中恢复。中断的连接将其命令排队,直到重新连接,Nchan 将发布在断开连接期间成功接收的任何消息。Nchan 还适应集群的修改。它将根据需要添加和删除新节点。

所有共享 Redis 服务器或集群的 Nchan 服务器应同步其时间(通过 ntpd 或您喜欢的 ntp 守护进程)。未能做到这一点可能会导致丢失或重复消息。

故障恢复

从版本 1.3.0 开始,Nchan 将尝试在不与整个集群断开连接的情况下,从集群节点故障、键槽错误和集群纪元变化中恢复。它将尝试这样做,直到超出 nchan_redis_cluster_max_failing_time。此外,恢复尝试延迟具有可配置的 抖动指数退避最大 值。

安全使用 Redis

可以通过在 upstream 块中使用 nchan_redis_ssl 配置设置,或通过使用 rediss:// 方案连接到 Redis 服务器。

可以通过 nchan_redis_usernamenchan_redis_password 配置设置 AUTH 命令的密码和可选用户名,放置在 upstream 块中,或通过使用 redis://<username>:<password>@hostname 服务器 URL 方案。

请注意,自动发现的 Redis 节点继承其父级的 SSL、用户名和密码设置。

调整和优化

从版本 1.2.0 开始,Nchan 使用 Redis 从服务器来负载均衡 PUBSUB 流量。默认情况下,频道的 PUBSUB 订阅有平等的机会转到任何主节点或从节点。可以使用 nchan_redis_subscribe_weights 设置来微调此负载均衡。

从 1.2.0 开始,nchan_redis_optimize_target 可用于优先优化 Redis 从服务器的 CPU 或带宽。对于重负载发布,权衡大约是每个从节点 35% 的复制带宽与 30% 的 CPU 负载。

性能统计

在版本 1.3.5 中添加了 Redis 命令统计信息。这些提供了不同 Redis 命令运行的总次数,以及它们所花费的总时间。统计信息仅适用于给定的 Nchan 服务器,而不是连接到 Redis 上游的所有服务器。它们按每个上游分组,并按节点汇总。

http {
  upstream my_redis_cluster {
    nchan_redis_server 127.0.0.1;
  }

  server {
    #[...]

    location ~ /nchan_redis_cluster_stats$ {
      nchan_redis_upstream_stats my_redis_cluster;
    }
  }

要获取统计信息,请向统计位置发送 GET 请求。

  curl http://localhost/nchan_redis_cluster_stats

响应为以下形式的 JSON:

{
  "upstream": "redis_cluster",
  "nodes": [
    {
      "address"        : "127.0.0.1:7000",
      "id"             : "f13d71b1d14d8bf92b72cebee61421294e95dc72",
      "command_totals" : {
        "connect"    : {
          "msec"     : 357,
          "times"    : 5
        },
        "pubsub_subscribe": {
          "msec"     : 749,
          "times"    : 37
        },
        "pubsub_unsubsribe": {
          "msec"     : 332,
          "times"    : 37
        }
        /*[...]*/
      }
    },
    {
      "address"        : "127.0.0.1:7001",
      "id"             : "b768ecb4152912bed6dc927e8f70284191a79ed7",
      "command_totals" : {
        "connect"    : {
          "msec"     : 4281,
          "times"    : 5
        },
        "pubsub_subscribe": {
          "msec"     : 309,
          "times"    : 33
        },
        "pubsub_unsubsribe": {
          "msec"     : 307,
          "times"    : 30
        },
        /*[...]*/
      },
    }
    /*[...]*/
  ]
}

为简洁起见,文档中省略了整个 command_totals 哈希。

自省

有几种方法可以查看 Nchan 内部发生的事情。这些对于调试应用程序集成和测量性能非常有用。

频道事件

频道事件是 Nchan 在频道发生某些事件时自动发布的消息。这对于调试频道的使用非常有用。然而,它们会带来显著的性能开销,应在开发期间使用,而不是在生产中使用。

频道事件发布到与正常频道关联的特殊“元”频道。以下是如何配置它们:

location ~ /pubsub/(.+)$ {
  nchan_pubsub;
  nchan_channel_id $1;
  nchan_channel_events_channel_id $1; #为此位置启用频道事件
}

location ~ /channel_events/(.+) {
  #频道事件订阅者位置
  nchan_subscriber;
  nchan_channel_group meta; #"meta" 是一个特殊的频道组
  nchan_channel_id $1;
}

请注意,/channel_events/... 位置具有 特殊nchan_channel_groupmeta。此组保留用于访问“频道事件频道”或“元频道”。

现在,假设我订阅 /channel_events/foo,我将其称为频道事件订阅者。

让我们看看当我向 /pubsub/foo 发布消息时,这个频道事件订阅者会收到什么。

订阅 /pubsub/foo 会产生频道事件

subscriber_enqueue foo

/pubsub/foo 发布消息:

channel_publish foo

/pubsub/foo 取消订阅:

subscriber_dequeue foo

删除 /pubsub/foo(使用 HTTP DELETE /pubsub/foo):

channel_delete foo

事件字符串本身可以通过 nchan_channel_event_string 配置。默认情况下,它设置为 $nchan_channel_event $nchan_channel_id。 此字符串可以使用任何 Nginx 和 Nchan 变量

nchan_stub_status 统计信息

类似于 Nginx 的 stub_statusnchan_stub_status 用于获取性能指标。

  location /nchan_stub_status {
    nchan_stub_status;
  }

向此位置发送 GET 请求会产生以下响应:

total published messages: 1906
stored messages: 1249
shared memory used: 1824K
channels: 80
subscribers: 90
redis pending commands: 0
redis connected servers: 0
redis unhealthy upstreams: 0
total redis commands sent: 0
total interprocess alerts received: 1059634
interprocess alerts in transit: 0
interprocess queued alerts: 0
total interprocess send delay: 0
total interprocess receive delay: 0
nchan version: 1.1.5

以下是每一行的含义,以及如何解释它: - total published messages: 通过此 Nchan 服务器发布到所有频道的消息数量。 - stored messages: 当前缓存在内存中的消息数量。 - shared memory used: 用于缓冲消息、存储频道信息和其他目的的共享内存总量。此值应远低于 nchan_shared_memory_size。 - channels: 此 Nchan 服务器上存在的频道数量。 - subscribers: 此 Nchan 服务器上所有频道的订阅者数量。 - redis pending commands: 发送到 Redis 的命令数量,等待回复。在高负载期间可能会激增,尤其是当 Redis 服务器超载时。应趋向于 0。 - redis connected servers: Nchan 当前连接的 Redis 服务器数量。 - redis unhealthy upstreams: 当前无法用于发布和订阅的 Redis 上游(单个服务器或集群模式)数量。 - total redis commands sent: 此 Nchan 实例发送到 Redis 的命令总数。 - total interprocess alerts received: 在 Nginx 工作进程之间传输的 Nchan 的进程间通信数据包数量。在高负载时每秒可能增长 100-10000。 - interprocess alerts in transit: 在 Nginx 工作进程之间传输的进程间通信数据包数量。在高负载时可能非零,但应随着时间的推移趋向于 0。 - interprocess queued alerts: 等待发送的进程间通信数据包数量。在高负载时可能非零,但应随着时间的推移趋向于 0。 - total interprocess send delay: 由于延迟而排队的进程间通信数据包花费的总时间。在高负载时可能增加。 - total interprocess receive delay: 由于延迟而在传输中花费的进程间通信数据包的总时间。在高负载时可能增加。 - nchan_version: 当前 Nchan 版本。自 1.1.5 以来可用。

此外,当至少有一个 nchan_stub_status 位置时,这些数据也可以通过 变量 获得。

保护频道

保护发布者端点

考虑一个应用程序的用例,其中经过身份验证的用户每个使用一个私有的、专用的频道进行实时更新。配置可能如下所示:

http {
  server {
    #仅在 localhost 上可用
    listen  127.0.0.1:8080;
    location ~ /pub/(\w+)$ {
      nchan_publisher;
      nchan_channel_group my_app_group;
      nchan_channel_id $1;
    }
  }

  server {
    #向全世界开放
    listen 80;

    location ~ /sub/(\w+)$ {
      nchan_subscriber;
      nchan_channel_group my_app_group;
      nchan_channel_id $1;
    }
  }
}

在这里,订阅者端点可以在公共端口 80 上访问,而发布者端点仅在 localhost 上可用,因此只能由驻留在该机器上的应用程序访问。限制访问发布者端点的另一种方法是使用允许/拒绝设置:

  server {
    #向全世界开放
    listen 80;
    location ~ /pub/(\w+)$ {
      allow 127.0.0.1;
      deny all;
      nchan_publisher;
      nchan_channel_group my_app_group;
      nchan_channel_id $1;
    }

在这里,仅允许本地 IP 127.0.0.1 使用发布者位置,即使它在非 localhost 服务器块中定义。

保持频道私有

一个旨在私有的频道 ID 应像会话 ID 令牌一样小心对待。考虑上述每用户一个频道的用例,我们如何确保只有经过身份验证的用户,而不是其他人,能够访问他的频道?

首先,如果您打算保护频道内容,您必须使用 TLS/SSL:

http {
  server {
    #仅在 localhost 上可用
    listen  127.0.0.1:8080;
    #...发布者端点配置
  }
  server {
    #向全世界开放
    listen 443 ssl;
    #SSL 配置在这里
    location ~ /sub/(\w+)$ {
      nchan_subscriber;
      nchan_channel_group my_app_group;
      nchan_channel_id $1;
    }
  }
}

现在,您在订阅者客户端和服务器之间有了安全连接,您不需要担心频道 ID 或消息被被动拦截。这是安全消息传递的最低要求,但还不够充分。

您还必须确保至少执行以下操作之一: - 生成良好的、高熵的频道 ID。 - 使用 nchan_authorize_request 配置指令授权所有订阅者。 - 使用 "X-Accel-Redirect" 机制授权订阅者并隐藏频道 ID

良好的 ID

一个可以被猜测的 ID 是一个可以被劫持的 ID。如果您没有对订阅者进行身份验证(如下所述),频道 ID 应该是无法猜测的。使用至少 128 位的熵生成随机令牌,将其与经过身份验证的用户关联,并仅与用户的客户端共享。不要重用令牌,就像您不会重用会话 ID 一样。

X-Accel-Redirect

此功能使用 Nginx 上游代理的 X-Accel 特性 执行对订阅者端点的内部请求。 它允许订阅者客户端通过您的应用程序进行身份验证,然后由 nginx 内部重定向到您的应用程序选择的位置(例如发布者或订阅者端点)。这使得可以拥有安全认证的客户端,而不需要知道他们所订阅的频道 ID。

考虑以下配置:

upstream upstream_app {
  server 127.0.0.1:8080;
}

server {
  listen 80;

  location = /sub_upstream {
    proxy_pass http://upstream_app/subscriber_x_accel_redirect;
    proxy_set_header X-Forwarded-For $remote_addr;
  }

  location ~ /sub/internal/(\w+)$ {
    internal; #此位置仅可通过内部 nginx 重定向访问
    nchan_subscriber;
    nchan_channel_id $1;
    nchan_channel_group test;
  }
}

如注释所示,/sub/internal/ 从外部无法访问:

> curl  -v  http://127.0.0.1/sub/internal/foo

  < HTTP/1.1 404 Not Found
  < Server: nginx/1.9.5
  <
  <html>
  <head><title>404 Not Found</title></head>
  <body bgcolor="white">
  <center><h1>404 Not Found</h1></center>
  <hr><center>nginx/1.9.5</center>
  </body>
  </html>

但是,如果请求 /sub_upstream,它将转发到您的应用程序(my_app)的 8080 端口,URL 为 /subscriber_x_accel_redirect。 请注意,您可以在此处设置任何转发头,就像任何 proxy_pass Nginx 位置一样, 但与 nchan_authorize_request 的情况不同,Nchan 特定的变量不可用。

现在,您的应用程序必须设置为处理对 /subscriber_x_accel_redirect 的请求。您应该确保客户端经过适当身份验证(可能使用会话 cookie),并生成相关的频道 ID。如果身份验证失败,则以正常的 403 Forbidden 响应进行响应。

如果您的应用程序成功验证了订阅者请求,您现在需要指示 Nginx 发出对 /sub/internal/my_channel_id 的内部重定向。 这通过响应一个空的 200 OK 响应并包含两个头部来完成: - X-Accel-Redirect: /sub/internal/my_channel_id - X-Accel-Buffering: no

在存在这些头的情况下,Nginx 将不会将您的应用程序的响应转发到客户端,而是将 内部 重定向到 /sub/internal/my_channel_id。 这将表现得好像客户端直接请求了订阅者端点位置。

因此,使用 X-Accel-Redirect,可以同时对所有订阅者进行身份验证 完全隐藏频道 ID。

此方法对于 EventSource 和 Websocket 订阅者尤其有用。长轮询订阅者需要在每条新消息时重新进行身份验证,这可能会使您的应用程序面临过多的身份验证请求。

撤销频道授权

在某些情况下,您可能希望撤销特定订阅者对给定频道的授权(例如,如果用户的权限发生变化)。如果频道是唯一的订阅者,则只需删除频道即可简单实现。对于共享频道,可以通过通过复用连接将每个订阅者同时订阅共享频道和特定于订阅者的频道来实现相同的效果。删除特定于订阅者的频道将终止订阅者的连接,从而也终止他们对共享频道的订阅。考虑以下配置:

location ~ /sub/(\w+) {
  nchan_subscriber;
  nchan_channel_id shared_$1 user_$arg_userid;
  nchan_authorize_request /authorize;
}

location /pub/user {
  nchan_publisher;
  nchan_channel_id user_$arg_userid;
}

/sub/foo?userid=1234 的请求将通过复用连接订阅到频道 "shared_foo" 和 "user_1234"。如果稍后向 /pub/user?userid=1234 发送 DELETE 请求,则该订阅者将被断开连接,因此也将取消对 "user_1234" 和 "shared_foo" 的订阅。

变量

Nchan 在配置文件中提供了几个可用的变量:

  • $nchan_channel_id 从发布者或订阅者位置请求中提取的频道 ID。对于复用位置,这是列表中的第一个频道 ID。

  • $nchan_channel_id1$nchan_channel_id2$nchan_channel_id3$nchan_channel_id4 如上所述,但用于复用频道中的第 n 个频道 ID。

  • $nchan_subscriber_type 对于订阅者位置,此变量设置为订阅者类型(websocket、longpoll 等)。

  • $nchan_channel_subscriber_last_seen 对于发布者位置,此变量设置为最后连接的订阅者的时间戳。

  • $nchan_channel_subscriber_count 对于发布者位置,此变量设置为发布频道中的订阅者数量。

  • $nchan_channel_message_count 对于发布者位置,此变量设置为发布频道中缓冲的消息数量。

  • $nchan_publisher_type 对于发布者位置,此变量设置为订阅者类型(http 或 websocket)。

  • $nchan_prev_message_id$nchan_message_id 发布者请求或订阅者响应的当前和先前(如果适用)消息 ID。

  • $nchan_channel_event 对于频道事件,这是事件名称。在配置 nchan_channel_event_string 时非常有用。

  • $nchan_version 当前 Nchan 版本。自 1.1.5 以来可用。

此外,nchan_stub_status 数据也以变量的形式暴露。这些仅在至少一个位置启用 nchan_stub_status 时可用:

  • $nchan_stub_status_total_published_messages
  • $nchan_stub_status_stored_messages
  • $nchan_stub_status_shared_memory_used
  • $nchan_stub_status_channels
  • $nchan_stub_status_subscribers
  • $nchan_stub_status_redis_pending_commands
  • $nchan_stub_status_redis_connected_servers
  • $nchan_stub_status_redis_unhealthy_upstreams
  • $nchan_stub_status_total_ipc_alerts_received
  • $nchan_stub_status_ipc_queued_alerts
  • $nchan_stub_status_total_ipc_send_delay
  • $nchan_stub_status_total_ipc_receive_delay

配置指令

  • nchan_channel_id 参数:1 - 7 默认值:(none) 上下文:服务器、位置、if

    发布者或订阅者位置的频道 ID。最多可以有 4 个值,以订阅最多 4 个频道。 更多细节

  • nchan_channel_id_split_delimiter 参数:1 默认值:(none) 上下文:服务器、位置、if

    将频道 ID 拆分为多个 ID,以便使用提供的分隔符字符串进行复用。 更多细节

  • nchan_deflate_message_for_websocket [ on | off ] 参数:1 默认值:off 上下文:服务器、位置

    存储压缩(压缩)消息的副本与原始消息一起发送给支持 permessage-deflate 协议扩展的 websocket 客户端

  • nchan_eventsource_event 参数:1 默认值:(none) 上下文:服务器、位置、if

    将 EventSource event: 行设置为此值。在发布者位置使用时,覆盖发布消息的 X-EventSource-Event 头,并将消息与给定值关联。在订阅者位置使用时,覆盖所有消息的相关 event: 字符串与给定值。

  • nchan_eventsource_ping_comment 参数:1 默认值:(empty) 上下文:服务器、位置、if

    设置服务器到客户端的定期 ping 的 EventSource 注释 : ... 行。禁止使用换行符。如果为空,则不会随 ping 发送注释。

  • nchan_eventsource_ping_data 参数:1 默认值:(empty) 上下文:服务器、位置、if

    设置服务器到客户端的定期 ping 的 EventSource data: 行。禁止使用换行符。如果为空,则不会随 ping 发送数据。

  • nchan_eventsource_ping_event 参数:1 默认值:ping 上下文:服务器、位置、if

    设置服务器到客户端的定期 ping 的 EventSource event: 行。禁止使用换行符。如果为空,则不会随 ping 发送事件类型。

  • nchan_eventsource_ping_interval <number> (seconds) 参数:1 默认值:0 (none) 上下文:服务器、位置、if

    向 EventSource 订阅者发送 ping 消息的间隔。默认情况下禁用。

  • nchan_longpoll_multipart_response [ off | on | raw ] 参数:1 默认值:off 上下文:服务器、位置、if

    设置为 'on' 时,启用在单个长轮询响应中发送多个消息,使用 multipart/mixed 内容类型方案分隔。如果在响应长轮询请求时仅有一条可用消息,则会按原样发送。这对于高延迟的长轮询连接非常有用,作为减少与服务器的往返次数的方法。当设置为 'raw' 时,使用 http-raw-stream 消息分隔符发送多个消息。

  • nchan_permessage_deflate_compression_level [ 0-9 ] 参数:1 默认值:6 上下文:http

    websocket 的 permessage-deflate 扩展中使用的 deflate 算法的压缩级别。0:无压缩,1:最快,最差,9:最慢,最好

  • nchan_permessage_deflate_compression_memlevel [ 1-9 ] 参数:1 默认值:8 上下文:http

    websocket 的 permessage-deflate 扩展中使用的 deflate 算法的内存级别。应为内部压缩状态分配多少内存。1 - 最小内存,慢且降低压缩比;9 - 最大内存以获得最佳速度

  • nchan_permessage_deflate_compression_strategy [ default | filtered | huffman-only | rle | fixed ] 参数:1 默认值:default 上下文:http

    websocket 的 permessage-deflate 扩展中使用的 deflate 算法的压缩策略。对于正常数据使用 'default',有关详细信息,请参见 zlib 的压缩策略部分

  • nchan_permessage_deflate_compression_window [ 9-15 ] 参数:1 默认值:10 上下文:http

    websocket 的 permessage-deflate 扩展中使用的 deflate 算法的压缩窗口。窗口大小的以 2 为底的对数(历史缓冲区的大小)。窗口越大,压缩效果越好,但压缩器使用的内存越多。

  • nchan_publisher [ http | websocket ] 参数:0 - 2 默认值:http websocket 上下文:服务器、位置、if 旧名称:push_publisher

    将服务器或位置定义为发布者端点。对发布者位置的请求被视为要发送到订阅者的消息。有关详细描述,请参见协议文档。 更多细节

  • nchan_publisher_channel_id 参数:1 - 7 默认值:(none) 上下文:服务器、位置、if

    发布者位置的频道 ID。

  • nchan_publisher_upstream_request <url> 参数:1 上下文:服务器、位置、if

    将 POST 请求发送到内部位置(可能代理到上游服务器),请求体中包含发布的消息。对于将 websocket 发布者与 HTTP 应用程序桥接,或在发布到频道之前通过上游应用程序转换消息非常有用。 上游响应代码决定发布将如何进行。200 OK 将发布来自上游响应主体的消息。304 Not Modified 将按原样发布消息。204 No Content 将导致消息不被发布。 更多细节

  • nchan_pubsub [ http | websocket | eventsource | longpoll | intervalpoll | chunked | multipart-mixed | http-raw-stream ] 参数:0 - 6 默认值:http websocket eventsource longpoll chunked multipart-mixed 上下文:服务器、位置、if

    将服务器或位置定义为发布/订阅端点。对于长轮询,GET 订阅,POST 发布。对于 Websockets,在连接上发布数据不会产生频道元数据响应。没有额外配置时,此位置变为回声服务器。 更多细节

  • nchan_subscribe_request <url> 参数:1 上下文:服务器、位置、if

    在订阅后向内部位置(可能代理到上游服务器)发送 GET 请求。对于长轮询和间隔轮询订阅者禁用。 更多细节

  • nchan_subscriber [ websocket | eventsource | longpoll | intervalpoll | chunked | multipart-mixed | http-raw-stream ] 参数:0 - 5 默认值:websocket eventsource longpoll chunked multipart-mixed 上下文:服务器、位置、if 旧名称:push_subscriber

    将服务器或位置定义为频道订阅者端点。此位置表示订阅者与频道消息队列的接口。队列会自动遍历,从 nchan_subscriber_first_message 设置定义的位置开始。 值是允许的订阅者类型列表。 更多细节

  • nchan_subscriber_channel_id 参数:1 - 7 默认值:(none) 上下文:服务器、位置、if

    订阅者位置的频道 ID。最多可以有 4 个值,以订阅最多 4 个频道。

  • nchan_subscriber_compound_etag_message_id 参数:1 默认值:off 上下文:服务器、位置、if

    覆盖使用 Last-ModifiedEtag 头作为消息 ID 的默认行为。 启用此选项将整个消息 ID 打包到 Etag 头中,并丢弃 Last-ModifiedIf-Modified-Since 头。 更多细节

  • nchan_subscriber_first_message [ oldest | newest | <number> ] 参数:1 默认值:oldest 上下文:服务器、位置、if

    控制新订阅者接收到的第一条消息。'oldest' 从频道消息队列中最旧的可用消息开始,'newest' 等待直到消息到达。如果指定数字 n,则从最旧的第 n 条消息开始。(-n 从现在开始的第 n 条消息)。0 相当于 'newest'。

  • nchan_subscriber_http_raw_stream_separator <string> 参数:1 默认值:\n 上下文:服务器、位置、if

    http-raw-stream 订阅者的消息分隔符字符串。如果未显式设置为空字符串,则自动以换行符结束。

  • nchan_subscriber_info 参数:0 上下文:位置

    用于调试给定频道上订阅者状态的订阅者位置。指定 nchan_channel_id 的频道的订阅者评估 nchan_subscriber_info_string 并将其发送回请求者。这对于查看订阅者在 Nchan 集群中的位置以及调试订阅者连接问题非常有用。

  • nchan_subscriber_info_string 参数:1 默认值:$nchan_subscriber_type $remote_addr:$remote_port $http_user_agent $server_name $request_uri $pid 上下文:服务器、位置

    此字符串由每个给定频道的订阅者评估,并发送给 nchan_subscriber_info 位置的请求者

  • nchan_subscriber_last_message_id 参数:1 - 5 默认值:$http_last_event_id $arg_last_event_id 上下文:服务器、位置、if

    如果缺少 If-Modified-SinceIf-None-Match 头,则将消息 ID 设置为这两个值中第一个非空的值。主要用作解决无法设置 Web 浏览器的 EventSource 对象的第一个 Last-Message-Id 的方法。

  • nchan_subscriber_message_id_custom_etag_header 参数:1 默认值:(none) 上下文:服务器、位置、if

    在订阅者响应中使用自定义头而不是 Etag 头作为消息 ID。此设置是一个 hack,当在像 Cloudflare 这样的缓存代理后面时非常有用,因为在某些条件下(例如使用 gzip 编码)会吞噬 Etag 头。

  • nchan_subscriber_timeout <number> (seconds) 参数:1 默认值:0 (none) 上下文:http、服务器、位置、if 旧名称:push