Spring Cloud RestTemplate @LoadBalanced 支持ip、域名、服务名 调用

Spring Cloud RestTemplate @LoadBalanced 支持ip、域名、服务名 调用

RestTemplate设置

@LoadBalanced
@Bean
public RestTemplate restTemplate(@Autowired ClientHttpRequestFactory clientHttpRequestFactory){
    return new RestTemplate(clientHttpRequestFactory);
}

如果使用时通过域名或者ip:port调用,例如

restTemplate.getForObject("https://fanyi.baidu.com/#en/zh/additional", String.class)

则会报错

java.lang.IllegalStateException: No instances available for fanyi.baidu.com
	at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.execute(RibbonLoadBalancerClient.java:105) ~[spring-cloud-netflix-ribbon-2.1.0.RELEASE.jar:2.1.0.RELEASE]
	at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.execute(RibbonLoadBalancerClient.java:93) ~[spring-cloud-netflix-ribbon-2.1.0.RELEASE.jar:2.1.0.RELEASE]
	at org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor.intercept(LoadBalancerInterceptor.java:55) ~[spring-cloud-commons-2.1.0.RELEASE.jar:2.1.0.RELEASE]

官方针对这种情况给出的方案是创建两个RestTemplate,在不同的的情况下区分使用

@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    RestTemplate loadBalanced() {
        return new RestTemplate();
    }

    @Primary
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

public class MyClass {
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    @LoadBalanced
    private RestTemplate loadBalanced;

    public String doOtherStuff() {
        return loadBalanced.getForObject("http://stores/stores", String.class);
    }

    public String doStuff() {
        return restTemplate.getForObject("http://example.com", String.class);
    }
}

@See https://cloud.spring.io/spring-cloud-static/spring-cloud-commons/2.0.3.RELEASE/single/spring-cloud-commons.html#_multiple_resttemplate_objects


我的方案

在“LoadBalancerInterceptor”(参见:LoadBalancerAutoConfiguration)之前添加自定的“MixLoadBalancerInterceptor”,拦截处理域名、ip方式调用

实现

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;

import MixLoadBalancerInterceptor;

@Configuration
public class LoadBalancedRestTemplateConfig {

    @Bean
    public SmartInitializingSingleton mixLoadBalancedRestTemplateInitializer(
            @Autowired List<RestTemplate> restTemplates,
            @Autowired MixLoadBalancerInterceptor mixLoadBalancerInterceptor) {
        return () -> {
            for (RestTemplate restTemplate : restTemplates) {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
                list.add(mixLoadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            }
        };
    }

    @Bean
    public MixLoadBalancerInterceptor mixLoadBalancerInterceptor(@Autowired ClientHttpRequestFactory clientHttpRequestFactory) {
        return new MixLoadBalancerInterceptor(clientHttpRequestFactory);
    }

}
import java.io.IOException;
import java.net.URI;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.PriorityOrdered;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.http.StreamingHttpOutputMessage;
import org.springframework.http.client.*;
import org.springframework.util.Assert;
import org.springframework.util.StreamUtils;

public class MixLoadBalancerInterceptor implements ClientHttpRequestInterceptor, PriorityOrdered {

    @Value("${mix.loadbalancer.ip.regex:(((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?))}")
    private String ipRegex;

    @Value("${mix.loadbalancer.domain.regex:.*\\.(com|xyz|net|top|tech|org|gov|edu|pub|cn|biz|cc|tv|info|im)}")
    private String domainRegex;

    @Value("${mix.loadbalancer.additional.regex:}")
    private String additionalRegex;


    private ClientHttpRequestFactory httpRequestFactory;

    public MixLoadBalancerInterceptor(ClientHttpRequestFactory httpRequestFactory) {
        this.httpRequestFactory = httpRequestFactory;
    }

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        final URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
        // 判断是否为ip、域名
        if (serviceName.matches(String.format("^%s|%s|%s$", ipRegex, domainRegex, additionalRegex))) {
            // 实际http请求,这段拷贝于:InterceptingClientHttpRequest
            HttpMethod method = request.getMethod();
            Assert.state(method != null, "No standard HTTP method");
            ClientHttpRequest delegate = httpRequestFactory.createRequest(request.getURI(), method);
            request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));
            if (body.length > 0) {
                if (delegate instanceof StreamingHttpOutputMessage) {
                    StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
                    streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(body, outputStream));
                }
                else {
                    StreamUtils.copy(body, delegate.getBody());
                }
            }
            return delegate.execute();
        }
        else {
            // 如果非ip、域名,继续走@LoadBalanced的原逻辑
            return execution.execute(request, body);
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

导入Config

@Import({ ***.config.LoadBalancedRestTemplateConfig.class })

在使用上面方式发起http请求时,将会被正确处理

默认支持:ip、以及域名后缀:com、xyz、net、top、tech、org、gov、edu、pub、cn、biz、cc、tv、info、im

如果想支持其他的域名格式,可以在application.properties中增加配置项

mix.loadbalancer.additional.regex=.*\\.(int|mil)

版本信息

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>Greenwich.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

<!-- 版本为:2.1.0.RELEASE -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

 

 

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值