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

发布时间:2023年12月17日

一、负载均衡概述

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

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

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

二、负载均衡验证

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

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的具体内容,可以参看一下官方文档:

地址: Cloud Native Applications

通过文档中的描述,

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包,添加一个类如下:

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上添加一个注解。

@LoadBalancerClient(name="userservice", configuration = LoadBalancedConfig.class)

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

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

@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。

文章来源:https://blog.csdn.net/lsqingfeng/article/details/134995950
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。