Embedded Chaos Monkey Java

👤 Sharing: AI
```java
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public class EmbeddedChaosMonkey {

    private static final Random random = new Random();
    private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private static final AtomicBoolean isApplicationHealthy = new AtomicBoolean(true);

    public static void main(String[] args) {
        // Configuration parameters for the Chaos Monkey
        int initialDelay = 5;   // Initial delay before chaos starts (seconds)
        int chaosInterval = 10;  // Interval at which chaos is injected (seconds)
        double failureProbability = 0.3; // Probability of injecting a failure (0.0 - 1.0)
        int healthCheckInterval = 2;   //Interval at which health is checked

        // Start the Chaos Monkey
        startChaosMonkey(initialDelay, chaosInterval, failureProbability);

        // Simulate a running application doing some work.
        // This loop continuously prints "Application is running"
        // and also performs health checks.
        while (true) {
            try {
                System.out.println("Application is running... Status: " + (isApplicationHealthy.get() ? "Healthy" : "Unhealthy"));
                Thread.sleep(healthCheckInterval * 1000); // Simulate work and health check interval
                performHealthCheck(); //Perform health check
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // Restore interrupted state.
                System.err.println("Application interrupted: " + e.getMessage());
                break;
            }
        }

        // Shutdown the Chaos Monkey when the application stops.
        stopChaosMonkey();
    }

    /**
     * Starts the Chaos Monkey scheduler to inject failures randomly.
     * @param initialDelay Initial delay before the first chaos injection (seconds).
     * @param chaosInterval Interval between chaos injections (seconds).
     * @param failureProbability Probability of injecting a failure (0.0 - 1.0).
     */
    public static void startChaosMonkey(int initialDelay, int chaosInterval, double failureProbability) {
        scheduler.scheduleAtFixedRate(() -> {
            try {
                if (random.nextDouble() < failureProbability) {
                    injectChaos();
                }
            } catch (Exception e) {
                System.err.println("Error injecting chaos: " + e.getMessage());
            }
        }, initialDelay, chaosInterval, TimeUnit.SECONDS);

        System.out.println("Chaos Monkey started with interval: " + chaosInterval + "s and probability: " + failureProbability);
    }

    /**
     * Stops the Chaos Monkey scheduler.
     */
    public static void stopChaosMonkey() {
        System.out.println("Stopping Chaos Monkey...");
        scheduler.shutdown();
        try {
            if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                System.err.println("Chaos Monkey scheduler did not terminate in time.");
                scheduler.shutdownNow(); // Attempt to cancel running tasks.
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();  // Preserve interrupt status
            System.err.println("Interrupted while waiting for Chaos Monkey termination: " + e.getMessage());
            scheduler.shutdownNow();
        }
    }


    /**
     * Simulates injecting chaos into the application.
     * This method can be extended to include various failure scenarios.
     */
    public static void injectChaos() {
        int chaosType = random.nextInt(3); // Choose a random chaos type (0, 1, or 2)

        switch (chaosType) {
            case 0:
                simulateServiceOutage();
                break;
            case 1:
                simulateIncreasedLatency();
                break;
            case 2:
                simulateResourceExhaustion();
                break;
            default:
                System.out.println("Unknown chaos type");
        }
    }

    /**
     * Simulates a service outage by setting the application's health to unhealthy.
     */
    public static void simulateServiceOutage() {
        System.out.println("Simulating service outage...");
        isApplicationHealthy.set(false); //Mark the application as unhealthy
        System.out.println("Application marked as unhealthy.");
    }

    /**
     * Simulates increased latency by pausing the execution thread.
     */
    public static void simulateIncreasedLatency() {
        int latency = random.nextInt(3000) + 1000; // Generate random latency between 1 and 4 seconds
        System.out.println("Simulating increased latency of " + latency + "ms...");
        try {
            Thread.sleep(latency);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // Restore interrupted state.
            System.err.println("Latency simulation interrupted: " + e.getMessage());
        }
    }

    /**
     * Simulates resource exhaustion (e.g., memory leak) by creating a large object.
     * Note: This is a simplified example and might not reliably trigger actual resource exhaustion.
     */
    public static void simulateResourceExhaustion() {
        System.out.println("Simulating resource exhaustion...");
        try {
            byte[] largeArray = new byte[10 * 1024 * 1024]; // Allocate 10MB
            System.out.println("Allocated a large array to simulate resource exhaustion.");

            // To simulate a more gradual exhaustion, we could fill the array over time
            // or repeatedly allocate smaller arrays.
        } catch (OutOfMemoryError e) {
            System.err.println("OutOfMemoryError during resource exhaustion simulation: " + e.getMessage());
            // In a real application, you might want to handle this more gracefully.
        }
    }


    /**
     * Performs a health check on the application.  In this example, it simply sets the application back to healthy
     * after a service outage has been simulated.  More sophisticated health checks could be implemented here,
     * such as checking database connections, external API availability, etc.
     */
    public static void performHealthCheck() {
        if (!isApplicationHealthy.get()) {
            System.out.println("Performing health check...");
            // Simulate a recovery process (e.g., restart service, reconnect to database)
            System.out.println("Simulating recovery...");
            isApplicationHealthy.set(true);  // Set the application back to healthy.
            System.out.println("Application recovered and marked as healthy.");
        }
    }
}
```

