SpringCloud笔记四:Ribbon
什么是Ribbon?
Ribbon是一个客户端的负载均衡。
我举个例子就明白了,我去超市买东西付钱,收银台有3个,一个收银台有10个人排队,一个收银台有5个人排队,一个收银台有2个人排队。只要我不是傻子,我就知道我该去2个人排队的那个收银台。
我是客户,我知道选择人最少的收银台。这就是客户端的负载均衡。这就是Ribbon
提一句,负载均衡有两种方式:
- 集中式:这个就是有一个硬件,比如F5,提供者和消费者之间通过硬件负载均衡,缺点是硬件一般都很贵
- 进程内:这个是软件的形式,把负载均衡的逻辑放到消费方,让消费方根据逻辑去选择一台合适的服务器。
Ribbon的配置
Maven引入
由于Ribbon是客户端的负载均衡,我们在consumer项目里面引入Maven配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
开启注解
注解这有两个地方需要标注,我们的consumer是使用restTemplate来访问的,在获取restTemplate的方法上开启负载均衡注解,@LoadBalanced
@Bean
@LoadBalanced
public RestTemplate getRestTemplate()
{
return new RestTemplate();
}
顺便,consumer里面的Controller里面,访问的地址我们原来写的是localhost,既然是微服务,我们肯定使用服务名称了,改一下
// private static final String REST_URL_PREFIX="http://localhost:8001";
private static final String REST_URL_PREFIX="http://PROVIDER-DEPT";
第二个注解就是主方法那里,加一个@EnableEurekaClient
@SpringBootApplication
@EnableEurekaClient
public class Consumer80Application {
public static void main(String[] args) {
SpringApplication.run(Consumer80Application.class, args);
}
}
再次重启启动eureka7001,eureka7002,eureka7003和provider,consumer,你会发现还是完全ok的。
Ribbon负载均衡
上面说了,Ribbon是客户端的负载均衡,所以我们要加在consumer项目上,Ribbon默认的负载均衡方式的轮询。我们可以新建两个provider来测试一下
新建provider8002和8003
新建项目就不多说了,记得把8001的yml,Mybatis,代码啥的复制过去。如下
server:
port: 8002
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml #mybatis配置文件所在路径
type-aliases-package: com.vae.springcloud.entity #所有Entity别名类所在包
mapper-locations: classpath:mybatis/mapper/**/*.xml #mapper映射文件
spring:
application:
name: provider-dept
datasource:
# type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/vae?serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间
eureka:
client: #客户端注册进eureka服务列表内
service-url:
# defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: provider-8002 #这个是修改Eureka界面的Status名称
prefer-ip-address: true #这个是设置鼠标放在status上的时候,出现的提示,设置ip地址显示
info:
app.name: provider-8001
company.name: www.vae.com
build.artifactId: $project.artifactId$
build.version: $project.version$
server:
port: 8003
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml #mybatis配置文件所在路径
type-aliases-package: com.vae.springcloud.entity #所有Entity别名类所在包
mapper-locations: classpath:mybatis/mapper/**/*.xml #mapper映射文件
spring:
application:
name: provider-dept
datasource:
# type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/jj?serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间
eureka:
client: #客户端注册进eureka服务列表内
service-url:
# defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: provider-8003 #这个是修改Eureka界面的Status名称
prefer-ip-address: true #这个是设置鼠标放在status上的时候,出现的提示,设置ip地址显示
info:
app.name: provider-8001
company.name: www.vae.com
build.artifactId: $project.artifactId$
build.version: $project.version$
看到没,yml文件,我只修改了端口号、数据库和instance-id状态id。因为每一个微服务都可以都一个独立的数据库,所以,三个provider的数据库我分别使用的是shuyunquan,vae,jj三个数据库,当然,表结构都是一样的没改
代码直接复制就行,没什么需要改的。
现在我们启动eureka7001,eureka7002,eureka7003,provider8001,provider8002,provider8003和consumer。算一下7个项目了,运行之后,你可以通过consumer访问试试,结果每次刷新都换了数据库,这刚好表明了Ribbon的默认负载均衡方式是轮询。




Ribbon核心组件IRule
Ribbon的核心组件IRule作用是定义以什么样的方式去负载均衡,比如轮询,比如随机。要想使用IRule,我们需要写一个类。
| 注意:IRule组件的类不能在有@ComponentScan组件的类的包以及子包下存在,主方法的@SpringBootApplication注解里面有@ComponentScan注解,所以,我们不能在主方法的所在包或者子包下新建IRule组件 |
|---|
我们在vae包下新建myrule包,包下新建MySelfRule类,代码如下:
package com.vae.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 MySelfRule {
@Bean
public IRule myRule(){
return new RandomRule(); //这个是负载均衡的随机访问
// return new RoundRobinRule(); 这个是负载均衡的默认的轮询访问
}
}
看一下项目目录图

Ribbon自定义
上面讲的都太简单了,完全体现不出技术含量,所以Ribbon负载均衡的方式只有轮询和随机这还不够,我们要学会自定义负载均衡的方式,先看一张图

