1

I am attempting to implement a sticky session load balancer rule in a Zuul proxy service. I am using the code from this example: https://github.com/alejandro-du/vaadin-microservices-demo/blob/master/proxy-server/src/main/java/com/example/StickySessionRule.java

I seem to have everything configured correctly, and the rule is triggering in my debugger, but the call to RequestContext.getCurrentContext().getResponse() always returns null, so the cookie is never found, so the rule never takes effect.

The rest of the Zuul config is working 100%. My traffic is proxied and routed and I can use the app fine, only the sticky session rule is not working.

Is there another step I am missing to get the request wired in to this rule correctly?

My route config:

zuul.routes.appname.path=/appname/**
zuul.routes.appname.sensitiveHeaders=
zuul.routes.appname.stripPrefix=false
zuul.routes.appname.retryable=true
zuul.add-host-header=true
zuul.routes.appname.service-id=APP_NAME
hystrix.command.APP_NAME.execution.isolation.strategy=THREAD
hystrix.command.APP_NAME.execution.isolation.thread.timeoutInMilliseconds=125000

APP_NAME.ribbon.ServerListRefreshInterval=10000
APP_NAME.ribbon.retryableStatusCodes=500
APP_NAME.ribbon.MaxAutoRetries=5
APP_NAME.ribbon.MaxAutoRetriesNextServer=1
APP_NAME.ribbon.OkToRetryOnAllOperations=true
APP_NAME.ribbon.ReadTimeout=5000
APP_NAME.ribbon.ConnectTimeout=5000
APP_NAME.ribbon.EnablePrimeConnections=true
APP_NAME.ribbon.NFLoadBalancerRuleClassName=my.package.name.StickySessionRule

The app:

@EnableZuulProxy
@SpringBootApplication
public class ApplicationGateway {

    public static void main(String[] args) {
        SpringApplication.run(ApplicationGateway.class, args);
    }

    @Bean
    public LocationRewriteFilter locationRewriteFilter() {
        return new LocationRewriteFilter();
    }
}

EDIT: As requested, the code:

import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ZoneAvoidanceRule;
import com.netflix.zuul.context.RequestContext;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * @author Alejandro Duarte.
 */
public class StickySessionRule extends ZoneAvoidanceRule {

    public static final String COOKIE_NAME_SUFFIX = "-" + StickySessionRule.class.getSimpleName();

    @Override
    public Server choose(Object key) {
        Optional<Cookie> cookie = getCookie(key);

        if (cookie.isPresent()) {
            Cookie hash = cookie.get();
            List<Server> servers = getLoadBalancer().getReachableServers();
            Optional<Server> server = servers.stream()
                    .filter(s -> s.isAlive() && s.isReadyToServe())
                    .filter(s -> hash.getValue().equals("" + s.hashCode()))
                    .findFirst();

            if (server.isPresent()) {
                return server.get();
            }
        }

        return useNewServer(key);
    }

    private Server useNewServer(Object key) {
        Server server = super.choose(key);
        HttpServletResponse response = RequestContext.getCurrentContext().getResponse();
        if (response != null) {
            String cookieName = getCookieName(server);
            Cookie newCookie = new Cookie(cookieName, "" + server.hashCode());
            newCookie.setPath("/");
            response.addCookie(newCookie);
        }
        return server;
    }

    private Optional<Cookie> getCookie(Object key) {
        HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
        if (request != null) {
            Server server = super.choose(key);
            String cookieName = getCookieName(server);
            Cookie[] cookies = request.getCookies();
            if (cookies != null) {
                return Arrays.stream(cookies)
                        .filter(c -> c.getName().equals(cookieName))
                        .findFirst();
            }
        }

        return Optional.empty();
    }

    private String getCookieName(Server server) {
        return server.getMetaInfo().getAppName() + COOKIE_NAME_SUFFIX;
    }

}
JCN
  • 509
  • 1
  • 11
  • 25
  • can you paste your sticky session rule implementation as well? – Turbut Alin Jun 05 '19 at 16:14
  • also, what is the url you are calling the app? it should be localhost:port/appname/something in order for zuul to filter the request – Turbut Alin Jun 05 '19 at 16:21
  • @TurbutAlin I will paste the code, but it will be the exact code linked to in the post. Also, I am hitting the correct URL because Zuul is routing my requests correctly, I can use the app through Zuul, its just that the sticky session rule is not working. – JCN Jun 05 '19 at 16:25

2 Answers2

0

I think you are missing a PreFilter, like this:

import com.netflix.zuul.context.RequestContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

public class PreFilter extends com.netflix.zuul.ZuulFilter {

    @Override
    public Object run() {       
            RequestContext ctx = RequestContext.getCurrentContext();           
         RequestContext.getCurrentContext().set(FilterConstants.LOAD_BALANCER_KEY, ctx.getRequest());

        return null;
    }

    @Override
    public boolean shouldFilter() {     
        return true;
    }

    @Override
    public int filterOrder() {
        return FilterConstants.SEND_RESPONSE_FILTER_ORDER;
    }

    @Override
    public String filterType() {
        return "pre";
    }

}

Mark as Bean

@Bean
public PreFilter preFilter() {
        return new PreFilter();
}

And use it in your rule

@Override
    public Server choose(Object key) {        
        javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest) key;
0

RequestContext not working cause "hystrix.command.APP_NAME.execution.isolation.strategy=THREAD"

fengshu
  • 1
  • 1