负载均衡框架Ribbon

  • 认识Ribbon[ˈrɪbən]
  • 第一个Ribbon程序
  • 负载均衡是分布式架构的重点,负载均衡机制将决定着整个服务集群的性能与稳定
  • 根据Spring Cloud_4_Eureka集群搭建可知,Eureka服务实例可以进行集群部署,每个实例都均衡处理服务请求,那么这些请求是如何被分摊到各个服务实例中的?
  • 讲解Netflix的负载均衡项目Ribbon

1、Ribbon介绍

1.1、Ribbon简介

  • Ribbon是Netflix下的负载均衡项目
  • 在集群中为各个客户端的通信提供了支持
  • 主要实现中间层应用程序的负载均衡
  • Ribbon提供了以下特性:
    1. 负载均衡器,可支持拔插式的负载均衡规则
    2. 对多种协议提供支持,HTTP,TCP,UDP
    3. 集成了负载均衡功能的客户端

1.2、Ribbon子模块

  • Ribbon主要有以下三大子模块:
    1. ribbon-core:该模块为Ribbon项目的核心,主要包括负载均衡的接口定义、客户端接口定义,内置的负载均衡实现等API
    2. ribbon-eureka:为Eureka客户端提供的负载均衡实现类
    3. ribbon-httpclient:对Apache的HttpClient进行封装,该模块提供了含有负载均衡功能的REST客户端

1.3、负载均衡组件

  • Ribbon的负载均衡器主要与集群中的各个服务器进行通讯
  • 负载均衡器主要提供以下基础功能:

    1. 维护服务器的IP、DNS名称等信息
    2. 根据特定的逻辑在服务器列表中循环
  • 为实现负载均衡的基础功能,Ribbon的负载均衡器有以下三大子模块:

    1. Rule:一个逻辑组件,这些逻辑将会决定,从服务器列表中返回哪个服务器实例
    2. Ping:该组件主要使用定时器,来确保服务器网络可以连接
    3. ServerList:服务器列表,可以通过静态的配置确定负载的服务器,也可以动态指定服务器列表。如果动态指定服务器列表,则会有后台的线程来刷新该列表

2、Ribbon程序入门

  • 关于Ribbon的知识,主要围绕负载均衡器组件进行
  • 接下来,编写一个Ribbon程序,进行初步入门认识
  • 先写一个简单的Hello World程序
  • 看看本案例的程序结构图

2.1、编写服务

  • 为了能查看负载均衡的效果,先编写一个简单的REST服务,通过指定不同的端口,让服务可以启动多个实例
  • 本列的请求服务器,仅仅是一个基于SpringBoot的Web应用
2.1.1、引入依赖
  • 创建新的Maven项目:atm_ribbon_server
  • 引入spring-boot-start-web依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.atm.cloud</groupId>
    <artifactId>atm_ribbon_server</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>atm_ribbon_server Maven Webapp</name>
    <url>http://maven.apache.org</url>

    <!-- 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>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>

        <!-- SpringBoot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>1.5.4.RELEASE</version>
        </dependency>

    </dependencies>
    <build>
        <finalName>atm_ribbon_server</finalName>
    </build>
</project>
2.1.2、编写启动类
  • 建立启动类
package com.atm.cloud;

import java.util.Scanner;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;

@SpringBootApplication
public class MyRibbonServer {


    public static void main(String[] args) {
        //读取控制台输入作为端口参数
        Scanner scanner=new Scanner(System.in);

        String port=scanner.nextLine();

        //设置启动的服务端口
        new SpringApplicationBuilder(MyRibbonServer.class).properties(
                "server.port=" + port).run(args);
    }
}
  • 运行main方法,并在控制台输入端口号,即可启动web服务器
  • 接下来编写控制器,添加一个REST服务
2.1.3、编写控制器
package com.atm.cloud;

import javax.servlet.http.HttpServletRequest;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
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.setName(request.getRequestURL().toString());
        person.setAge(18);

        return person;
    }

    @GetMapping("/hello")
    public String hello(){
        return "Hello Ribbon";
    }
}
  • 在控制器中,发布了两个REST服务。

2.2、编写请求客户端

  • 新建Maven项目:atm_ribbon_client
2.2.1、引入依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.atm.cloud</groupId>
    <artifactId>atm_ribbon_client</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>atm_ribbon_client Maven Webapp</name>
    <url>http://maven.apache.org</url>

    <!-- 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>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>

        <!-- ribbon -->
        <dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-httpclient</artifactId>
            <version>2.2.2</version>
        </dependency>

    </dependencies>
    <build>
        <finalName>atm_ribbon_client</finalName>
    </build>
</project>
2.2.2、测试类编写
package com.atm.cloud;

import com.netflix.client.ClientFactory;
import com.netflix.client.http.HttpRequest;
import com.netflix.client.http.HttpResponse;
import com.netflix.config.ConfigurationManager;
import com.netflix.niws.client.http.RestClient;

public class MyRESTClient {

    public static void main(String[] args) {

        // 设置请求的服务器
        ConfigurationManager.getConfigInstance().setProperty(
                "my-client.ribbon.listOfServers",
                "localhost:8080,localhost:8081");

        // 获取REST请求客户端
        RestClient client = (RestClient) ClientFactory
                .getNamedClient("my-client");

        // 创建请求实例
        HttpRequest request = HttpRequest.newBuilder().uri("/person/1").build();

        //发送6次请求到服务器中
        for (int i = 0; i < 6; i++) {
            try {

                HttpResponse response=client.executeWithLoadBalancer(request);

                String result=response.getEntity(String.class);

                System.out.println(result);

            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

}
  • 使用了ConfigurationManager来配置了请求的服务器列表
  • 为“localhos:8080”和”localhost:8081”,再使用RestClient对象,向“/person/1”地址发送6次请求

  • 分别以8080端口和8081端口启动MyRibbonServer(atm_ribbon_server)

  • 根据输入我们可以知道,RestClient轮流向8080与8081端口发送请求
  • 可见,RestClient已经帮我们实现了负载均衡的功能

2.3、Ribbon配置

  • 在编写客户端时,使用了ConfigurationManager来配置,除了在代码中指定配置项外,还可以将配置放到”.properties”文件中,ConfigurationManager的loadPropertiesFromResource方法可以指定properties文件的位置,配置格式如下:
<client>.<nameSpace>.<property>=<value>
  • 其中client为客户的名称,声明该配置属于哪一个客户端,在使用ClientFactory时可以传入客户端的名称,即可返回对应的“请求客户端”实例
  • nameSpace为该配置的命名空间,默认为“ribbon”
  • property为属性名
  • value为属性值
  • 如果想对全部的客户端生效,可以将客户端名称去掉,直接以“< nameSpace >.< property >”的格式进行配置
  • 以下的配置,为客户端指定服务器生效
my-client.ribbon.listOfServers=localhost:8080,localhost:8081
  • Ribbon的配置,同样可以使用SpringCloud的配置文件(application.yml)
Logo

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

更多推荐