0

Introduction

I would like to be able to have two different spring profiles, and depending on the profile to change to a hardcoded address for our feign builders.

Currently was have the following:

return builder.target(cls, "http://" + serviceName);

But I would actually like to do the following and over-ride the address:

return builder.target(cls, "http://our-server:8009/" + serviceName);

Why

Sometimes we don't want to run all the services within our development environment. Additionally, some of the services are only available through a zuul gateway sometimes.

So we run the same code in different situations and conditions.

Technical Details

We have the following code that we use for building our Feign Clients.

We had been using the @FeignClient annotation in the past, but lately we decided to start building our feignClients manually.

Example below:

@FeignClient(name = "ab-document-store",  configuration = MultiPartSupportConfiguration.class, fallback = DocumentStoreFallback.class)

We call the feignRegistrar class with the following command:

return registerFeignClient(DocumentStoreClient.class, true);



@RequiredArgsConstructor
//@Component
@Slf4j
public class FeignRegistrar {

    @Autowired
    private Decoder decoder;

    @Autowired
    private Encoder encoder;

    @Autowired
    private Client client;

    @Autowired
    private Contract feignContract;

    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters;

    @Autowired
    private List<RequestInterceptor> interceptors;

    public <T> T register(Class<T> cls, String serviceName, boolean isDocumentStore) {

        if(isDocumentStore){
            encoder = new MultipartFormEncoder(new SpringEncoder(messageConverters));
        }

        //Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());

        Feign.Builder builder = Feign.builder()
            .client(client)
            .encoder(encoder)
            .decoder(decoder)
            .contract(feignContract)
            .logger(new Slf4Logger())
            .logLevel(Logger.Level.HEADERS);

        builder.requestInterceptor(new RequestInterceptor() {

            @Override
            public void apply(RequestTemplate template) {
                template.header("X-Service-Name", serviceName);
            }
        });

        for(RequestInterceptor interceptor : interceptors) { 

            builder.requestInterceptor(interceptor);
        }

        log.debug("Registering {} - as feign proxy ", serviceName);

        return builder.target(cls, "http://" + serviceName);
    }

    public static class Slf4Logger extends Logger {

        @Override
        protected void log(String configKey, String format, Object... args) {
            log.info("{} - {}", configKey, args);
        }
    }
}

Spring Cloud Property Over-ride

We have also been using property files such as application-ENV.property with entries such as the following:

ab-document-store.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
ab-document-store.ribbon.listOfServers: localhost:8025

Unfortunately, listOfServers is not enough for us. We would like to be able to assign a directory/path as well. Something like:

ab-document-store.ribbon.listOfServers: localhost:8025/ab-document-store

Otherworkaround

I have thought about sneaking in a header into all requests such as X-SERVICE-NAME using a feign interceptor. Then we could point all services to an address (e.g. localhost:9001) , and forward/proxy those requests to localhost:9001/X-SERVICE-NAME.

However, I would prefer a much easier solution such as:

ab-document-store.ribbon.listOfServers: localhost:8025/ab-document-store

But this doesn't work :(

Menelaos
  • 23,508
  • 18
  • 90
  • 155

1 Answers1

0

Introduction

I found a solution for this using a proxy that detects a header. So, I have a feign interceptor on the java-side that attaches a header x-service-name to every feign-request.

I also have a NodeJS proxy, that analyzes requests, finds x-service-name, and re-writes the requests to become: x-service-name/originalRequestPath.

This allows me to have all the microservices behind a zuul gateway but also access them using a eureka-over-ride.

Java-Feign-Interceptor

 Feign.Builder builder = Feign.builder()
            .client(client)
            .encoder(usedEncoder)
            .decoder(decoder)
            .contract(feignContract)
            .logger(new Slf4Logger())
            .logLevel(Logger.Level.HEADERS);

        builder.requestInterceptor(new RequestInterceptor() {

            @Override
            public void apply(RequestTemplate template) {
                template.header("X-Service-Name", serviceName);
            }
        });

NodeJS proxy

In the example, my zuul gateway ( or another proxy ) is on localhost:9001. I'm listening on localhost:1200 .

let enableProxyForJava = process.env.ENABLE_PROXY_FOR_JAVA;
if (enableProxyForJava != undefined &&  enableProxyForJava.toLowerCase() === 'true') {
    var httpProxyJava = require('http-proxy');
    var proxyJava = httpProxyJava.createProxy();

    gutil.log(  gutil.colors.green('Enabling Proxy for Java. Set your Eureka overrides to localhost:1200.') );

    require('http').createServer(function(req, res) {
        console.log("req.headers['x-service-name'] = " + req.headers['x-service-name']);
        console.log("Before req.url:"+ req.url);

        if( req.headers['x-service-name'] != undefined){
            let change =  req.headers['x-service-name'] +req.url;
            console.log("After req.url:"+ change);
            req.url = change;
        }

        proxyJava.web(req, res, {
            target: 'http://localhost:9001/'
        });

    }).listen(1200);
}

Property file inside Java Application that has feign clients

mbak-microservice1.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-microservice1.ribbon.listOfServers: localhost:1200

mbak-microservice2.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-microservice2.ribbon.listOfServers: localhost:1200

mbak-document-store.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-document-store.ribbon.listOfServers: localhost:1200
Menelaos
  • 23,508
  • 18
  • 90
  • 155