refactoring(ping-service)
TODO-Roadmap.md
This commit is contained in:
@@ -1,13 +1,45 @@
|
||||
package at.mocode.temp.pingservice
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.RequestParam
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@RestController
|
||||
class PingController {
|
||||
class PingController(
|
||||
private val pingServiceCircuitBreaker: PingServiceCircuitBreaker
|
||||
) {
|
||||
|
||||
/**
|
||||
* Standard ping endpoint - maintains backward compatibility
|
||||
*/
|
||||
@GetMapping("/ping")
|
||||
fun ping(): Map<String, String> {
|
||||
return mapOf("status" to "pong")
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhanced ping endpoint with circuit breaker protection
|
||||
*
|
||||
* @param simulate - whether to simulate failures for testing circuit breaker
|
||||
*/
|
||||
@GetMapping("/ping/enhanced")
|
||||
fun enhancedPing(@RequestParam(defaultValue = "false") simulate: Boolean): Map<String, Any> {
|
||||
return pingServiceCircuitBreaker.ping(simulate)
|
||||
}
|
||||
|
||||
/**
|
||||
* Health check endpoint with circuit breaker protection
|
||||
*/
|
||||
@GetMapping("/ping/health")
|
||||
fun health(): Map<String, Any> {
|
||||
return pingServiceCircuitBreaker.healthCheck()
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint to test circuit breaker behavior by forcing failures
|
||||
*/
|
||||
@GetMapping("/ping/test-failure")
|
||||
fun testFailure(): Map<String, Any> {
|
||||
return pingServiceCircuitBreaker.ping(simulateFailure = true)
|
||||
}
|
||||
}
|
||||
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
package at.mocode.temp.pingservice
|
||||
|
||||
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* Service demonstrating a Circuit Breaker pattern with Resilience
|
||||
*
|
||||
* This service simulates potential failures and uses circuit breaker
|
||||
* to handle service degradation gracefully with fallback responses.
|
||||
*/
|
||||
@Service
|
||||
class PingServiceCircuitBreaker {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(PingServiceCircuitBreaker::class.java)
|
||||
|
||||
companion object {
|
||||
const val PING_CIRCUIT_BREAKER = "pingCircuitBreaker"
|
||||
private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary ping method with circuit breaker protection
|
||||
*
|
||||
* @param simulateFailure - if true, randomly throws exceptions to test circuit breaker
|
||||
* @return Map containing ping response with timestamp and status
|
||||
*/
|
||||
@CircuitBreaker(name = PING_CIRCUIT_BREAKER, fallbackMethod = "fallbackPing")
|
||||
fun ping(simulateFailure: Boolean = false): Map<String, Any> {
|
||||
logger.info("Executing ping service call...")
|
||||
|
||||
if (simulateFailure && Random.nextDouble() < 0.6) {
|
||||
logger.warn("Simulating service failure for circuit breaker testing")
|
||||
throw RuntimeException("Simulated service failure")
|
||||
}
|
||||
|
||||
val currentTime = LocalDateTime.now().format(formatter)
|
||||
logger.info("Ping service call successful")
|
||||
|
||||
return mapOf(
|
||||
"status" to "pong",
|
||||
"timestamp" to currentTime,
|
||||
"service" to "ping-service",
|
||||
"circuitBreaker" to "CLOSED"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback method called when circuit breaker is OPEN
|
||||
*
|
||||
* @param simulateFailure - original parameter (ignored in fallback)
|
||||
* @param exception - the exception that triggered the fallback
|
||||
* @return Map containing fallback response
|
||||
*/
|
||||
fun fallbackPing(simulateFailure: Boolean = false, exception: Exception): Map<String, Any> {
|
||||
logger.warn("Circuit breaker fallback triggered due to: {}", exception.message)
|
||||
|
||||
val currentTime = LocalDateTime.now().format(formatter)
|
||||
|
||||
return mapOf(
|
||||
"status" to "fallback",
|
||||
"message" to "Service temporarily unavailable",
|
||||
"timestamp" to currentTime,
|
||||
"service" to "ping-service-fallback",
|
||||
"circuitBreaker" to "OPEN",
|
||||
"error" to (exception.message ?: "Unknown error")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Health check method with circuit breaker protection
|
||||
*/
|
||||
@CircuitBreaker(name = PING_CIRCUIT_BREAKER, fallbackMethod = "fallbackHealth")
|
||||
fun healthCheck(): Map<String, Any> {
|
||||
logger.info("Executing health check...")
|
||||
|
||||
// Health check is now deterministic for reliable integration testing
|
||||
// Random failures were causing intermittent test failures
|
||||
|
||||
return mapOf(
|
||||
"status" to "UP",
|
||||
"timestamp" to LocalDateTime.now().format(formatter),
|
||||
"circuitBreaker" to "CLOSED"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback for health check
|
||||
*/
|
||||
fun fallbackHealth(exception: Exception): Map<String, Any> {
|
||||
logger.warn("Health check fallback triggered: {}", exception.message)
|
||||
|
||||
return mapOf(
|
||||
"status" to "DOWN",
|
||||
"message" to "Health check temporarily unavailable",
|
||||
"timestamp" to LocalDateTime.now().format(formatter),
|
||||
"circuitBreaker" to "OPEN"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,45 @@ management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health,info
|
||||
include: health,info,circuitbreakers
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
|
||||
# Resilience4j Circuit Breaker Configuration
|
||||
resilience4j:
|
||||
circuitbreaker:
|
||||
configs:
|
||||
default:
|
||||
# Circuit breaker opens when the failure rate exceeds 50%
|
||||
failure-rate-threshold: 50
|
||||
# Minimum number of calls to calculate the failure rate
|
||||
minimum-number-of-calls: 5
|
||||
# Time to wait before transitioning from OPEN to HALF_OPEN
|
||||
wait-duration-in-open-state: 10s
|
||||
# Number of calls in HALF_OPEN state before deciding to close/open
|
||||
permitted-number-of-calls-in-half-open-state: 3
|
||||
# Sliding window size for calculating failure rate
|
||||
sliding-window-size: 10
|
||||
# Type of sliding window (COUNT_BASED or TIME_BASED)
|
||||
sliding-window-type: COUNT_BASED
|
||||
# Record exceptions that should be considered as failures
|
||||
record-exceptions:
|
||||
- java.lang.Exception
|
||||
# Ignore certain exceptions (don't count as failures)
|
||||
ignore-exceptions:
|
||||
- java.lang.IllegalArgumentException
|
||||
instances:
|
||||
pingCircuitBreaker:
|
||||
# Use default configuration
|
||||
base-config: default
|
||||
# Override specific settings for this instance if needed
|
||||
failure-rate-threshold: 60
|
||||
minimum-number-of-calls: 4
|
||||
wait-duration-in-open-state: 5s
|
||||
|
||||
# Optional: Metrics configuration
|
||||
metrics:
|
||||
enabled: true
|
||||
legacy:
|
||||
enabled: true
|
||||
|
||||
Reference in New Issue
Block a user