Spring Cloud 之 Ribbon 负载均衡
文章目录
简介
什么是负载均衡
负载均衡(Load Balance), 是利用特定的方式将流量分摊到多个操作单元上的一种手段, 它对系统吞吐量和系统处理能力有质的提升. 可分为软负载和硬负载, 软负载即通过软件的方式实现负载均衡, 软负载有分为客户端负载和服务端负载, Ribbon 属于客户端负载均衡.
简单的入门案例
- 一个注册中心
- 一个服务端可以通过修改端口号的形式, 启动多个实例
- 一个客户端
- 项目整体结构采用 maven 多 Module 结构:
|_ ribbon-demo-ms |_ eureka-server // 注册中心, 给内部服务做服务发现 |_ app-server // 服务提供端, 提供 Restful 接口, 启动多个实例 |_ app-client // 服务调用端, 通过 FeignClient 调用 ribbon-server 的接口
创建一个 Eureka Server
创建一个 AppServer
通过修改端口号的方式, 启动多个实例:
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 等
| | |
---|
| | |
| | 按顺序循环选择 server (Ribbon 默认策略) |
| | 在一个配置时间段内选择 server 不成功, 则一直尝试选择一个可用的 server (默认重试时间 500 ms, 默认重试策略还是 RoundRobinRule) |
| | 根据 server 的响应时间分配权重. 响应时间越长, 权重越低. 被选择到的概率就越低; 响应时间越短, 权重越高, 被选择到的概率就越高. 这个策略很贴切, 综合了各种因素, 如: 网络、磁盘、IO等, 这些因素直接影响响应时间 |
| | 逐个考察 server, 如果 server 断路器打开, 则忽略, 再选择其中并发链接最低的 server |
AvailabilityFilteringRule | | 过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server(active connections超过配置的阈值) |
| | 综合判断 server 所在区域的性能, 和 server 的可用性, 轮询选择server 并且判断一个 AWS Zone 的运行性能是否可用, 剔除不可用的Zone 中的所有 server |
Ribbon 工作原理
Ribbon 核心接口
接口是一个组件的骨架, 通过了解接口, 能够把握其核心设计及功能, Ribbon 完全是按照这些接口搭建起来的. Ribbon 有 7 个核心接口:
| | |
---|
| | |
| | |
| | |
| | ConfigurationBasedServerList |
| | ZonePreferenceServerListFilter |
| | |
| 为 DynamicServerListLoadBalancer 定义动态更新服务列表的接口 | |
性能优化
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
问题