@TOC
一、Ribbon 简介
1、什么是 Ribbon?
Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡的工具,它可以很好地控制 HTTP 和 TCP 客户端的行为。简单的说,Ribbon 是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Ribbon 的客户端组件提供一系列完整的配置项,如:连接超时、重试等。简单的说,就是在配置文件中列出 LoadBalancer (简称 LB:负载均衡) 后面所有的及其,Ribbon 会自动的帮助你基于某种规则 (如简单轮询,随机连接等等) 去连接这些机器。我们也容易使用 Ribbon 实现自定义的负载均衡算法!
2、Ribbon 能干什么?
LB,即负载均衡 (LoadBalancer) ,在微服务或分布式集群中经常用的一种应用。
负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的 HA (高用)。
常见的负载均衡软件有 Nginx、Lvs(中国人研发的) 等等。
其中 lvs 是中国技术专家章文嵩发明的
负载均衡简单分类:
二、使用 Ribbon
1、客户端导入依赖
      <!--引入Eureka的依赖-->     <dependencies>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-eureka</artifactId>            <version>1.4.6.RELEASE</version>        </dependency>        <!--引入ribbon-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-ribbon</artifactId>            <version>1.4.6.RELEASE</version>        </dependency>    </dependencies>
       复制代码
 2、application.yml 配置
 server:  port: 801
eureka:  client:    register-with-eureka: false  #不向eureka注册自己    service-url:      defaultZone: http://localhost:7001/eureka/ #去哪个地方获取
       复制代码
 3、Controller 配置
和前面两节不一样的是,用 Ribbon 做负载均衡,地址不能写死,也就是不能和前面的一样写成一个具体的值如:localhost:8001,而是这个微服务的名字,也就是这个名字,如下。
 package com.you.controller;
import com.you.pojo.Dept;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController@ResponseBodypublic class DeptComsumerController {    @Autowired    RestTemplate restTemplate;//    public static final String REST_URL_PREFIX = "http://localhost:8001";    public static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
    @GetMapping("/consumer/dept/getDept/{id}")    public Dept getDeptOfId(@PathVariable("id") Long id)    {
        System.out.println(REST_URL_PREFIX+"/dept"+"/aDept/"+id);        return restTemplate.getForObject(REST_URL_PREFIX + "/dept" + "/aDept/"+id, Dept.class);    }}
       复制代码
 4、Config 的配置
在此文件里,增加了 @LoadBalanced 注解,该注解的作用是让 RestTemplate 有了负载均衡的能力,而且默认的负载均衡算法是轮询(也就是一个一个的尝试),可以使用系统配备的负载均衡算法,也可以自己写自己的负载均衡算法。
 package com.you.config;
import com.netflix.loadbalancer.IRule;import com.netflix.loadbalancer.RandomRule;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.client.RestTemplate;
@Configurationpublic class ConfigBean {    @Bean    @LoadBalanced  //ribbon    /*配置负载均衡实现RestTemplate*/    /*IRule*/    /*RoundRobinRule 轮询 */    /*RandomRule 随机*/    /*AvailabilityFilteringRule 优先过滤掉跳闸、访问故障的服务,对剩下的进行轮询 */    public RestTemplate getRestTemplate() {        return new RestTemplate();    }
}
       复制代码
 5、启动类的配置
 package com.you;
import com.tan.tanRule;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;import org.springframework.cloud.netflix.ribbon.RibbonClient;
@SpringBootApplication
@EnableEurekaClient/*下面是处理负载均衡算法*/@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = tanRule.class)public class DeptConsumer_80 {    public static void main(String[] args) {        SpringApplication.run(DeptConsumer_80.class,args);    }}
       复制代码
 三、Ribbon 实现负载均衡
为了实现负载均衡,扩充一下服务提供者,将原来的一个服务提供者,改为三个。
新建三个 module,springboot-provider-8002、springboot-provider-8003。
参考 springboot-provider-8001,修改 application.xml(主要是端口号,数据库名,instance-id),其中 application-name 要保持一致。和微服务的名字一样。
启动 Eureka_7001,启动这个三个提供者,根据自己的情况,如果电脑性能比较差,可以少启动一个。启动 consumer_80。
访问 consumer_80 配置的 Getmapping 地址,然后不断的刷新,会看到依次访问三个数据库,并且一直重复,这就是默认的负载均衡算法:轮询
四、设计负载均衡算法
1、80 启动类的改动
@RibbonClient()注释的应用,在 psvm 上面添加该注释,其具体内容为 @RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = tanRule.class),其中 name 的值即为微服务的名字,configuration 的值对应的就是自己写的路由类
2、自定义路由类
需要注意,自定义的路由类,不可以用启动类放在同一目录,一般要比启动类高一级目录,放在同一目录下,需要配置 CompentScan。
 package com.tan;
import com.netflix.loadbalancer.IRule;import com.netflix.loadbalancer.RandomRule;import org.springframework.context.annotation.Bean;
public class tanRule {    @Bean    public IRule myRule()    {        return new tanRandomRule();    }}
       复制代码
 3、负载均衡算法的实现
可以模仿系统中的负载均衡算法,撰写自己的负载均衡算法,如下面的例子即为:每个端口的提供者访问 5 次,然后切换下一个端口,全部访问完成后则重新开始,代码如下:
 package com.tan;
import com.netflix.client.config.IClientConfig;import com.netflix.loadbalancer.AbstractLoadBalancerRule;import com.netflix.loadbalancer.ILoadBalancer;import com.netflix.loadbalancer.Server;
import java.util.List;import java.util.concurrent.ThreadLocalRandom;
public class tanRandomRule extends AbstractLoadBalancerRule {    int total = 0;    int currentIndex = 0;    public tanRandomRule() {    }
    @SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {            return null;        } else {            Server server = null;            while(server == null) {                if (Thread.interrupted()) {                    return null;                }
                List<Server> upList = lb.getReachableServers();                List<Server> allList = lb.getAllServers();
                if(total<5)                {                    server = (Server)upList.get(currentIndex);                    total++;                }else{                    total = 0;                    currentIndex++;                    if(currentIndex>2)                    {                        currentIndex = 0;                    }                    server = (Server)upList.get(currentIndex);                }                System.out.println("CurrentIndex:"+currentIndex);                System.out.println("Total:"+total);                System.out.println("sever 的值是:"+server);                if (server == null) {                    Thread.yield();                } else {                    if (server.isAlive()) {                        return server;                    }
                    server = null;                    Thread.yield();                }            }
            return server;        }    }
    protected int chooseRandomInt(int serverCount) {        return ThreadLocalRandom.current().nextInt(serverCount);    }
    public Server choose(Object key) {        return this.choose(this.getLoadBalancer(), key);    }
    public void initWithNiwsConfig(IClientConfig clientConfig) {    }}
       复制代码
 
评论