Spring Cloud_7_Ribbon负载均衡器

Ribbon负载均衡器

  • Ribbon负载均衡器

1、Ribbon负载均衡器

  • Ribbon的负载均衡接口,定义了服务器的操作,主要是用于进行服务器的选择
  • 《Spring Cloud_6_负载均衡框架Ribbon》的案例中,客户端使用了RestClient类,在发送请求时,会使用负载均衡器(ILoadBalancer)接口,根据特定的逻辑来选择服务器,服务器列表可以使用listOfServers进行配置,也可以使用动态更新机制
  • 试用负载均衡器

1.1、修改测试类

package com.atm.cloud;

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

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

public class MyChoseRESTClient {

    public static void main(String[] args) {

        // 创建负载均衡器
        ILoadBalancer iLoadBalancer = new BaseLoadBalancer();

        // 添加服务器
        List<Server> serverList = new ArrayList<Server>();
        serverList.add(new Server("localhost", 8080));
        serverList.add(new Server("localhost", 8081));

        iLoadBalancer.addServers(serverList);

        //进行6次服务器选择
        for (int i = 0; i < 6; i++) {
            Server server=iLoadBalancer.chooseServer(null);
            System.out.println(server);
        }
    }
}

  • 代码中使用了BaseLoadBalancer这个负载均衡器,将两个服务器对象加入到负载均衡器中,再调用了6次chooseServer方法
  • 根据结果可以判定,选择服务器的逻辑是一致的,在默认情况下,会使用RoundRobinRule的规则逻辑

1.2、自定义负载规则

  • 由前面小节可知,选择那个服务器进行请求处理,由ILoadBalancer接口的chooserServer方法决sing,而在BaseLoadBalancer类中,则使用IRule接口的choose方法来决定选择哪一个服务器对象
  • 如果想自定义负载均衡规定,可以编写一个IRule接口的实现类,实现自己的负载规定
package com.atm.cloud;

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

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

public class MyRule implements IRule {

    ILoadBalancer ibBalancer;

    public MyRule() {

    }

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

    public Server choose(Object key) {
        // 获取全部的服务器
        List<Server> servers = ibBalancer.getAllServers();

        /**
        Random random=new Random();
        int randNum=random.nextInt(10);

        if(randNum>7){
            getServerByPort(servers, 8081);
        }
        **/

        // 至返回第一个Server对象
        //return getServerByPort(servers, 8080);
        return servers.get(0);
    }

    public Server getServerByPort(List<Server> servers,int port){
        for (Server server : servers) {
            if(server.getPort()==port){
                return server;
            }
        }
        return null;
    }

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

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

}
  • 在自定义规则类中,实现了choose方法,调用了ILoadBalancer的gerAllServers方法返回全部的服务器,为了简单起见,本例只返回第一个服务器
  • 为了能在负载均衡器中使用自定义的规则,需要修改选择服务器的代码
package com.atm.cloud;

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

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

public class MyChoseRESTClient {

    public static void main(String[] args) {

        // 创建负载均衡器
        //ILoadBalancer iLoadBalancer = new BaseLoadBalancer();
        BaseLoadBalancer iLoadBalancer=new BaseLoadBalancer();

        //设置自定义的负载规则
        iLoadBalancer.setRule(new MyRule(iLoadBalancer));

        // 添加服务器
        List<Server> serverList = new ArrayList<Server>();
        serverList.add(new Server("localhost", 8080));
        serverList.add(new Server("localhost", 8081));

        iLoadBalancer.addServers(serverList);

        //进行6次服务器选择
        for (int i = 0; i < 6; i++) {
            Server server=iLoadBalancer.chooseServer(null);
            System.out.println(server);
        }
    }
}

  • 可以看到,请求6次所得到的服务器均为8080
  • 以上是直接使用编码方式来设置负载规则,可以使用配置的方式来完成这些工作
  • 修改Ribbon的配置,让请求的客户端,使用我们定义的负载规则
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");

        // *****配置规则处理类
        ConfigurationManager.getConfigInstance().setProperty(
                "my-client.ribbon.NFLoadBalancerRuleClassName",
                MyRule.class.getName());

        // 获取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();
            }

        }
    }

}
  • 请求客户端中,基本与前面张杰的客户端基本一致,只是加入了“my-client.ribbon.NFLoadBalancerRuleClassName”属性,设置了自定义规则处理类为MyRule,这个配置项同样可以在配置文件中使用,包括SpringCloud的配置文件等

  • 在实际环境中,如果要实现自定义的负载规则,可能还需要结合各种因素:具体业务的发生时间、服务器性能等

