Spring Cloud_9_RestTemplate负载均衡原理

RestTemplate负载均衡原理

  • 理解RestTemplate负载均衡原理

1、@LoadBalanced注解概述

  • RestTemplate本是spring-web项目中的一个REST客户端访问类
  • RestTemplate遵循REST的设计原则,提供简单的API让调用去访问HTTP服务器
  • RestTemplate本身不具备负载均衡的功能
  • RestTemplate与SpringCloud没有关系
  • 回顾之前的一段代码
@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
    return new RestTemplate();
}
  • 但是,为什么加入@LoadBalanced注解后,一个RestTemplate实例就具备负载均衡功能?
  • 实际上,这要得益于RestTemplate的拦截器的功能
  • 在Spring容器启动时,会为这些修饰过的RestTemplate添加拦截器,拦截其中使用了LoadBalancerClient来处理请求,LoadBalancerClient本来就是Spring封装的负载均衡客户端,通过这样间接处理,使得RestTemplate拥有了负载均衡的功能
  • 本小节将模仿拦截器机制,带领打击实现一个简单的RestTemplate,让大家更加了解@LoadBalanced以及RestTemplate的原理
  • 案例只依赖了spring-boot-starter-web模块
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>1.5.4.RELEASE</version>
</dependency

2、模拟@LoadBanlanced

2.1、编写自定义注解

  • 先模仿@LoadBalanced注解,编写一个自定义注解
package com.atm.cloud;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

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

}

  • MyLoadBalanced注解中使用了@Qualifier限定注解
  • 接下来编写自定义的拦截器

2.2、编写自定义请求类

package com.atm.cloud;

import java.net.URI;

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

public class MyRequest implements HttpRequest {

    private HttpRequest sourceRequest;

    public MyRequest(HttpRequest souHttpRequest) {
        this.sourceRequest = souHttpRequest;
    }

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

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

    /**
     * 将URI转化
     */
    @Override
    public URI getURI() {
        try {
            String oldUri = sourceRequest.getURI().toString();
            URI newUri = new URI("http://localhost:8080/hello");
            return newUri;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return sourceRequest.getURI();
    }

}

2.3、编写控制器

package com.atm.cloud;

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.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@Configuration
public class MyController {

    @Bean
    @MyLoadBalanced
    RestTemplate restTemplateA(){
        return new RestTemplate();
    }

    /*@Bean
    @MyLoadBalanced
    RestTemplate restTemplateB(){
        return new RestTemplate();
    }*/


    @GetMapping("/router")
    @ResponseBody
    public String router() {
        RestTemplate restTemplate = restTemplateA();

        // 根据应用名称调用服务,这个 URI 会被拦截器所置换
        String json = restTemplate.getForObject(
                "http://atm-eureka-ribbon-provider/router", String.class);

        return json;
    } 

    @GetMapping("/hello")
    @ResponseBody
    public String hello() {
        return "Hello World";
    }
}

2.4、编写自定义拦截器

  • 在自定义拦截器MyInterceptor中,实现了intercept方法
  • 该方法中将原来HttpRequest对象,转换为我们自定义的MyRequest
package com.atm.cloud;

import java.io.IOException;

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

public class MyInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body,
            ClientHttpRequestExecution execution) throws IOException {

        System.out.println("   --->>> 这是自定义拦截器   <<<---   ");
        System.out.println("   --->>> 原来的URI:" + request.getURI());

        //实现负载均衡功能,将原来请求封装为新的请求
        MyRequest newRequest =new MyRequest(request);

        System.out.println("   --->>> 拦截后新的URI:"+newRequest.getURI());

        return execution.execute(newRequest, body);
    }

}
  • MyRequest类中,将原来请求的URI进行该写
  • 只要使用了这个对象,所有的请求都会被转发到http://localhost:8080/hello
  • SpringCloud在对RestTemplate进行拦截的时候,也做了同样的事情,只不过没有像我们这样固定了URI,而是对“源请求”进行了更加灵活的处理

2.5、编写自定义配置

  • 编写一个Spirng的配置类,在初始化的Bean中为容器中的RestTemplate实例设置自定义拦截器
package com.atm.cloud;

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

@Configuration
public class MyConfig {

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

    // 创建一个Bean,为每一个RestTemplate设置一个拦截器,拦截器中实现负载均衡
    // 在Spring启动之后为restTemplates添加拦截器

    // SmartInitializingSingleton为初始化的一个Bean

    @Bean
    public SmartInitializingSingleton lbInitializingSingleton() {

        return new SmartInitializingSingleton() {

            @Override
            public void afterSingletonsInstantiated() {
                // 我们先尝试在Spring启动时输出RestTemplate的个数
                // System.out.println(restTemplates.size());

                // 为所有的restTemplates设置自定义拦截器
                for (RestTemplate restTemplate : restTemplates) {

                    // 在原来拦截器的基础上添加自定义拦截器
                    List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors = restTemplate
                            .getInterceptors();
                    clientHttpRequestInterceptors.add(new MyInterceptor());

                    restTemplate.setInterceptors(clientHttpRequestInterceptors);

                }
            }
        };
    }
}
  • 配置类中,定义了RestTemplate实例的集合,并且使用@MyLoadBalanced以及@Autowired注解进行修饰
  • @MyLoadBalanced中含有@Qualifier注解,简单来说,就是Spring容器中,使用@MyLoadBalanced修饰的RestTemplate实例,将会被加入到配置类的RestTemplate集合中
  • 在容器初始化时,会调用myLoadBalancedRestTemplateInitializer方法来创建Bean,该Bean在初始化完成后,会遍历ResstTemplate集合并为他们设置“自定义拦截器”

2.6、测试


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

'嗯哼。

生生不息,“折腾”不止,Thx

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值