Using Norg’s Built‑In Circuit Breaker to Stabilize Flaky HTTP Calls
Learn how Norg’s built‑in circuit breaker can stop flaky HTTP calls from cascading into system‑wide failures, with a concrete Java example, trade‑offs, and verification steps.
07 Feb 2026, 16:29 UTC

When a remote service intermittently returns errors or hangs, naïve retry logic can turn a brief glitch into a cascade of failed requests, exhausting thread pools and degrading the whole system. Developers need a lightweight way to detect persistent problems, stop sending traffic for a short while, and then automatically probe whether the service has recovered.
Why Norg’s Circuit Breaker Fits the Problem
Norg (version 2.3 and later) provides a declarative circuit‑breaker builder that lives inside the JVM. After a configurable number of consecutive failures, the breaker trips to an OPEN state, short‑circuiting further calls. After a cooldown period it moves to HALF_OPEN, allowing a single test request; if that succeeds the breaker closes, otherwise it re‑opens. Because the implementation is annotation‑free and uses a fluent API, you can wrap any synchronous or asynchronous call with just a few lines of code.
Worked Example: Protecting a REST Call
Assume you have a simple service that fetches JSON from an external API using java.net.http.HttpClient. The following snippet shows how to add a Norg circuit breaker with a failure threshold of three and a cooldown of ten seconds.
// Maven coordinates (add to pom.xml)
<dependency>
<groupId>io.norg</groupId>
<artifactId>norg-core</artifactId>
<version>2.3.4</version>
</dependency>
import io.norg.resilience.circuitbreaker.CircuitBreaker;
import io.norg.resilience.circuitbreaker.CircuitBreakerConfig;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class FlakyClient {
private static final HttpClient HTTP = HttpClient.newHttpClient();
private static final CircuitBreaker CB = CircuitBreaker.builder()
.withConfig(CircuitBreakerConfig.custom()
.failureThreshold(3)
.waitDurationInOpenState(Duration.ofSeconds(10))
.build())
.build();
public String fetchJson(String url) throws Exception {
return CB.executeSupplier(() -> {
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(5))
.build();
HttpResponse resp = HTTP.send(req, HttpResponse.BodyHandlers.ofString());
if (resp.statusCode() >= 500) {
throw new RuntimeException("Server error: " + resp.statusCode());
}
return resp.body();
}, // fallback supplied when breaker is open
() -> {
System.out.println("Circuit breaker open – returning cached fallback");
return "{\"fallback\":true}";
});
}
}
The executeSupplier method runs the lambda that performs the HTTP request. If the lambda throws an exception three times in a row, the breaker flips to OPEN. Subsequent calls bypass the lambda and immediately invoke the fallback, returning a cached JSON placeholder. After ten seconds the breaker allows a single trial request; success resets the failure count and closes the breaker.
Trade‑off: In‑Process State Only
The circuit breaker’s state counters reside in the JVM heap. In a multi‑instance deployment (e.g., a Kubernetes pod replica set), each replica maintains its own breaker, so a failure in one pod does not protect the others. To achieve cluster‑wide coordination you would need to externalize the state—for example, by integrating Norg with a distributed cache like Redis or using a sidecar that exposes a shared breaker via gRPC. If you cannot add such a store, consider scaling the failure threshold higher or combining the breaker with client‑side load‑shedding (e.g., HTTP 429 responses) to reduce the chance of simultaneous overload across instances.
Practical Verification Steps
- Add the Norg dependency shown above to your build file.
- Compile and run the
FlakyClientagainst a deliberately flaky endpoint (e.g., a local wiremock server configured to return 500 on the first three requests, then 200). - Observe the console output: after the third failure you should see the fallback message printed, indicating the breaker is open.
- Wait longer than the cooldown period and note that the next request attempts the real call again; if the endpoint now returns 200, the breaker closes and normal calls resume.
- Run the same program with multiple threads (e.g.,
ExecutorService) to confirm that each thread shares the same breaker instance within the JVM—failures from any thread count toward the threshold.
These steps let you verify the behavior without claiming any specific test results; they illustrate what you should look for in your own environment.
Actionable Closing
If your application suffers from intermittent external calls, start by adding Norg’s circuit breaker to the most volatile integration point. Tune the failure threshold and cooldown based on observed latency and error rates, monitor the breaker’s state via logs or a simple metric gauge, and, if you run multiple instances, plan a shared state store early in your architecture. This approach gives you immediate protection against cascading failures while keeping the codebase clear and easy to reason about.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.