spring-cloud-ribbon负载均衡组件

Ribbon简介:                 

  Spring Cloud Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于 Netflix Ribbon 实现。 通过 Spring Cloud 的封装, 可以让我们轻松地将面向服务的 REST 模板请求自动转换成客户端负载均衡的服务调用。 Spring Cloud Ribbon 虽然只是一个工具类框架,它不像服务注册中心、 配置中心、 API 网关那样需要独立部署, 但是它几乎存在于每一个Spring Cloud 构建的微服务和基础设施中。 因为微服务间的调用,API 网关的请求转发等内容实际上都是通过Ribbon 来实现的,包括后续我们将要介绍的 Feign, 它也是基于 Ribbon实现的工具。   在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http Restful的。Spring Cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign。   我们通常所说的负载均衡都指的是服务端负载均衡, 其中分为硬件负载均衡和软件负载均衡。 硬件负载均衡主要通过在服务器节点之间安装专门用于负载均衡的设备,比如F5等;而软件负载均衡则是通过在服务器上安装 一些具有均衡负载功能或模块的软件来完成请求分发工作, 比如Nginx等。 不论采用硬件负载均衡还是软件负载均衡,硬件负载均衡的设备或是软件负载均衡的软件模块都会维护一个下挂可用的服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。
  当客户端发送请求到负载均衡设备的时候, 该设备按某种算法(比如线性轮询、 按权重负载、 按流量负载等)从维护的可用服务端清单中取出 一台服务端的地址, 然后进行转发。而客户端负载均衡和服务端负载均衡最大的不同点在千上面所提到的服务清单所存储的位置。 在客户端负载均衡中, 所有客户端节点都维护着自己要访问的服务端清单, 而这些服务端的清单来自于服务注册中心,比如上一章我们介绍的Eureka服务端。同服务端负载均衡的架构类似, 在客户端负载均衡中也需要心跳去维护服务端清单的健康性, 只是这个步骤需要与服务注册中心配合完成。 所有微服务汇集到了 Eureka 之中,这样所有的微服务汇集到了Eureka之中,而客户端的调用也应该通过Eureka完成,这种调用可以通过Ribbon技术来实现。Ribbon是一个服务调用组件,并且是一个客户端实现负载均衡的处理的组件。

  如上图,当配置了负载均衡的客户端对服务端发情请求时,会根据Eureka服务中已经注册的并且处于活动状态的服务,结合自己配置里的Robbin对应的负载均衡算法进行负载分流。常见的轮询、随机等算法,当然也可以自定义算法。

1.添加pom依赖

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR3</spring-cloud.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <!-- SpringCloud 所有子项目 版本集中管理. 统一所有SpringCloud依赖项目的版本依赖-->
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin><!-- SpringBoot 项目打jar包的Maven插件 -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

2. 配置文件application.yml

server:
  port: 9001

spring:
  application:
    name: ribbon-server #服务注册到Eureka上使用的名称

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/
  instance:
    instance-id: ribbon-server-9001

3.主启动类注解

@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer8002_App 
{
    public static void main( String[] args )
    {
    	SpringApplication.run(DeptConsumer8002_App.class, args);
    }
}

4.负载规则配置

@Configuration
public class ConfigBean {

    @Bean
    @LoadBalanced // ribbon是客户端 的负载均衡工具
    //默认算法是轮询算法 核心组件IRule
    public RestTemplate getRestTemplate() {

        return new RestTemplate();
    }
}

5.Controller

@RestController
public class RibbonController {

    //  private static final String REST_URL_PREFIX="http://localhost:8001"; 单机版
    //集群的时候  需要配置该服务在eureka里注册的名字
    private static final String REST_URL_PREFIX="http://cloud-provider";

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value ="/hello")
    public String get() {
        return restTemplate.getForObject(REST_URL_PREFIX+"/hello", String.class);
    }

    //消费端可以调用服务发现
    @RequestMapping(value ="/discovery")
    public Object discovery() {

        return restTemplate.getForObject(REST_URL_PREFIX+"/discovery", Object.class);
    }
}

  如此便配置好了客户端负载均衡,Robbin的默认负载均衡算法是轮询算法,如果我们需要配置其他的算法规则   则需要在容器中注入我们需要的bean 例如:

@Bean
public IRule myRule() { // 负载均衡算法。
		
	return new RandomRule();//随机
}

  以下是自带的一些负载均衡算法:

  可根据自己的需求寻找合适的配置。如果其中没有我们需要的算法,那么我们可以重新自己的负载算法。

  自定义类需要实现 IRule 接口,我们先来看一下该接口

public interface IRule{
    /*
     * choose one alive server from lb.allServers or
     * lb.upServers according to key
     * 
     * @return choosen Server object. NULL is returned if none
     *  server is available 
     */

    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

  该接口很简单,其核心方法 choose(Object key) 才是执行算法逻辑的核心。其中cloud为我们提供了IRule接口的抽象子类AbstractLoadBalancerRule ,因此我们只需要继承该类,并且重写choose方法既可以达到我们需要的算法逻辑。我们拿轮询算法作为例子,依然使用轮询算法,但是我们让他每个服务调用5次后再进行轮询。代码如下:

public class MyRule_ZY extends AbstractLoadBalancerRule {

    private int total = 0;
    private int currentIndex = 0;

    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        Server server = null;

        while (server == null) {
            if (Thread.interrupted()) {// 测试线程是否中断
                return null;
            }
            // 返回当前可正常服务的实例列表
            List<Server> upList = lb.getReachableServers();
            // 返回所有已知的服务实例列表,包括正常服务和停止服务的实例
            List<Server> allList = lb.getAllServers();
            //获取服务列表长度
            int serverCount = allList.size();

            if (serverCount == 0) {
                return null;
            }
            //自定义的一些算法
            if (total < 5) {
                server = upList.get(currentIndex);
                total++;
            } else {
                total = 0;
                currentIndex++;
                if (currentIndex >= upList.size()) {
                    currentIndex = 0;
                }
            }
            if (server == null) {
                /*
                 * The only time this should happen is if the server list were somehow trimmed.
                 * This is a transient condition. Retry after yielding.
                 */
                Thread.yield();//线程让步
                continue;
            }

            if (server.isAlive()) {//判断服务是否活跃
                return (server);
            }

            // Shouldn't actually happen.. but must be transient or a bug.
            server = null;
            Thread.yield();
        }

        return server;

    }

    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        // TODO Auto-generated method stub

    }
}

  此刻需要修改主启动类

@EnableEurekaClient
@SpringBootApplication
//自定义负载均衡算法 自定义配置类不能跟主启动类一个包或在子包下面
//name: 表示对哪个服务采用自定义算法
//configuration:负载算法类
@RibbonClient(name="cloud-provider" ,configuration= MyRule_ZY.class)
public class RibbonApp {
    private final static Logger log = LoggerFactory.getLogger(RibbonApp.class);

    public static void main(String[] args) {
        SpringApplication.run(RibbonApp.class,args);
        log.info("服务启动成功");

    }
}

  这样即可使用自己的负载均衡算法。

猜你喜欢

转载自www.cnblogs.com/wuzhenzhao/p/9468928.html