v0.0.18.5 замена ws на sse
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
madipo2611 2025-08-17 09:16:35 +03:00
parent f9e9347389
commit 2a3ae72db9
2 changed files with 38 additions and 65 deletions

View File

@ -121,45 +121,29 @@ func (r *queryResolver) GetUserChats(ctx context.Context, userID int) ([]*domain
// MessageStream реализация подписки на новые сообщения // MessageStream реализация подписки на новые сообщения
func (r *subscriptionResolver) MessageStream(ctx context.Context, userID int) (<-chan *domain.Message, error) { func (r *subscriptionResolver) MessageStream(ctx context.Context, userID int) (<-chan *domain.Message, error) {
messageChan := make(chan *domain.Message, 10) // Буферизованный канал // Создаем канал для GraphQL подписки
messageChan := make(chan *domain.Message)
go func() { // Вызываем gRPC стриминг
defer close(messageChan)
retryCount := 0
maxRetries := 5
for {
if retryCount >= maxRetries {
log.Printf("Max retries (%d) reached for user %d", maxRetries, userID)
return
}
// Создаем новое соединение
stream, err := r.MessageClient.StreamMessages(ctx, &proto.StreamMessagesRequest{ stream, err := r.MessageClient.StreamMessages(ctx, &proto.StreamMessagesRequest{
UserId: int32(userID), UserId: int32(userID),
}) })
if err != nil { if err != nil {
log.Printf("Stream connection error for user %d: %v (retry %d)", userID, err, retryCount) return nil, fmt.Errorf("failed to stream messages: %w", err)
retryCount++
select {
case <-ctx.Done():
return
case <-time.After(time.Duration(retryCount) * time.Second):
continue
}
} }
retryCount = 0 // Сброс счетчика при успешном подключении go func() {
defer close(messageChan)
for { for {
msg, err := stream.Recv() msg, err := stream.Recv()
if err != nil { if err != nil {
log.Printf("Stream receive error for user %d: %v", userID, err) log.Printf("gRPC stream error: %v", err)
break return
} }
// Пропускаем heartbeat сообщения // Пропускаем heartbeat сообщения
if msg.Message.GetContent() == "__heartbeat__" { if msg.Message.Content == "__heartbeat__" {
continue continue
} }
@ -167,16 +151,6 @@ func (r *subscriptionResolver) MessageStream(ctx context.Context, userID int) (<
case <-ctx.Done(): case <-ctx.Done():
return return
case messageChan <- protoMessageToDomain(msg.Message): case messageChan <- protoMessageToDomain(msg.Message):
// Сообщение успешно отправлено в канал
}
}
// Пауза перед повторной попыткой
select {
case <-ctx.Done():
return
case <-time.After(1 * time.Second):
retryCount++
} }
} }
}() }()
@ -186,9 +160,6 @@ func (r *subscriptionResolver) MessageStream(ctx context.Context, userID int) (<
// Преобразование proto-сообщения в domain-модель // Преобразование proto-сообщения в domain-модель
func protoMessageToDomain(msg *proto.Message) *domain.Message { func protoMessageToDomain(msg *proto.Message) *domain.Message {
if msg == nil {
return nil
}
return &domain.Message{ return &domain.Message{
ID: int(msg.Id), ID: int(msg.Id),
ChatID: int(msg.ChatId), ChatID: int(msg.ChatId),

View File

@ -5,10 +5,9 @@ import (
"database/sql" "database/sql"
"github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/handler/transport" "github.com/99designs/gqlgen/graphql/handler/transport"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/gorilla/websocket"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/vektah/gqlparser/v2/ast"
"log" "log"
"net/http" "net/http"
"os" "os"
@ -72,26 +71,29 @@ func (s *Server) configureRouter() {
Resolvers: resolver, Resolvers: resolver,
})) }))
wsTransport := transport.Websocket{ srv.AddTransport(&transport.POST{})
Upgrader: websocket.Upgrader{ srv.AddTransport(&transport.GET{})
CheckOrigin: func(r *http.Request) bool { srv.AddTransport(&transport.Options{})
origin := r.Header.Get("Origin")
for _, allowed := range allowedOrigins {
if origin == allowed {
return true
}
}
return false
},
},
}
srv.AddTransport(&wsTransport) // Настройка SSE транспорта (правильный способ)
sseTransport := &transport.SSE{}
srv.AddTransport(sseTransport)
s.router.Handle("/", playground.Handler("GraphQL playground", "/query")) // Простейший кеш запросов (альтернатива MemoryCache)
s.router.Handle("/query", srv) srv.SetQueryCache(&NoopCache{})
s.router.Handle("/uploads/*", http.StripPrefix("/uploads/", http.FileServer(http.Dir("./uploads")))) })
s.router.Handle("/", playground.Handler("GraphQL playground", "/query"))
s.router.Handle("/query", srv)
s.router.Handle("/uploads/*", http.StripPrefix("/uploads/", http.FileServer(http.Dir("./uploads"))))
} }
type NoopCache struct{}
func (n *NoopCache) Get(ctx context.Context, key string) (*ast.QueryDocument, bool) {
return nil, false
}
func (n *NoopCache) Add(ctx context.Context, key string, value *ast.QueryDocument) {}
func (s *Server) configureMetrics() { func (s *Server) configureMetrics() {
metricsRouter := chi.NewRouter() metricsRouter := chi.NewRouter()