AWS EC2与负载均衡器(LB)在WebSocket上没有返回101状态码。

huangapple go评论67阅读模式
英文:

Aws ec2 with lb does not return 101 on websocket

问题

我有一个关于我的应用程序的问题。
基本上,在本地主机上一切正常运行,但是当我部署到AWS时,WebSocket连接就无法工作。
问题是,当我在EC2上进行wss请求时,似乎它不允许响应离开机器,所以我的应用程序挂起在等待状态,直到1分钟过去并超时,然后返回200,但没有建立连接。
API使用Go语言和gin框架编写,应用程序使用React框架和JavaScript编写。
当我向一些人寻求建议时,他们告诉我可以使用https包装wss请求,并通过负载均衡器将其传递,并使用nginx升级它,但我真的找不到类似的东西。
这是我的后端代码

main.go

ws := router.Group("/ws/")
{

    ws.GET("/:id", func(c *gin.Context) {
        websocket.ServeWs(c, pool)
    })
}

websocket.go

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool { return true },
}

func Upgrade(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) {
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println(err)
        return ws, err
    }
    return ws, nil
}

func ServeWs(c *gin.Context, pool *Pool) {

    w := c.Writer
    r := c.Request
    conn, err := Upgrade(w, r)
    if err != nil {
        fmt.Fprintf(w, "%+V\n", err)
    }
    client := &Client{
        Conn: conn,
        Pool: pool,
        ID:   c.Param("id"),
    }

    pool.Register <- client
    client.Read()
}

这是React代码

const socket = new WebSocket(
  `${
    process.env.REACT_APP_ENV === "dev"
      ? "ws://localhost:8080"
      : "wss://api.blabla.prod"
  }/ws/${sessionStorage.getItem("userID")}`
);


let WebSocketConnect = () => {
  console.log("Attempting Connection...");

  socket.onopen = () => {
    console.log("Successfully Connected");
  };

  socket.onclose = (event) => {
    console.log("Socket Closed Connection: ", event);
    // WebSocketConnect();
  };

  socket.onerror = (error) => {
    console.log("Socket Error: ", error);
  };
};

let sendMsg = (msg) => {
  //   console.log("sending msg: ", msg);
  socket.send(msg);
};

export { WebSocketConnect, sendMsg, socket };

这是nginx配置

server {

    listen 80;
    server_name api.blabla.prod;

    if ($http_x_forwarded_proto = 'http') {
        return 301 https://$server_name$request_uri;
    }
    location /ws {
        proxy_pass "http://127.0.0.1:8080";
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
    location / {
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass       "http://127.0.0.1:8080";
    }
}

提前感谢!

英文:

i have a problem with my app.
Basically while everything runs smooth on localhost, when i deploy to aws websocket connections does not work.
The problem is that when I make an wss request on ec2 it seems it does let the response to get out of the machine ,so instead of becoming 101 and stable a connection my app hangs on pending until 1 min pass and get a timeout, then it returns 200 but no connection establish.
Api is written in go lang with gin framework and app is written in js with React framework.
While asking some people for their advise they told me that I can "wrap" an wss request with https and pass it through lb and upgrade it with nginx, but I really cannot find anything similar.
Here is my backend code

main.go

	{

		ws.GET(&quot;/:id&quot;, func(c *gin.Context) {
			websocket.ServeWs(c, pool)
		})
	}

websocket.go

ar upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool { return true },
}

func Upgrade(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) {
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println(err)
        return ws, err
    }
    return ws, nil
}

func ServeWs(c *gin.Context, pool *Pool) {

	w := c.Writer
	r := c.Request
    conn, err := Upgrade(w, r)
    if err != nil {
        fmt.Fprintf(w, &quot;%+V\n&quot;, err)
    }
    client := &amp;Client{
        Conn: conn,
        Pool: pool,
		ID: c.Param(&quot;id&quot;),
    }

    pool.Register &lt;- client
    client.Read()
}

Here is react code

const socket = new WebSocket(
  `${
    process.env.REACT_APP_ENV === &quot;dev&quot;
      ? &quot;ws://localhost:8080&quot;
      : &quot;wss://api.blabla.prod&quot;
  }/ws/${sessionStorage.getItem(&quot;userID&quot;)}`
);


let WebSocketConnect = () =&gt; {
  console.log(&quot;Attempting Connection...&quot;);

  socket.onopen = () =&gt; {
    console.log(&quot;Successfully Connected&quot;);
  };

  socket.onclose = (event) =&gt; {
    console.log(&quot;Socket Closed Connection: &quot;, event);
    // WebSocketConnect();
  };

  socket.onerror = (error) =&gt; {
    console.log(&quot;Socket Error: &quot;, error);
  };
};

