KNET oferuje różne strategie automatycznego ponawiania requestów przy błędach.
import rip.nerd.kitsunenet.interceptor.KNETRetryInterceptor
val client = KNETClient.builder()
.addInterceptor(KNETRetryInterceptor(
maxRetries = 3,
initialDelayMs = 1000
))
.build()
// Request będzie ponawiany do 3 razy
// z exponential backoff od 1s
// Metody fabryczne
KNETRetryInterceptor.simple(maxRetries = 3)
KNETRetryInterceptor.aggressive(maxRetries = 5)
val client = KNETClient.builder()
.addInterceptor(KNETExponentialBackoffInterceptor(
maxRetries = 5,
initialDelayMs = 100, // 100ms
maxDelayMs = 30_000, // Max 30s
multiplier = 2.0 // x2 każdy retry
))
.build()
// Opóźnienia: ~100ms → ~200ms → ~400ms → ~800ms → ~1600ms
val client = KNETClient.builder()
.addInterceptor(KNETExponentialBackoffInterceptor(
maxRetries = 5,
initialDelayMs = 100,
maxDelayMs = 30_000,
multiplier = 2.0,
jitterFraction = 0.1 // 10% losowości
))
.build()
// Opóźnienia z jitter: ~100ms → ~200ms → ~400ms → ...
// Zapobiega problemowi "thundering herd"
val interceptor = KNETRetryInterceptor(
maxRetries = 3,
initialDelayMs = 1000,
maxDelayMs = 30_000,
multiplier = 2.0,
// Statusy HTTP do retry
retryOnErrors = setOf(
408, // Request Timeout
429, // Too Many Requests
500, // Internal Server Error
502, // Bad Gateway
503, // Service Unavailable
504 // Gateway Timeout
),
// Retry przy błędach sieciowych (SocketTimeout, UnknownHost itp.)
retryOnNetworkError = true,
// Callback przy każdym retry
onRetry = { attempt, error, response ->
Log.w("Retry", "Próba $attempt, błąd: ${error?.message}")
}
)
val interceptor = KNETRetryInterceptor(
maxRetries = 3,
onRetry = { attempt, error, response ->
Log.w("Retry", "Próba $attempt")
Log.w("Retry", "Błąd: ${error?.message}")
Log.w("Retry", "Status: ${response?.statusCode}")
// Analytics
analytics.log("api_retry", mapOf(
"attempt" to attempt,
"error" to error?.javaClass?.simpleName
))
}
)
import rip.nerd.kitsunenet.interceptor.KNETAutoRetryInterceptor
import rip.nerd.kitsunenet.util.KNETRetryPolicies
val client = KNETClient.builder()
.addInterceptor(KNETAutoRetryInterceptor(
policy = KNETRetryPolicies.standard,
onRetry = { attempt, error ->
Log.d("KNET", "Retry #$attempt: ${error?.message}")
},
onGiveUp = { attempts, lastError ->
Log.e("KNET", "Poddałem się po $attempts próbach")
}
))
.build()
class ApiService(private val client: KNETClient) {
private suspend fun <T> withRetry(
maxRetries: Int,
initialDelay: Long,
exponential: Boolean = false,
block: suspend () -> T
): T {
var lastError: Exception? = null
var currentDelay = initialDelay
repeat(maxRetries) { attempt ->
try {
return block()
} catch (e: Exception) {
lastError = e
Log.w("API", "Retry ${attempt + 1}/$maxRetries: ${e.message}")
if (attempt < maxRetries - 1) {
kotlinx.coroutines.delay(currentDelay)
if (exponential) {
currentDelay = minOf(currentDelay * 2, 30_000)
}
}
}
}
throw lastError ?: Exception("Max retries exceeded")
}
}
| Strategia | Opóźnienia | Zastosowanie |
|---|---|---|
| Simple | 1s, 1s, 1s | Szybkie błędy serwera |
| Exponential | 1s, 2s, 4s, 8s | Przeciążony serwer |
| Exp + Jitter | ~1s, ~2s, ~4s | Wielu równoczesnych klientów |
| Linear | 1s, 2s, 3s, 4s | Stopniowe odciążanie serwera |