在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign。
简介
是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。也很容易使用Ribbon实现自定义的负载均衡算法。
集中式LB
即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发到服务的提供方。
进程内LB
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
配置初步
因为Ribbon是客户端的负载均衡工具,所以只能配置客户端:micoservicecloud-consumer-dept-80
配置pom.xml
在micoservicecloud-consumer-dept-80的pom.xml添加以下信息:
<!-- 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>
配置application.yml
在micoservicecloud-consumer-dept-80的application.yml添加以下信息:
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
register-with-eureka: false
Configuration配置类添加标签@LoadBalanced
controller层添加微服务地址
对应页面中的server的地址
启动类添加标签
小结
Ribbon和Eureka整合后Consumer可以直接调用服务而不用关心地址和端口号。
负载均衡案例
Ribbon在工作时分成两步:
第一步,先选择EurekaServer,它优先选择在同一个区域负载均衡较少的server。
第二步,再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。
其中Ribbon提供多种策略:比如轮询、随机和根据响应时间加权。
1. 另外创建两个maven工程:
micoservicecloud-provider-dept-8002
micoservicecloud-provider-dept-8003
2. 其他代码一样,需要修改红色部分,配置各自的application.yml文件:
server:
port: 8002 #配置各自的端口号
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.minn.springcloud.entities # 所有Entity别名类所在包
mapper-locations: classpath:mybatis/mapper/**/*.xml # mapper映射文件
spring:
application:
name: micoservicecloud-dept #这name不能修改,三个配置文件一样
datasource:
type: com.alibaba.druid.pool.DruidDataSource #当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver #mysql驱动包
url: jdbc:mysql://localhost:3306/clouddb02 #数据库名称,配置各自的数据库名字
username: root
password: 123456
dbcp2:
min-idle: 5 #数据库连接池的最小维持连接数
initial-size: 5 #初始化连接数
max-total: 5 #最大连接数
max-wait-millis: 200 #等待连接获取的最大超时时间
#客户端注册进eureka服务列表内
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
instance:
instance-id: micoservicecloud-dept8002 #自定义服务名称信息
prefer-ip-address: true #访问路径可以显示IP地址
info:
app.name: minn-microservicecloud
company.name: www.minn.com
build.artifactId: $project.artifactId$
build.version: $project.version$
3. 创建另外两个数据库clouddb2,clouddb3。各自对应新创建的maven工程。
DROP DATABASE IF EXISTS clouddb03;
CREATE DATABASE clouddb03 CHARACTER SET UTF8;
USE clouddb03;
CREATE TABLE dept(
deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
dname VARCHAR(60),
db_source VARCHAR(60)
);
INSERT INTO dept(dname,db_source) VALUES('开发部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('人事部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('财务部',DATABASE());
4. 依次启动各自的启动类:
micoservicecloud-eureka-7001 ,micoservicecloud-eureka-7002,micoservicecloud-eureka-7003,
micoservicecloud-provider-dept-8001,micoservicecloud-provider-dept-8002,
micoservicecloud-provider-dept-8003,micoservicecloud-consumer-dept-80
5. 分别访问三个服务端页面,可以查到数据
6. 查看消费端界面
点击刷新按钮,发现轮询访问三个服务端界面,这就是通过@LoadBalanced
实现负载均衡。
核心组件IRule
根据特定算法中从服务列表中选取一个要访问的服务。
RoundRobinRule:轮询
RandomRule:随机
AvailabilityFilteringRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阀值的服务,然后对剩余的服务列表按照轮询策略进行访问。
WeightedResponseTimeRule:根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高。刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够,会切换到WeightedResponseTimeRule。
RetryRule:先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务。
BestAvailableRule:会先过滤由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。
ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器。
自定义
需求:让每个微服务提供方被调用5次,再切换到下一个提供方。
1. 在启动类上加下面信息:
//在启动该微服务的时候能去加载我们自定义Ribbon配置类,从而使配置生效
@RibbonClient(name = "MICOSERVICECLOUD-DEPT",configuration = MySelfRule.class)
2. 创建MySelfRule的类
package com.minn.myrule;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MySelfRule {
@Bean
public IRule myRule(){
return new RandomRule_ZY();
}
}
注意:该类不能跟启动类在同一个包或者启动类的子包里,必须在别的包里。
3. 创建RandomRule_ZY类
package com.minn.myrule;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.util.List;
public class RandomRule_ZY 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 void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(),key);
}
}