Timeouts, Logging and Fallbacks
Timeouts
Without timeouts a slow product-service makes order-service threads wait for a long time, and soon order-service has no free threads either. Always set a connect timeout (time to open the connection) and a read timeout (time to wait for the response):
spring:
cloud:
openfeign:
client:
config:
default: # every Feign client
connect-timeout: 2000 # ms
read-timeout: 3000
logger-level: basic # none | basic | headers | full
payment-service: # override for one client (by name)
read-timeout: 8000 # banks are slow
circuitbreaker:
enabled: true # enables fallbacks (needs a circuit breaker on the classpath)
logging:
level:
com.shopease.order.client: DEBUG # Feign logs at DEBUGExample
When product-service takes 5 seconds:
curl -i localhost:8080/api/orders/1001/detailsExample output
feign.RetryableException: Read timed out executing GET http://product-service/api/products/7
at feign.FeignException.errorExecuting(FeignException.java:300)
Caused by: java.net.SocketTimeoutException: Read timed outFallbacks
With spring.cloud.openfeign.circuitbreaker.enabled: true and a circuit breaker on the classpath (spring-cloud-starter-circuitbreaker-resilience4j), you can give a client a fallback. A FallbackFactory also receives the cause:
@FeignClient(name = "product-service", path = "/api/products",
fallbackFactory = ProductClientFallbackFactory.class)
public interface ProductClient {
@GetMapping("/{id}")
ProductDto getProduct(@PathVariable("id") Long id);
}
@Component
@Slf4j
class ProductClientFallbackFactory implements FallbackFactory<ProductClient> {
private final ProductCache cache; // last known product data (e.g. Redis)
ProductClientFallbackFactory(ProductCache cache) { this.cache = cache; }
@Override
public ProductClient create(Throwable cause) {
return id -> {
log.warn("product-service failed for {}: {}", id, cause.toString());
return cache.find(id).orElseThrow(() -> new ServiceUnavailableException("product-service down"));
};
}
}Note: A fallback must be honest. Showing a cached product name and price on a product page is fine. Placing an order with a cached price or stock value is not; there, failing with a clear 503 is correct.
Exercise
Exercise: user-service is fast but payment-service is slow. Configure Feign so all clients have a 1 s connect timeout and 2 s read timeout, user-service gets 1 s read timeout, payment-service 10 s, and only payment-service logs full request/response bodies.
Show answer
spring:
cloud:
openfeign:
client:
config:
default:
connect-timeout: 1000
read-timeout: 2000
logger-level: basic
user-service:
read-timeout: 1000
payment-service:
read-timeout: 10000
logger-level: full
logging:
level:
com.shopease.order.client: DEBUG