1.3、Ribbon自带的负载规则

  • Ribbon提供了若干个内置的负载规则,使用者完全可以直接使用

    1. RoundRobinRule:系统默认的规则,通过简单的轮询服务列表来选择服务器,其他的规则在很多情况下,仍然使用RoundRobinRule
    2. AvailablilityFilteringRule:该各种会忽略以下服务器

      无法连接的服务器:在默认情况下,如果3次连接失败,该服务器将会被置为“短路”的状态,该状态将持续30秒,如果再次连接失败,“短路”状态的持续时间将会以几何级增加。可以通过修改niws.loadbalance..connerctionFailureCountThreshold属性来配置连接失败的次数
      并发数过高的服务器:如果连接到该服务器的并发数过高,也会被这个规则忽略,可以通过修改.ribbon.ActiveConnectionLimit属性来设定最高并发数

    3. WeightedResponseTimeRule:为每个服务器赋予一个权重值,服务器的响应时间越长,该权重值就越少,这个规则会随机选择服务器,这个权重值有可以能会决定服务器的选择

    4. ZoneAvoidanceRule:该规则以区域、可用服务器为基础,进行服务器选择。使用Zone对服务器进行分类,可以理解为机架或者机房
    5. BestAvailiableRule:忽略“短路”的服务器,并选择并发数较低的服务器
    6. RandomRule:随机选择可用服务器
    7. RetryRule:含有重试的选择逻辑,如果使用RoundRobinRule
  • 以上提供的负载规则,基本上可以满足大部分的需求

1.4、Ping机制

  • 在负载均衡器中,提供了Ping的机制,每隔一段时间,会去Ping服务器,判断服务器是否存活
  • 该工作由IPing接口的实现类负责,如果单独使用Ribbon,在默认情况下,不会激活Ping机制,默认的实现类为DummyPing
package com.atm.cloud;

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

import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.PingUrl;
import com.netflix.loadbalancer.Server;

public class MyTestPingUrl {

    public static void main(String[] args) throws InterruptedException {
        // 创建负载均衡器
        BaseLoadBalancer lb = new BaseLoadBalancer();

        // 添加服务器
        List<Server> servers = new ArrayList<Server>();

        // 8080 端口连接正常
        servers.add(new Server("localhost", 8080));
        // 一个不存在的端口
        servers.add(new Server("localhost", 8888));

        lb.addServers(servers);

        // 设置 IPing 实现类
        lb.setPing(new PingUrl());

        // 设置 Ping 时间间隔为 2 秒
        lb.setPingInterval(2);

        Thread.sleep(6000);

        for (Server s : lb.getAllServers()) {
            System.out.println(s.getHostPort() + " 状态:" + s.isAlive());
        }
    }

}
  • 使用了代码的方法来设置负载均衡器使用PingUrl,设置了每隔2秒,就向两个服务器请求
  • PingUrl实际是使用的是HttpClient

  • 除了在代码中配置IPing类外,还可以在配置中设置IPing实现类
package com.atm.cloud;

import java.util.List;

import com.netflix.client.ClientFactory;
import com.netflix.config.ConfigurationManager;
import com.netflix.loadbalancer.PingUrl;
import com.netflix.loadbalancer.Server;
import com.netflix.niws.client.http.RestClient;

public class MyPingConfig {

    public static void main(String[] args) throws InterruptedException {
        // 设置请求的服务器
        ConfigurationManager.getConfigInstance().setProperty(
                "my-client.ribbon.listOfServers",
                "localhost:8080,localhost:8888");

        // 配置 Ping 处理类
        ConfigurationManager.getConfigInstance().setProperty(
                "my-client.ribbon.NFLoadBalancerPingClassName",
                PingUrl.class.getName());

        // 配置 Ping 时间间隔
        ConfigurationManager.getConfigInstance().setProperty(
                "my-client.ribbon.NFLoadBalancerPingInterval", 2);

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

        Thread.sleep(6000);

        // 获取全部服务器
        List<Server> servers = client.getLoadBalancer().getAllServers();
        System.out.println(servers.size());

        // 输出状态
        for (Server s : servers) {
            System.out.println(s.getHostPort() + " 状态:" + s.isAlive());
        }

    }
}
  • my-client.ribbon.NFLoadBalancerPingClassName:配置 IPing 的实现类
  • my-client.ribbon.NFLoadBalancerPingInterval:配置 Ping 操作的时间间隔。
  • 以上两个配置,同样可以使用在配置文件中

1.5、自定义Ping

  • 实现自定义Ping较为简单,先实现IPing接口,再通过配置来设定具体的Ping实现类
package com.atm.cloud;

import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.Server;

public class MyPing implements IPing{

    public boolean isAlive(Server server) {
        System.out.println("这是自定义 Ping 实现类:" + server.getHostPort());
        return true;
    }

}
  • 要使用自定义的Ping类,通 过 修 改client.nameSpace.NFLoadBalancerPingClassName 配置即可

1.6、其他配置

  • NFLoadBalancerClassName:指定负载均衡器的实现类,可利用该配置,实现自己的负载均衡器。
  • NIWSServerListClassName:服务器列表处理类,用来维护服务器列表,Ribbon
    已经实现动态服务器列表。

  • NIWSServerListFilterClassName:用于处理服务器列表拦截。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章