优化
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
package com.baiyue.common.web;
|
||||
|
||||
import feign.Request;
|
||||
import feign.Capability;
|
||||
import feign.Retryer;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** Explicitly selected by reliable event clients; never registered as a global configuration. */
|
||||
public class EventDeliveryFeignConfiguration {
|
||||
@Bean
|
||||
public DeliveryTransportCapability eventDeliveryCapability(
|
||||
@Value("${baiyue.event-delivery.connect-timeout-ms:3000}") int connectTimeout,
|
||||
@Value("${baiyue.event-delivery.read-timeout-ms:15000}") int readTimeout,
|
||||
@Value("${baiyue.event-delivery.lease-seconds:60}") long leaseSeconds) {
|
||||
if (connectTimeout < 1 || readTimeout < 1 || leaseSeconds < 1
|
||||
|| leaseSeconds > Integer.MAX_VALUE || (long) connectTimeout + readTimeout >= leaseSeconds * 1000) {
|
||||
throw new IllegalArgumentException("Event delivery timeouts must fit inside the lease");
|
||||
}
|
||||
return new DeliveryTransportCapability(new Request.Options(connectTimeout, TimeUnit.MILLISECONDS, readTimeout, TimeUnit.MILLISECONDS, false));
|
||||
}
|
||||
/** Applied at Feign build time after global/client properties, without changing other clients. */
|
||||
public static final class DeliveryTransportCapability implements Capability {
|
||||
private final Request.Options options;
|
||||
public DeliveryTransportCapability(Request.Options options) { this.options = options; }
|
||||
@Override public Request.Options enrich(Request.Options ignored) { return options; }
|
||||
@Override public Retryer enrich(Retryer ignored) {
|
||||
// The persisted Outbox owns the retry budget.
|
||||
return Retryer.NEVER_RETRY;
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.baiyue.common.web;
|
||||
|
||||
import feign.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
class EventDeliveryFeignConfigurationTest {
|
||||
interface Endpoint { @RequestLine("POST /events") String send(); }
|
||||
|
||||
@Test void builtClientOverridesLegacyTimeoutAndRedirectSettings() {
|
||||
var capability = new EventDeliveryFeignConfiguration().eventDeliveryCapability(3000, 15000, 60);
|
||||
var observed = new AtomicReference<Request.Options>();
|
||||
Endpoint endpoint = Feign.builder().options(new Request.Options(1, 5000))
|
||||
.addCapability(capability).client((request, options) -> {
|
||||
observed.set(options);
|
||||
return Response.builder().request(request).status(200).reason("OK")
|
||||
.headers(Map.of()).body("ok", StandardCharsets.UTF_8).build();
|
||||
}).target(Endpoint.class, "http://event-consumer.invalid");
|
||||
assertThat(endpoint.send()).isEqualTo("ok");
|
||||
assertThat(observed.get().connectTimeoutMillis()).isEqualTo(3000);
|
||||
assertThat(observed.get().readTimeoutMillis()).isEqualTo(15000);
|
||||
assertThat(observed.get().isFollowRedirects()).isFalse();
|
||||
}
|
||||
|
||||
@Test void retryAfterResponseCannotTriggerHiddenHttpRetries() {
|
||||
var calls = new AtomicInteger();
|
||||
Endpoint endpoint = Feign.builder().retryer(new Retryer.Default(1, 1, 3))
|
||||
.addCapability(new EventDeliveryFeignConfiguration().eventDeliveryCapability(3000, 15000, 60))
|
||||
.client((request, options) -> {
|
||||
calls.incrementAndGet();
|
||||
return Response.builder().request(request).status(503).reason("Unavailable")
|
||||
.headers(Map.of("Retry-After", List.of("0"))).build();
|
||||
}).target(Endpoint.class, "http://event-consumer.invalid");
|
||||
assertThatThrownBy(endpoint::send).isInstanceOf(RetryableException.class);
|
||||
assertThat(calls.get()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test void invalidTimeoutsAndTooShortLeaseAreRejected() {
|
||||
var configuration = new EventDeliveryFeignConfiguration();
|
||||
assertThatThrownBy(() -> configuration.eventDeliveryCapability(0, 15000, 60)).isInstanceOf(IllegalArgumentException.class);
|
||||
assertThatThrownBy(() -> configuration.eventDeliveryCapability(3000, 15000, 18)).isInstanceOf(IllegalArgumentException.class);
|
||||
assertThatThrownBy(() -> configuration.eventDeliveryCapability(3000, -1, 60)).isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user