【问题标题】:Apache websocket redirection to Tomcat: mod_proxy and mod_proxy_wstunnelApache websocket 重定向到 Tomcat:mod_proxy 和 mod_proxy_wstunnel
【发布时间】:2018-03-22 01:52:25
【问题描述】:

我正在尝试使用 mod_proxy 和 mod_proxy_wstunnel 模块将流量从 Apache 重定向到 Tomcat。 HTTP 流量重定向没有问题,但我无法使用我迄今为止尝试的任何配置成功重定向 websocket 流量。

我正在使用 Apache 2.4.28 和 Tomcat 8.5.13

我必须说,当我在没有 Apache 的情况下使用 Tomcat 时,websockets 工作得非常好:

下一个适用于此配置的 Tomcat 连接器:

<Connector URIEncoding="UTF-8"
        compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/javascript"
        compression="on"
        compressionMinSize="1024"
        connectionTimeout="20000"
        noCompressionUserAgents="gozilla, traviata"
        port="443"
        protocol="org.apache.coyote.http11.Http11AprProtocol"
        SSLEnabled="true"
        scheme="https"
        secure="true">
        <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
        <SSLHostConfig>
            <Certificate certificateKeyFile="/opt/tomcat/cert/privkey.pem"
                         certificateFile="/opt/tomcat/cert/cert.pem"
                         certificateChainFile="/opt/tomcat/cert/chain.pem"
                         type="RSA" />
        </SSLHostConfig>
    </Connector>

到目前为止,一切都很清楚。现在我在 Tomcat 前面启动了一个 Apache 服务器,我首先更改的是 Tomcat 连接器,如下所示:

<Connector URIEncoding="UTF-8"
    compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/javascript"
    compression="on"
    compressionMinSize="1024"
    connectionTimeout="20000"
    noCompressionUserAgents="gozilla, traviata"
    port="8080"
    protocol="org.apache.coyote.http11.Http11AprProtocol">
    <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
</Connector>

在 Apache 中,我成功加载了下一个模块(我检查了它们是否真的加载了):

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
LoadModule ssl_module modules/mod_ssl.so

这是我在 vhosts.conf 文件中尝试的配置之一:

<VirtualHost *:443>
    ServerName www.example.com
    ServerAdmin server@example.com
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined
    CustomLog /var/log/httpd/lavnet_access.log combined
    ErrorLog /var/log/httpd/lavnet_error.log

    SSLProxyEngine on
    #websocket
    RewriteEngine on
    RewriteCond %{HTTP:Upgrade} "(?i)websocket"
    RewriteRule ^/(.*)$ wss://www.example.com/$1 [P]
    #rest
    ProxyPass "/" "http://www.example.com:8080/"
    ProxyPassReverse "/" "http://www.example.com:8080/"

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/example.com/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
</VirtualHost>

这是我取得的效果最好的配置,但它仍然无法正常工作。当我尝试建立此连接时,lavnet_error.log 中的日志跟踪似乎相当不错:

[Tue Oct 10 16:46:39.014980 2017] [proxy:debug] [pid 10558:tid 47319680603904] proxy_util.c(2209): [client XX.XX.XX.109:11208] AH00944: connecting wss://www.example.com:443/rest/notify/675/fgcw02lm/websocket to www.example.com:443
[Tue Oct 10 16:46:39.016495 2017] [proxy:debug] [pid 10558:tid 47319680603904] proxy_util.c(2418): [client XX.XX.XX.109:11208] AH00947: connected /rest/notify/675/fgcw02lm/websocket to www.example.com:443
[Tue Oct 10 16:46:39.016567 2017] [proxy:debug] [pid 10558:tid 47319680603904] proxy_util.c(2887): AH02824: WSS: connection established with XX.XX.XX.109:443 (*)
[Tue Oct 10 16:46:39.016590 2017] [proxy:debug] [pid 10558:tid 47319680603904] proxy_util.c(3054): AH00962: WSS: connection complete to XX.XX.XX.109:443 (www.example.com)
[Tue Oct 10 16:46:39.016603 2017] [ssl:info] [pid 10558:tid 47319680603904] [remote 217.61.129.109:443] AH01964: Connection to child 0 established (server www.example.com:443)
[Tue Oct 10 16:46:39.026370 2017] [proxy:debug] [pid 10558:tid 47319680603904] proxy_util.c(2171): AH00943: WSS: has released connection for (*)

但这是 Chrome 显示的错误:

我也试过这个其他配置:

