Spring Cloud 之 Ribbon 负载均衡
文章目录
- 简介
- 什么是负载均衡
- 简单的入门案例
- 创建一个 Eureka Server
- 创建一个 AppServer
- 创建一个 AppClient 服务
- 测试效果
- Ribbon 负载均衡策略
- Ribbon 工作原理
- Ribbon 核心接口
- 性能优化
- 问题
简介
什么是负载均衡
负载均衡(Load Balance), 是利用特定的方式将流量分摊到多个操作单元上的一种手段, 它对系统吞吐量和系统处理能力有质的提升. 可分为软负载和硬负载, 软负载即通过软件的方式实现负载均衡, 软负载有分为客户端负载和服务端负载, Ribbon 属于客户端负载均衡.
简单的入门案例
- 一个注册中心
- 一个服务端可以通过修改端口号的形式, 启动多个实例
- 一个客户端
- 项目整体结构采用 maven 多 Module 结构: |_ ribbon-demo-ms |_ eureka-server // 注册中心, 给内部服务做服务发现 |_ app-server // 服务提供端, 提供 Restful 接口, 启动多个实例 |_ app-client // 服务调用端, 通过 FeignClient 调用 ribbon-server 的接口
创建一个 Eureka Server
- 参考: Spring Cloud 之 Eureka 服务注册与发现
创建一个 AppServer
通过修改端口号的方式, 启动多个实例:
代码语言:javascript复制java -Dserver.port=8762 -jar appserver.jar
java -Dserver.port=8763 -jar appserver.jar
- pom 文件配置 <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
- 编写工程的配置文件: application.yml eureka: client: serverUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8762 spring: application: name: app-server
- 编写项目的启动类, 在类名上添加 @EnableEurekaClient 注解. 启动项目后, 在服务注册页面可以看到 app-server 的注册信息 @SpringBootApplication @EnableEurekaClient public class EurekaClientApplication { public static void main(String[] args) { SpringApplication.run(EurekaClientApplication.class, args); } }
- 定义一个 Restful 接口给客户端使用 @GetMapping("/port") public String printRequest(HttpServletRequest request) { // 返回当前实例的端口号 return request.getServerPort(); }
创建一个 AppClient 服务
- pom 配置, 使用 FeignClient 调用 AppServer 的服务, 中间使用到 Ribbon 负载均衡, 所以要引入 Feign 和 Ribbon 的依赖 <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> </dependencies>
- 编写工程配置文件 eureka: client: serverUrl: defaultZone: http://localhost:8761/eureka/ instance: lease-renewal-interval-in-seconds: 5 lease-expiration-duration-in-seconds: 15 perfer-in-address: true server: port: 8765 spring: application: name: app-client feign: client: config: default: connectTimeout: 5000 # 连接超时时间 readTimeout: 5000 # 读超时间 loggerLevel: full # 日志内容 logging: level: com.deepflow.clients.api.*: debug # 日志级别
- 编写项目的启动类, 在类名上添加 @EnableEurekaClient 和 EnableFeignClients 注解. 启动项目后, 在服务注册页面可以看到 app-client 的注册信息 @SpringBootApplication @EnableFeignClients({"com.deepflow.clients.api"}) public class CommentServerApplication { public static void main(String[] args) { SpringApplication.run(CommentServerApplication.class, args); } }
- 编写 FeignClient 接口, 调用 app-server 的服务, feign 自带了 Ribbon 功能 package com.deepflow.clients.api; @FeignClient(name = "app-server",url="${custom.feign.url}") public interface appServer { @GetMapping(value = "/port", consumes = MediaType.APPLICATION_JSON_VALUE) String printRequest(); }
- 定义一个 Restful 接口给 Postman 使用 package com.deepflow.controller; @RestController @RequestMapping("/test/ribbon") public class appClientController { @Autowired private appServer appServerService; @GetMapping("/ports") public String getappPort(Integer a, Integer b) { return appServerService.printRequest(); } }
测试效果
用 Postman 调用 http://localhost:8765/test/ribbon/ports, 返回结果 8762 和 8763 依次交替出现. 说明负载均衡已经起到作用了, 并且是按顺序交替把请求分配到 app-server:8762 和 app-server:8763 两个实例上
Ribbon 负载均衡策略
Ribbon 有7种负载均衡策略, 默认的是轮询策略. 补充一下 nginx 常用的负载均衡策略: 轮询(Round Robin)、权重(Weigh)、ip_hash 等
策略类 | 命名 | 描述 |
---|---|---|
RandomRule | 随机策略 | 随机选择策略 server |
RoundRobinRule | 轮询策略 | 按顺序循环选择 server (Ribbon 默认策略) |
RetryRule | 重试策略 | 在一个配置时间段内选择 server 不成功, 则一直尝试选择一个可用的 server (默认重试时间 500 ms, 默认重试策略还是 RoundRobinRule) |
ResponseTimeWeightedRule | 响应时间加权策略 | 根据 server 的响应时间分配权重. 响应时间越长, 权重越低. 被选择到的概率就越低; 响应时间越短, 权重越高, 被选择到的概率就越高. 这个策略很贴切, 综合了各种因素, 如: 网络、磁盘、IO等, 这些因素直接影响响应时间 |
BestAvailableRule | 最低并发策略 | 逐个考察 server, 如果 server 断路器打开, 则忽略, 再选择其中并发链接最低的 server |
AvailabilityFilteringRule | 可用过滤策略 | 过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server(active connections超过配置的阈值) |
ZoneAvoidanceRule | 区域权重策略 | 综合判断 server 所在区域的性能, 和 server 的可用性, 轮询选择server 并且判断一个 AWS Zone 的运行性能是否可用, 剔除不可用的Zone 中的所有 server |
Ribbon 工作原理
Ribbon 核心接口
接口是一个组件的骨架, 通过了解接口, 能够把握其核心设计及功能, Ribbon 完全是按照这些接口搭建起来的. Ribbon 有 7 个核心接口:
接口 | 描述 | 默认实现类 |
---|---|---|
IClientConfig | 定义 Ribbon 中管理配置的接口 | DefaultClientConfigImpl |
IRule | 定义 Ribbon 中负载均衡策略的接口 | ZoneAvoidanceRule |
IPing | 定义定期 ping 服务检查可用性接口 | DummyPing |
ServerList | 定义获取服务列表方法的接口 | ConfigurationBasedServerList |
ServerListFilter | 定义特定场景下, 获取服务列表的方法接口 | ZonePreferenceServerListFilter |
ILoadBalancer | 定义负载均衡选择服务的核心方法的接口 | ZoneAwareLoadBalancer |
ServerListUpdater | 为 DynamicServerListLoadBalancer 定义动态更新服务列表的接口 | PollingServerListUpdater |
性能优化
Ribbon 是懒加载机制, 并不是在项目启动的时候就加载上下文, 而是在实际请求的时候才去创建. 这种情况下, 第一次调用可能会出现用时比较长的现象, 甚至有可能会引起调用超时. 可以通过配置的方式开启饥饿加载
- application.yaml 配置 ribbon: eager-load: enabled: true clients: joke-server
- 测试效果 启动项目后, 可以看到打印的堆栈日志, 启动的时候 Ribbon 便加载上了应用程序的上下文 2020-04-21 11:48:57.897 INFO 15668 --- [ main] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client joke-server initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=joke-server,current list of Servers=[localhost:8763],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:1; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;] },Server stats: [[Server:localhost:8763; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0] ]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@22d477c2 2020-04-21 11:49:27.865 INFO 15668 --- [mer-joke-server] c.n.l.WeightedResponseTimeRule : Weight adjusting job started
问题
- 如何更换负载均衡策略?