SpringBoot实战(二十四)集成 LoadBalancer

目录

一、简介

1.定义

Spring Cloud LoadBalancerSpring Cloud 框架提供的负载均衡组件,用于在微服务框架中实现服务之间的负载均衡。

2.取代 Ribbon

Spring Cloud LoadBalancer 的设计目标是简化和统一负载均衡的使用方式,并提供良好的扩展性。从 Spring Cloud 2020.0 版本、Spring Boot 2.4.x 版本开始,Spring Boot 框架官方放弃了对 Ribbon 的支持,转而使用 Spring Cloud LoadBalancer 作为默认的负载均衡器。虽然提供了与 Ribbon 类似的功能,但是与 Ribbon 相比,Spring Cloud LoadBalancer 具有更轻量级的实现,更好的性能表现 ,并且更适合与 Spring Cloud Gateway 等其他组件集成使用

3.主要特点与功能

Spring Cloud LoadBalancer 的主要特点和功能包括:

1)简化集成:Spring Cloud LoadBalancer 可以与 Spring Cloud 服务注册与发现组件(如 Eureka、Consul)集成,从注册中心获取服务信息,并进行负载均衡
2)内置负载均衡策略:Spring Cloud LoadBalancer 提供了一些内置的负载均衡策略,如轮询(Round Robin)随机(Random) 等,可以根据需要选择合适的负载均衡策略。
3)定制负载均衡策略:Spring Cloud LoadBalancer 还支持定制负载均衡策略,开发人员可以根据实际需求自定义负载均衡算法
4)故障转移:当某个服务节点不可用时,Spring Cloud LoadBalancer 可以自动将请求发送到其他可用的节点 ,提高系统的可靠性和容错性。
5)可扩展性:Spring Cloud LoadBalancer 提供了扩展点,开发人员可以根据需要进行扩展和定制,以满足特定的负载均衡需求。

在使用 Spring Cloud LoadBalancer 时,可以通过相关的依赖和配置来进行集成和使用。它可以与其他 Spring Cloud 组件(如 Spring Cloud Gateway、Spring Cloud OpenFeign 等)一起使用,为应用程序提供完整的微服务环境。

4.LoadBalancer 和 OpenFeign 的关系

很多人一直认为 OpenFeign 和 LoadBalancer 的关系就是简单的包含 ,其实这种看法是错误的。我们可以看看 Spring Cloud 里产品 Map 图里的关系,就一目了然了。

OpenFeign 的定位是 annotation 化的 RESTFUL Client 。需要认识到 OpenFeign 的本质其实是 Spring Cloud 里面比 RestTemplate 更高阶的一个升级组件,实现的是 RESTFUL Client,只是通过 Open Feign 的一些 annotaion 可以实现的比较简单而已。

LoadBalancer 是 Spring Cloud 里的一个 Common 组件,是可以给其他组件提供服务的基础组件。使用 LoadBalancer 无需 Open Feign 的集成打开 LoadBalancer 的支持功能 ,有关 RestTemplate 的地方就可以实现客户端的负载均衡了,OpenFeign 是 RestTemplate 的扩展,当然也就同样可以支持到负载均衡。

细心的朋友可以发现,咱们下面介绍的有关 Loadbalance 的使用,基本上都是和 OpenFeign 没有任何联系的; OpenFeign 只是我们后来进行验证效果的方式。


二、使用场景一:Eureka + LoadBalancer

服务架构图如下:

服务列表如下:

服务A:loadbalancer-consumer 消费者

1.Maven依赖

注意:这里的 LoadBalancer 依赖必须引入,否则即使编译不报错,负载均衡也是失效的。

xml 复制代码
<!-- Web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<!-- Eureka -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

<!-- LoadBalancer -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
2.application.yml配置
yaml 复制代码
server:
  port: 8081

spring:
  application:
    name: loadbalancer-consumer

#eureka client
eureka:
  client:
    service-url:
      defaultZone: http://demo:Demo2023@localhost:1001/eureka/
  instance:
    hostname: localhost
    prefer-ip-address: true # 是否使用 ip 地址注册
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port
    lease-renewal-interval-in-seconds: 5 # 实例续期心跳间隔(默认30s),设置之后启动服务不需要等很久就可以访问到服务的内容
    lease-expiration-duration-in-seconds: 15 # 实例续期持续多久后失效(默认90s)
3.RestTemplateConfig.java
java 复制代码
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * <p> @Title RestTemplateConfig
 * <p> @Description RestTemplate配置类
 *
 * @author zhj
 * @date 2023/9/17 20:58
 */
@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
4.DemoController.java
java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * <p> @Title DemoController
 * <p> @Description 测试Controller
 *
 * @author ACGkaka
 * @date 2023/4/24 18:02
 */
@Slf4j
@RestController
@RequestMapping("/demo")
public class DemoController {

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/consumeTest")
    public Object test() {
        log.info(">>>>>>>>>>【INFO】DemoController.consumeTest()...");
        String url = "http://loadbalancer-producer/demo/produceTest";
        return restTemplate.getForObject(url, Object.class);
    }
}

服务B:loadbalancer-producer 生产者

1.Maven依赖
xml 复制代码
<!-- Web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<!-- Eureka -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.application.yml配置
yaml 复制代码
server:
  port: 8082

spring:
  application:
    name: loadbalancer-producer

#eureka client
eureka:
  client:
    service-url:
      defaultZone: http://demo:Demo2023@localhost:1001/eureka/
  instance:
    hostname: localhost
    prefer-ip-address: true # 是否使用 ip 地址注册
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port
    lease-renewal-interval-in-seconds: 5 # 实例续期心跳间隔(默认30s),设置之后启动服务不需要等很久就可以访问到服务的内容
    lease-expiration-duration-in-seconds: 15 # 实例续期持续多久后失效(默认90s)
