LoadBalancer集成Nacos实现负载均衡

往期回顾

前面我们已经介绍了Nacos 的安装与配置,以及Spring Cloud 集成Nacos 作为服务的注册中心。

Nacos的安装与配置

Spring Cloud集成Nacos作为注册中心

接下来,我们接着上一讲,我们使用Spring Cloud 自带的LoadBalacer 来实现负载均衡

负载均衡

负载均衡(Load Balance) ,简单点说就是将用户的请求平摊分配到多个服务器上运行,以达到扩展服务器带宽、增强数据处理能力、增加吞吐量、提高网络的可用性和灵活性的目的。

负载均衡一般分为硬件负载均衡和软件负载均衡,硬件负载均衡因为知识受限,这里不做说明

软件负载均衡又分为:

  1. 服务端负载均衡

    服务端的负载均衡就是传统的Nginx 方式,它的一个特点是调用的客户端不知道具体是哪一个Server提供的服务,只需要将请求发送给Nginx ,再由Nginx 转发给Tomcat ,客户端只需要记住Nginx 的地址即可

img

  1. 客户端负载均衡

    客户端负载均衡是将负载均衡逻辑以代码的形式封装到客户端上,即负载均衡器位于客户端。客户端通过服务注册中心(例如 Eureka Nacos )获取到一份服务端提供的可用服务清单。有了服务清单后,负载均衡器会在客户端发送请求前通过负载均衡算法选择一个服务端实例再进行访问,以达到负载均衡的目的

    img

Ribbon 就是一个很典型的客户端负载均衡器,RibbonNetflix 公司发布的开源项目(组件、框架、jar包),主要功能是提供客户端的软件负载均衡算法,它会从注册中心中获取一个可用的服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点

但是,Ribbon 已经在最新的Spring Cloud 版本中被废弃,Spring Cloud Loadbalancer 是官方正式推出的一款新负载均衡利器,在未来,LoadBalancer 很有可能取代Ribbon的地位成为新一代的负载均衡器

而今天,我们的主角就是LoadBalancer ,我们将使用它与Nacos 集成,实现客户端的负载均衡,下面让我们开始愉快的编码吧

构建项目

创建新工程

首先,我们需要在原来的项目基础上再创建一个服务消费者,用于消费,如图所示:

image-20220826161023929

导入依赖

这里需要重点注意一下,Ribbon 的依赖从Nacos 的依赖中排除

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <exclusions>
                <!-- 将ribbon排除 -->
                <exclusion>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--添加loadbalancer依赖
            由于 Netflix Ribbon 进入停更维护阶段,因此 SpringCloud 2020.0.1 版本之后 删除了eureka中的ribbon,
            替代ribbon的是spring cloud自带的LoadBalancer,默认使用的是轮询的方式
            新版本的 Nacos discovery 都已经移除了 Ribbon ,此时我们需要引入 loadbalancer 代替,才能调用服务提供者提供的服务
        -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

配置application.yml

server:
  port: 7001
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.199.128
  application:
    name: UserConsumer
  profiles:
    active: dev
    
management:
  endpoints:
    web:
      exposure:
        include: "*"

配置LoadBalancer

使用RestTemplate 与注解的方式配置对应的负载均衡策略

  1. 创建LoadBalancer配置类
package cuit.epoch.pymjl.config;

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.core.env.Environment;

/**
 * 注意不要加@Configuration
 *
 * @author Pymjl
 * @version 1.0
 * @date 2022/8/26 13:05
 **/
public class MyLoadBalancerConfig {
    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                                            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        // 随机轮询
        return new RandomLoadBalancer(loadBalancerClientFactory
                .getLazyProvider(name, ServiceInstanceListSupplier.class),
                name);
    }
}

注意,MyLoadBalancerConfig这里有一个细节大家需要注意一下

  • 如果你在MyLoadBalancerConfig 上面加了@Configuration 注解,使用到的服务必须显式的在启动类上配置好
//假设项目中使用了2个服务,stock-service和product-service
 
