SpringCloud系列(六)| 聊聊负载均衡

一、负载均衡概述

上一篇文章中,我们在集成OpenFeign的过程中提示我们需要加入了一个依赖就是: spring-cloud-starter-loadbalancer。 顾名思义,这个包的作用就是用来做负载均衡的。

简单解释一下什么是负载均衡,就是当我们的服务有多个实例的时候,比如我们userservice, 一个端口号是8081,一个端口号是8082。当然这是因为我只有一天电脑只能一台机器上模拟。正常情况我们的两个用户服务肯定应该是部署在两台机器上,这样就可以防止其中一台机器出现故障而导致的服务不可用的问题,这叫做分布式部署。而部署了两个相同的服务,当我们有大量的请求过来的时候,就可以把请求平均分布到两台机器上分别处理,降低每个服务的负载,使他们的压力得到均衡。这就是服务均衡。

那我们实际来验证一下,我们加入了这个包是否实现了负载均衡的功能。怎么验证的,就是同时启动两个用户服务。然后调用的时候,看请求是否能够均衡的打到两个服务上。

二、负载均衡验证

我们先来改造一下我们的用户接口,由于两个服务的端口号是不一样的,我们就在程序当中打印一下端口号。

java 复制代码
package com.lsqingfeng.springcloud.user.controller;

import com.lsqingfeng.springcloud.common.base.Result;
import lombok.extern.slf4j.Slf4j;
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;

/**
 * @className: UserController
 * @description:
 * @author: sh.Liu
 * @date: 2022-03-29 17:25
 */
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

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

    @GetMapping("hello")
    public Result hello(String name){
        log.info("被请求到的服务端口号为: " + port);
        return Result.success(name);
    }

}

然后按照上篇文章的方式,启动两个用户服务。

另外一个:

然后自启动订单服务,订单服务的接口中已经通过openFeign成功调用了userservice中的接口。我们观察日志,看看调用的是哪个服务。

第一次调用:

服务一打印了日志:

再次调用:

服务二打印了日志。这就验证了请求是在两个机器上分别出现了。说明我们的负载均衡已经实现了。

而且Spring-Cloud-Loadbalancer默认的负载均衡策略就是轮训策略。也就是一人一下,轮着来。而负载均衡也有其他的策略。

三、spring-cloud-starter-loadbalancer

Spring Cloud LoadBalancer是一个客户端负载均衡器,类似于Ribbon,但是由于Ribbon已经进入维护模式,并且Ribbon 2并不与Ribbon 1相互兼容,所以Spring Cloud全家桶在Spring Cloud Commons项目中,添加了Spring cloud Loadbalancer作为新的负载均衡器,并且做了向前兼容。由于我们使用的SpringCloud版本比较新,引入的nacos依赖中已经不包含ribbon的相关依赖了。如果你使用的是相对比较老的版本,在使用Spring-cloud-loadbalancer的时候,需要将 ribbon的相关依赖排除掉,同时在配置文件中禁用ribbon。

关于spring-cloud-loadbalancer的具体内容,可以参看一下官方文档:

地址: docs.spring.io/spring-clou...

通过文档中的描述,

Spring Cloud 中内部微服务调用默认是 http 请求,主要通过下面三种 API:

  • RestTemplate:同步 http API
  • WebClient:异步响应式 http API
  • 三方客户端封装,例如 openfeign

如果项目中加入了 spring-cloud-loadbalancer 的依赖并且配置启用了,那么会自动在相关的 Bean 中加入负载均衡器的特性。

  • 对于 RestTemplate,会自动对所有 @LoadBalanced 注解修饰的 RestTemplate Bean 增加 Interceptor 从而加上了负载均衡器的特性。
  • 对于 WebClient,会自动创建 ReactorLoadBalancerExchangeFilterFunction,我们可以通过加入ReactorLoadBalancerExchangeFilterFunction会加入负载均衡器的特性。
  • 对于三方客户端,一般不需要我们额外配置什么。

所以由于我们使用的是openFein,如果想要自己定义一套负载均衡的算法策略,我找了很多的资料都没有相关的介绍,应该是目前来讲,可能还不支持针对openFeign的定制化负载均衡策略。如果大家有相关资料也可以分享给我。目前来讲,这种轮训机制的策略也可以满足一般的负载均衡需求。

当然如果确实有自定义负载均衡策略的需要,那么我们也可以把openFeign的调用改为RestTemplate或者 WebClient。这样就可以实现了。

代码已经上传至gitCode:一缕82年的清风 / spring-cloud-learning · GitCode, 有需要的朋友可以下载下来查看。