3.DemoController.java
java 复制代码
import com.demo.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * <p> @Title DemoController
 * <p> @Description 测试Controller
 *
 * @author ACGkaka
 * @date 2023/4/24 18:02
 */
@Slf4j
@RestController
@RequestMapping("/demo")
public class DemoController {

    @Value("${server.port}")
    private String port;

    @RequestMapping("/produceTest")
    public Result<Object> produceTest() {
        Result<Object> result = Result.succeed();
        log.info(">>>>>>>>>>【INFO】DemoController.produceTest()...");
        return result.setData("Hello, I'm from port: " + port);
    }
}

调用测试

请求地址:http://localhost:8081/demo/consumeTest

可以发现分别打印了不同的服务端口,说明负载生效:


三、使用场景二:Eureka + LoadBalancer + OpenFeign

服务架构图如下:

服务列表如下:

服务A:loadbalancer-feign-a

1.Maven依赖
xml 复制代码
<!-- Web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<!-- Eureka -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

<!-- OpenFeign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.application.yml配置
yaml 复制代码
server:
  port: 8081

spring:
  application:
    name: loadbalancer-feign-a

#eureka client
eureka:
  client:
    service-url:
      defaultZone: http://demo:Demo2023@localhost:1001/eureka/
  instance:
    hostname: localhost
    prefer-ip-address: true # 是否使用 ip 地址注册
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port
    lease-renewal-interval-in-seconds: 5 # 实例续期心跳间隔(默认30s),设置之后启动服务不需要等很久就可以访问到服务的内容
    lease-expiration-duration-in-seconds: 15 # 实例续期持续多久后失效(默认90s)

##feign参数优化
feign:
  client:
    config:
      # 这里用 default 就是全局配置,如果是写服务名称,则是针对某个服务的配置。
      default:
        # 日志级别(忽略大小写),包括:NONE(默认)、BASIC、HEADERS、FULL
        loggerLevel: FULL
3.DemoController.java
java 复制代码
import com.demo.common.Result;
import com.demo.feign.DemoFeignClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequestMapping("/demo")
public class DemoController {

    @Value("${server.port:}")
    private String port;

    @Autowired
    private DemoFeignClient demoFeignClient;

    @GetMapping("/feignTest")
    public Result<Object> feignTest() {
        return demoFeignClient.test();
    }
}
4.DemoFeignClient.java
java 复制代码
import com.demo.common.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(value = "loadbalancer-feign-b")
public interface DemoFeignClient {

    @GetMapping("/demo/test")
    Result<Object> test();
}

服务B:loadbalancer-feign-b

1.Maven依赖
xml 复制代码
<!-- Web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<!-- Eureka -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.application.yml配置
yaml 复制代码
server:
  port: 8082

spring:
  application:
    name: loadbalancer-feign-b

#eureka client
eureka:
  client:
    service-url:
      defaultZone: http://demo:Demo2023@localhost:1001/eureka/
  instance:
    hostname: localhost
    prefer-ip-address: true # 是否使用 ip 地址注册
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port
    lease-renewal-interval-in-seconds: 5 # 实例续期心跳间隔(默认30s),设置之后启动服务不需要等很久就可以访问到服务的内容
    lease-expiration-duration-in-seconds: 15 # 实例续期持续多久后失效(默认90s)
3.DemoController.java
java 复制代码
import com.demo.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequestMapping("/demo")
public class DemoController {

    @Value("${server.port:}")
    private String port;

    @GetMapping("/test")
    public Result<Object> test() {
        String data = "This is a test! port:" + port;
        log.info(">>>>>>>>>> 【INFO】data:{}", data);
        return Result.succeed().setData(data);
    }
}

调用测试

请求地址:http://localhost:8081/demo/feignTest

可以发现分别打印了不同的服务端口,说明负载生效:

整理完毕,完结撒花~ 🌻

参考地址:

1.SpringCloud集成LoadBalance,负载均衡,https://blog.csdn.net/inthirties/article/details/126821335

2.SpringBoot 2 使用 SpringCloud LoadBalancer 实现客户端负载均衡,https://blog.csdn.net/stevenchen1989/article/details/105009292

相关推荐
yq19820430115610 分钟前
静思书屋:基于Java Web技术栈构建高性能图书信息平台实践
java·开发语言·前端
一个public的class11 分钟前
你在浏览器输入一个网址,到底发生了什么?
java·开发语言·javascript
有位神秘人12 分钟前
kotlin与Java中的单例模式总结
java·单例模式·kotlin
golang学习记17 分钟前
IntelliJ IDEA 2025.3 重磅发布:K2 模式全面接管 Kotlin —— 告别 K1,性能飙升 40%!
java·kotlin·intellij-idea
爬山算法20 分钟前
Hibernate(89)如何在压力测试中使用Hibernate?
java·压力测试·hibernate
暮色妖娆丶42 分钟前
SpringBoot 启动流程源码分析 ~ 它其实不复杂
spring boot·后端·spring
消失的旧时光-194342 分钟前
第十四课:Redis 在后端到底扮演什么角色?——缓存模型全景图
java·redis·缓存
BD_Marathon43 分钟前
设计模式——依赖倒转原则
java·开发语言·设计模式
BD_Marathon1 小时前
设计模式——里氏替换原则
java·设计模式·里氏替换原则
Coder_Boy_1 小时前
Deeplearning4j+ Spring Boot 电商用户复购预测案例中相关概念
java·人工智能·spring boot·后端·spring