我一整天都在尝试仅使用 nginx(没有 proxy_pass)使 SSE 工作,后来为了尝试而使用 proxy_pass,但是当使用 JavaScript 的 EventSource 连接到端点时,它在收到第一个事件,然后再次重新连接。这违背了 SSE 的初衷,因为它感觉更像是民意调查。我的目标是当我们将新的 nginx 静态服务器部署到生产环境时,让 SSE 重新连接,以便我可以发送带有当前提交哈希的事件消息(在 docker 构建或 docker 运行期间添加到 nginx 配置中),这里的代码几乎是工作,正如你将看到的,我通过 proxy_pass 尝试了两种方法,如果没有它,两个端点都会遇到相同的“错误”:
# load_module /usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so;
# user www www; ## Default: nobody
worker_processes 1;
# error_log logs/error.log;
# pid logs/nginx.pid;
# worker_rlimit_nofile 8192;
events {
worker_connections 1024;
}
http {
# send_timeout 1800;
# keepalive_timeout 10m;
# client_max_body_size 128M;
# proxy_max_temp_file_size 0;
# proxy_buffering off;
# server_names_hash_bucket_size 256;
server {
listen 8080;
server_name localhost;
# index index.html;
# root /usr/share/nginx/html;
location = /sse {
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://proxy;
proxy_buffering off;
proxy_request_buffering off;
proxy_cache off;
# proxy_connect_timeout 159s;
# proxy_send_timeout 600;
# proxy_read_timeout 600;
# proxy_buffer_size 64k;
# proxy_buffers 16 32k;
# proxy_busy_buffers_size 64k;
# proxy_temp_file_write_size 64k;
fastcgi_keep_conn on;
# add_header Connection "keep-alive";
# add_header X-Accel-Buffering "no";
# chunked_transfer_encoding on;
# more_clear_headers "Content-Length";
# default_type text/event-stream;
}
}
server {
listen 8081;
server_name localhost;
location = /sse {
# add_header Connection "keep-alive";
# expires off;
# default_type text/event-stream;
default_type "";
add_header Content-Type "text/event-stream; charset=utf-8";
add_header Cache-Control "no-store";
add_header Access-Control-Allow-Origin "*";
chunked_transfer_encoding off;
push_stream_eventsource_support on;
add_header X-Accel-Buffering "no";
# chunked_transfer_encoding off;
# keepalive_timeout 100s;
return 200 "event: ping\ndata: {\"time\": \"foo\"}\n\n";
}
}
upstream proxy {
server 127.0.0.1:8081;
}
}
document.addEventListener('DOMContentLoaded', () => {
const source = new EventSource('http://localhost:8080/sse');
source.addEventListener(
'open',
function (e) {
console.log('Connection was opened.', e.readyState);
},
false
);
source.addEventListener(
'error',
function (e) {
console.log(e.readyState, e);
if (e.readyState == EventSource.CLOSED) {
// Connection was closed.
}
},
false
);
source.addEventListener('ping', function (event) {
console.log('ping', event);
});
});
我看到的是“连接已打开”。随后成功发送“ping”消息,3 秒后同样如此。连接正在从 nginx 端“关闭”。我已经在一个小型的express.js 服务器上尝试过同样的操作,但这并没有发生,它会按预期工作。发生什么事了?
Nginx 将在 3 秒后关闭连接,同时未从客户端接收任何数据。这是因为 SSE 连接预计是单向的。它的服务器向客户端发送数据。
要修复,您需要每隔几秒创建一条从客户端到服务器的心跳消息。这将使连接保持活动状态并防止 Nginx 关闭它。
const source = new EventSource('http://example:8080/sse');
source.addEventListener(
'open',
function (event) {
console.log('Connection was opened.', event.readyState);
// This is heartbeat message every 3 sec.
setInterval(() => {
source.send('ping');
}, 3000);
},
false
);