SpringCloud之Ribbon负载均衡的使用

介绍:

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端  (调用方)负载均衡的工具。

 

简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务链接在一块儿。Ribbon客户端组件提供一系列完善的配置项如链接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面全部的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机链接等)去链接这些机器。咱们也很容易使用Ribbon实现自定义的负载均衡算法。html

 

LB,即负载均衡(Load Balance),在微服务或分布式集群中常常用的一种应用。负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA常见的负载均衡有软件Nginx, LVS,硬件F5等。目应的在中间件,例如: dubbo和SpringCloud中均给咱们提供了负载均衡, SpringCloud的负载均衡算法能够自定义。java

 

集中式LB:mysql

既在服务的消费方和提供方之间使用独立的LB设施(能够是硬件,如F5,也能够是软件,如Nginx),由该设施负责把访问请求经过某种策略转发至服务的提供方.算法

 

进程内LB:spring

将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,而后本身再从这些地址中选择出一个合适的服务器.sql

Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方经过它来获取到服务提供方的地址.数据库

初步配置

因为ribbon是集成于消费方,因此,咱们只须要在调用方作一些改动便可.服务器

pom文件:mybatis

<!-- Ribbon相关 -->
<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>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

配置文件中加上: defaultZone后面加的是全部的eureka集群地址

 

#调用方配置eureka,作负载均衡
eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

在调用方的controller层,也就是调用别的服务的地方把连接地址改为服务名便可,由于也弄了eureka集群,因此直接填eureka的地址是不对的,直接填上被调用的服务名:下方的例子是须要调用被调用方要用上的连接,因为提供方也使用了集群(见上篇博客),因此这里能够直接使用服务名

private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT";app

在调用方的configuration类的RestTemplate方法上加上注解@LoadBalanced,

由于微服务之间联系是经过rest方式因此在这里标识负载均衡的注解以标识联系是负载均衡的.

@Bean
@LoadBalanced   //声明须要负载均衡
public RestTemplate getRestTemplate(){
    return new RestTemplate();
}

最后就是在调用方的启动类上加上注解@EnableEurekaClient  //eureka客户端

结果:

结论:

Ribbon和eureka整合后Consumer能够直接调用服务而不用再关心地址和客户端.

实现负载均衡

Ribbon在工做时分红两步:

第一步先选择EurekaServer,它优先选择在同一个区域内负载比较少的Server

第二部再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址.

 

要实现负载均衡,你得多准备几个被调用方的model,而且它们的代码要和之前的被调用服务一致,惟一不一样的可能只有启动类名和配置文件了:

不一样的被调用服务端口必须不同,而后的话以前也说过不一样的服务能够有不一样的数据库,因此能够的话数据库信息也要不同,贴其中的两个集群的配置文件:

server:
  port: 8001
mybatis:
  type-aliases-package: com.hw.entity                       # 全部Entity别名类所在包
spring:
   application:
    name: microservicecloud-dept    #微服务名这个名字作集群的话同一个服务是不用改的
   datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操做类型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
    url: jdbc:mysql://localhost:3306/cloudDB01?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT            # 数据库名称
    username: root
    password: root
    dbcp2:
      min-idle: 5                                           # 数据库链接池的最小维持链接数
      initial-size: 5                                       # 初始化链接数
      max-total: 5                                          # 最大链接数
      max-wait-millis: 200                # 等待链接获取的最大超时时间
#客户端注册进rureka服务列表内
eureka:
  client:
    service-url:
      #单机
      #defaultZone: http://localhost:7001/eureka
      #集群
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/,http://eureka7001.com:7001/eureka/
  instance:
    instance-id: microservicecloud-dept8001   #在eureka中显示的名称
    prefer-ip-address: true                  #是否显示主机ip
    lease-expiration-duration-in-seconds: 500 #注销服务时间(s)
