1 package com.guinetik.examples;
2
3 import com.guinetik.rr.RocketRest;
4 import com.guinetik.rr.RocketRestConfig;
5 import com.guinetik.rr.http.CircuitBreakerOpenException;
6 import com.guinetik.rr.http.HttpConstants;
7 import com.guinetik.rr.result.ApiError;
8 import com.guinetik.rr.result.Result;
9
10 import java.time.LocalDateTime;
11
12
13
14
15
16 public class RocketRestCircuitBreakerExample implements Example {
17
18 private static final String API_BASE_URL = "https://httpstat.us";
19 private static final int CIRCUIT_FAILURE_THRESHOLD = 3;
20 private static final long CIRCUIT_RESET_TIMEOUT_MS = 5000;
21
22 @Override
23 public String getName() {
24 return "RocketRest Circuit Breaker Pattern";
25 }
26
27 @Override
28 public void run() {
29 System.out.println("Demonstrating RocketRest with Circuit Breaker pattern...");
30
31
32 RocketRestConfig config = RocketRestConfig.builder(API_BASE_URL)
33 .defaultOptions(options -> {
34
35 options.set(HttpConstants.CircuitBreaker.CIRCUIT_BREAKER_ENABLED, true);
36 options.set(HttpConstants.CircuitBreaker.CIRCUIT_BREAKER_FAILURE_THRESHOLD, CIRCUIT_FAILURE_THRESHOLD);
37 options.set(HttpConstants.CircuitBreaker.CIRCUIT_BREAKER_RESET_TIMEOUT_MS, CIRCUIT_RESET_TIMEOUT_MS);
38 options.set(HttpConstants.CircuitBreaker.CIRCUIT_BREAKER_FAILURE_POLICY, HttpConstants.CircuitBreaker.CIRCUIT_BREAKER_POLICY_SERVER_ONLY);
39 })
40 .build();
41
42
43 RocketRest client = new RocketRest(API_BASE_URL, config);
44
45 try {
46
47 demonstrateBasicCircuitBreaker(client);
48
49
50 demonstrateCircuitBreakerRecovery(client);
51
52 } finally {
53 client.shutdown();
54 }
55
56 System.out.println("\nCircuit Breaker example completed.");
57 }
58
59
60
61
62 private void demonstrateBasicCircuitBreaker(RocketRest client) {
63 System.out.println("\n=== Basic Circuit Breaker Demonstration ===");
64
65
66 System.out.println("\n1. Making successful requests (200 status)...");
67 try {
68
69 for (int i = 0; i < 2; i++) {
70 System.out.println(LocalDateTime.now() + " - Requesting 200 OK");
71 String response = client.get("/200", String.class);
72 System.out.println("✅ Success response: " + response);
73 Thread.sleep(500);
74 }
75 } catch (Exception e) {
76 System.out.println("❌ Unexpected error: " + e.getMessage());
77 }
78
79
80 System.out.println("\n2. Triggering circuit breaker with 500 errors...");
81 try {
82
83 for (int i = 0; i < CIRCUIT_FAILURE_THRESHOLD + 1; i++) {
84 try {
85 System.out.println(LocalDateTime.now() + " - Requesting 500 Internal Server Error");
86
87 Result<String, ApiError> result = client.fluent().get("/500", String.class);
88
89 if (result.isSuccess()) {
90 System.out.println("✅ Got response: " + result.getValue());
91 } else {
92 ApiError error = result.getError();
93 System.out.println("❌ Error (failure #" + (i+1) + "): " + error.getMessage() +
94 " (Status: " + error.getStatusCode() + ")");
95 }
96 } catch (CircuitBreakerOpenException e) {
97 System.out.println("⚡ Circuit breaker is now OPEN: " + e.getMessage());
98
99 long millisUntilReset = getTimeUntilReset(e);
100 System.out.println("Circuit will reset in approximately: " + millisUntilReset + "ms");
101 break;
102 } catch (Exception e) {
103 System.out.println("❌ Error: " + e.getMessage());
104 }
105
106 Thread.sleep(500);
107 }
108
109
110 System.out.println("\n3. Testing fast-fail with circuit open...");
111 for (int i = 0; i < 3; i++) {
112 try {
113 System.out.println(LocalDateTime.now() + " - Attempting request when circuit is OPEN");
114 client.get("/200", String.class);
115 System.out.println("✅ Request succeeded (circuit might be closed)");
116 } catch (CircuitBreakerOpenException e) {
117 System.out.println("⚡ Fast fail! Circuit is still OPEN: " + e.getMessage());
118 long millisUntilReset = getTimeUntilReset(e);
119 System.out.println("Circuit will reset in approximately: " + millisUntilReset + "ms");
120 } catch (Exception e) {
121 System.out.println("❌ Different error: " + e.getMessage());
122 }
123
124 Thread.sleep(1000);
125 }
126
127
128 System.out.println("\n4. Waiting for circuit reset timeout (" + CIRCUIT_RESET_TIMEOUT_MS + "ms)...");
129 Thread.sleep(CIRCUIT_RESET_TIMEOUT_MS);
130
131
132 System.out.println("\n5. Circuit should be HALF-OPEN now, trying to close it with successful requests...");
133 for (int i = 0; i < 2; i++) {
134 try {
135 System.out.println(LocalDateTime.now() + " - Requesting 200 OK to close circuit");
136 String response = client.get("/200", String.class);
137 System.out.println("✅ Success! Response: " + response);
138 System.out.println("Circuit should be CLOSED now");
139 } catch (CircuitBreakerOpenException e) {
140 System.out.println("⚡ Circuit is still OPEN: " + e.getMessage());
141 } catch (Exception e) {
142 System.out.println("❌ Error: " + e.getMessage());
143 }
144
145 Thread.sleep(500);
146 }
147
148 } catch (InterruptedException e) {
149 Thread.currentThread().interrupt();
150 System.out.println("Demo interrupted");
151 }
152 }
153
154
155
156
157 private void demonstrateCircuitBreakerRecovery(RocketRest client) {
158 System.out.println("\n=== Circuit Breaker Recovery Demonstration ===");
159 System.out.println("This example shows how the circuit breaker pattern helps with temporary service outages");
160
161 try {
162
163 System.out.println("\n1. Starting with circuit CLOSED (successful requests)");
164 for (int i = 0; i < 2; i++) {
165 try {
166 String response = client.get("/200", String.class);
167 System.out.println("✅ Success response: " + response);
168 } catch (Exception e) {
169 System.out.println("❌ Unexpected error: " + e.getMessage());
170 }
171 Thread.sleep(500);
172 }
173
174
175 System.out.println("\n2. Simulating a service outage with 500 errors");
176 for (int i = 0; i < CIRCUIT_FAILURE_THRESHOLD; i++) {
177 try {
178 client.get("/500", String.class);
179 System.out.println("Request somehow succeeded (unexpected)");
180 } catch (CircuitBreakerOpenException e) {
181 System.out.println("⚡ Circuit breaker opened after " + (i+1) + " failures: " + e.getMessage());
182 break;
183 } catch (Exception e) {
184 System.out.println("Error #" + (i+1) + ": " + e.getMessage());
185 }
186 Thread.sleep(500);
187 }
188
189
190 System.out.println("\n3. Verifying circuit is OPEN (requests should fail fast)");
191 try {
192 client.get("/200", String.class);
193 System.out.println("Circuit might not be open yet");
194 } catch (CircuitBreakerOpenException e) {
195 System.out.println("⚡ Circuit is open as expected: " + e.getMessage());
196 } catch (Exception e) {
197 System.out.println("❌ Unexpected error type: " + e.getMessage());
198 }
199
200
201 System.out.println("\n4. Waiting for circuit reset timeout to move to HALF-OPEN state...");
202 Thread.sleep(CIRCUIT_RESET_TIMEOUT_MS);
203
204
205 System.out.println("\n5. Service has recovered (returning 200 OK)");
206 try {
207 String response = client.get("/200", String.class);
208 System.out.println("✅ Success! Circuit should move from HALF-OPEN to CLOSED");
209 System.out.println("Response: " + response);
210 } catch (CircuitBreakerOpenException e) {
211 System.out.println("⚡ Circuit is still open (unexpected): " + e.getMessage());
212 } catch (Exception e) {
213 System.out.println("❌ Error: " + e.getMessage());
214 }
215
216
217 System.out.println("\n6. Verifying circuit is CLOSED with more requests");
218 for (int i = 0; i < 3; i++) {
219 try {
220 String response = client.get("/200", String.class);
221 System.out.println("✅ Success #" + (i+1) + ": " + response);
222 } catch (Exception e) {
223 System.out.println("❌ Error (unexpected): " + e.getMessage());
224 }
225 Thread.sleep(500);
226 }
227
228 } catch (InterruptedException e) {
229 Thread.currentThread().interrupt();
230 System.out.println("Demo interrupted");
231 }
232 }
233
234
235
236
237 private long getTimeUntilReset(CircuitBreakerOpenException e) {
238
239 long timeUntilReset = e.getEstimatedMillisUntilReset();
240
241
242
243 return timeUntilReset > 0 ? timeUntilReset : CIRCUIT_RESET_TIMEOUT_MS;
244 }
245 }