SpringCloud与Ribbon

  • SpringCloud中使用Ribbon
  • SpringCloud_7_Ribbon负载均衡器中,主要讲解Ribbon负载均衡机制
  • SpringCloud集成了Ribbon,结合Eureka,可实现客户端的负载均衡
  • SpingCloud_4_Eureka集群中所使用RestTemplate(被@LoadBalancer修饰),都已经拥有负载均衡功能
  • 本小节以RestTemplate为基础,讲述及测试Eureka中Ribbon配置
  • 使用Spring自动配置
  • 在配置文件中配置

1、准备工作

  • 新建Eureka服务器项目:atm_eureka_ribbon_server,端口为8761
  • 新建Eureka服务提供者项目:atm_eureka_ribbon_provider,主要工作

    1. 在控制器里面,发布一个REST服务,地址为“/person/{personId}”,请求后返回Person实例,其中Pereson的message为Http请求的URL
    2. 服务提供者需要启动两次,所以在控制台中需要输入启动端口
  • 新建Eureka服务调用者项目:atm_eureka_ribbon_invoker,对外端口9000

  • 下面是项目结构图

1.1、新建atm_eureka_ribbon_server

1.1.1、引入依赖
<!-- Spring Cloud -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
1.1.2、配置文件
server:
  port: 8761

eureka:
 client:
  registerWithEureka: false
  fetchRegistry: false
1.1.3、启动类
package com.atm.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class MyEurekaRibbonServer {

    public static void main(String[] args) {
        SpringApplication.run(MyEurekaRibbonServer.class, args);
    }

}

1.2、新建atm_eureka_ribbon_provider

1.2.1、引入依赖
<!-- Spring Cloud -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<!-- 服务提供者 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- 服务提供者 -->
1.2.2、配置文件
spring:
 application:
  name: atm-eureka-ribbon-provider
eureka:
 client:
  serviceUrl:
   defaultZone: http://localhost:8761/eureka/
1.2.3、启动类
package com.atm.cloud;

import java.util.Scanner;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class MyEurekaRibbonProvider {

    public static void main(String[] args) {

        // 读取控制台输入,避免端口冲突
        Scanner scanner = new Scanner(System.in);

        String port = scanner.nextLine();

        new SpringApplicationBuilder(MyEurekaRibbonProvider.class).properties(
                "server.port=" + port).run(args);
    }

}
1.2.4、控制器
package com.atm.cloud;

import javax.servlet.http.HttpServletRequest;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @RequestMapping(value = "/person/{personId}", method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
    public Person findPerson(@PathVariable("personId")Integer personId,HttpServletRequest request){
        Person person=new Person();

        person.setId(personId);
        person.setAge(18);
        person.setName(request.getRequestURL().toString());

        return person;
    }
}

1.3、新建atm_erueka_ribbon_invoker

1.3.1、引入依赖
<!-- Spring Cloud -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<!-- 服务调用者 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency><!-- 负载均衡框架 -->
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<!-- 服务调用者 -->
1.3.2、配置文件
server:
 port: 9000
spring:
 application:
  name: atm-eurake-ribbon-invoker
eureka:
 client:
  serviceUrl:
   defaultZone: http://localhost:8761/eureka/
1.3.3、启动类
package com.atm.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class MyEurekaRibbonInvoker {

    public static void main(String[] args) {
        SpringApplication.run(MyEurekaRibbonInvoker.class, args);
    }

}
1.3.4、控制器
package com.atm.cloud;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@Configuration
public class MyInvokerController {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

    @RequestMapping(value="/router",method=RequestMethod.GET,
            produces=MediaType.APPLICATION_JSON_VALUE)
    public String router() {
        RestTemplate restTemplate = getRestTemplate();

        // 根据应用名称调用服务
        String json = restTemplate.getForObject(
                "http://atm-eureka-ribbon-provider/person/1", String.class);

        return json;
    }
}

