java - 假注册 - Spring Cloud - 无需功能区覆盖即可更改目标

标签 java spring-cloud spring-cloud-feign feign openfeign

简介

我希望能够有两个不同的 Spring 配置文件,并根据配置文件更改为我们的假构建器的硬编码地址。

目前有以下内容:

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

但我实际上想执行以下操作并覆盖地址:

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

为什么

有时我们不想在开发环境中运行所有服务。此外,某些服务有时只能通过 zuul 网关获得。

因此我们在不同的情况和条件下运行相同的代码。

技术细节

我们有以下代码用于构建我们的假客户端。

我们过去一直使用@FeignClient注释,但最近我们决定开始手动构建feignClients。

示例如下:

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

我们使用以下命令调用 feignRegistrar 类:

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 属性覆盖

我们还一直在使用诸如application-ENV.property之类的属性文件,其条目如下:

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

不幸的是,listOfServers 对我们来说还不够。我们也希望能够分配一个目录/路径。像这样的东西:

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

其他解决方法

我考虑过使用假拦截器将 header 潜入所有请求中,例如X-SERVICE-NAME。然后我们可以将所有服务指向一个地址(例如 localhost:9001),并将这些请求转发/代理到 localhost:9001/X-SERVICE-NAME。

但是,我更喜欢更简单的解决方案,例如:

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

但这不起作用:(

最佳答案

简介

我找到了一个解决方案,使用检测 header 的代理。 因此,我在 java 端有一个假冒拦截器,它将 header x-service-name 附加到每个假冒请求。

我还有一个 NodeJS 代理,它分析请求,找到 x-service-name,并将请求重写为:x-service-name/原始请求路径

这使我能够拥有 zuul 网关后面的所有微服务,而且还可以使用 eureka-over-ride 访问它们。

Java-Feign-拦截器

 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 代理

在示例中,我的 zuul 网关(或另一个代理)位于 localhost:9001 上。 我正在监听 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);
}

具有假客户端的 Java 应用程序内的属性文件

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

关于java - 假注册 - Spring Cloud - 无需功能区覆盖即可更改目标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58237646/

相关文章:

java - 何时使用 Spring Integration 与 Camel?

java - 我可以在 Spring Cloud Config Server 中托管 JSON 文件吗?

spring-cloud - 如何禁用特定@FeignClient 上的 Eureka 查找

java - Feign Client : Post a Map<String, Object> in Request Body => feign.FeignException: status 400 reading MAp

java - 是否可以使用 Rake 来自动化 Android 应用程序的构建过程?

Java:如何从 HTML 标签中剥离文本内容?

java - 将 Hashmap 恢复为之前的值

java - 在 hystrix 调用后备之前,Ribbon 是否会遍历所有 Eurkea 注册实例?

spring - 如何在 POST 中发送 ClientId 和 ClientSecret,而不是在带有 ClientCredentialsAccessTokenProvider 的 header 中

java - Spring boot - 线程/Feign-Client/消息/Streamlistener