let sendMsg = (msg) =&gt; {
  //   console.log(&quot;sending msg: &quot;, msg);
  socket.send(msg);
};

export { WebSocketConnect, sendMsg, socket };

Here is nginx config

server {

    listen 80;
    server_name api.blabla.prod;

    if ($http_x_forwarded_proto = &#39;http&#39;) {
        return 301 https://$server_name$request_uri;
    }
    location /ws {
        proxy_pass &quot;http://127.0.0.1:8080&quot;;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection &quot;upgrade&quot;;
    }
    location / {
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass       &quot;http://127.0.0.1:8080&quot;;
    }
}

Thanks in advance!

答案1

得分: 1

问题似乎出在你的nginx配置中,我会在/ws端点上复制这个配置。似乎缺少$proxy_add_x_forwarded_for

server {
    listen 80;
    server_name goddit.pro; // 更改此处
    location / { // 你可以保留为 / 或设置为其他位置,例如 /ws
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        proxy_pass http://localhost:8080/;
        proxy_redirect off;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

问题也可能出在你的JavaScript代码中,下面是连接和升级的示例:

window.onload = function () {
  // 在你的HTML文档中的某个位置
  var conn;
  // 然后你需要连接
  function connectWs(id) {
    if (window["WebSocket"]) {
      conn = new WebSocket("wss://" + document.location.host + "/id/" + id);
      conn.onopen = function (evt) {
        var item = document.createElement("li");
        item.innerHTML = "<em>Connected to " + id + "</em>";
        log.appendChild(item);
      };
      conn.onclose = function (evt) {
        var item = document.createElement("li");
        item.innerHTML = "<em>Connection to " + id + " closed</em>";
        log.appendChild(item);
      };
      conn.onmessage = function (evt) {
        var message = JSON.parse(evt.data);
        // console.log(message);
        printMessage(message);
      };
    } else {
      var item = document.createElement("li");
      item.innerHTML = "<b>Your browser does not support WebSockets.</b>";
      log.appendChild(item);
    }
  }
  connectWs(id);
}

在你分享了React控制台的错误日志之后:

关闭Code 1006是一个特殊的代码,表示连接异常关闭(由浏览器实现)。

如果你的浏览器客户端报告关闭代码1006,那么你应该查看websocket.onerror(evt)事件以获取详细信息。

英文:

The issue seems to be in your nginx configuration, I would replicate this for your /ws endpoint. Seems to be missing the $proxy_add_x_forwarded_for

server {
        listen 80;
        server_name goddit.pro; // change this
        location / { // You can leave it at / or set it for another location, i.e. /ws
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_set_header X-NginX-Proxy true;

                proxy_pass http://localhost:8080/;
                proxy_redirect off;

                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection &quot;upgrade&quot;;
        }
}

The issue could also be in your javaScript, here's how you connect and upgrade.

window.onload = function () {
  // Somewhere in your HTML document
  var conn;
  // Then you need to connect
  function connectWs(id) {
    if (window[&quot;WebSocket&quot;]) {
      conn = new WebSocket(&quot;wss://&quot; + document.location.host + &quot;/id/&quot; + id);
      conn.onopen = function (evt) {
        var item = document.createElement(&quot;li&quot;);
        item.innerHTML = &quot;&lt;em&gt;Connected to &quot; + id + &quot;&lt;/em&gt;&quot;;
        log.appendChild(item);
      };
      conn.onclose = function (evt) {
        var item = document.createElement(&quot;li&quot;);
        item.innerHTML = &quot;&lt;em&gt;Connection to &quot; + id + &quot; closed&lt;/em&gt;&quot;;
        log.appendChild(item);
      };
      conn.onmessage = function (evt) {
        var message = JSON.parse(evt.data);
        // console.log(message);
        printMessage(message);
      };
    } else {
      var item = document.createElement(&quot;li&quot;);
      item.innerHTML = &quot;&lt;b&gt;Your browser does not support WebSockets.&lt;/b&gt;&quot;;
      log.appendChild(item);
    }
  }
  connectWs(id);
}

After you shared the error logs from the React console:

Close Code 1006 is a special code that means the connection was closed abnormally (locally) by the browser implementation.

If your browser client reports close code 1006, then you should be looking at the websocket.onerror(evt) event for details.

huangapple
  • 本文由 发表于 2021年12月23日 16:20:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/70459403.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定