四、负载均衡实现

上面的内容写于2022年,现在是2023年,我又重新开始写这个专题的时候,又重新研究了一下这个问题。发现OpenFeign和 loadbalancer集成的时候,可以可以来配置负载均衡策略的。 只不过负载均衡的策略目前只有两种,一种是随机、一种是轮询。这里来讲解一下具体的配置过程。

首先要注意,我们要在调用方进行配置!在调用方进行配置!在调用方进行配置!在调用方进行配置!

那我们进入调用方的工程,先添加一个负载均衡的配置类。 创建一个conf包,添加一个类如下:

java 复制代码
package com.lsqingfeng.springcloud.conf;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

/**
* @className: LoadBalancedConfig
* @description: 
* @author liushuai
* @date 2023/3/8 3:45 PM
*/
@Configuration
public class LoadBalancedConfig {
    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                                            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        // 随机方式,轮询方式: RoundRobinLoadBalancer
        return new RandomLoadBalancer(loadBalancerClientFactory
                .getLazyProvider(name, ServiceInstanceListSupplier.class),
                name);
    }
}

这里最后返回的RandomLoadBalancer 就是代表的随机轮询策略机制。

然后在feignClient上添加一个注解。

java 复制代码
@LoadBalancerClient(name="userservice", configuration = LoadBalancedConfig.class)

这里的configuration就指向我们刚刚写的那个配置类。完整代码如下:

java 复制代码
package com.lsqingfeng.springcloud.order.client;

import com.lsqingfeng.springcloud.common.base.Result;
import com.lsqingfeng.springcloud.conf.LoadBalancedConfig;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * @interface: UserFeignClient
 * @description:
 * @author: sh.Liu
 * @date: 2022-03-29 19:10
 */
@Component
@FeignClient(name="userservice")
@LoadBalancerClient(name="userservice", configuration = LoadBalancedConfig.class)
public interface UserFeignClient {
    /**
     * userController中的hello
     * @param name
     * @return
     */
    @GetMapping("/user/hello")
    Result hello(@RequestParam("name") String name);
}

接下来就可以了。我们启动三个userService实例。 一个OrderService实例。 通过调用接口,查看被调用的服务情况。(关于idea启动过个服务报错,前面讲过)

我们调用了三次order/getOrder接口。查看UserService服务中的的日志:

发现8084被调用了两次,8082被调用了一次。 看样子应该是随机的。

接下来换成轮询的试试。修改OrderService中的配置类。改为:RoundRobinLoadBalancer

java 复制代码
@Configuration
public class LoadBalancedConfig {
    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                                            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        // 随机方式,轮询方式: RoundRobinLoadBalancer
        return new RoundRobinLoadBalancer(loadBalancerClientFactory
                .getLazyProvider(name, ServiceInstanceListSupplier.class),
                name);
    }
}

重新启动OrderService.

再来调用三次服务:

发现每个服务都被调用了一次,验证了我们的猜测。说明我们还是可以对负载均衡的策略进行配置的。

如果觉得这两种方式满足不了,那么我们就只能自己去写实现了。可以参考RoundRobinLoadBalancer的方式尝试着去做下。其实如果没有啥特殊需求,这两种策略其实也足够使用了。

好了,关于这一部分的内容,我们就先到这里吧。代码已经上传到了gitCode。

相关推荐
海宇AI10 分钟前
微服务架构实战:基于海宇对外投资历史查询服务构建自动化合规审计网关
人工智能·微服务·架构·自动化
苏三说技术1 小时前
为什么越来越多人用OpenWiki?
后端
IT_陈寒1 小时前
Java里用Stream.parallel()翻车实录,这性能还不如单线程
前端·人工智能·后端
Bs_MoneyMagnet2 小时前
基于springboot+vue的生态果园采摘预约系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·毕业设计·计算机毕业设计
码事漫谈2 小时前
FDE 的起源、形态与真实边界
后端
行百里er2 小时前
Spring Insight 里如何画出服务拓扑
spring boot·spring cloud·监控
ServBay2 小时前
Grok Bot 还是 OpenClaw?聊聊真正能替人打杂的 AI 智能体
后端·aigc·grok
掘金挖土2 小时前
前端手摸手跑路之 AI 应用开发(五)
前端·后端
弈栈录3 小时前
LangGraph 入门:用状态机设计可靠的 Agent 工作流
后端·程序员
LEE4 小时前
前端转型全栈 00:AI 时代该学哪些,不该学哪些
前端·后端