SpringCloud Ribbon
Ribbon介绍
基本介绍
核⼼功能:Spring Cloud Ribbon是基于***flix Ribbon实现的⼀套客户端负载均衡⼯具,主要提供负载均衡算法和服务调⽤功能。
实现原理:通过"负载均衡+RestTemplate调⽤"机制⼯作,在客户端本地缓存服务列表(默认30秒刷新),⾃动处理连接超时和重试机制。
配置特性:提供完善的客户端配置项,包括连接超时、重试次数等容错机制,默认采⽤轮询算法进⾏服务调⽤。
实际应⽤:在会员中⼼等微服务场景中,Ribbon会从Eureka Server获取服务列表并在本地缓存,根据负载均衡算法选择服务实例(如10000或10002端⼝)。
算法特点:默认轮询算法类似⽕⻋站售票窗⼝的分流机制,能均衡分配请求到性能相近的服务节点,是经过验证的合理⽅案
官网
https://github.***/***flix/ribbon
项目已进入维护状态, 在生产环境中仍在大规模使用, 替代方案为LoadBalancer
维护现状:***flix官⽅声明Ribbon进⼊维护模式,不再增加新功能,但核⼼组件仍在⽣产环境⼤规模使⽤。
LB(Load Balance)
1.负载均衡的分类
核⼼概念:负载均衡(Load Balance)是将⼯作负载分配到多个计算资源上的技术,在微服务架构中⽤于优化资源使⽤、最⼤化吞吐量、最⼩化响应时间。
1)集中式LB
定义:在服务消费⽅和提供⽅之间使⽤独⽴的LB设施进⾏请求转发
实现⽅式:
o 硬件实现:如F5等专⽤负载均衡设备
o 软件实现:如Nginx等反向代理服务器
⼯作流程:
o 客户端请求⾸先到达LB设施
o LB根据预设策略(轮询/随机/最⼩并发等)选择⽬标服务实例
o 请求被转发到选定的服务实例
特点:
o 作为独⽴中间层存在
o 对客户端透明,客户端⽆需知道后端服务细节
o 常⽤于⽹关层或⼊⼝流量分发
2)进程内LB
定义:将负载均衡逻辑集成到消费⽅进程中
典型代表:Spring Cloud Ribbon
⼯作流程:
o 消费⽅从服务注册中⼼(如Eureka)获取可⽤服务列表
o 在本地缓存服务实例信息
o 根据负载均衡策略直接选择⽬标实例进⾏调⽤
特点:
o 以类库形式集成在消费⽅进程内
o 消费⽅⾃主决定调⽤哪个服务实例
o 默认采⽤轮询算法(可替换)
o 需要配合服务发现组件使⽤
实际应⽤:如member-consumer轮询访问10000/10002端⼝的实现
2.两种LB的对⽐
架构差异:
o 集中式:独⽴中间层架构
o 进程内:客户端集成架构
性能影响:
o 集中式可能成为单点瓶颈
o 进程内增加客户端复杂度但减少跳数
适⽤场景:
o 集中式适合⼊⼝流量管理
o 进程内适合微服务间调⽤
混合模式:实际系统中可能同时使⽤两种⽅式
Ribbon原理
ribbon机制和负载均衡算法
核⼼组件:包含注册中⼼集群、服务健康监控、限流服务、负载均衡等多个模块
系统架构:采⽤分布式微服务架构,通过⽹关统⼀管理服务调⽤和流量控制
学习价值:理解该图可以提升系统架构思维,掌握分布式系统各组件协作关系
Ribbon架构图机制
⼯作流程:
o 注册阶段:服务提供者向Eureka Server注册实例
o 发现阶段:服务消费者从Eureka Server拉取可⽤服务列表
o 调⽤阶段:Ribbon根据负载算法选择具体服务实例进⾏调⽤
缓存机制:
o 刷新间隔:默认每30秒从注册中⼼刷新⼀次服务列表
o 本地缓存:服务列表会缓存在JVM内存中,提⾼调⽤效率
o 动态感知:新增服务实例会⾃动加⼊轮询列表,⽆需重启消费者
验证⽅法:可通过逐步启动服务提供者实例,观察负载均衡效果来验证机制
一、Ribbon 怎么从注册中心拿服务?
就像你去餐厅吃饭前会先看菜单,Ribbon 启动时会从 Eureka 服务器 “扒拉” 一份服务列表存在本地(比如美团商家的地址和状态),而且每隔一段时间会自动更新,保证列表是最新的。这样调用服务时就不用每次都问服务器,速度更快。
二、Ribbon 怎么挑服务?5 种常见 “挑人” 策略,用大白话讲清楚:
-
“谁最闲选谁”(BestAvailableRule)
先把 “生病” 的机器(比如经常出错的服务器)排除掉,然后在剩下的机器里,挑当前正在处理请求最少的那个。就像外卖小哥接单,优先派给手里订单最少的人。
-
“过滤掉坑货”(AvailabilityFilteringRule)
分两步挑:第一步排除 “连不上” 的机器(比如服务器死机了),第二步排除 “太忙” 的机器(比如同时接了 100 个请求的服务器)。就像点奶茶时,先排除关门的店,再排除排队太长的店。
-
“看速度给权重”(WeightedResponseTimeRule)
给每个机器打 “分”:响应速度快的机器(比如 1 秒内就返回结果)多派单,响应慢的(比如 5 秒才返回)少派单。就像快递站分配任务,手脚麻利的快递员多派件,磨叽的少派。
-
“不行就再试一次”(RetryRule)
如果第一次调用机器没反应,会在短时间内(比如 5 秒)反复试几次,直到成功或者试够次数为止。就像打电话给朋友,一次没接就再打几个,直到接通或放弃。
-
“轮流干活”(RoundRobinRule)
这是默认策略,就像排队买票,第一个请求给 A 机器,第二个给 B 机器,第三个给 C 机器,循环下去。适合所有机器性能差不多的情况。
-
“随机抓阄”(RandomRule)
完全随机选一个机器,就像抽奖一样。长期来看,每个机器被选中的次数差不多,适合没特殊要求的场景。
-
“看地区挑最优”(ZoneAvoidanceRule)
综合考虑机器所在的 “区域”(比如北京机房、上海机房)和可用性,优先选又近又不忙的机器。就像点外卖时优先选同小区或附近商圈的店,配送快还不容易超时。
三、选哪个策略最合适?
没特殊要求:直接用默认的 “轮流干活”(RoundRobinRule),简单公平。
机器性能差别大:用 “看速度给权重”(WeightedResponseTimeRule),让快的机器多干活。
怕服务超时:用 “不行就再试一次”(RetryRule),失败了多试几次,提高成功率。
Ribbon 确实是客户端进程内的负载均衡组件,可以理解为 “长在服务调用方代码里的调度员”。
为啥说它是 “进程内” 的?
举个例子:比如你有一个订单服务(A)要调用用户服务(B),Ribbon 不是独立的第三方服务,而是直接集成在订单服务 A 的代码里。当 A 要调用 B 时,Ribbon 就在 A 的进程内部直接处理负载均衡逻辑,比如从本地缓存的服务列表里挑一个 B 的实例,然后发起调用。
和 “进程外” 负载均衡的区别?
进程外(比如 Nginx):请求先经过 Nginx 这样的中间代理,由 Nginx 负责转发到不同的服务实例,相当于 “中间商赚差价”。
进程内(Ribbon):服务调用方自己就能决定调哪个实例,不需要经过中间代理,效率更高,也更灵活(比如可以针对不同服务定制不同策略)。
Ribbon 和 Eureka 怎么配合?
Eureka 负责 “记名单”:各个服务启动时向 Eureka 注册自己的地址和状态(比如用户服务 B 有 3 个实例:192.168.1.1:8080、192.168.1.2:8080 等)。
Ribbon 负责 “挑名单”:订单服务 A 启动时,从 Eureka 拉取用户服务 B 的所有实例列表,存在本地内存里。当 A 要调用 B 时,Ribbon 直接从本地列表里按规则选一个实例调用,不用每次都问 Eureka,速度更快。
动态更新名单:如果用户服务 B 的某个实例挂了,Eureka 会感知到并通知 Ribbon(或者 Ribbon 自己定期检查),Ribbon 就会把这个挂掉的实例从本地列表里 “划掉”,避免调错地方。
简单来说:
Ribbon 就像是服务调用方的 “私人助理”,坐在调用方的 “办公室”(进程)里,手里拿着 Eureka 给的 “通讯录”(服务列表),当调用方需要找某个服务时,助理直接按规则挑一个合适的联系方式(实例地址),让调用方直接沟通,省去了找中间人的麻烦。
切换负载均衡算法
启动顺序:
先启动Eureka Server集群(9001和9002端⼝)
启动服务提供⽅(10000和10002端⼝)
最后启动服务消费⽅(80端⼝)
分布式部署:这些服务可以部署在不同主机上形成分布式系统
替换⽬标:将轮询算法改为随机算法
预期效果:
刷新时可能连续多次调⽤同⼀端⼝服务
访问分布不再严格交替
实现⽅式:通过配置Ribbon的负载均衡规则来实现
访问⽅式:通过浏览器输⼊http://localhost/member/consumer/selectById/1进⾏访问
实现
1.创建RibbonRule
⽬的: 配置⾃定义的负载均衡算法。
实现⽅式: 创建⼀个名为RibbonRule的Java类,并使⽤@Configuration注解将其标识为配置类。
配置类作⽤: ⽤于配置和注⼊⾃定义的负载均衡算法。
关键⽅法: 在RibbonRule类中定义⼀个返回IRule接⼝实现类的⽅法,并使⽤@Bean注解将其注⼊Spring容器。
算法选择: 可以选择多种负载均衡算法,如RandomRule(随机选择)、RoundRobinRule(轮询)等。
实现⽅式:在RibbonRule类的⽅法中,通过return new RandomRule();等⽅式返回所选算法的对象实例。
@Configuration
public class RibbonRule {
//配置和注⼊⾃定义的负载均衡算法
//可以选择多种负载均衡算法,如RandomRule(随机选择)、RoundRobinRule(轮询)等。
//
@Bean
public IRule getRibbonRule() {
return new RandomRule();
}
}
2.指定Ribbon的负载均衡算法
指定⽅式: 使⽤@RibbonClient注解在主启动类上,指定Ribbon客户端的名称和配置
参数说明:
name: Ribbon客户端的名称,对应服务提供者的名称。
configuration: ⾃定义的负载均衡配置类,即RibbonRule.class
@RibbonClient(name = "member-service-provider", configuration = RibbonRule.class)
@EnableEurekaClient
@EnableDiscoveryClient
@SpringBootApplication
public class MemberConsumerApplication80 {
public static void main(String[] args) {
ConfigurableApplicationContext ioc = SpringApplication.run(MemberConsumerApplication80.class, args);
}
}
OpenFeign
什么是OpenFeign
大体流程:消费者 -> openfeign -> eureka查地址 -> ribbon选实例 -> 发起请求
1.Ribbon+RestTemplate要自己打电话订奶茶
自己查找商家电话(在Eureka那找地址),要自己拨号(调用RestTemplate发请求),选哪家店(Ribbon选实例),代码是
String mobile = 查通讯录("大杯奶茶店");
Result 奶茶 = 打电话(电话 + "/买奶茶?规格=大杯");
2.OpenFeign(用外卖 APP 订奶茶)
直接在 APP 里搜 “大杯奶茶店”,选 “大杯珍珠奶茶”,APP 自动帮你查地址、选分店、下单:
Result 奶茶 = 外卖菜单.buyMilkTea("大杯");
最后总结:OpenFeign 就是 “微服务外卖 APP”
你只需要列菜单(定义 Feign 接口)、点单(Controller 调用),其他像查地址、选分店、发请求全由 APP(OpenFeign 框架)搞定,比自己打电话订奶茶省心 100 倍!
现在讲的Feign默认指的都是OpenFeign。
需求
实现
配置文件编写->主启动类配置->注册测试->接口开发->Controller开发->注解启用
1.建立个新模块e-***merce-consumer-openfeign-80,避免与原来的消费服务造成混淆。
2.端口保持一致,pom一致,配置文件修改应用名。
3.引入OpenFeign场景启动器
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--引入eureka client场景启动器starter-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-***flix-eureka-client</artifactId>
</dependency>
4.启动类MemberConsumerOpenFeignApplication80.java添加注解 @EnableFeignClients
4.创建接口
@FeignClient注解:用于标注一个接口是OpenFeign的客户端,value属性指定服务提供方的名称;
远程调用方式:可以在接口中定义远程调用的方法,包括HTTP请求方式(如GET)和URL路径
服务注册与发现:OpenFeign结合Eureka等服务注册与发现组件,可以根据服务名动态调用服务实例
“奶茶店"就相当于“MEMBER-SERVICE-PROVIDER”,好多服务实例都是”奶茶店“,当你点了杯奶茶(调用了该服务下的某个接口),那么底层的ribbon就会负载均衡去调用其中的服务实例。
你只需要指定某个服务下的某个接口。由ribbon完成负载均衡和URL替换。
只需要考虑“能点什么”,就像外卖APP的菜单列表。
@FeignClient“搜行业”,@GetMappping“选菜品”,参数是“甜度,冰量”
@FeignClient("奶茶店") //搜"奶茶店"这个商家
public interface 奶茶订单接口 {
@GetMapping("/珍珠奶茶/大杯") //菜单里的"大杯珍珠奶茶"
奶茶 下单(@PathVariable("甜度") String 甜度); //甜度是参数
}
@***ponent
@FeignClient(value = "member-service-provider")
public interface MemberFeignService {
@GetMapping("/selectById")
@ResponseBody
public Result selectById(Integer id);
@PostMapping("/insert")
@ResponseBody
public Result insert(@RequestBody Member member);
}
5.创建controller
点单服务,消费者点击下单,调用feign接口
@***ponent
public class 点单服务 {
@Resource
private 奶茶订单接口 外卖APP; // 注入外卖APP
public 奶茶 点奶茶() {
奶茶 我的奶茶 = 外卖APP.下单("半糖"); // 点击“下单半糖大杯珍珠奶茶”
return 奶茶;
}
}
@RestController
public class MemberFeignController {
@Resource
private MemberFeignService memberFeignService;
@GetMapping("/selectById/{id}")
@ResponseBody
public Result selectById(@PathVariable("id") Integer id) {
return memberFeignService.selectById(id);
}
@PostMapping("/insert")
@ResponseBody
public Result insert(@RequestBody Member member) {
return memberFeignService.insert(member);
}
}
6.测试
OpenFeign日志配置
基本介绍
实现
1.创建src/main/java/***/zzw/springcloud/config/OpenFeignConfig.java
@Configuration
public class OpenFeignConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
2.修改配置文件
logging:
level:
***.zzw.springcloud.service.MemberFeignService: debug
3.演示完成后需注销@Bean和yml配置,通过重启服务关闭⽇志输出.忘记关闭可能导致敏感信息泄露或性能损耗
OpenFeign调用超时
ribbon:
# 单位毫秒
ReadTimeout: 8000
ConnectTimeout: 8000