A robust distributed messaging system implemented in Java using Apache ZooKeeper for coordination and a custom Raft consensus algorithm for strong consistency and fault tolerance.
- Strong Consistency: Implements Raft consensus algorithm for linearizable message ordering
- High Availability: Automatic leader election and failover with sub-400ms recovery time
- Fault Tolerance: Tolerates up to (N-1)/2 node failures in an N-node cluster
- Message Durability: Persistent append-only logs with crash recovery
- Time Synchronization: NTP integration with Hybrid Logical Clock (HLC) for distributed ordering
- ZooKeeper Integration: Cluster membership management and leader discovery
- Real-time Messaging: HTTP-based client API with automatic leader discovery
- ServerNode: Distributed server instances that form the messaging cluster
- ClientNode: Client applications that send and retrieve messages
- RaftConsensus: Implementation of Raft consensus algorithm
- LogManager: Persistent message storage with crash recovery
- TimeService: NTP synchronization with Hybrid Logical Clock
- ZooKeeperManager: Cluster coordination and leader election
- ReplicationManager: Background log synchronization between nodes
Client → Leader → Followers → Consensus → Commit → Response
↘ ↗
ZooKeeper (Leader Discovery)
- Java 21 or higher
- Apache ZooKeeper 3.9.2 (for cluster coordination)
- Maven 3.8+ (for building)
Download and install Apache ZooKeeper from https://zookeeper.apache.org/
Set the ZOOKEEPER_HOME environment variable:
set ZOOKEEPER_HOME=C:\path\to\zookeepercd messageSys
mvn clean compile
mvn dependency:copy-dependencies -DoutputDirectory=target/lib# Windows
scripts\start-zookeeper.bat
# Or manually
cd %ZOOKEEPER_HOME%\bin
zkServer.cmdOpen separate terminals for each server:
# Terminal 1 - Server 1
scripts\start-server1.bat
# Terminal 2 - Server 2
scripts\start-server2.bat
# Terminal 3 - Server 3
scripts\start-server3.batOr manually:
java -cp "target\classes;target\lib\*" cs.ds.messagesys.DistributedServerNode config\server1.propertiesscripts\start-client.batOr manually:
java -cp "target\classes;target\lib\*" cs.ds.messagesys.ClientNode config\client.propertiesThe client provides an interactive CLI:
=== Distributed Messaging Client ===
Client ID: client-1
Commands:
send <message> - Send a message
get - Get new messages
get <index> - Get messages from index
status - Show current status
help - Show this help
quit - Exit
client-1> send Hello, distributed world!
Message sent successfully
client-1> get
[1] client-1: Hello, distributed world! (term=1, ts=1697123456789)
client-1> status
Client ID: client-1
Current Leader: ServerNode{nodeId='server-1', address='localhost:8081', zkPath='null'}
Last Retrieved Index: 1
-
POST /send- Send a message{ "message": "Hello, World!", "sender": "client-1" } -
GET /messages?fromIndex=1- Retrieve messages -
GET /status- Get node status
POST /appendEntry- Raft AppendEntries RPCPOST /requestVote- Raft RequestVote RPCGET /health- Health check
node.id=server-1
node.host=localhost
node.port=8081
zookeeper.connect=localhost:2181client.id=client-1
zookeeper.connect=localhost:2181mvn test- Start 3 servers
- Start client
- Send messages:
send Hello World - Retrieve messages:
get
- Start 3 servers, identify leader
- Send some messages
- Kill leader process (Ctrl+C)
- Send more messages (should work with new leader)
- Verify all messages are present
- Start 5 servers
- Send messages
- Stop 2 servers (minority)
- Continue sending (should work)
- Restart stopped servers
- Verify consistency
- Start servers with different system times
- Send messages from multiple clients
- Verify HLC maintains ordering
- Leader Election Time: < 400ms
- Message Throughput: ~1000 messages/second per leader
- Replication Latency: < 50ms for 3-node cluster
- Clock Drift Tolerance: ±40ms with NTP sync
- Recovery Time: < 5 seconds for follower rejoin
curl http://localhost:8081/statusResponse:
{
"nodeId": "server-1",
"role": "LEADER",
"term": 3,
"commitIndex": 42,
"isLeader": true,
"timestamp": 1697123456789,
"ntpSynced": true,
"ntpOffset": -12
}- Server logs: Console output with SLF4J
- Persistent logs:
data/log_<nodeId>.json - Raft state:
data/raft_state_<nodeId>.json
-
ZooKeeper Connection Failed
- Ensure ZooKeeper is running on port 2181
- Check firewall settings
-
Leader Election Timeout
- Verify all nodes can communicate
- Check network connectivity between servers
-
Message Loss
- Check that majority of nodes are running
- Verify disk space for log files
-
Clock Skew Warnings
- Synchronize system clocks with NTP
- Check network latency between nodes
Enable verbose logging:
java -Dorg.slf4j.simpleLogger.defaultLogLevel=debug -cp "target\classes;target\lib\*" cs.ds.messagesys.DistributedServerNode config\server1.properties- Leader Election: Randomized timeouts (150-300ms)
- Log Replication: Batched AppendEntries with heartbeats
- Safety: Log matching property ensures consistency
- Persistence: Current term, voted for, and log entries
- NTP Client: Queries pool.ntp.org, time.google.com
- Hybrid Logical Clock: Combines physical and logical time
- Drift Detection: Warns if clock drift exceeds 40ms
- Membership: Ephemeral sequential nodes under
/servers - Leader Discovery: Leader info stored in
/leader - Failure Detection: ZooKeeper session timeouts
- Fork the repository
- Create a feature branch
- Implement changes with tests
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
- Distributed Systems Team - Implementation of fault-tolerant messaging system
- Apache ZooKeeper team for coordination primitives
- Raft consensus algorithm by Diego Ongaro and John Ousterhout
- NTP protocol for time synchronization
- Hybrid Logical Clock concept by Kulkarni et al.