Ошибка подключения WebSocket в React и Docker: WebSocket connection to 'ws://backend:7000/api/getData' failed
При запуске моего приложения React в Docker с использованием Nginx в качестве обратного прокси-сервера возникает ошибка при подключении к WebSocket. Получаю сообщение об ошибке:
WebSocket connection to 'ws://backend:7000/api/getData' failed: WebSocket error
Однако локально все работает идеально. Проблема возникает только в Docker.
Backend (Go) настроен для обработки WebSocket запросов, а также работает с Redis для получения данных.
При попытке подключения клиентского кода React к WebSocket серверу, который находится в контейнере Docker, возникает ошибка.
Как исправить эту проблему?
вот мой docker-compose:
version: '3'
services:
frontend:
build:
context: ./front/app
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
NODE_ENV: production
restart: always
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "7000:7000"
restart: always
bot:
build:
context: ./bot_crypto/crypto
dockerfile: Dockerfile
restart: always
redis:
image: redis
ports:
- "6379:6379"
restart: always
nginx:
image: nginx
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- frontend
- backend
- bot
restart: always
nginx:
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://frontend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api {
proxy_pass http://backend:7000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
frontend:
import { name_table } from "data/data";
import { useEffect, useState } from "react";
import { DataGetProps } from "interfaces/data.props";
import Filter from "components/filter/filter";
import NProgress from "nprogress";
const Content = () => {
const [data, setData] = useState<DataGetProps[]>([]);
const [socket, setSocket] = useState<WebSocket | null>(null);
const [time, setTime] = useState<string>("");
const [filterData, setFilterData] = useState<DataGetProps[]>([])
const [stateFilter, setStateFilter] = useState<boolean>(false)
const [stateFilterNum, setFilterNum]=useState<number>(0)
useEffect(() => {
const newSocket = new WebSocket("ws://backend:7000/api/getData");
setSocket(newSocket);
return () => {
newSocket.close();
};
}, []);
useEffect(() => {
if (!socket) return;
socket.onmessage = (event) => {
NProgress.start();
const message = JSON.parse(event.data);
setData(message.data);
setTime(new Date().toLocaleString());
setStateFilter(true)
NProgress.done();
console.log(message);
};
socket.onerror = (error) => {
console.error("WebSocket error:", error);
};
return () => {
socket.close();
};
}, [socket]);
return (
<div className="min-h-screen flex items-center justify-center">
<div className="max-w-max grid grid-cols-1 gap-4 px-4">
<div className={"text-center"} > Обновление: {time}</div>
<Filter data={data} setFilterData={setFilterData} stateFilter={stateFilter} setStateFilter={setStateFilter} stateFilterNum={stateFilterNum} setFilterNum={setFilterNum}/>
<div className={"grid grid-cols-6 gap-4"}>
{name_table && name_table.map((item) => (
<div key={item.id} className="p-4 bg-gray-200 text-center rounded">
<div className={"text-black font-bold"}>{item.name}</div>
</div>
))}
</div>
{filterData && filterData.map((item) => (
<div key={item.id} className={"grid grid-cols-6 gap-4"}>
<div className="p-4 bg-gray-200 text-center rounded">{item.symbol}</div>
<div className="p-4 bg-gray-200 text-center rounded">{item.exchange1}</div>
<div className="p-4 bg-gray-200 text-center rounded">{item.price1}</div>
<div className="p-4 bg-gray-200 text-center rounded">{item.exchange2}</div>
<div className="p-4 bg-gray-200 text-center rounded">{item.price2}</div>
<div className="p-4 bg-gray-200 text-center rounded">{item.spread}</div>
</div>
))}
</div>
</div>
);
};
export default Content;
Backend:
package main
import (
"encoding/json"
"fmt"
"github.com/codeza-org/backend_crypto/connect"
"github.com/go-redis/redis/v8"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/websocket/v2"
"time"
)
type Comparison struct {
ID string `json:"id"`
Symbol string `json:"symbol"`
Exchange1 string `json:"exchange1"`
Price1 float64 `json:"price1"`
Exchange2 string `json:"exchange2"`
Price2 float64 `json:"price2"`
Spread float64 `json:"spread"`
}
var client *redis.Client
func main() {
var err error
client, err = connect.Connect()
if err != nil {
panic(err)
}
defer client.Close()
app := fiber.New()
app.Use(cors.New(cors.Config{
AllowOrigins: "http://frontend:3000",
}))
app.Use("api/getData", func(c *fiber.Ctx) error {
if websocket.IsWebSocketUpgrade(c) {
return c.Next()
}
return fiber.ErrUpgradeRequired
}, websocket.New(getData))
app.Post("api/data", data)
app.Listen(":7000")
}
func data(c *fiber.Ctx) error {
var comparisons []Comparison
if err := c.BodyParser(&comparisons); err != nil {
return err
}
jsonData, err := json.Marshal(comparisons)
if err != nil {
return err
}
err = client.Set(client.Context(), "data", jsonData, 0).Err()
if err != nil {
return err
}
fmt.Println("Данные успешно записаны в Redis")
return c.JSON(fiber.Map{
"message": "Данные успешно получены и обработаны",
})
}
func getData(c *websocket.Conn) {
for {
fmt.Println(1)
if c == nil {
fmt.Println("Соединение WebSocket закрыто")
return
}
val, err := client.Get(client.Context(), "data").Result()
if err != nil {
fmt.Println("Ошибка при получении данных из Redis:", err)
return
}
var comparisons []Comparison
err = json.Unmarshal([]byte(val), &comparisons)
if err != nil {
fmt.Println("Ошибка при разборе данных JSON:", err)
return
}
data, err := json.Marshal(fiber.Map{"data": comparisons})
if err != nil {
fmt.Println("Ошибка при преобразовании данных в JSON:", err)
return
}
err = c.WriteMessage(websocket.TextMessage, data)
if err != nil {
fmt.Println("Ошибка при отправке данных через WebSocket:", err)
return
}
из Redis
time.Sleep(5 * time.Second)
}
}