Skip to main content

Overview

The Sentinel Service is Argos Mesh’s security monitoring microservice that provides real-time DDoS attack detection. It consumes sales events from the Orders service, analyzes traffic patterns using a sliding window rate limiting algorithm, and publishes alerts when suspicious behavior is detected.
Sentinel uses Virtual Threads (Project Loom) for high-throughput event processing and Redis for distributed state management.

Architecture

Service Responsibilities

Traffic Monitoring

Consumes sales events and tracks request rates per IP address

Rate Limiting

Enforces 50 requests per 10-second window using Redis counters

Threat Detection

Identifies DDoS attacks and suspicious traffic patterns

Alert Publishing

Publishes critical alerts to the Notify service

Core Components

SalesListener

The SalesListener is the entry point for all sales events from RabbitMQ:
@Component
public class SalesListener {
    private final TrafficAnalyzer analyzer;
    private final RabbitTemplate rabbitTemplate;
    private final RedisService redisService;

    @RabbitListener(queues = "argos.sales.queue")
    public void processSalesEvents(ProductSoldInternalEvent data) {
        String ip = data.ipAddress();

        if (redisService.isBanned(ip)) {
            return; // Skip processing for already banned IPs
        }

        if (analyzer.processAndCheckLimit(ip)) {
            AlertInternalEvent event = new AlertInternalEvent(
                "Suspicious behavior", 
                ip, 
                "CRITICAL", 
                LocalDateTime.now()
            );            
            rabbitTemplate.convertAndSend(
                RabbitMQConfig.ALERT_EXCHANGE,
                "argos.alert.security",
                event
            );
        } else {
            System.out.println("[ Sentinel🛡️ ] Normal traffic of the IP: " + ip);
        }
    }
}
1

Event Reception

Listens to the argos.sales.queue for ProductSoldInternalEvent messages
2

Blacklist Check

Immediately returns if the IP is already banned (early exit optimization)
3

Traffic Analysis

Delegates to TrafficAnalyzer to check if the IP exceeds rate limits
4

Alert Generation

If suspicious behavior is detected, publishes an AlertInternalEvent with CRITICAL severity

TrafficAnalyzer

The TrafficAnalyzer implements the rate limiting algorithm using Redis:
@Component
public class TrafficAnalyzer {

    private final RedisService redisService;
    private final StringRedisTemplate redisTemplate;

    private static final int LIMIT = 50; 
    private static final int WINDOW_SECONDS = 10;
    private static final String RATE_PREFIX = "rate:ip:";

    public boolean processAndCheckLimit(String ip) {
        if (redisService.isBanned(ip)) return true;

        String key = RATE_PREFIX + ip;
        
        Long currentCount = redisTemplate.opsForValue().increment(key);

        if (currentCount != null && currentCount == 1) {
            redisTemplate.expire(key, Duration.ofSeconds(WINDOW_SECONDS));
        }

        if (currentCount != null && currentCount > LIMIT) {
            redisService.banIp(ip, 10);
            return true;
        }

        return false;
    }
}

Rate Limiting Algorithm

Sentinel implements a sliding window rate limiter using Redis atomic operations:

How It Works

1

Increment Counter

For each request, increment the Redis counter for the IP:
Long currentCount = redisTemplate.opsForValue().increment("rate:ip:127.0.0.1");
2

Set Expiration

If this is the first request (count = 1), set a TTL of 10 seconds:
if (currentCount == 1) {
    redisTemplate.expire(key, Duration.ofSeconds(10));
}
3

Check Threshold

If the count exceeds 50 requests, ban the IP:
if (currentCount > 50) {
    redisService.banIp(ip, 10); // Ban for 10 minutes
    return true; // Suspicious behavior detected
}

RedisService

The RedisService manages IP blacklisting:
@Service
public class RedisService {
    private final StringRedisTemplate redisTemplate;
    private static final String BLACKLIST_PREFIX = "blacklist:ip:";

