RestTemplate 负载均衡原理

Posted gyli20170901

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RestTemplate 负载均衡原理相关的知识,希望对你有一定的参考价值。

RestTemplate 是通过拦截器改变请求的URI的方式来指定服务器的,此处将通过一个自定义LoadBalanced的方式来进行说明

 

1.导入jar包

<parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.0.2.RELEASE</version>
  </parent>

  <dependencies>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
  </dependencies>

 

2.自定义 MyLoadBalanced 注解

import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.*;

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MyLoadBalanced {
}

 

3.编写配置类

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.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;
import java.util.List;

@Configuration
public class MyConfig {

    @Autowired(required = false)
    @MyLoadBalanced
    private List<RestTemplate> tpls = Collections.emptyList();

    @Bean
    public SmartInitializingSingleton lbInitializing() {
        return new SmartInitializingSingleton() {
            @Override
            public void afterSingletonsInstantiated() {
                System.out.println("tpls 的数量:"+tpls.size());
                for (RestTemplate tpl : tpls) {
                    List<ClientHttpRequestInterceptor> interceptors = tpl.getInterceptors();
                    interceptors.add(new MyInterceptor());
                    tpl.setInterceptors(interceptors);
                }

            }
        };
    }
}

 

4.编写Controller测试接口 (访问 /getUser 可以发现执行的是自定义的 MyLoadBalanced  此处应该会报错,因为地址不存在,不过我们主要是为了测试是否会执行 MyLoadBalanced)

import com.idelan.ribbon.config.MyLoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@Configuration
public class MyController {

    @Bean
    @MyLoadBalanced
    public RestTemplate tplA() {
        return new RestTemplate();
    }

    @GetMapping(value = "/getUser")
    public String getUser() {
        RestTemplate restTemplate = tplA();
       String json = restTemplate.getForObject("http://smart-platform-base/platform/base/getUser", String.class);
        return json;
    }

    @GetMapping(value = "/hello")
    public String hello() {
        return "hello world";
    }
}

 

5.自定义拦截器来更改接口的访问地址 (@LoadBalanced 此处的逻辑会别我们复杂很多,我们只是简单模拟一下)

(1)自定义 Request 类

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;

import java.net.URI;
import java.net.URISyntaxException;

public class MyRequest implements HttpRequest {

    HttpRequest httpRequest;

    public MyRequest(HttpRequest httpRequest) {
        this.httpRequest = httpRequest;
    }

    @Override
    public HttpMethod getMethod() {
        return httpRequest.getMethod();
    }

    @Override
    public String getMethodValue() {
        return httpRequest.getMethodValue();
    }

    @Override
    public URI getURI() {
        try {
            URI newUri = new URI("http://localhost:8080/hello");
            return newUri;
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return httpRequest.getURI();
    }

    @Override
    public HttpHeaders getHeaders() {
        return httpRequest.getHeaders();
    }
}

(2)自定义拦截器

import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

import java.io.IOException;

public class MyInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
        System.out.println("~~~~~~~~自定义拦截器,uri:"+httpRequest.getURI());
        System.out.println("旧的uri:"+httpRequest.getURI());

        HttpRequest newRequest = new MyRequest(httpRequest);
        System.out.println("新的uri:"+newRequest.getURI());
        return clientHttpRequestExecution.execute(newRequest, bytes);
    }
}

测试:可以通过访问 /getUser 接口来测试,最终会返回 /hello 接口的内容,因为我们更改了访问地址

以上是关于RestTemplate 负载均衡原理的主要内容,如果未能解决你的问题,请参考以下文章

06 RestTemplate负载均衡

springcloud-feign实现原理,如何实现负载均衡?

SpringCloud -- Ribbon负载均衡(负载均衡流程负载均衡原理(探究原码)负载均衡策略懒加载(饥饿加载))

Spring Cloud客户端负载均衡 - Ribbon

spring cloud --- Ribbon 客户端负载均衡 + RestTemplate ---心得无熔断器

spring cloud --- Ribbon 客户端负载均衡 + RestTemplate ---心得无熔断器