<VirtualHost *:443>
    ServerName www.example.com
    ServerAdmin server@example.com
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined
    CustomLog /var/log/httpd/lavnet_access.log combined
    ErrorLog /var/log/httpd/lavnet_error.log
    SSLProxyEngine on

    ProxyPass "/rest/notify/" "wss://www.example.com:8080/rest/notify"
    ProxyPassReverse "/rest/notify/" "wss://www.example.com:8080/rest/notify"

    ProxyPass "/" "http://www.example.com:8080/"
    ProxyPassReverse "/" "http://www.example.com:8080/"

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/example.com/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
</VirtualHost>

但在这种情况下,我得到一个“500(内部服务器错误)”,而且我可以在 lavnet_error.log 中看到下一个跟踪:

[Tue Oct 10 17:14:14.778824 2017] [proxy:warn] [pid 11924:tid 47694559057664] [client XX.XX.XXX.189:11665] AH01144: No protocol handler was valid for the URL /rest/notify/info (scheme 'wss'). If you are using a DSO version of mod_proxy, make sure the proxy submodules are included in the configuration using LoadModule., referer: https://www.example.com/equipment-command-panel/8287/8482

我已经尝试了很多配置,但我无法让它工作。我希望你能帮助我。

谢谢。

【问题讨论】:

    标签: apache tomcat websocket mod-proxy mod-proxy-wstunnel


    【解决方案1】:

    经过多次尝试,我终于解决了它。接下来是工作配置,以防有人需要:

    这是Apache的vhost.conf文件:

    <VirtualHost *:443>
        ServerName www.example.com
        ServerAdmin admin@example.com
        LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined
        CustomLog /var/log/httpd/lavnet_access.log combined
        ErrorLog /var/log/httpd/lavnet_error.log
    
        ProxyPreserveHost On
        ProxyPass / http://www.example.com:8080/
        ProxyPassReverse / http://www.example.com:8080/
        ProxyRequests Off
        RewriteEngine on
        RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
        RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
        RewriteRule .* ws://www.example.com:8080%{REQUEST_URI} [P]
    
        SSLEngine on
        SSLCertificateFile    /etc/letsencrypt/live/example.com/cert.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
        SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
    </VirtualHost>
    

    还有这个是Tomcat中server.xml中定义的连接器:

    <Connector URIEncoding="UTF-8"
        connectionTimeout="20000"
        port="8080"
        protocol="org.apache.coyote.http11.Http11AprProtocol">
        <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
    </Connector>
    

    谢谢。

    【讨论】:

    • 谢谢,很好的答案。只想添加 2 个注释: 1. 我不需要更改连接器。 2.不要错过apache2上proxy_wstunnel的激活!我已经添加了您的配置,但错过了 proxy_wstunnel 的激活,apache2 启动时没有问题,但应请求返回 500。只需运行“a2enmod proxy_wstunnel”,重新启动 apache2 即可。
    • 我有 2 个实例的集群环境。如果我在 httpd.conf 中使用单个 url,websocket 工作正常,但两个实例 url websocket 不工作。 RewriteRule .* ws://host1:8080%{REQUEST_URI} [P]RewriteRule .* ws://host2:8080%{REQUEST_URI} [P]
    • 对不起,我没有尝试那个特定的例子,但是 Apache 可能无法使用这个简单的配置将相同的请求重定向到多个后端。有关 mod_proxy 和集群的详细信息,请查看以下链接,它可能有用:httpd.apache.org/docs/2.4/howto/reverse_proxy.html
    • @Passionatedeveloper 也许我的回答对你有帮助。
    【解决方案2】:

    接受的解决方案在单个实例中有效。但是我有多个服务器的集群,并且我有带有mod_jk 模块的负载均衡器。在这种情况下,经过数小时的搜索,我可以找到解决问题的解决方案。在mod_jk 中,服务器名称附加到JSESSIONID 的末尾,如下所示:JSESSIONID=25B9813E079BA3543C242438FD74CA84.banana

    所以我可以编写一个规则,使用下面的代码将 websocket 请求代理到正确的服务器:

    SSLProxyEngine On
    RewriteEngine on
    RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
    RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
    RewriteCond %{HTTP_COOKIE} (^|;\ *)JSESSIONID=.*\.(.+?)(?=;|$) [NC]
    RewriteRule .* wss://%2:8443%{REQUEST_URI} [P]
    

    正如您在最后一个RewriteCond 中看到的,我编写了一个正则表达式,它在第二个正则表达式组中找到服务器的名称。所以我编写了一个代理,将请求路由到正确的服务器wss://%2:8443%{REQUEST_URI}

    p.s:不要忘记将服务器名称和 IP 添加到 /etc/hosts

    【讨论】:

      猜你喜欢
      • 2013-09-18
      • 2017-08-06
      • 2017-02-06
      • 2013-07-13
      • 2013-07-27
      • 2017-07-06
      • 1970-01-01
      • 2015-02-16
      • 2017-05-08
      相关资源
      最近更新 更多