    // Block an IP address for a specified duration
    public void banIp(String ipAddress, long durationMinutes) {
        redisTemplate.opsForValue().set(
            BLACKLIST_PREFIX + ipAddress,
            "BANNED",
            Duration.ofMinutes(durationMinutes)
        );
    }

    public boolean isBanned(String ipAddress) {
        return Boolean.TRUE.equals(
            redisTemplate.hasKey(BLACKLIST_PREFIX + ipAddress)
        ); 
    }
}
Key Methods:
  • banIp(): Adds an IP to the blacklist with a TTL of 10 minutes
  • isBanned(): Checks if an IP exists in the blacklist
The blacklist is shared across services - the Orders service also checks this Redis key before processing sales.

Event Schemas

ProductSoldInternalEvent (Input)

Sentinel consumes sales events from the Orders service:
public record ProductSoldInternalEvent(
    Long productID,
    Integer quantity,
    String ipAddress,
    LocalDateTime timeStamp
) {}
Key Fields:
  • ipAddress: Used for rate limiting and DDoS detection
  • timeStamp: Records when the sale occurred

AlertInternalEvent (Output)

When suspicious behavior is detected, Sentinel publishes alerts:
public record AlertInternalEvent(
    String type,        // "Suspicious behavior"
    String sourceIp,    // "127.0.0.1"
    String severity,    // "CRITICAL"
    LocalDateTime timeStamp
) {}

RabbitMQ Configuration

Sentinel configures two sets of queues and exchanges:

Input: Sales Queue

public static final String QUEUE_SALE = "argos.sales.queue";    
public static final String EXCHANGE_SOLD = "shop.exchange";
public static final String RK_SALES = "shop.event.sold";

Sales Event Consumption

Queue: argos.sales.queueExchange: shop.exchangeRouting Key: shop.event.soldReceives sales events from the Orders service

Output: Alert Queue

public static final String QUEUE_ALERT = "argos.alert.queue";
public static final String ALERT_EXCHANGE = "alert.exchange";
public static final String RK_ALERT = "argos.alert.#";

Alert Publishing

Queue: argos.alert.queueExchange: alert.exchangeRouting Key: argos.alert.securityPublishes alerts consumed by the Notify service

Virtual Threads Configuration

Sentinel leverages Java 21 Virtual Threads for high-throughput message processing:
spring.threads.virtual.enabled=true
spring.rabbitmq.listener.simple.prefetch=1

Why Virtual Threads?

Virtual Threads (Project Loom) enable Sentinel to handle thousands of concurrent messages efficiently:
  • Lightweight: Each message is processed on a virtual thread with minimal memory overhead
  • Scalable: Can handle high-volume traffic without thread pool exhaustion
  • Non-blocking: Ideal for I/O-heavy operations like Redis lookups
Virtual threads are automatically used by Spring Boot when spring.threads.virtual.enabled=true is set.

Traffic Flow

Security Features

DDoS Prevention

Automatically detects and blocks IPs exceeding 50 requests per 10 seconds

Distributed State

Uses Redis for shared blacklist across multiple service instances

Automatic Unbanning

IPs are automatically unbanned after 10 minutes using Redis TTL

Real-time Alerts

Immediately notifies operators of critical security events

Deployment Considerations

1

Redis Connection

Ensure Redis is accessible for distributed rate limiting
2

RabbitMQ Configuration

Configure connection to the message broker:
spring.rabbitmq.host=message_broker
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin123
3

Java 21 Runtime

Virtual threads require Java 21 or later
4

Horizontal Scaling

Multiple Sentinel instances can run concurrently, sharing Redis state

Next Steps

Orders Service

Learn about the service that generates sales events

Notify Service

See how alerts are delivered to operators

Redis Configuration

Configure the Redis cache layer

Rate Limiting

Deep dive into rate limiting strategies

Build docs developers (and LLMs) love