SpringCloud项目(四)Ribbon负载均衡

作者:陆金龙    发表时间:2019-08-02 11:18   

关键词:Ribbon负载均衡  IRule  RoundRobinRule  RandomRule  

3.4 Ribbon负载均衡

3.4.1 Ribbon配置

1.在klblog-consumer工程配置pom.xml

pom.xml添加依赖项

<!-- Ribbon相关 客户端负载均衡方案 所以在consumer工程配置-->

<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>

2.在klblog-consumer工程配置application.yml

eureka:

  client:

    register-with-eureka: false

    service-url:

defaultZone: http://eureka8051.com:8051/eureka/,http://eureka8052.com:8052/eureka/

3.代码实现

ConfigBean的 getRestTemplate方法添加@LoadBalanced注解

//@Configuration配置 ConfigBean = applicationContext.xml

@Configuration

public class ConfigBean {

@Bean

@LoadBalanced //基于Netflix Ribbon实现的一套客户端负载均衡的工具。

public RestTemplate getRestTemplate()

{

return new RestTemplate();

}

}

 

@RestController

@RequestMapping("/category")

public class CategoryConsumerController {

@Autowired

private RestTemplate template;

//private static final String RESTURL_PREFIX = "http://localhost:8061";

//使用Ribbon负载均衡时,微服务名“KLBLOG-PROVIDER”,不再关心地址和端口

private static final String RESTURL_PREFIX = "http://KLBLOG-PROVIDER";

......

}

3.4.2 实现多个provider工程的负载均衡

为了减少重复代码,将provider工程中service、dao、mybatis等部分提取到provider-core中,避免多个provider工程有大量的重复代码。

 

复制一个新的provider工程,命名为klblog-provider-balance-node。

修改1:yml配置修改端口

server:

  port: 8062

修改2:修改微服务的当前实例id

eureka:

  instance:

instance-id: klblog-provider-8062

注:如果不修改这里的配置,eureka.instance.instance-id与klblog-provider工程相同,在eureka的服务中看不到两个实例,负载均衡也不生效。详见:2.问题处理:

修改3:数据库

如果数据库有集群,也可有各自的数据库连接配置。数据库连接可以不改。

保留:对外暴露的实例名不能改

保持spring.application.name的值相同。

3.4.3 主启动类添加注解

主启动类添加注解@EnableEurekaClient

@SpringBootApplication

@EnableEurekaClient

public class ConsumerMainApplication{

}

1.错误处理:

No instances available for localhost at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.execute

No instances available for KLBLOG-PROVIDER

原因:主启动类漏掉了注解@EnableEurekaClient。

解决方案:主启动类添加注解@EnableEurekaClient。存在缓存导致不生效的情况,可以project->clean, 执行maven clean。

还是不行,在父工程-右键->delete,重新import工程,在pom.xml上执行maven install。测试OK。

 

2.问题处理:

问题:Eureka是上只显示了一个服务实例,负载均衡页始终访问同一个服务,被访问的服务停掉后,不会成功访问另一个。

GET request for "http://KLBLOG-PROVIDER/category/rootlist": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect

解决方案:修改eureka.instance.instance-id,eurekaKLBLOG-PROVIDER微服务下显示了2个实例,负载均衡也生效了,问题解决。

eureka:

 instance:

instance-id:klblog-provider-8062

3.4.4 Ribbon的核心组件和算法

1.IRule组件

根据特定的算法,从服务列表中选取一个要访问的服务。

2.RoundRobinRule

轮询调度算法,是默认的实现方案。

@Bean

public IRule customizedRule(){

return new RoundRobinRule();//默认的算法:轮询

}

3.RandomRule

@Bean

public IRule customizedRule(){

return new RandomRule();//用随机算法替代默认的轮询

}

4.RetryRule

先按照轮询算法获取服务,如果获取失败,在指定的时间内尝试重新获取。超过一定次数的失败后,不再尝试获取异常的服务。

@Bean

public IRule customizedRule(){

return new RetryRule();

}

5.自定义负载均衡算法

1)主启动类添加@RibbonClient注解

@RibbonClient(name="KLBLOG-PROVIDER",configuration=BalanceConfig.class)

2)定义自配置类BalanceConfig

KlBalanceRule 的路径不能与主启动类在同一个包名下,即不能在@CompoentScan所扫描的当前包及子包下。

package com.klfront.balance;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import com.netflix.loadbalancer.IRule;

@Configuration

public class BalanceConfig {

@Bean

public IRule customizedRule()

{

return new BalanceRule();// 自定义的负载均衡算法

}

}

3)自定义规则类BalanceRule ,继承AbstractLoadBalancerRule

主要重写方法public Server choose(Object key)

改进的轮询算法:要求每台被调用5次,再切换到下一个服务实例

public class BalanceRule extends AbstractLoadBalancerRule{

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();

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) {

Thread.yield();

continue;

}

if (server.isAlive()) {

return (server);

}

 

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

}

}