问题
前几天在研究NATS部署应用的时候,遇到了这么一个问题。
考虑到未来在生产环境中的部署,可能会受到一些网络条件的限制。部署人员可能无法随意管理互联网端口发布,只能受限在一个HTTPS服务和域名空间目录之下。现有的NATS原生协议和端口的发布方式,可能就无法使用了。
关于NATS系统本身的特性、应用和配置,不在本文讨论的范围,读者可以查阅笔者其他相关文章,或者自行检索相关材料。
解决方案概述
通过查阅相关资料,笔者发现可以使用NATS的WS支持特性,来解决这个问题。
大致的解决思路如下:
- 将NATS配置来支持WS的服务发布
- 修改HTTP的NGINX配置,将某路径的请求转发到NATS WS端点
- 配置HTTP协议升级方面的问题
- 由于HTTP本身已经配置了TLS,WS端口无需配置TLS
- 改进客户端程序,支持NATS.WS模式
这个请求响应链的工作原理应当是:
- nats客户端,使用wss协议发起请求,地址是https服务的一个路径
- https服务(nginx)收到wss请求,解析路径,发现应当使用路径的配置升级并使用ws协议
- nginx将请求转发到nats ws端点(nats服务)之上,并响应端点返回的内容
- nats客户端和nats服务建立连接,开始正常的应用
实操
Talk Is Cheap, Show Me The Code !!
NATS服务器
需要在NSTA服务器的配置信息中,增加WS协议支持相关的部分。就是在配置文件中,增加类似下面的内容:
js
websocket {
port: 4223
no_tls: true
# 可以指定允许的源(CORS 支持)
allowed_origins: [ "*" ]
}
内容非常简单:
- nats配置信息中,有一个websocket对象和区域,可以配置ws相关内容
- 设置一个独立的端口,如4223,来提供WS端点
- 选择不使用TLS(因为会使用HTTPS入口)
- 跨域设置,应当配置成为合法域名
配置完成后,重新启动nats服务,并且检查端口和防火墙是否开启:
shell
// 启动nats服务器
./nats-server -c n.conf -js
[233317] 2025/07/23 18:45:00.217555 [INF] Starting nats-server
[233317] 2025/07/23 18:45:00.217839 [INF] Version: 2.11.6
[233317] 2025/07/23 18:45:00.217849 [INF] Git: [bc813ee]
[233317] 2025/07/23 18:45:00.217855 [INF] Name: ...
// 查看侦听端口打开
sudo netstat -lpnt | grep 4223
tcp6 0 0 :::4223 :::* LISTEN 233317/./nats-serve
Nginx
需要修改HTTPS服务器的nginx配置信息,将一个WSS请求,转发到nats ws端点之上。相关的参考配置方式如下:
nginx.conf
## HTTP区段
location /nats {
proxy_pass http://192.168.9.194:4223;
proxy_http_version 1.1;
# WebSocket 关键头
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 60s;
proxy_buffering off;
}
这里其他的HTTPS协议相关的配置,都已经配置好,我们使用其中的一个路径(nats)来提高WSS服务,不影响其他的https应用,而且可以确保连接和通信通道安全。这里面有几个关键信息:
- proxy_pass指向nats ws端点地址,但注意这里是http服务
- 为WS配置了关键的header
- 关闭缓冲
这里建议先从外部检查一下端点地址,确保已经启动和工作。比如这里使用curl访问,虽然出错(bad request),却可以确定这个路径转发的端点应该是开始工作的:
shell
// 外部检查
curl -v https://ulsc.net:10443/nats
VERBOSE: GET with 0-byte payload
curl : Bad Request
At line:1 char:1
+ curl -v https://ulsc.net:10443/nats
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebExc
eption
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
客户端程序
在客户端程序,也需要修改连接方式,来适应和使用这个新的端点地址,参考代码如下:
nats.ts
// 引用和连接
import { connect } from "nats.ws";
// 连接和主题配置
const
TOPIC = "ws.yanjh",
CONN_NATS = {
servers: "wss://ulsc.net:10443/nats",
user: 'user',
pass: 'pass',
reconnect: true,
maxReconnectAttempts: 100,
};
// 连接nats服务
const nc = await connect(CONN_NATS);
// 发布消息
setTimeout(() => {
nc.publish( TOPIC, new TextEncoder().encode("world"));
}, 2000);
// 订阅消息
const sub = nc.subscribe(TOPIC);
for await (const m of sub) {
console.log(`Received: ${new TextDecoder().decode(m.data)}`);
}
这里相关的修改如下:
- 模块修改使用nats.ws,这是一个独立的npm,需要单独安装
- 注意服务端点的地址,不是https,而是wss(将会从https升级),并且带有配置好的路径信息
- 这里可以不使用ca和tls配置,因为那是由https入口保证的
- 理论上只需要修改连接方式,其他应用模式可以不变
小结
本文讨论了由于无法直接使用nats端口,而使用已有的https服务发布nats的解决方案。包括了简单的原理说明,相关nats服务、nginx服务和客户端代码的相关改造内容。