Ribbon
一、 Ribbon:负载均衡及Ribbon
ribbon是什么?
- SpringCloud Ribbon是基于NetFlix Ribbon实现的一套客户端负载均衡的工具(轮询、随机两种算法)
ribbon能干嘛?
- LB,即负载均衡
- 负载均衡简单的说就是将用户请求平摊的分配到多个服务上,从而达到系统的HA(高可用)
- 常见的负载均衡软件有Nginx、Lvs等
- Spring Cloud的负载均衡算法可以自定义
- 负载均衡简单分类:
- 集中式LB
- 即在服务的消费方和提供方之间使用独立的LB设施,如Nginx,由该设施负责把访问请求通过某种策略转发至服务的提供方
- 进程式LB
- 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器
- Ribbon就属于进程内LB,他只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址
- 在springcloud-consumer-dept-80子模块pom增加Ribbon和Eureka的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud</artifactId>
<groupId>com.giao</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-consumer-dept-80</artifactId>
<dependencies>
<!-- Ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>com.giao</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
</project>
- springcloud-consumer-dept-80子模块增加Eureka配置
server:
port: 80
#Eureka配置
eureka:
client:
register-with-eureka: false #不像Eureka中注册自己
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
- 启动类加Enable注解
package com.giao.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//Ribbon和Eureka整合以后客户端可以直接调用,不用关心ip地址
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class, args);
}
}
4.配置负载均衡实现RestTemplate增加一个注解@@LoadBalanced即可
package com.giao.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ConfigBean {//@Configuration---Spring的applicaContext.xml
//配置负载均衡实现RestTemplate
@Bean
@LoadBalanced//Ribbon
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
5.通过ribbon实现,我们这里的地址应该是一个变量,通过服务名来访问
package com.giao.springcloud.controller;
import com.giao.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class DeptConsumerController {
//理解:消费者,不应该有service层
//RestTemplate供我们调用,注册到spring中
//(url,s实体:Map,Class<T> responseType)
@Autowired
private RestTemplate restTemplate;//提供多种便捷访问远程http 服务的方法,简单的的restful服务模版
//通过ribbon实现,我们这里的地址应该是一个变量,通过服务名来访问
// private static final String REST_URL_PREFIX = "http://localhost:8001";
private static final String REST_URL_PREFIX = "SPRINGCLOUD-PROVIDER-DEPT";
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept) {
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
}
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get" + id, Dept.class);
}
@RequestMapping("/consumer/dept/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
}
}
二、 Ribbon:使用Ribbon实现负载均衡
1.新建两个子项目springcloud-provider-dept-8002、springcloud-provider-dept-8003springcloud-eureka-7003
2.将springcloud-provider-dept-8001的pom中的依赖复制粘贴至springcloud-provider-dept-8003、springcloud-provider-dept-8003
3.将springcloud-provider-dept-8001的resource文件夹复制粘贴至springcloud-provider-dept-8002、springcloud-provider-dept-8003,分别修改application.yaml的端口号为8002,8003,并将数据库配置修改为db02,db03,修改eureka上的默认描述信息分别为 instance-id: springcloud-privider-dept-8002, instance-id: springcloud-privider-dept-8003
4.复制springcloud-provider-dept-8001的com.giao.springcloud
包下的所有文件至两个子项目springcloud-provider-dept-8002,springcloud-provider-dept-8003,修改启动类为对应项目,图示,8003同理,三个服务提供者唯一不同的是数据库连的分别是db01,db02,db03
三、Ribbon:自定义负载均衡算法
- 配置负载均衡实现RestTemplate
- IRule
- RoundRobinRule 轮询
- RandomRule 随机
- AvailabilityFilteringRule:会先过滤掉跳闸、访问故障的服务,对剩下的进行轮询
- RetryRule:会先按照轮询获取服务 如果服务获取失败,则会在指定时间内进行重试
1.新建一个com.giao.myrule包,调自己写的随机算法
package com.giao.myrule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class GiaoRule {
@Bean
public IRule myRule(){
return new GiaoRandomRule(); //默认是轮询,调自己写的随机算法
}
}
package com.giao.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;
import java.util.concurrent.ThreadLocalRandom;
public class GiaoRandomRule extends AbstractLoadBalancerRule {
//每个服务访问五次,换下一个服务(3个)
//total=0,默认=0,如果=5,我们指向下一个服务节点
//index=0,默认=0,如果total=5,index+1
private int total=0;//被调用的次数
private int currentIndex=0;//当前是谁在提供服务
// @SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
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;
}
// int index = this.chooseRandomInt(serverCount);//生成区间随机数
// server = (Server)upList.get(index);//从活着的服务随机获取一个
//-=================================================================
if(total<5){
server = upList.get(currentIndex);
total++;
}else{
total=0;
currentIndex++;
if(currentIndex>upList.size()){
currentIndex=0;
}
server = upList.get(currentIndex);//从活着的服务中,获取指定的服务来进行操作
}
//-=================================================================
if (server == null) {
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}
server = null;
Thread.yield();
}
}
return server;
}
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}