ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Spring Cloud Ribbon 负载均衡实战:从入门到原理

Spring Cloud Ribbon 负载均衡实战:从入门到原理 1. 引言在微服务架构中一个服务往往会被部署成多个实例以提升系统的可用性和吞吐量。当客户端调用某个服务时如何从多个实例中选择一个进行请求就成了一个关键问题。Spring Cloud Ribbon 正是为了解决这个问题而诞生的客户端负载均衡组件。Ribbon 是 Netflix 开源的一款基于 HTTP 和 TCP 的客户端负载均衡工具。它内嵌在服务消费方通过一系列内置的负载均衡策略帮助开发者轻松实现服务调用的负载均衡。虽然 Spring Cloud 官方在后续版本中推荐使用 Spring Cloud LoadBalancer 作为替代但 Ribbon 在大量存量项目中依然广泛使用理解其原理对阅读和维护老项目非常有帮助。本文将带你从零开始手把手搭建一个基于 Spring Cloud Ribbon 的负载均衡示例并深入剖析其核心工作原理。2. 环境准备在开始编码之前我们需要准备好以下环境JDK 1.8 及以上Maven 3.6 及以上IntelliJ IDEA 或其他主流 IDE一个可用的注册中心本文以 Eureka 为例本文使用的 Spring Cloud 版本为 Hoxton.SR12对应的 Spring Boot 版本为 2.3.12.RELEASE。这两个版本是 Ribbon 时代比较稳定且常用的组合。3. 创建服务提供者首先我们创建一个服务提供者模块provider-service它负责对外提供具体的业务接口。3.1 引入依赖在pom.xml中引入必要的依赖dependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency/dependencies3.2 编写启动类SpringBootApplicationEnableEurekaClientpublicclassProviderApplication{publicstaticvoidmain(String[]args){SpringApplication.run(ProviderApplication.class,args);}}3.3 编写业务接口RestControllerpublicclassHelloController{Value(${server.port})privateStringport;GetMapping(/hello)publicStringhello(){returnHello from provider, port: port;}}3.4 配置文件server:port:8081spring:application:name:provider-serviceeureka:client:service-url:defaultZone:http://localhost:8761/eureka/为了让负载均衡效果更明显我们可以将服务提供者启动两个实例端口分别设置为 8081 和 8082。在 IDEA 中可以通过配置多个启动项来实现也可以使用--server.port8082参数启动第二个实例。4. 创建服务消费者接下来创建服务消费者模块consumer-service它将通过 Ribbon 调用服务提供者。4.1 引入依赖dependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-ribbon/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency/dependencies4.2 编写启动类SpringBootApplicationEnableEurekaClientpublicclassConsumerApplication{publicstaticvoidmain(String[]args){SpringApplication.run(ConsumerApplication.class,args);}}4.3 配置 RestTemplate在启动类中注入一个带有LoadBalanced注解的RestTemplateBeanBeanLoadBalancedpublicRestTemplaterestTemplate(){returnnewRestTemplate();}LoadBalanced是 Ribbon 的核心注解它会让RestTemplate具备负载均衡的能力。加上这个注解后RestTemplate发出的请求会经过 Ribbon 的拦截器自动完成服务实例的选择。4.4 编写调用接口RestControllerpublicclassConsumerController{AutowiredprivateRestTemplaterestTemplate;GetMapping(/call)publicStringcall(){Stringurlhttp://provider-service/hello;returnrestTemplate.getForObject(url,String.class);}}注意这里的 URL 使用的是服务名称provider-service而不是具体的 IP 和端口。Ribbon 会从注册中心获取该服务名对应的实例列表然后根据负载均衡策略选择一个实例进行调用。4.5 配置文件server:port:8080spring:application:name:consumer-serviceeureka:client:service-url:defaultZone:http://localhost:8761/eureka/5. 启动与测试6. 结合 OpenFeign 使用 Ribbon除了RestTemplateSpring Cloud 还提供了 OpenFeign 这一声明式 HTTP 客户端。OpenFeign 底层同样依赖 Ribbon 实现负载均衡使用起来更加简洁优雅。6.1 引入依赖在服务消费者的pom.xml中引入 OpenFeign 依赖dependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependency/dependencies注意spring-cloud-starter-openfeign会自动传递引入 Ribbon 相关依赖因此无需再单独添加spring-cloud-starter-netflix-ribbon。6.2 开启 Feign 客户端在启动类上添加EnableFeignClients注解SpringBootApplicationEnableEurekaClientEnableFeignClientspublicclassConsumerApplication{publicstaticvoidmain(String[]args){SpringApplication.run(ConsumerApplication.class,args);}}6.3 编写 Feign 接口创建一个 Feign 接口通过声明式注解直接调用服务提供者FeignClient(nameprovider-service)publicinterfaceProviderClient{GetMapping(/hello)Stringhello();}FeignClient注解中的name属性指定了要调用的服务名称。Feign 会将该名称交给 Ribbon由 Ribbon 从注册中心获取实例列表并完成负载均衡。6.4 在 Controller 中调用RestControllerpublicclassFeignConsumerController{AutowiredprivateProviderClientproviderClient;GetMapping(/feign/call)publicStringcall(){returnproviderClient.hello();}}启动服务后多次访问http://localhost:8080/feign/call同样可以看到请求被轮流分发到 8081 和 8082 两个实例上。6.5 配置 Ribbon 策略Feign 使用 Ribbon 时可以通过配置文件为指定服务设置负载均衡策略provider-service:ribbon:NFLoadBalancerRuleClassName:com.netflix.loadbalancer.RandomRule上面的配置将provider-service的负载均衡策略设置为随机策略。也可以像第 6.4 节那样通过配置类全局注入自定义的IRuleBean两种方式对 Feign 同样生效。6.6 Feign 与 RestTemplate 的适用场景方式优点适用场景RestTemplate灵活度高可动态拼接 URL适合简单调用调用逻辑简单、URL 需要动态变化的场景OpenFeign声明式接口代码简洁支持注解配置易于维护服务间调用较多、接口固定的微服务场景简单来说如果只是偶尔调用一两个服务使用RestTemplate足够如果服务间调用关系复杂、接口众多推荐使用 OpenFeign它能让代码更加清晰、可维护性更高。按照以下步骤启动所有服务启动 Eureka 注册中心默认端口 8761。启动两个服务提供者实例端口分别为 8081 和 8082。启动服务消费者端口 8080。启动完成后在浏览器中多次访问http://localhost:8080/call观察返回结果Hello from provider, port: 8081 Hello from provider, port: 8082 Hello from provider, port: 8081 Hello from provider, port: 8082可以看到请求被轮流分发到了两个不同的服务实例上说明 Ribbon 的负载均衡已经生效。默认情况下Ribbon 使用的是轮询策略所以两个实例会交替被访问。6. Ribbon 核心原理了解了基本用法之后我们来深入剖析 Ribbon 的工作原理。6.1 核心组件Ribbon 的核心组件包括ILoadBalancer负载均衡器负责维护服务实例列表并根据策略选择实例。IRule负载均衡策略接口定义了如何从实例列表中选择一个实例。IPing心跳检测接口用于检查服务实例是否存活。ServerList服务实例列表负责从注册中心获取实例信息。ServerListUpdater服务实例列表更新器定时刷新实例列表。6.2 工作流程Ribbon 的工作流程可以概括为以下几个步骤服务消费者发起请求URL 中使用服务名称。LoadBalanced注解的RestTemplate被 Ribbon 的拦截器拦截。拦截器将服务名称交给ILoadBalancer处理。ILoadBalancer从ServerList中获取可用的服务实例列表。IRule根据配置的负载均衡策略从实例列表中选择一个实例。Ribbon 将请求转发到选中的实例并返回结果。下面用一张流程图来展示这个过程客户端发起请求RestTemplate 拦截器ILoadBalancer 负载均衡器ServerList 获取实例列表IRule 选择实例转发请求到目标实例返回响应结果6.3 负载均衡策略Ribbon 内置了多种负载均衡策略常用的有以下几种策略类说明RoundRobinRule轮询策略按顺序依次选择实例默认策略RandomRule随机策略随机选择一个实例WeightedResponseTimeRule根据响应时间加权响应时间越短权重越大RetryRule带重试的轮询策略失败后自动重试BestAvailableRule选择并发量最小的实例AvailabilityFilteringRule先过滤掉故障实例再轮询选择6.4 自定义负载均衡策略如果内置策略不满足需求我们可以自定义负载均衡策略。只需要实现IRule接口即可publicclassMyRuleextendsAbstractLoadBalancerRule{OverridepublicServerchoose(Objectkey){ListServerserversgetLoadBalancer().getAllServers();// 自定义选择逻辑例如始终选择第一个实例returnservers.get(0);}}然后在配置类中注入ConfigurationpublicclassRibbonConfig{BeanpublicIRuleribbonRule(){returnnewMyRule();}}7. 常见问题与注意事项在使用 Ribbon 的过程中有几个常见问题需要特别注意。7.1 服务名大小写问题Ribbon 在匹配服务名时是区分大小写的。如果注册中心中的服务名是provider-service那么调用时也必须使用完全一致的名称否则会报No instances available错误。7.2 超时配置Ribbon 默认的连接超时和读取超时时间可能不适合所有场景可以通过配置进行调整ribbon:ConnectTimeout:3000ReadTimeout:50007.3 饥饿加载默认情况下Ribbon 是在第一次调用时才去注册中心拉取服务列表这会导致第一次请求较慢。可以通过开启饥饿加载来解决ribbon:eager-load:enabled:trueclients:provider-service8. 总结本文从零开始完整演示了如何使用 Spring Cloud Ribbon 实现客户端负载均衡。我们首先搭建了服务提供者和服务消费者然后通过LoadBalanced注解让RestTemplate具备负载均衡能力最后深入剖析了 Ribbon 的核心组件和工作原理。Ribbon 作为 Spring Cloud Netflix 时代的核心组件虽然官方已进入维护模式但理解它的设计思想对于学习 Spring Cloud LoadBalancer 以及阅读存量项目代码仍然非常有帮助。希望本文能帮助你更好地掌握 Ribbon 的使用和原理。
返回列表