草庐IT

LoadBalancer集成Nacos实现负载均衡

Pymj 2023-04-21 原文

往期回顾

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

Nacos的安装与配置

Spring Cloud集成Nacos作为注册中心

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

负载均衡

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

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

软件负载均衡又分为:

  1. 服务端负载均衡

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

  1. 客户端负载均衡

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

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

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

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

构建项目

创建新工程

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

导入依赖

这里需要重点注意一下,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,点击如图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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)]

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

调用接口

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

项目源码:gitee github
62117457390)]

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

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

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

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

项目源码:gitee github

有关LoadBalancer集成Nacos实现负载均衡的更多相关文章

  1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  2. ruby-on-rails - 如何使辅助方法在 Rails 集成测试中可用? - 2

    我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel

  3. ruby-on-rails - 我如何将 Hoptoad 与 DelayedJob 和 DaemonSpawn 集成? - 2

    我一直很高兴地使用DelayedJob习惯用法:foo.send_later(:bar)这会调用DelayedJob进程中对象foo的方法bar。我一直在使用DaemonSpawn在我的服务器上启动DelayedJob进程。但是...如果foo抛出异常,Hoptoad不会捕获它。这是任何这些包中的错误...还是我需要更改某些配置...或者我是否需要在DS或DJ中插入一些异常处理来调用Hoptoad通知程序?回应下面的第一条评论。classDelayedJobWorker 最佳答案 尝试monkeypatchingDelayed::W

  4. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  5. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  6. jenkins部署1--jenkins+gitee持续集成 - 2

    前置步骤我们都操作完了,这篇开始介绍jenkins的集成。话不多说,看操作1、登录进入jenkins后会让你选择安装插件,选择第一个默认的就行。安装完成后设置账号密码,重新登录。2、配置JDK和Git都需要执行路径,所以需要先把执行路径找到,先进入服务器的docker容器,2.1JDK的路径root@69eef9ee86cf:/usr/bin#echo$JAVA_HOME/usr/local/openjdk-82.2Git的路径root@69eef9ee86cf:/#whichgit/usr/bin/git3、先配置JDK和Git。点击:ManageJenkins>>GlobalToolCon

  7. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  8. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  9. ruby - Arrays Sets 和 SortedSets 在 Ruby 中是如何实现的 - 2

    通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复

  10. ruby - "public/protected/private"方法是如何实现的,我该如何模拟它? - 2

    在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定

随机推荐