// Implement simple circuit breaker given a rpc method.
// please consider the generic circuit breaker options like the following:
//
// * timeWindowSec - (10s) time sliding window size.
// * failureRatioThreshhold - (50%) failure ratio threshold in which the circuit open
// * circuitCloseTimeSec - (5s) required time to circuit re-close
// * min requests - 10
// Main class should be named 'Solution' and should not be public
package com.example.designpatterns.circuitbreaker;
import com.example.designpatterns.circuitbreaker.handler.MethodHandler;
import com.example.designpatterns.circuitbreaker.model.Method;
import com.example.designpatterns.circuitbreaker.model.Request;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
@Slf4j
@AllArgsConstructor
public class CircuitBreaker {
Map<String, MethodHandler> methodHandlerMap;
public void addMethod(String name) {
Method method = new Method(UUID.randomUUID().toString(), name, new AtomicBoolean(false));
MethodHandler methodHandler = new MethodHandler(method);
methodHandlerMap.put(name, methodHandler);
log.info("Added hystrix for this method");
}
public void sendRequest(String name, Request request) {
MethodHandler methodHandler = methodHandlerMap.get(name);
if (methodHandler == null) {
log.info("Hystrix not available in this method");
return;
}
if (methodHandler.getMethod().checkIfCircuitOpen()) {
log.info("Dropping this request as circuit is open");
return;
}
log.info("sending request to method {}", name);
methodHandler.getMethod().addRequest(request);
log.info(String.valueOf(methodHandler.getMethod().getListOfRequest().size()));
methodHandler.run();
}
}
package com.example.designpatterns.circuitbreaker.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@Getter
@Data
@Slf4j
public class Method {
String id;
String name;
AtomicBoolean isCircuitOpen;
List<Request> listOfRequest;
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();
public Method(String _id, String _name, AtomicBoolean _isCircuitOpen) {
id = _id;
name = _name;
isCircuitOpen = _isCircuitOpen;
listOfRequest = new ArrayList<>();
}
public void addRequest(Request request) {
reentrantLock.lock();
if (isCircuitOpen.get() == true) {
log.error("Circuit is open. Cannot send request");
} else {
listOfRequest.add(request);
}
reentrantLock.unlock();
}
public void circuitOpen() {
reentrantLock.lock();
isCircuitOpen.compareAndSet(isCircuitOpen.get(), true);
reentrantLock.unlock();
}
public void circuitClose() {
reentrantLock.lock();
isCircuitOpen.compareAndSet(isCircuitOpen.get(), false);
log.info("Closing circuit");
reentrantLock.unlock();
}
public boolean checkIfCircuitOpen() {
return isCircuitOpen.get();
}
}
package com.example.designpatterns.circuitbreaker.model;
import com.example.designpatterns.circuitbreaker.commons.ResponseCode;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
@AllArgsConstructor
@Getter
@Data
@Builder
public class Request {
String requestId;
ResponseCode responseCode;
long time;
}
package com.example.designpatterns.circuitbreaker.handler;
import com.example.designpatterns.circuitbreaker.commons.ResponseCode;
import com.example.designpatterns.circuitbreaker.model.Method;
import com.example.designpatterns.circuitbreaker.model.Request;
import com.example.designpatterns.messegingQueue.model.Message;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@Data
@Slf4j
public class MethodHandler {
Method method;
ReentrantLock reentrantLock;
Condition condition;
public MethodHandler(Method _method) {
method = _method;
reentrantLock = new ReentrantLock();
condition = reentrantLock.newCondition();
}
public void run() {
reentrantLock.lock();
List<Request> requestList = method.getListOfRequest();
long fromTime = System.currentTimeMillis() - 10000;
long toTime = System.currentTimeMillis();
long totalErrors = 0;
long totalRequest = 0;
log.info("from time {}, to Time {}", fromTime, toTime);
for (Request request : requestList) {
log.info(String.valueOf(request.getTime()));
if (request.getTime() >= fromTime && request.getTime() <= toTime) {
if (!request.getResponseCode().equals(ResponseCode.OK)) {
totalErrors++;
}
totalRequest++;
}
}
log.info("Total Request {}, Error request{} ,{}",totalRequest,totalErrors, (double)totalErrors/totalRequest);
if (totalRequest >= 2 && ((double)totalErrors/totalRequest >= 0.5)) {
log.info("Opening Circuit for 5 seconds");
method.circuitOpen();
new Thread(() -> {
try {
Thread.sleep(5000);
method.circuitClose();
} catch (Exception e) {
}
}).start();
}
reentrantLock.unlock();
}
}
package com.example.designpatterns.circuitbreaker.commons;
public enum ResponseCode {
OK,
INTERNAL_SERVER_ERROR,
BAD_REQUEST
}