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 的打字稿版本(最新) 由于某些原因,我不知道连接是否正常,但握手呼叫以超时结束。 我在网上查看了一些解决方案并应用了这个:

  1. 添加了 URL 重写 wss:// 和 ws://
  2. 的入站规则
  3. 增加了 signalR 的超时时间
  4. 在 ARR 中配置了响应缓冲区(见下图)
  5. 启用失败请求跟踪

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>