//都配置好不会报错
@LoadBalancerClients(value = {
        @LoadBalancerClient(name = "stock-services",configuration = MyLoadBalancerConfig.class),
        @LoadBalancerClient(name = "product-service",configuration = MyLoadBalancerConfig.class)
})
 
//调用stock-service不会报错,调用product-service会报错
@LoadBalancerClients(value = {
        @LoadBalancerClient(name = "stock-services",configuration = MyLoadBalancerConfig.class)
  • 如果没有加@Configuration 注解那么配了的服务会使用配置的负载均衡策略,没有配的服务会使用默认的策略

  • 或者将对应的配置类放在Spring 的扫描包外,效果同第二点一样

  1. 配置RestTemplates
package cuit.epoch.pymjl.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * 在这里配置我们自定义的LoadBalancer策略 如果想自己扩展算法 需要实现ReactorServiceInstanceLoadBalancer接口
 * /@LoadBalancerClients(defaultConfiguration = {name = "CLOUD-PAYMENT-SERVICE", configuration = MyLoadBalancerConfig.class})
 * 注意这里的name属性 需要和Nacos页面中的服务提供者名字一致
 *
 * @author Pymjl
 * @version 1.0
 * @date 2022/8/26 12:20
 **/
@Configuration
@LoadBalancerClient(name = "UserService", configuration = MyLoadBalancerConfig.class)
public class RestTemplateConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

编写Controller

package cuit.epoch.pymjl.controller;

import cuit.epoch.pymjl.result.CommonResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/8/26 12:17
 **/
@RestController
@RequestMapping("/consumer")
public class TestController {
    private static final String SERVICE_URL = "http://UserService";

    @Resource
    RestTemplate restTemplate;

    @GetMapping("/test")
    public CommonResult consumerTest() {
        return restTemplate.getForObject(SERVICE_URL + "/user/test", CommonResult.class);
    }
}

你也可以采用直接使用LoadBalanceClient 不使用注解的方式(这是我Copy的Nacos官方的样例,使用方法是一样的,就不再演示)

@SpringBootApplication
@EnableDiscoveryClient
public class NacosConsumerApp {

    @RestController
    public class NacosController{

        @Autowired
        private LoadBalancerClient loadBalancerClient;
        @Autowired
        private RestTemplate restTemplate;

        @Value("${spring.application.name}")
        private String appName;

        @GetMapping("/echo/app-name")
        public String echoAppName(){
            //使用 LoadBalanceClient 和 RestTemolate 结合的方式来访问
            ServiceInstance serviceInstance = loadBalancerClient.choose("nacos-provider");
            String url = String.format("http://%s:%s/echo/%s",serviceInstance.getHost(),serviceInstance.getPort(),appName);
            System.out.println("request url:"+url);
            return restTemplate.getForObject(url,String.class);
        }

    }

    //实例化 RestTemplate 实例
    @Bean
    public RestTemplate restTemplate(){

        return new RestTemplate();
    }

    public static void main(String[] args) {

        SpringApplication.run(NacosConsumerApp.class,args);
    }
}

开始测试

  1. 我们先启动UserService ,因为涉及到负载均衡,所以我们需要使用idea配置启动类,启动多个UserService,点击如图所示:

image-20220826163322654

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aNMyQJJi-1662117457388)(https://pymjl.oss-cn-shanghai.aliyuncs.com/picgo/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202022-08-26%20163659.png)]

然后点击启动服务提供者和消费者,如图所示:

image-20220826164831441

调用接口

image-20220826164913670

image-20220826164923572

经过多次刷新后你会发现消费者会随机的去调用服务提供者的服务,两者的概率大概是相同的

image-20220826165109101

image-20220826165131681

项目源码:gitee github
62117457390)]

[外链图片转存中…(img-Z73V2blX-1662117457391)]

经过多次刷新后你会发现消费者会随机的去调用服务提供者的服务,两者的概率大概是相同的

[外链图片转存中…(img-fojNGyVX-1662117457392)]

[外链图片转存中…(img-O63oaM6O-1662117457393)]

项目源码:gitee github

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pymj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值