A lightweight Go package for managing Server-Sent Events (SSE) connections with Redis backend for cross-service messaging.
- ✅ Simple and clean API
- ✅ Redis backend for scalability
- ✅ Cross-service message broadcasting
- ✅ Automatic connection cleanup
- ✅ Heartbeat mechanism
- ✅ User-based message targeting
- ✅ Graceful shutdown
- ✅ Connection pooling and management
go mod init your-sse-service
go get github.com/go-redis/redis/v8
go get github.com/gorilla/muxpackage main
import (
"log"
"os"
"github.com/charmmtech/sseor"
)
func main() {
// Initialize SSE manager
sse, err := sseor.NewManager("redis://localhost:6379/5")
if err != nil {
log.Fatalf("Failed to create SSE manager: %v", err)
}
defer sse.Close()
// Create your handler
transactionHandler := NewTransactionHandler()
// Setup SSE route
sse.Route("/api/v1/sse/transactions", transactionHandler.Handle)
// Start server
log.Println("🚀 [SSEOR]: Server started on port 9090")
if err := sse.ListenAndServe(":9090"); err != nil {
log.Println("👮 [SSEOR]: failed to start server:", err)
os.Exit(1)
}
}NewManager(redisURL string) (*Manager, error)- Create new SSE managerRoute(path string, handler HandlerFunc)- Register SSE routeListenAndServe(addr string) error- Start HTTP serverHandleSSE(w, r, userID string) error- Handle SSE connectionPublish(ctx, userID string, event Event) error- Send event to userPublishToAll(ctx context.Context, event Event) error- Broadcast to allGetConnectionCount(userID string) int- Get user's active connectionsGetTotalConnections() int- Get total active connectionsClose() error- Graceful shutdown
type Event struct {
ID string `json:"id,omitempty"`
Type string `json:"type"`
Data map[string]interface{} `json:"data"`
Retry int `json:"retry,omitempty"`
}func (h *Handler) Handle(w http.ResponseWriter, r *http.Request, manager *sseor.Manager) {
userID := r.URL.Query().Get("user_id")
if userID == "" {
http.Error(w, "user_id required", http.StatusBadRequest)
return
}
if err := manager.HandleSSE(w, r, userID); err != nil {
log.Printf("SSE error: %v", err)
}
}// Send to specific user
event := sseor.Event{
Type: "transaction.updated",
Data: map[string]interface{}{
"transaction_id": "txn_123",
"status": "completed",
"amount": 500.00,
},
}
if err := manager.Publish(ctx, "user123", event); err != nil {
log.Printf("Failed to publish event: %v", err)
}
// Broadcast to all users
if err := manager.PublishToAll(ctx, event); err != nil {
log.Printf("Failed to broadcast event: %v", err)
}const eventSource = new EventSource('http://localhost:9090/api/v1/sse/transactions?user_id=user123');
eventSource.addEventListener('transaction.updated', function(event) {
const data = JSON.parse(event.data);
console.log('Transaction update:', data);
});
eventSource.onerror = function(event) {
console.error('SSE error:', event);
};The Redis URL supports various formats:
redis://localhost:6379/0redis://user:password@localhost:6379/0rediss://localhost:6379/0(SSL)
REDIS_URL=redis://localhost:6379/5
SERVER_PORT=9090
LOG_LEVEL=info┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Service A │ │ Service B │ │ Service C │
│ (SSE Manager) │ │ (SSE Manager) │ │ (Publisher) │
└─────────┬───────┘ └─────────┬───────┘ └─────────┬───────┘
│ │ │
└──────────────────────┼──────────────────────┘
│
┌─────────────┴─────────────┐
│ Redis PubSub │
│ (Cross-service msg) │
└───────────────────────────┘
-
Start Redis:
docker run -d -p 6379:6379 redis:alpine
-
Run the example:
go run main.go
-
Open
client_example.htmlin your browser -
Test the flow:
- Connect to SSE
- Trigger a deposit
- Watch real-time updates
- Use connection pooling for Redis
- Implement proper authentication
- Add rate limiting
- Monitor connection counts
- Use HTTPS in production
- Configure proper CORS headers
- Implement reconnection logic on client-side
- Add structured logging
- Set up health checks
MIT License