SpringCloud项目(四)Ribbon负载均衡
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
}
}