【问题标题】:How to add custom restTemplate istance into RestTemplateEurekaHttpClient如何将自定义 restTemplate 实例添加到 RestTemplate Eureka HttpClient
【发布时间】:2023-03-09 07:10:01
【问题描述】:

我的意图是用 Spring restTemplate 提供的替换 eureka 客户端的 Jersey 实现。

然后,将其余模板的标准实现替换为信任所有证书的模板(我知道它没有被强制执行,但现在没关系),以便将我的服务注册到安全 (https) eureka 服务器。

我正在使用 Spring boot 2.2.1-RELEASE 和 Spring cloud Hoxton.RC2。

根据Spring Boot项目中的官方文档以这种方式声明eureka客户端

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.sun.jersey</groupId>
                    <artifactId>jersey-client</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jersey</groupId>
                    <artifactId>jersey-core</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jersey.contribs</groupId>
                    <artifactId>jersey-apache-client4</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

强制应用使用 RestTemplate 作为 Eureka 客户端。

我的应用程序中已经有一个自定义的 RestTemplate 实现,我们称之为 RestTemplateImpl,我将省略实现代码,因为它无关紧要。

当应用程序启动时,我希望我的 RestTemplateImpl 作为默认的 eureka 客户端实现被注入,但事实并非如此。

正如例外所说

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://localhost:3000/eureka/apps/MY-API": java.security.cert.CertificateException: No name matching localhost found; nested exception is javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching localhost found
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:751) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:677) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:586) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient.register(RestTemplateEurekaHttpClient.java:73) ~[spring-cloud-netflix-eureka-client-2.2.0.RC2.jar:2.2.0.RC2]
    at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$1.execute(EurekaHttpClientDecorator.java:59) [eureka-client-1.9.13.jar:1.9.13]
    at com.netflix.discovery.shared.transport.decorator.RedirectingEurekaHttpClient.executeOnNewServer(RedirectingEurekaHttpClient.java:118) ~[eureka-client-1.9.13.jar:1.9.13]
    at com.netflix.discovery.shared.transport.decorator.RedirectingEurekaHttpClient.execute(RedirectingEurekaHttpClient.java:79) ~[eureka-client-1.9.13.jar:1.9.13]
    at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.register(EurekaHttpClientDecorator.java:56) [eureka-client-1.9.13.jar:1.9.13]
    at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$1.execute(EurekaHttpClientDecorator.java:59) [eureka-client-1.9.13.jar:1.9.13]
    at com.netflix.discovery.shared.transport.decorator.RetryableEurekaHttpClient.execute(RetryableEurekaHttpClient.java:120) [eureka-client-1.9.13.jar:1.9.13]
    at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.register(EurekaHttpClientDecorator.java:56) [eureka-client-1.9.13.jar:1.9.13]
    at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$1.execute(EurekaHttpClientDecorator.java:59) [eureka-client-1.9.13.jar:1.9.13]
    at com.netflix.discovery.shared.transport.decorator.SessionedEurekaHttpClient.execute(SessionedEurekaHttpClient.java:77) [eureka-client-1.9.13.jar:1.9.13]
    at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.register(EurekaHttpClientDecorator.java:56) [eureka-client-1.9.13.jar:1.9.13]
    at com.netflix.discovery.DiscoveryClient.register(DiscoveryClient.java:847) [eureka-client-1.9.13.jar:1.9.13]
    at com.netflix.discovery.InstanceInfoReplicator.run(InstanceInfoReplicator.java:121) [eureka-client-1.9.13.jar:1.9.13]
    at com.netflix.discovery.InstanceInfoReplicator$1.run(InstanceInfoReplicator.java:101) [eureka-client-1.9.13.jar:1.9.13]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_212]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_212]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_212]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_212]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_212]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_212]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_212]

Spring 使用 RestTemplateEurekaHttpClient

查看源代码,我发现无法注入我的 RestTemplateImpl 来支持现有的。

public class RestTemplateTransportClientFactory implements TransportClientFactory {

    @Override
    public EurekaHttpClient newClient(EurekaEndpoint serviceUrl) {
        return new RestTemplateEurekaHttpClient(restTemplate(serviceUrl.getServiceUrl()),
                serviceUrl.getServiceUrl());
    }