1.4、回顾

  • 启动atm_eureka_ribbon_server
  • 分别以8080、8081端口启动atm_eureka_ribbon_provider
  • 启动atm_eureka_ribbon_invoker
  • 浏览器访问http://localhost:9000/router


  • 端口轮询调用

2、调用者配置Ribbon

  • 之前已经讲述了负载规则以及Ping,在SpringCloud中,可将自定义的负载规则以及Ping类,发到服务器调用者中

2.1、自定义IRule(编写自定义规则类)

package com.atm.cloud;

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

import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.Server;

/**
 * 编写自定义规则
 * 
 * @author aitemi
 *
 */
public class MyRule implements IRule {

    ILoadBalancer iLoadBalancer;

    public Server choose(Object key) {
        // 获取所有的服务器
        List<Server> servers = iLoadBalancer.getAllServers();

        // 使用自定义规则,随机数大于7,访问8080端口,否则使用8081端口
        Random random = new Random();
        int ranNum = random.nextInt(10);
        System.out.println("-- 这是自定义规则类, 输出服务器信息  ,随机数为:" + ranNum + " --->>>");
        if (ranNum > 7) {

            return getServerByPort(servers, 8080);
        }

        return getServerByPort(servers, 8081);
    }

    public Server getServerByPort(List<Server> servers, int port) {

        for (Server server : servers) {
            if (server.getPort() == port) {
                System.out.println("     " + server.getHostPort());
                return server;
            }
        }
        return null;
    }

    public ILoadBalancer getLoadBalancer() {
        return this.iLoadBalancer;
    }

    public void setLoadBalancer(ILoadBalancer ibBalancer) {
        this.iLoadBalancer = ibBalancer;
    }

}

2.2、配置自定义规则

package com.atm.cloud;

import org.springframework.context.annotation.Bean;

import com.netflix.loadbalancer.IRule;

/**
 * 配置自定义规则
 * @author aitemi
 *
 */
public class MyConfig {

    @Bean
    public IRule getRule(){
        return new MyRule();
    }

    /**
     * 问题:如何让Spring容器知道,有这么一个配置类存在呢?
     */
}

2.3、Spring中自动配置

package com.atm.cloud;

import org.springframework.cloud.netflix.ribbon.RibbonClient;


/**
 * 1.使Spring容器得知我们自定义的配置类
 * 2.name="atm-eureka-ribbon-provider",建议调用那个服务就配置那个服务
 * 3.当调用"atm-eureka-ribbon-provider",这个服务的时候,将使用这个配置
 * 
 * @author aitemi
 *
 */
@RibbonClient(name = "atm-eureka-ribbon-provider", configuration = MyConfig.class)
public class MyInvokerConfig {

}
  • 启动atm_eureka_ribbon_server服务器
  • 启动atm_eureka_ribbon_provide客户端,分别以8080、8081端口启动
  • 启动atm_eureka_ribbon_invoker客户端
  • 浏览器访问http://localhost:9000/router
  • @RibbonClient(name = “atm-eureka-ribbon-provider”, configuration = MyConfig.class),这个时候访问atm-eureka-ribbon-provider,自定义规则生效

2.4、配置文件中配置

  • 测试时,记得先注释MyInvokerConfig中@RibbonClient
server:
 port: 9000
spring:
 application:
  name: atm-eurake-ribbon-invoker
atm-eureka-ribbon-provider:
 ribbon:
  NFLoadBalancerRuleClassName: com.atm.cloud.MyRule
eureka:
 client:
  serviceUrl:
   defaultZone: http://localhost:8761/eureka/

3、使用Spring封装的Ribbon

  • SpringCloud对Ribbon进行了封装(负载客户端、负载均衡器等,可以直接使用Spring的LoadBalancerClient来处理请求以及服务选择)

3.1、LoadBalancerClient

3.1.1、修改MyInvokerController