info:   #info表明的是当使用者点击这个服务的连接是会展现下面这些信息.
  app.name: hw-test
  company.name: www.baidu.com
  build.artifactId: microservicecloud-provider-dept-8001
  build.version: '1.0'



第二份:要改的地方:instance-id,其他的都大部分一致.
server:
  port: 8002
mybatis:
  type-aliases-package: com.hw.entity                       # 全部Entity别名类所在包
spring:
   application:
    name: microservicecloud-dept    #微服务名
   datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操做类型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
    url: jdbc:mysql://localhost:3306/cloudDB02?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT            # 数据库名称
    username: root
    password: root

    dbcp2:
      min-idle: 5                                           # 数据库链接池的最小维持链接数
      initial-size: 5                                       # 初始化链接数
      max-total: 5                                          # 最大链接数
      max-wait-millis: 200                # 等待链接获取的最大超时时间
#客户端注册进rureka服务列表内
eureka:
  client:
    service-url:
      #单机
      #defaultZone: http://localhost:7001/eureka
      #集群
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/,http://eureka7001.com:7001/eureka/
  instance:
    instance-id: microservicecloud-dept8002   #在eureka中显示的名称
    prefer-ip-address: true                  #是否显示主机ip
    lease-expiration-duration-in-seconds: 500 #注销服务时间(s)
info:   #info表明的是当使用者点击这个服务的连接是会展现下面这些信息.
  app.name: hw-test
  company.name: www.baidu.com
  build.artifactId: microservicecloud-provider-dept-8001
  build.version: '1.0'

 

效果图:  默认的算法是一个来一次雨露均沾,被称为轮询

 

能够看到同一连接范围的内容不一致,这是由于每一个提供者的数据库不一致致使的,这也说明了确实是实现了负载均衡

 

更多负载均衡算法:

想要改变负载均衡的算法?想要什么算法,根据上图的名字new出对应的对象就ok,

可是记得这个方法要写在有@Configuration注解的配置类中..

自定义负载均衡策略

@componentScan这个注解在主启动类的@SpringBootApplication

因此综合上方的意思,咱们若是须要自定义负载均衡策略的话须要在主启动类上一层再新建一个包,包中来编写咱们的自定义负载均衡策略,下面贴出一个自定义负载均衡的示例,需求是每一个服务被调用五次,而后轮流调用.

首先得要在调用方的启动类上加上注解:

name为被调用的服务名,也是要被使用这个负载策略调用的服务名,configuration是自定义的负载均衡类

@RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MySelfRule.class)

而后下面这两个文件就是新建包下的文件:

package com.atguigu.myrule;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;

@Configuration
public class MySelfRule
{
	@Bean
	public IRule myRule()
	{

		return new RandomRule_ZY();// 我自定义为每台机器5次
	}
}
package com.atguigu.myrule;

import java.util.List;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

public class RandomRule_ZY extends AbstractLoadBalancerRule
{

	// total = 0 // 当total==5之后,咱们指针才能往下走,
	// index = 0 // 当前对外提供服务的服务器地址,
	// total须要从新置为零,可是已经达到过一个5次,咱们的index = 1
	// 分析:咱们5次,可是微服务只有8001 8002 8003 三台,OK?
	// 
	
	
	private int total = 0; 			// 总共被调用的次数,目前要求每台被调用5次
	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();	//若是没有,那么返回为null
			if (serverCount == 0) {
				/*
				 * No servers. End regardless of pass, because subsequent passes only get more
				 * restrictive.
				 */
				return null;
			}

//			int index = rand.nextInt(serverCount);// java.util.Random().nextInt(3);	随机算法中的
//			server = upList.get(index);		//随机获取一个服务,这个是原生态的随机策略

			
//			private int total = 0; 			// 总共被调用的次数,目前要求每台被调用5次
//			private int currentIndex = 0;	// 当前提供服务的机器号
            if(total < 5)	//次数小于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

	}

}

 

点赞或者评论是我最大的动力,有问题欢迎留言或者联系q:1559810637