    private RestTemplate restTemplate(String serviceUrl) {
        RestTemplate restTemplate = new RestTemplate();
        try {
            URI serviceURI = new URI(serviceUrl);
            if (serviceURI.getUserInfo() != null) {
                String[] credentials = serviceURI.getUserInfo().split(":");
                if (credentials.length == 2) {
                    restTemplate.getInterceptors().add(new BasicAuthorizationInterceptor(
                            credentials[0], credentials[1]));
                }
            }
        }
        catch (URISyntaxException ignore) {

        }

        restTemplate.getMessageConverters().add(0, mappingJacksonHttpMessageConverter());
        restTemplate.setErrorHandler(new ErrorHandler());

        return restTemplate;
    }

    .....

请...帮我解决这个问题...

【问题讨论】:

  • 为什么你需要一个自定义的RestTemplate 呢?只需为 SSL 正确配置 RestTEmplate(以允许一切),而不是创建各种自定义扩展。
  • 导致在从 rest 模板使用的 httpclient 中添加一个始终返回 true 的 NoopH​​ostnameVerifier 是不够的。 RestTemplateEurekaHttpClient 似乎没有从spring rest模板继承配置......这就是我问的原因

标签: java spring spring-boot spring-cloud spring-cloud-netflix


【解决方案1】:

这可以通过以下方式完成,但不建议在生产环境中使用

拥有您的自定义 RestTemplateTransportClientFactory 实现,例如使用以下代码,(使用 eureka-client-1.9.17 和 spring 2.2.2.RELEASE)

@Bean
public RestTemplateDiscoveryClientOptionalArgs discoveryClientOptionalArgs() throws Exception {
    RestTemplateDiscoveryClientOptionalArgs args = new RestTemplateDiscoveryClientOptionalArgs();
    args.setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
    args.setTransportClientFactories(new CustomRestTemplateTransportClientFactories());
    return args;
}

public class CustomRestTemplateTransportClientFactories extends RestTemplateTransportClientFactories {

    @Override
    public TransportClientFactory newTransportClientFactory(final EurekaClientConfig clientConfig,
            final Collection<Void> additionalFilters, final InstanceInfo myInstanceInfo,
            final Optional<SSLContext> sslContext, final Optional<HostnameVerifier> hostnameVerifier) {
        return new CustomRestTemplateTransportClientFactory();
    }
}

public class CustomRestTemplateTransportClientFactory extends RestTemplateTransportClientFactory {

    @Override
    public EurekaHttpClient newClient(EurekaEndpoint serviceUrl) {
        try {
            return new RestTemplateEurekaHttpClient(restTemplate(serviceUrl.getServiceUrl()),
                    serviceUrl.getServiceUrl());
        }
        catch (HttpClientException e) {
            e.printStackTrace();
        }
        return null;
    }

    private RestTemplate restTemplate(String serviceUrl) throws HttpClientException {
        /*
         * Inject your custom rest template
         */
        RestTemplate restTemplate = restTemplateConfig.restTemplate(null);
        try {
            URI serviceURI = new URI(serviceUrl);
            if (serviceURI.getUserInfo() != null) {
                String[] credentials = serviceURI.getUserInfo().split(":");
                if (credentials.length == 2) {
                    restTemplate.getInterceptors()
                            .add(new BasicAuthenticationInterceptor(credentials[0], credentials[1]));
                }
            }
        }
        catch (URISyntaxException ignore) {

        }

        restTemplate.getMessageConverters().add(0, mappingJacksonHttpMessageConverter());
        restTemplate.setErrorHandler(new ErrorHandler());

        return restTemplate;
    }
}

class ErrorHandler extends DefaultResponseErrorHandler {

    @Override
    protected boolean hasError(HttpStatus statusCode) {
        /**
         * When the Eureka server restarts and a client tries to sent a
         * heartbeat the server will respond with a 404. By default
         * RestTemplate will throw an exception in this case. What we want
         * is to return the 404 to the upstream code so it will send another
         * registration request to the server.
         */
        if (statusCode.is4xxClientError()) {
            return false;
        }
        return super.hasError(statusCode);
    }

}

希望这会有所帮助。谢谢。

【讨论】:

    猜你喜欢
    • 2015-12-13
    • 2019-01-22
    • 1970-01-01
    • 2018-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-13
    • 1970-01-01
    相关资源
    最近更新 更多