signalR Core + ARR3 反向代理:握手超时
signalR Core + ARR3 Reverse Proxy: Handshake Timeouts
我在 docker 环境中使用 .net core 3.1,指向安装了 ARR 的 IIS 反向代理。
更新
我成功重写了运行。 BNut 因为我不知道为什么它会这样工作我无法回答这个问题。
我将重写规则从 localhost: 更改为 127.0.0.1:
现在握手有效。但它似乎不适用于仅与 ServerSentEvents 一起使用的 websocket 协议。
Error: Failed to start the transport 'WebSockets': Error: WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled.
但是安装了 Websockets。我正在使用 ARR3。
所以对我来说,最后两个问题是:
为什么它使用 IP 而不是本地主机? (下面的完整配置)
为什么即使安装了 WS 也会抛出这个错误?
完全重写配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<urlCompression doStaticCompression="false" doDynamicCompression="false" />
<rewrite>
<rules>
<rule name="ReverseProxyInboundRule1" stopProcessing="true">
<match url="wss://(.*)" />
<action type="Rewrite" url="wss://127.0.0.1:3101/{R:1}" />
</rule>
<rule name="ReverseProxyInboundRule2" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="https://127.0.0.1:3101/{R:1}" />
</rule>
</rules>
<outboundRules>
<rule name="ReverseProxyOutboundRule1" preCondition="ResponseIsHtml1">
<match filterByTags="A, Form, Img" pattern="^http(s)?://127.0.0.1:3101/(.*)" />
<action type="Rewrite" value="http{R:1}://sub.example.com/{R:2}" />
</rule>
<preConditions>
<preCondition name="ResponseIsHtml1">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
</rewrite>
</system.webServer>
</configuration>
编辑
感谢 Bruce Zhang,我找到了问题的根源,但仍然不知道如何解决它。
我通过 docker 在 windows 服务器上编写 docker 环境 运行。
在那里,我将 IIS10 与 UrlRewrite 和 ARR3 以及 .net 核心 3.1 应用程序和 signalR 核心一起使用。
作为客户,我使用 @microsoft/signalr 的打字稿版本(最新)
由于某些原因,我不知道连接是否正常,但握手呼叫以超时结束。
我在网上查看了一些解决方案并应用了这个:
- 添加了 URL 重写 wss:// 和 ws://
的入站规则
- 增加了 signalR 的超时时间
- 在 ARR 中配置了响应缓冲区(见下图)
- 启用失败请求跟踪
docker env 运行 与启用了 ARR 和 Websockets 功能的 IIS 在同一台服务器上。
Web-App 容器公开端口 6652,将其映射到 docker 容器中的端口 443。
我的重写规则定义如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<urlCompression doStaticCompression="false" doDynamicCompression="false" />
<rewrite>
<rules>
<rule name="WS reverse proxy" stopProcessing="true">
<match url="ws://devlocal.example.com" />
<conditions>
<add input="{CACHE_URL}" pattern="^(.+)://" />
</conditions>
<action type="Rewrite" url="{C:1}://127.0.0.1:6652" />
</rule>
<rule name="WSS reverse proxy" stopProcessing="true">
<match url="wss://devlocal.example.com" />
<conditions>
<add input="{CACHE_URL}" pattern="^(.+)://" />
</conditions>
<action type="Rewrite" url="{C:1}://127.0.0.1:6652" />
</rule>
<rule name="_Inbound_devlocal.example.com.com" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="{C:1}://localhost:6652/{R:1}" logRewrittenUrl="true" />
<conditions logicalGrouping="MatchAny">
<add input="{CACHE_URL}" pattern="^(.+)://" />
<add input="{HTTP_HOST}" pattern="^devlocal\.example.com\.com$" />
</conditions>
</rule>
</rules>
<outboundRules>
<rule name="_Outboundund_devlocal.example.com.com" stopProcessing="true">
<match filterByTags="A, Form, Img, Link, Script" pattern="^http(s)?://localhost:6652/(.*)" />
<action type="Rewrite" value="http{R:1}://devlocal.example.com.com/{R:2}" />
</rule>
</outboundRules>
</rewrite>
</system.webServer>
</configuration>
SignalR 配置为:
services.AddSignalR(hubOptions =>
{
hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(240);
hubOptions.HandshakeTimeout = TimeSpan.FromSeconds(120);
hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(60);
hubOptions.EnableDetailedErrors = true;
hubOptions.MaximumReceiveMessageSize = 200;
hubOptions.StreamBufferCapacity = 300;
});
// Mappig hub like:
app.UseEndpoints(api =>
{
api.MapControllers();
api.MapHub<CommentsHub>("/sockR/hub");
}
);
ARR 配置:
signalR 客户端的跟踪日志显示其握手调用超时:
[2022-01-13T18:29:07.232Z] Information: Normalizing '/sockR/hub' to 'https://devlocal.example.com/sockR/hub'.
Utils.js:151 [2022-01-13T18:29:07.233Z] Debug: Starting HubConnection.
Utils.js:151 [2022-01-13T18:29:07.233Z] Debug: Starting connection with transfer format 'Text'.
Utils.js:147 [2022-01-13T18:29:07.342Z] Information: WebSocket connected to wss://devlocal.example.com/sockR/hub.
Utils.js:151 [2022-01-13T18:29:07.342Z] Debug: The HttpConnection connected successfully.
Utils.js:151 [2022-01-13T18:29:07.342Z] Debug: Sending handshake request.
Utils.js:147 [2022-01-13T18:29:07.342Z] Information: Using HubProtocol 'json'.
... After 3 minutes:
ERROR Error: Uncaught (in promise): Error: Server timeout elapsed without receiving a message from the server. Error: Server timeout elapsed without receiving a message from the server.
有什么方法可以调试请求以超时结束的原因吗?奇怪的是,我还在 server.Also 上通过具有相同设置的反向代理绑定了一个 jira 实例,并且 WSS/WS 调用不会失败。
显然它现在可以通过删除:
Startup.cs
app.UseWebSockets();
并且在将我的重写配置更改为:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<urlCompression doStaticCompression="false" doDynamicCompression="false" />
<rewrite>
<rules>
<rule name="ReverseProxyInboundRule1" stopProcessing="true">
<match url="wss://(.*)" />
<action type="Rewrite" url="wss://127.0.0.1:3101/{R:1}" />
</rule>
<rule name="ReverseProxyInboundRule2" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="https://127.0.0.1:3101/{R:1}" />
</rule>
</rules>
<outboundRules>
<rule name="ReverseProxyOutboundRule1" preCondition="ResponseIsHtml1">
<match filterByTags="A, Form, Img" pattern="^http(s)?://127.0.0.1:3101/(.*)" />
<action type="Rewrite" value="http{R:1}://sub.example.com/{R:2}" />
</rule>
<preConditions>
<preCondition name="ResponseIsHtml1">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
</rewrite>
</system.webServer>
</configuration>
我在 docker 环境中使用 .net core 3.1,指向安装了 ARR 的 IIS 反向代理。
更新
我成功重写了运行。 BNut 因为我不知道为什么它会这样工作我无法回答这个问题。 我将重写规则从 localhost: 更改为 127.0.0.1: 现在握手有效。但它似乎不适用于仅与 ServerSentEvents 一起使用的 websocket 协议。
Error: Failed to start the transport 'WebSockets': Error: WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled.
但是安装了 Websockets。我正在使用 ARR3。 所以对我来说,最后两个问题是: 为什么它使用 IP 而不是本地主机? (下面的完整配置) 为什么即使安装了 WS 也会抛出这个错误?
完全重写配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<urlCompression doStaticCompression="false" doDynamicCompression="false" />
<rewrite>
<rules>
<rule name="ReverseProxyInboundRule1" stopProcessing="true">
<match url="wss://(.*)" />
<action type="Rewrite" url="wss://127.0.0.1:3101/{R:1}" />
</rule>
<rule name="ReverseProxyInboundRule2" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="https://127.0.0.1:3101/{R:1}" />
</rule>
</rules>
<outboundRules>
<rule name="ReverseProxyOutboundRule1" preCondition="ResponseIsHtml1">
<match filterByTags="A, Form, Img" pattern="^http(s)?://127.0.0.1:3101/(.*)" />
<action type="Rewrite" value="http{R:1}://sub.example.com/{R:2}" />
</rule>
<preConditions>
<preCondition name="ResponseIsHtml1">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
</rewrite>
</system.webServer>
</configuration>
编辑
感谢 Bruce Zhang,我找到了问题的根源,但仍然不知道如何解决它。
我通过 docker 在 windows 服务器上编写 docker 环境 运行。 在那里,我将 IIS10 与 UrlRewrite 和 ARR3 以及 .net 核心 3.1 应用程序和 signalR 核心一起使用。 作为客户,我使用 @microsoft/signalr 的打字稿版本(最新) 由于某些原因,我不知道连接是否正常,但握手呼叫以超时结束。 我在网上查看了一些解决方案并应用了这个:
- 添加了 URL 重写 wss:// 和 ws:// 的入站规则
- 增加了 signalR 的超时时间
- 在 ARR 中配置了响应缓冲区(见下图)
- 启用失败请求跟踪
docker env 运行 与启用了 ARR 和 Websockets 功能的 IIS 在同一台服务器上。
Web-App 容器公开端口 6652,将其映射到 docker 容器中的端口 443。
我的重写规则定义如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<urlCompression doStaticCompression="false" doDynamicCompression="false" />
<rewrite>
<rules>
<rule name="WS reverse proxy" stopProcessing="true">
<match url="ws://devlocal.example.com" />
<conditions>
<add input="{CACHE_URL}" pattern="^(.+)://" />
</conditions>
<action type="Rewrite" url="{C:1}://127.0.0.1:6652" />
</rule>
<rule name="WSS reverse proxy" stopProcessing="true">
<match url="wss://devlocal.example.com" />
<conditions>
<add input="{CACHE_URL}" pattern="^(.+)://" />
</conditions>
<action type="Rewrite" url="{C:1}://127.0.0.1:6652" />
</rule>
<rule name="_Inbound_devlocal.example.com.com" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="{C:1}://localhost:6652/{R:1}" logRewrittenUrl="true" />
<conditions logicalGrouping="MatchAny">
<add input="{CACHE_URL}" pattern="^(.+)://" />
<add input="{HTTP_HOST}" pattern="^devlocal\.example.com\.com$" />
</conditions>
</rule>
</rules>
<outboundRules>
<rule name="_Outboundund_devlocal.example.com.com" stopProcessing="true">
<match filterByTags="A, Form, Img, Link, Script" pattern="^http(s)?://localhost:6652/(.*)" />
<action type="Rewrite" value="http{R:1}://devlocal.example.com.com/{R:2}" />
</rule>
</outboundRules>
</rewrite>
</system.webServer>
</configuration>
SignalR 配置为:
services.AddSignalR(hubOptions =>
{
hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(240);
hubOptions.HandshakeTimeout = TimeSpan.FromSeconds(120);
hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(60);
hubOptions.EnableDetailedErrors = true;
hubOptions.MaximumReceiveMessageSize = 200;
hubOptions.StreamBufferCapacity = 300;
});
// Mappig hub like:
app.UseEndpoints(api =>
{
api.MapControllers();
api.MapHub<CommentsHub>("/sockR/hub");
}
);
ARR 配置:
signalR 客户端的跟踪日志显示其握手调用超时:
[2022-01-13T18:29:07.232Z] Information: Normalizing '/sockR/hub' to 'https://devlocal.example.com/sockR/hub'.
Utils.js:151 [2022-01-13T18:29:07.233Z] Debug: Starting HubConnection.
Utils.js:151 [2022-01-13T18:29:07.233Z] Debug: Starting connection with transfer format 'Text'.
Utils.js:147 [2022-01-13T18:29:07.342Z] Information: WebSocket connected to wss://devlocal.example.com/sockR/hub.
Utils.js:151 [2022-01-13T18:29:07.342Z] Debug: The HttpConnection connected successfully.
Utils.js:151 [2022-01-13T18:29:07.342Z] Debug: Sending handshake request.
Utils.js:147 [2022-01-13T18:29:07.342Z] Information: Using HubProtocol 'json'.
... After 3 minutes:
ERROR Error: Uncaught (in promise): Error: Server timeout elapsed without receiving a message from the server. Error: Server timeout elapsed without receiving a message from the server.
有什么方法可以调试请求以超时结束的原因吗?奇怪的是,我还在 server.Also 上通过具有相同设置的反向代理绑定了一个 jira 实例,并且 WSS/WS 调用不会失败。
显然它现在可以通过删除: Startup.cs
app.UseWebSockets();
并且在将我的重写配置更改为:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<urlCompression doStaticCompression="false" doDynamicCompression="false" />
<rewrite>
<rules>
<rule name="ReverseProxyInboundRule1" stopProcessing="true">
<match url="wss://(.*)" />
<action type="Rewrite" url="wss://127.0.0.1:3101/{R:1}" />
</rule>
<rule name="ReverseProxyInboundRule2" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="https://127.0.0.1:3101/{R:1}" />
</rule>
</rules>
<outboundRules>
<rule name="ReverseProxyOutboundRule1" preCondition="ResponseIsHtml1">
<match filterByTags="A, Form, Img" pattern="^http(s)?://127.0.0.1:3101/(.*)" />
<action type="Rewrite" value="http{R:1}://sub.example.com/{R:2}" />
</rule>
<preConditions>
<preCondition name="ResponseIsHtml1">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
</rewrite>
</system.webServer>
</configuration>