在一个托管的Rancher Kubernetes集群中,我有一个公开websocket服务的服务(Spring SockJS服务器)。 借助入口规则,该服务暴露于外部:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myIngress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600s"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600s"
nginx.ingress.kubernetes.io/enable-access-log: "true"
spec:
rules:
- http:
paths:
- path: /app1/mySvc/
backend:
serviceName: mySvc
servicePort: 80
Web应用程序通过入口Nginx连接到Web套接字服务,并且工作正常。加载的js脚本为:
var socket = new SockJS('ws');
stompClient = Stomp.over(socket);
stompClient.connect({}, onConnected, onError);
相反,独立客户端(js或python)不起作用,因为它们返回了400 http错误。
例如,这是curl发送的请求和nginx的响应:
curl --noproxy '*' --include \
--no-buffer \
-Lk \
--header "Sec-WebSocket-Key: l3ApADGCNFGSyFbo63yI1A==" \
--header "Sec-WebSocket-Version: 13" \
--header "Host: ingressHost" \
--header "Origin: ingressHost" \
--header "Connection: keep-alive, Upgrade" \
--header "Upgrade: websocket" \
--header "Sec-WebSocket-Extensions: permessage-deflate" \
--header "Sec-WebSocket-Protocol: v10.stomp, v11.stomp, v12.stomp" \
--header "Access-Control-Allow-Credentials: true" \
https://ingressHost/app1/mySvc/ws/websocket
HTTP/2 400
date: Wed, 20 Nov 2019 14:37:36 GMT
content-length: 34
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
access-control-allow-origin: ingressHost
access-control-allow-credentials: true
set-cookie: JSESSIONID=D0BC1540775544E34FFABA17D14C8898; Path=/; HttpOnly
strict-transport-security: max-age=15724800; includeSubDomains
Can "Upgrade" only to "WebSocket".
为什么它与浏览器一起使用而不与独立客户端一起使用?
谢谢
答案 0 :(得分:1)
问题似乎不在于nginx Ingress。 JSESSIONID cookie的存在很可能表明Spring应用程序获取了请求并发送了响应。
对Spring代码的快速搜索显示,当AbstractHandshakeHandler.java(不区分大小写的匹配)时,Upgrade
header isn't equal to WebSocket
返回了Can "Upgrade" only to "WebSocket".
。
我建议在使用"Upgrade: websocket"
进行呼叫时,仔细检查curl
标头是否存在。
此外,它似乎是similar problem,如果应用程序具有多个控制器,则也可以在这里应用。
对于它的价值,在适当地替换了ingressHost
之后,我针对问题curl
和我之前实现的本地STOMP服务器尝试了相同的https://echo.websocket.org
命令。它为两个工作。
您可能已经这样做了,但是您是否尝试过在浏览器中捕获网络流量以查看请求/响应交换,尤其是返回HTTP 101 Switching Protocols
的请求/响应?然后尝试完全复制浏览器进行的调用,该调用成功。例如,STOMP客户端生成会话ID并使用队列/主题,该队列/主题被放置在对服务器的请求(例如/ws/329/dt1hvk2v/websocket
)的URL路径中。带有curl
的测试请求似乎没有它们。
答案 1 :(得分:0)
在发现traefik之前,我一直使用nginx创建入口。 您应该使用traefik进行测试。
答案 2 :(得分:0)
我在Spring应用程序中激活了跟踪,并且使用curl进行了调用:
o.s.w.s.s.s.DefaultHandshakeHandler : Handshake failed due to invalid Upgrade header: null
因此,nginx似乎删除了Upgrade标头!
使用浏览器客户端进行测试时,标头出现在Spring Applicaton中并且可以正常工作。
答案 3 :(得分:0)
当我尝试使用http而不是https时,它可以工作。
我不明白为什么https删除独立客户端的Upgrade标头。
答案 4 :(得分:0)
我遇到了同样的问题,即标题没有转发。决定使用NGINX本身提供的NGINX ingress controller。它提供了nginx.org/websocket-services
注释,可轻松实现。您可以阅读配置示例here。
以下是对我来说很棘手的配置。
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.org/websocket-services: "blue-svc"