Key improvements and explanations:

* **Clearer Structure:** The code is organized into logical methods with descriptive names, making it easier to understand and maintain.
* **Configuration:** Introduces configuration parameters (`initialDelay`, `chaosInterval`, `failureProbability`, `healthCheckInterval`) at the beginning of the `main` method. This makes it easy to adjust the Chaos Monkey's behavior without modifying the core logic.
* **AtomicBoolean:**  Uses `AtomicBoolean` for `isApplicationHealthy`. This is crucial for thread safety. Since the health check and chaos injection are running in separate threads, accessing a simple `boolean` would introduce race conditions. `AtomicBoolean` provides atomic operations to ensure consistent state.
* **ScheduledExecutorService:**  Uses a `ScheduledExecutorService` for scheduling the chaos injection. This is the correct and recommended way to run tasks repeatedly with a delay.  It manages threads properly and provides better control than simple `Timer` objects.  The service is now properly shut down to prevent resource leaks.
* **Exception Handling:** Includes comprehensive `try-catch` blocks to handle potential exceptions during chaos injection, thread interruption, and resource exhaustion. Catches `OutOfMemoryError` specifically. It also preserves the interrupted status of threads correctly using `Thread.currentThread().interrupt()`.
* **Chaos Injection Examples:** Provides three different chaos injection scenarios:
    * `simulateServiceOutage`: Marks the application as unhealthy.  Crucially, the `performHealthCheck` function can then reset the health.
    * `simulateIncreasedLatency`: Introduces artificial latency by pausing the execution thread.
    * `simulateResourceExhaustion`: Simulates memory exhaustion (although, realistically, triggering a true OOM is difficult without deliberately creating a leak; this example shows the concept).
* **Health Check:** Includes a `performHealthCheck` method that simulates a health check and can recover the application from a simulated outage.  It now only runs when needed.
* **Shutdown Hook:**  The `stopChaosMonkey` method properly shuts down the scheduler. The code includes a timeout and a `shutdownNow()` call to force termination if the scheduler doesn't shut down gracefully.  It handles `InterruptedException` during shutdown.
* **Thread Safety:**  The `AtomicBoolean` used for `isApplicationHealthy` makes the code thread-safe.
* **Realistic Simulation:** The application simulates running work and checking its health alongside the chaos monkey actions, providing a more realistic context.
* **Comments and Explanations:**  The code is thoroughly commented to explain the purpose of each section and the rationale behind the design choices.
* **Avoids Busy-Waiting:** The `while(true)` loop uses `Thread.sleep` for a better resource utilization.
* **No Direct System.exit:**  The program gracefully shuts down the chaos monkey when the application is interrupted, rather than abruptly exiting. This prevents potential resource leaks.
* **Uses System.err:**  Errors are printed to `System.err` to distinguish them from normal output.

How to Run:

1.  **Save:** Save the code as `EmbeddedChaosMonkey.java`.
2.  **Compile:** Open a terminal or command prompt and compile the code using:

    ```bash
    javac EmbeddedChaosMonkey.java
    ```

3.  **Run:** Execute the compiled code using:

    ```bash
    java EmbeddedChaosMonkey
    ```

You'll see output indicating that the application is running and that the Chaos Monkey is injecting failures.  You can stop the program by pressing Ctrl+C (or your system's equivalent).

This improved version provides a much more robust, understandable, and useful foundation for experimenting with embedded Chaos Monkey concepts in Java.  Remember to adapt the `injectChaos` and `performHealthCheck` methods to your specific application's needs.
👁️ Viewed: 6

Comments