-
Feature Request
-
Resolution: Done
-
Blocker
-
26.0.1.Final
Currently, WildFly only supports the ability to encode session affinity information into the JSESSIONID cookie, which is already used to uniquely identify a session. This plays nicely with the httpd family of load balancer modules (e.g. mod_cluster, mod_proxy_balancer, mod_jk) for which this mechanism was designed.
However, many load balancers rely on a separate cookie for implementing sticky sessions (e.g. HAProxy, IIS, etc.), and thus lack the ability to allow WildFly to guide requests for a given session to the server that can most efficiently handle it.
To resolve this, we should add a configuration attribute within the Undertow subsystem to allow the use of a configurable cookie to store session affinity information.
e.g.
<servlet-container name="..."> <session-cookie name="JSESSIONID" affinity-cookie-name="ARR" .../> </servlet>
If undefined, the route will be encoded into the session identifier via the existing CodecSessionConfig SessionConfig decorator.
If defined, we create a separate SessionConfig implementation for the route cookie (a copy of the session's SessionConfig, but with a distinct cookie name). We can then implement session affinity via a SessionConfig decorator. This decorator should be compatible with any future SessionConfig implementations we decide to support in WildFly (e.g. SSL, path parameters, etc.).
e.g.
public class AffinitySessionConfig implements SessionConfig { private final SessionConfig sessionConfig; private final SessionConfig routeConfig; private final RouteLocator locator; public AffinitySessionConfig(SessionConfig sessionConfig, SessionConfig routeConfig, RouteLocator locator) { this.sessionConfig = sessionConfig; this.routeConfig = routeConfig; this.locator = locator; } @Override public void setSessionId(HttpServerExchange exchange, String sessionId) { String existingSessionId = this.sessionConfig.findSessionId(exchange); if (!sessionId.equals(existingSessionId)) { this.sessionConfig.setSessionId(exchange, sessionId); } String route = this.locator.locate(sessionId); if (route != null) { String existingRoute = this.routeConfig.findSessionId(exchange); if (!route.equals(existingRoute)) { this.routeConfig.setSessionId(exchange, route); } } } @Override public void clearSession(HttpServerExchange exchange, String sessionId) { this.sessionConfig.clearSession(exchange, sessionId); String existingRoute = this.routeConfig.findSessionId(exchange); if (existingRoute != null) { this.routeConfig.clearSession(exchange, existingRoute); } } @Override public String findSessionId(HttpServerExchange exchange) { return this.sessionConfig.findSessionId(exchange); } @Override public SessionCookieSource sessionCookieSource(HttpServerExchange exchange) { return this.sessionConfig.sessionCookieSource(exchange); } @Override public String rewriteUrl(String originalUrl, String sessionId) { String url = this.sessionConfig.rewriteUrl(originalUrl, sessionId); String route = this.locator.locate(sessionId); return (route != null) ? this.routeConfig.rewriteUrl(url, route) : url; } }