package com.atm.cloud;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
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 MyInvokerController {

    @LoadBalanced
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

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

        // 根据应用名称调用服务
        String json = restTemplate.getForObject(
                "http://atm-eureka-ribbon-provider/person/1", String.class);

        return json;
    }

    // 使用SpringCloud集成Ribbon -- start

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @GetMapping("/lb")
    @ResponseBody
    public ServiceInstance lb() {
        // 让SpringCloud集成的Ribbon帮助我们选择服务实例
        ServiceInstance serviceInstance = loadBalancerClient
                .choose("atm-eureka-ribbon-provider");
        return serviceInstance;
    }

    // 使用SpringCloud集成Ribbon -- end
}
  • 同样使用了自定义规则


3.2、SpringCloud中Ribbon的默认配置

  • 查看SpringCloud中Ribbon的默认配置
package com.atm.cloud;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
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;

import com.netflix.loadbalancer.ZoneAwareLoadBalancer;

@RestController
@Configuration
public class MyInvokerController {

    @LoadBalanced
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

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

        // 根据应用名称调用服务
        String json = restTemplate.getForObject(
                "http://atm-eureka-ribbon-provider/person/1", String.class);

        return json;
    }

    // 使用SpringCloud集成Ribbon -- start

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @GetMapping("/lb")
    @ResponseBody
    public ServiceInstance lb() {
        // 让SpringCloud集成的Ribbon帮助我们选择服务实例
        ServiceInstance serviceInstance = loadBalancerClient
                .choose("atm-eureka-ribbon-provider");
        return serviceInstance;
    }

    // 使用SpringCloud集成Ribbon -- end

    // 查看SpringCloud中的Ribbon配置 -- start

    @Autowired
    private SpringClientFactory springClientFactory;

    @GetMapping("/fa")
    public String factory() {
        System.out.println("----  >>> 查看默认配置   <<<  ----\n");
        ZoneAwareLoadBalancer zoneAwareLoadBalancer = (ZoneAwareLoadBalancer) springClientFactory
                .getLoadBalancer("default");

        System.out.println("ICientConfig\t"
                + springClientFactory.getLoadBalancer("default").getClass()
                        .getName() + "\n");

        System.out.println("IRule\t"
                + zoneAwareLoadBalancer.getRule().getClass().getName() + "\n");

        System.out.println("ServerList\t"
                + zoneAwareLoadBalancer.getServerListImpl().getClass()
                        .getName() + "\n");

        System.out
                .println("ServerListFilter\t"
                        + zoneAwareLoadBalancer.getFilter().getClass()
                                .getName() + "\n");

        System.out.println("ILoadBalancer\t"
                + zoneAwareLoadBalancer.getClass().getName() + "\n");

        System.out.println("PingInterval\t"
                + zoneAwareLoadBalancer.getPingInterval() + "\n\n");

        System.out
                .println("----  >>> atm-eureka-ribbon-privoder配置   <<<  ----\n");

        ZoneAwareLoadBalancer zoneAwareLoadBalancer2 = (ZoneAwareLoadBalancer) springClientFactory
                .getLoadBalancer("atm-eureka-ribbon-privoder");

        System.out.println(" IClientConfig\t"
                + springClientFactory
                        .getLoadBalancer("atm-eureka-ribbon-privoder-provider")
                        .getClass().getName() + "\n");
        System.out.println(" IRule\t"
                + zoneAwareLoadBalancer2.getRule().getClass().getName() + "\n");

        System.out.println(" IPing\t"
                + zoneAwareLoadBalancer2.getPing().getClass().getName() + "\n");

        System.out.println(" ServerLis\t"
                + zoneAwareLoadBalancer2.getServerListImpl().getClass()
                        .getName() + "\n");

        System.out.println(" ServerListFilter\t"
                + zoneAwareLoadBalancer2.getFilter().getClass().getName()
                + "\n");

        System.out.println(" ILoadBalancer\t"
                + zoneAwareLoadBalancer2.getClass().getName() + "\n");

        System.out.println(" PingInterval\t"
                + zoneAwareLoadBalancer2.getPingInterval());

        return "";
    }

    // 查看SpringCloud中的Ribbon配置 -- end

}

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