A high-performance, scalable real-time chat service built with Go. Features WebSocket support, message persistence, file uploads, and push notifications.
____ _ _ ____ _
/ ___| |__ __ _| |_ / ___| ___ _ ____ _(_) ___ ___
| | | '_ \ / _` | __| \___ \ / _ \ '__\ \ / / |/ __/ _ \
| |___| | | | (_| | |_ ___) | __/ | \ V /| | (_| __/
\____|_| |_|\__,_|\__| |____/ \___|_| \_/ |_|\___\___|
- Real-time Messaging - WebSocket-based real-time communication
- Room Types - Support for Private chats, Groups, and Channels
- Message Types - Text messages, file attachments, replies, and forwards
- File Uploads - S3-compatible storage with presigned URLs
- Push Notifications - Firebase Cloud Messaging integration
- High Availability - PostgreSQL replication with PgPool load balancing
- Message Queue - RabbitMQ cluster for reliable message delivery
- Caching - Redis for connection management and caching
- JWT Authentication - Secure token-based authentication
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NGINX (Load Balancer) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββ΄ββββββββββββββββββ
β β
βββββββΌββββββ βββββββΌββββββ
β HTTP API β β WebSocket β
βββββββ¬ββββββ βββββββ¬ββββββ
β β
βββββββββββββββββββ¬ββββββββββββββββββ
β
βββββββββββββββββββΌββββββββββββββββββ
β Chat Service (Go) β
β - Fiber HTTP Framework β
β - Hexagonal Architecture β
β - Wire Dependency Injection β
βββββββββββββββββββ¬ββββββββββββββββββ
β
βββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββ
β β β
βββββββΌββββββ βββββββΌββββββ βββββββΌββββββ
β PgPool β β RabbitMQ β β Redis β
β β β Cluster β β β
βββββββ¬ββββββ βββββββββββββ βββββββββββββ
β
βββββββΌββββββββββββββββββββββββββββββββββ
β PostgreSQL β
β βββββββββββ βββββββββββ βββββββββββ
β β Primary β βReplica 1β βReplica 2β
β βββββββββββ βββββββββββ βββββββββββ
βββββββββββββββββββββββββββββββββββββββββ
- Go 1.22+ - Download Go
- Docker & Docker Compose - Install Docker
- Make - Build automation tool
| Service | Purpose | Required |
|---|---|---|
| PostgreSQL | Primary data storage with streaming replication | β Yes |
| Redis | WebSocket connection state, chunk upload tracking, distributed locks | β Yes |
| RabbitMQ | Message queue cluster for real-time message delivery | β Yes |
| S3-Compatible Storage | File uploads (AWS S3, MinIO, Arvan Cloud, etc.) | β Yes |
| Firebase | Push notifications for offline users | βͺ Optional |
This service requires an S3-compatible object storage for file uploads. You can use:
- AWS S3 - Amazon's object storage
- MinIO - Self-hosted S3-compatible storage
- Arvan Cloud - Iranian cloud provider (used in development)
- DigitalOcean Spaces - S3-compatible object storage
Configure in .env:
S3_ACCESS_KEY=your_access_key
S3_SECRET_KEY=your_secret_key
S3_ENDPOINT=https://your-s3-endpoint.com
S3_BUCKET_NAME=your-bucket-name
S3_REGION=defaultgit clone https://github.com/mehdi124/chat-service.git
cd chat-service# Copy the example environment file
cp .env.example .env
# Edit .env with your configuration
# IMPORTANT: Update all placeholder values with secure credentials# Generate JWT secret
openssl rand -base64 32
# Generate Redis password
openssl rand -hex 24
# Generate RabbitMQ Erlang cookie
openssl rand -hex 32
# Generate API key
openssl rand -hex 16# Start all infrastructure services (PostgreSQL, Redis, RabbitMQ, etc.)
docker-compose up -d# Install Go dependencies and development tools
make install
# Generate Wire dependency injection code
make wire
# Build the application
go build -o main .# Run migrations with your config file
./main migrate up -c .env# Run the chat service
./main app run -c .envThe service will be available at:
- HTTP API:
http://localhost:8000/api/v1 - WebSocket:
ws://localhost:8000/ws/v1
docker build -t chat-service:latest .The docker-compose.yaml includes all necessary services:
| Service | Description | Ports |
|---|---|---|
| PgPool | PostgreSQL connection pooler | 9999 |
| PostgreSQL Primary | Main database | - |
| PostgreSQL Replica 1-2 | Read replicas | - |
| Redis | Session/cache store | 6379 |
| RabbitMQ 1-3 | Message queue cluster | - |
| HAProxy | RabbitMQ load balancer | 5672, 15672 |
| Nginx | HTTP/WebSocket proxy | 80, 443 |
chat-service/
βββ cmd/ # CLI commands
β βββ app.go # Application runner
β βββ migrate.go # Database migration commands
β βββ root.go # Root command setup
β βββ token.go # Token generation utilities
βββ config/ # Configuration loading
βββ core/
β βββ app/ # Application bootstrap & DI
βββ database/
β βββ migration/ # SQL migration files
βββ infra/ # Infrastructure adapters
β βββ logger.go # Logging setup
β βββ pg.go # PostgreSQL connection
β βββ rabbitmq.go # RabbitMQ connection
β βββ redis.go # Redis connection
β βββ s3.go # S3 storage client
β βββ server.go # HTTP server setup
βββ internal/
β βββ chat/
β β βββ adapter/
β β β βββ inbound/ # HTTP, WebSocket, RabbitMQ handlers
β β β βββ outbound/ # Repository implementations
β β βββ core/
β β βββ domain/ # Domain models & value objects
β β βββ port/ # Inbound & Outbound port interfaces
β β βββ service/ # Business logic services
β βββ shared/ # Shared utilities & middleware
βββ volumes/ # Docker volume configurations
βββ docker-compose.yaml # Infrastructure services
βββ Dockerfile # Application container
βββ Makefile # Build automation
βββ .env.example # Environment template
# Run the application
./main app run -c <config-file>
# Database migrations
./main migrate up -c <config-file> # Apply migrations
./main migrate down -c <config-file> # Rollback migrations
./main migrate seed -c <config-file> # Seed database# Generate test JWT tokens for development
./main token init -c .env
β οΈ WARNING: Thetoken initcommand generates tokens for hardcoded test user IDs and is intended for development and testing only. Do NOT use this in production. For production environments, implement a proper user authentication flow through your application's login endpoint.
All API requests require authentication via JWT Bearer token.
Authorization: Bearer <jwt_token>Full API documentation is available in OpenAPI 3.0 format:
π docs/openapi.yaml
You can view it using:
- Swagger Editor - Paste the file content
- Swagger UI - Host locally
- VS Code with OpenAPI extension
- PRIVATE - One-to-one direct messages
- GROUP - Multi-user group chat (max 50 members by default)
- CHANNEL - Broadcast channel (max 100 members by default)
- TEXT - Plain text message
- FILE - File attachment
- REPLIED - Reply to another message
- FORWARDED - Forwarded message
Connect to the WebSocket endpoint for real-time messaging. Authentication is done via JWT token in the query parameter.
const token = 'your_jwt_token';
const ws = new WebSocket(`ws://localhost:8000/ws/v1/chat?token=${token}`);
// Handle incoming messages (MessagePack encoded)
ws.onmessage = async (event) => {
const buffer = await event.data.arrayBuffer();
const message = msgpack.decode(new Uint8Array(buffer));
console.log('Received:', message);
};WebSocket messages use MessagePack binary format for efficient serialization. MessagePack is a binary-based serialization format that is more compact and faster than JSON.
# JavaScript/Node.js
npm install @msgpack/msgpack
# Go (already included)
# github.com/vmihailenco/msgpack/v5import { encode } from '@msgpack/msgpack';
// Send a text message
const request = {
Type: 'message', // 'message' | 'seen' | 'ping'
RoomID: 'room-uuid', // Target room UUID
ReceiverID: 'user-uuid', // For private messages (optional if RoomID provided)
Content: 'Hello World', // Message content or filename
ContentType: 'text', // 'text' | 'image' | 'video' | 'audio' | 'file'
MessageType: 'direct', // 'direct' | 'replied' | 'forwarded'
Sign: 'unique-sign-123', // Unique client-side message identifier
ParentMessageID: '' // For replies/forwards
};
ws.send(encode(request));import { decode } from '@msgpack/msgpack';
// Response format
{
Type: 'response', // 'response' | 'new_message' | 'seen' | 'pong'
Success: true,
Error: '',
Data: {
ID: 'message-uuid',
RoomID: 'room-uuid',
SenderID: 'user-uuid',
Content: 'Hello World',
ContentType: 'text',
Status: 'sent',
CreatedAt: '2024-01-01T00:00:00Z',
// ... additional fields
}
}| Type | Description |
|---|---|
message |
Send a new message |
seen |
Mark messages as seen in a room |
ping |
Keep connection alive (receives pong) |
import "github.com/vmihailenco/msgpack/v5"
// Decode incoming message
var req ChatRequest
if err := msgpack.Unmarshal(msg, &req); err != nil {
return err
}
// Encode response
response := ChatResponse{
Type: "response",
Success: true,
Data: message,
}
encoded, _ := msgpack.Marshal(response)The service supports multipart/chunked file uploads for large files using S3's multipart upload API.
ββββββββββββ ββββββββββββ βββββββββ ββββββ
β Client β β Server β β Redis β β S3 β
ββββββ¬ββββββ ββββββ¬ββββββ βββββ¬ββββ βββ¬βββ
β β β β
β 1. Send chunk 1 β β β
β (FileID, ChunkIndex=1, β β β
β TotalChunk=N) β β β
βββββββββββββββββββββββββββββββ>β β β
β β 2. CreateMultipartUpload β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ>β
β β β UploadID β
β β<ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β 3. Store UploadID β β
β ββββββββββββββββββββββββββββββ>β β
β β 4. UploadPart (chunk 1) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ>β
β β β ETag β
β β<ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β 5. Store ETag β β
β ββββββββββββββββββββββββββββββ>β β
β 6. Repeat for chunks 2..N-1 β β β
βββββββββββββββββββββββββββββββ>β β β
β β β β
β 7. Send final chunk N β β β
βββββββββββββββββββββββββββββββ>β β β
β β 8. Get all ETags β β
β ββββββββββββββββββββββββββββββ>β β
β β<ββββββββββββββββββββββββββββββ β
β β 9. CompleteMultipartUpload β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ>β
β β β β
β 10. Broadcast to room β β β
β<βββββββββββββββββββββββββββββββ β β
- Minimum chunk size: 5 MB (S3 requirement for multipart uploads)
- Maximum file size: 20 MB (configurable via
S3_MAX_UPLOAD_SIZE) - Supported formats: Images, Videos, Audio, Documents, Archives
async function uploadFile(file, messageId, token) {
const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB minimum
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
for (let i = 0; i < totalChunks; i++) {
const start = i * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('File', chunk);
formData.append('ChunkIndex', i + 1);
formData.append('TotalChunk', totalChunks);
await fetch(`/api/v1/files/${messageId}/upload`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
body: formData
});
}
}The service uses Redis distributed locks to handle concurrent chunk uploads safely:
- Lock acquisition prevents duplicate
UploadIDcreation - Each chunk's ETag is tracked in Redis
- Upload completes automatically when all chunks are received
The service uses FNV-1a consistent hashing to distribute messages across multiple RabbitMQ queues, enabling horizontal scaling and ordered message delivery per room.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Message Publishing β
β β
β User sends message β Hash(UserID) β Queue N β Consumer β WebSocket β
β β
β Example with 10 queues: β
β βββββββββββ ββββββββββββββββ βββββββββββββββββββ β
β β User A βββββ>β hash % 10 = 3βββββ>β chat.queue.3 β β
β β User B βββββ>β hash % 10 = 7βββββ>β chat.queue.7 β β
β β User C βββββ>β hash % 10 = 3βββββ>β chat.queue.3 β (same queue) β
β βββββββββββ ββββββββββββββββ βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
With the default configuration of 10 queues per service instance:
| Metric | Value |
|---|---|
| Queues per instance | 10 |
| Messages/sec per queue | ~1,000 |
| Throughput per instance | ~10,000 msg/sec |
| Concurrent WebSocket connections | ~10,000 per instance |
To handle 100,000 concurrent users:
- Deploy 10 service instances behind a load balancer
- Each instance handles ~10,000 connections
- Total queue count: 100 (10 queues Γ 10 instances)
- Configure
RABBITMQ_TOTAL_QUEUE=10per instance
βββββββββββββββββββ
β Load Balancer β
β (Nginx) β
ββββββββββ¬βββββββββ
β
ββββββββββββββββββββββΌβββββββββββββββββββββ
β β β
ββββββΌβββββ ββββββΌβββββ ββββββΌβββββ
βInstance1β βInstance2β βInstance3β
β10 queuesβ β10 queuesβ β10 queuesβ
ββββββ¬βββββ ββββββ¬βββββ ββββββ¬βββββ
β β β
ββββββββββββββββββββββΌβββββββββββββββββββββ
β
ββββββββββΌβββββββββ
β RabbitMQ Clusterβ
β (3 nodes HA) β
βββββββββββββββββββ
- Ordered Delivery: Messages from the same user always go to the same queue
- Horizontal Scaling: Add more instances to handle more users
- Fault Tolerance: RabbitMQ cluster ensures no message loss
- Load Distribution: FNV-1a hash provides even distribution
# Run all tests
make test
# Run tests with coverage
make test-cover
# Run tests with race detector
make test-race- Environment Variables: Never commit
.envfiles. Use.env.exampleas a template. - JWT Secrets: Generate strong, random secrets for JWT signing.
- Database Passwords: Use strong, unique passwords for all database users.
- SSL/TLS: Enable SSL in production for all connections.
- API Keys: Rotate API keys regularly.
- Firebase Credentials: Keep service account keys secure and never expose them publicly.
Access at http://localhost:15672 (default: guest/guest in development)
Connect via PgPool at localhost:9999
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is open source and available under the MIT License.
- Fiber - Fast HTTP framework
- Wire - Compile-time dependency injection
- Bun - SQL-first ORM for Go
- Viper - Configuration management
- Cobra - CLI framework
Made with β€οΈ in Go