要想自定义Ribbon负载均衡,我们要自己写个类,首先要继承AbstractLoadBalancerRule这个类,然后剩下的代码嘛,你可以去Ribbon的官方GitHub看看,Ribbon官方随机代码,把这里面的方法复制出来,我们看看里面的内容都是啥,我们只摘取方法
package com.vae.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 RandomRule extends AbstractLoadBalancerRule {
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
//线程是否中断,中断返回null
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
}
int index = chooseRandomInt(serverCount);
server = upList.get(index);
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;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
}
可以看到啊,大概就是这样子的,initWithNiwsConfig方法是我们继承了AbstractLoadBalancerRule实现的,上面的方法中,很容易看出choose方法才是真正有内容的,我们现在就可以对这个随机算法进行修改了,比如,我想修改为每个服务访问5次,然后再随机的访问下一个服务。肯定是修改choose方法了
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) {
/*
* 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)
{
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;
}
可以看到,我就定义了两个变量,自定义算法这个是很主观的,你需要什么样的方式来负载均衡就自己改代码就好了。
SpringCloud笔记四:Ribbon的更多相关文章
- C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻
前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...
- 《MFC游戏开发》笔记四 键盘响应和鼠标响应:让人物动起来
本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9327377 作者:七十一雾央 新浪微博:http:// ...
- IOS学习笔记(四)之UITextField和UITextView控件学习
IOS学习笔记(四)之UITextField和UITextView控件学习(博客地址:http://blog.csdn.net/developer_jiangqq) Author:hmjiangqq ...
- java之jvm学习笔记四(安全管理器)
java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...
- Java学习笔记四---打包成双击可运行的jar文件
写笔记四前的脑回路是这样的: 前面的学习笔记二,提到3个环境变量,其中java_home好理解,就是jdk安装路径:classpath指向类文件的搜索路径:path指向可执行程序的搜索路径.这里的类文 ...
- Java加密与解密笔记(四) 高级应用
术语列表: CA:证书颁发认证机构(Certificate Authority) PEM:隐私增强邮件(Privacy Enhanced Mail),是OpenSSL使用的一种密钥文件. PKI:公钥 ...
- SpringCloud学习之Ribbon
一.负载均衡与Ribbon 负载均衡,在集群中是很常见的一个“名词”,顾名思义是根据一定的算法将请求分摊至对应的服务节点上,常见的算法有如下几种: 轮询法:所有请求被依次分发到每台应用服务器上,每台服 ...
- Learning ROS for Robotics Programming Second Edition学习笔记(四) indigo devices
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...
- Typescript 学习笔记四:回忆ES5 中的类
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
随机推荐
- c/c++ 继承与多态 子类隐藏父类的同名非虚函数
问题1:若类A里有公有成员方法int fcn(),类B公有继承类A,并类B里有公有成员方法int fcn(int),可以在用户代码里,用类B的对象调用类A的int fcn()吗?? 不可以,编译出错, ...
- MVC Remote 服务器验证
用此验证必须在Controller中编写返回值为JsonResult的Action public JsonResult CheckUserName(string UserName) { EFHelpe ...
- Docker:测试环境的准备-centos7上安装docker
Dockers官方部署文档:https://docs.docker.com/install/linux/docker-ce/centos/ 1.建议先关闭 selinux (selinux是 linu ...
- HTML---引入css,js | 常用标签示例
一.前端基础包括哪些?如何理解 二.css,js引入_及head中其他标签 三.特殊符号 四.常见的标签 4.1,form表单 4.2,input系列(单选框.复选框.input传文件.重置) 4.3 ...
- excel poi导出demo
最近做了一个excel 导出的demo,是抄写老大的(嘻嘻嘻),现在把demo源码放在这里 链接:https://pan.baidu.com/s/1_xj0hej-1xwX5JF39acEOg 提取码 ...
- python之shutil
''' shutil 用来处理 文件 文件夹 压缩包 的模块 ''' import shutil # 拷贝文件内容 shutil.copyfileobj(open('old.xml', 'r'), o ...
- Golang 入门系列(二)学习Go语言需要注意的坑
上一章节我们已经了解了 Go 环境的配置,不了解的,请查看前面的文章 https://www.cnblogs.com/zhangweizhong/p/9459945.html,本章节我们将学习 Go ...
- 基于 HTML5 的 WebGL 楼宇自控 3D 可视化监控
前言 智慧楼宇和人们的生活息息相关,楼宇智能化程度的提高,会极大程度的改善人们的生活品质,在当前工业互联网大背景下受到很大关注.目前智慧楼宇可视化监控的主要优点包括: 智慧化 -- 智慧楼宇是一个生态 ...
- AOP从静态代理到动态代理 Emit实现
[前言] AOP为Aspect Oriented Programming的缩写,意思是面向切面编程的技术. 何为切面? 一个和业务没有任何耦合相关的代码段,诸如:调用日志,发送邮件,甚至路由分发.一切 ...
- 单机部署 ELK
对于一个体量不大的系统,运行在单机上的 ELK 就足以胜任日志的处理任务了.本文介绍如何在单台服务器上安装并配置 ELK(elalasticsearch + logstash + kibana),并最 ...