SpringCloud之Ribbon负载均衡的使用

介绍:

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

 

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

 

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

 

集中式LB:

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

 

进程内LB:

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

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

初步配置

由于ribbon是集成于消费方,所以,我们只需要在调用方做一些改动即可.

pom文件:

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

在调用方的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 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值