Zuul 4.0.0 replaces the RxJava Observable based async filter execution with CompletableFuture. RxJava 1.x is long past end-of-life, and moving to CompletableFuture lets the filter runner drop its custom RxJava Scheduler and Observer for plain JDK primitives.
Breaking changes
ZuulFilter.applyAsyncnow returnsCompletableFuture<O>instead ofObservable<O>. The Observable-based method is renamed toapplyAsyncObservableand deprecated. Both have default bridge implementations on the interface, so a filter can override either one (#2075)- The
debugRouting/debugRequestinfrastructure is removed (#2078) - Connection draining is now event-based rather than channel-attribute based (#2161). Zuul-core fires a
ConnectionCloseEventdown the pipeline instead of setting a channel attribute that every write has to check, so a graceful shutdown starts immediately on the event instead of waiting for the next response (an idle HTTP/1.1 connection can be closed right away), and OOS closes get random jitter to spread them out. The close handlers were reworked andCLOSE_AFTER_RESPONSEwas removed - see Connection draining below SessionContextno longer extendsHashMap<String, Object>; it now wraps one via composition (#2171) and is@NullMarked(#2174). The commonMapmethods are re-exposed directly, but code that relied on it being aMapneeds updating, and nullmarking turns the accessors@Nullablewhich can surface new NullAway warnings in consumers. ThegetRouteHost/setRouteHost/removeRouteHostaccessors are removed - use the genericget/setwith your ownKey<T>.
Migrating a filter
A filter overriding applyAsync to return an Observable will not compile against 4.0.0. The minimal fix is a one-line rename:
Before:
@Override
public Observable<HttpResponseMessage> applyAsync(HttpRequestMessage request) {
return service.call(request).map(this::toResponse);
}After:
@Override
public Observable<HttpResponseMessage> applyAsyncObservable(HttpRequestMessage request) {
return service.call(request).map(this::toResponse);
}The default applyAsync bridges applyAsyncObservable back to a CompletableFuture, so the rest of the filter keeps working unchanged. The filter should be rewritten to return a CompletableFuture directly and delete applyAsyncObservable. applyAsyncObservable will be removed entirely in a later 4.x release.
Connection draining
Consumers that build their own channel pipelines need small updates:
- The close and expiry handlers moved to the
com.netflix.netty.common.closepackage - HTTP/2 connection close now lives on the connection pipeline, not the stream - drop any stream-level close-handler wiring
CLOSE_AFTER_RESPONSE/ theallow_then_closerejection type is gone; throttled connections just close
Additional Changes
Also in this release: lazy hashCode caching (#2085), Objects.requireNonNull / @NonNull over preconditions (#2090), removal of unused classes (#2153), and a switch to typed SessionContext.Key<T> keys instead of stringly-typed context keys, both in the filter runner (#2167) and across SessionContext's own internal state (#2174).
Full Changelog: v3.6.21...v4.0.0