三、搭建springCloudAlibaba2021.1版本分布式微服务-springcloud loadbalancer负载均衡

什么是负责均衡

Spring Cloud LoadBalancer是一个客户端负载均衡器,类似于Ribbon,但是由于Ribbon已经进入维护模式,并且Ribbon 2并不与Ribbon 1相互兼容,所以Spring Cloud全家桶在Spring Cloud Commons项目中,添加了Spring cloud Loadbalancer作为新的负载均衡器,并且做了向前兼容,就算你的项目中继续用 Spring Cloud Netflix 套装(包括Ribbon,Eureka,Zuul,Hystrix等等)让你的项目中有这些依赖,你也可以通过简单的配置,把ribbon替换成Spring Cloud LoadBalancer。

ribbon与LoadBalance区别

SpringCloud原有的客户端负载均衡方案Ribbon已经被废弃,取而代之的是SpringCloud LoadBalancer。本文介绍SpringCloud LoadBalancer的搭建和测试验证过程。

  1. ribbon和loadbalancer都是springcloud的负载均衡组件
  2. ribbon是Netflix开源的基于HTTP和TCP等协议负载均衡组件,loadBalancer是SpringCloud自己写的,根据服务id获取负载均衡器rpc地址。
  3. Ribbon的使用需要代码里手动调用目标服务,loadBalancer底层原理是默认调用ribbon的实现客户端负载均衡
  4. Ribbon从2019年5月份后就不维护了,后期loadbalancer会成为主流,目前还是ribbon用的多。Loadbalancer支持ribbon。
  5. ribbon 提供7中默认的负载均衡策略,常见的常见都有覆盖,一般我们都是使用 ZoneAvoidanceRule 复合判断server所在区域的性能和server的可用性选择server
  6. ribbon 支持超时、懒加载处理、重试及其和 hystrix整合高级属性等
  7. 目前spring-cloud-loadbalancer 仅支持 重试操作的配置

2021.x 注意事项

Spring cloud alibaba 的Nacos最新版中,有以下几个问题

  1. nacos 2021 版本已经没有自带ribbon的整合,所以需要引入另一个支持的jar包 loadbalancer
  2. nacos 2021 版本已经取消了对ribbon的支持,所以无法通过修改Ribbon负载均衡的模式来实现nacos提供的负载均衡模式

开始搭建

1、创建工程

首下创建一个名为 spring-cloud-alibaba-2021-loadbalancer 的工程,继承spring-cloud-alibaba-2021父项目

2、pom.xml 文件内容
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-alibaba-2021</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-cloud-alibaba-2021-loadbalancer</artifactId>


    <dependencies>
        <!-- springweb 启动依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- nacos 服务注册发现(客户端)依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- nacos-config 配置中心依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!--spring-cloud-dependencies 2020.0.0 版本不在默认加载bootstrap.yml 文件,如果需要加载bootstrap 文件需要手动添加依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>


        <!--loadbalancer ,负载均衡,用来替代ribbon的组件 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

    </dependencies>
</project>
3、application.yml
复制代码
server:
  port: 7001
spring:
  # 后面的bean会覆盖前面相同名称的bean
  main:
    allow-bean-definition-overriding: true
4、bootstrap.yml
复制代码
spring:
  application:
    name: ribbon-demo
  profiles:
    active: yexindong_active
  cloud:
    nacos:
      discovery:
        server-addr: chn520.cn:8848 # 服务注册中心地址
        namespace: public # 注册到nacos的名称空间,默认为public
      config:
        prefix: yexindong_nacos_prefix
        file-extension: yaml  # 指定yaml格式的配置, 必须要放到bootstrao.yml 才会生效,放到application下不会生效
        server-addr: chn520.cn:8848 #配置中心地址
        group: DEFAULT_GROUP
5、启动类 RibbonApp.java
java 复制代码
package com.alibaba.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class RibbonApp {
    public static void main(String[] args) {
        SpringApplication.run(RibbonApp.class, args);
    }
}
6、控制层 RibbonController.java
java 复制代码
package com.alibaba.cloud.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/ribbon")
public class RibbonController {

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 远程调用订单服务的接口
     * @return
     */
    @RequestMapping("/getRibbon")
    public String getRibbon(){
        // 以下2种方式都都可以调用远程服务
        String url  = "http://order-demo/order/getOrderById";
//        String url  = "http://localhost:8088/order/getOrderById";
        String res = restTemplate.getForObject(url, String.class);
        return "restTemplate 响应:"+res;
    }
}
7、自定义负载均衡配置

SpringBeanConfiguration.java 内容如下

java 复制代码
package com.alibaba.cloud.config;

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * @program: my-town
 * @author: 洛天
 * @create: 2021-12-13 16:27
 **/
@Configuration
// 在这里配置我们自定义的LoadBalancer策略,注:这里的类为注入Bean的类,而非负载均衡的实现类
@LoadBalancerClients(defaultConfiguration = {NacosSameClusterConfiguration.class})
public class SpringBeanConfiguration {

    @Bean
    @LoadBalanced // 开启负载均衡,必须的
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
        return restTemplateBuilder.build();
    }
}

NacosSameClusterConfiguration.java 调用负载均衡算法,选取其中的一个节点

java 复制代码
package com.alibaba.cloud.config;
import org.springframework.cloud.client.ServiceInstance;
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
public class NacosSameClusterConfiguration{
    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                                            LoadBalancerClientFactory loadBalancerClientFactory) {
        // 获取远程调用的服务名称
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);

        // 返回内容为自定义负载均衡的配置类
        return new NacosSameClusterWeightedRule(loadBalancerClientFactory
                .getLazyProvider(name, ServiceInstanceListSupplier.class),
                name);
    }
}

NacosSameClusterWeightedRule.java 自定义负载均衡的实现类

java 复制代码
package com.alibaba.cloud.config;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.List;
import java.util.Random;


// 自定义负载均衡实现需要实现 ReactorServiceInstanceLoadBalancer 接口 以及重写choose方法
public class NacosSameClusterWeightedRule implements ReactorServiceInstanceLoadBalancer {

    // 注入当前服务的nacos的配置信息
    @Resource
    private NacosDiscoveryProperties nacosDiscoveryProperties;

    // loadbalancer 提供的访问当前服务的名称
    final String serviceId;

    // loadbalancer 提供的访问的服务列表
    ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

    public NacosSameClusterWeightedRule(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
    }

    /**
     * 服务器调用负载均衡时调的放啊
     * 此处代码内容与 RandomLoadBalancer 一致
     */
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
        Mono<Response<ServiceInstance>> map1 = supplier.get(request).next().map((list) -> {
            Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(list);
            ;
            return serviceInstanceResponse;
        });
        return map1;

    }

    /**
     * 对负载均衡的服务进行筛选的方法
     * 自定义
     * 此处的 instances 实例列表  只会提供健康的实例  所以不需要担心如果实例无法访问的情况
     */
    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
        if (instances.isEmpty()) {
            return new EmptyResponse();
        }
        // 获取当前服务所在的集群名称
//        String currentClusterName = nacosDiscoveryProperties.getClusterName();
        // 过滤在同一集群下注册的服务 根据集群名称筛选的集合
//        List<ServiceInstance> sameClusterNameInstList  = instances.stream().filter(i-> StringUtils.equals(i.getMetadata().get("nacos.cluster"),currentClusterName)).collect(Collectors.toList());
//        ServiceInstance sameClusterNameInst;
//        if (sameClusterNameInstList.isEmpty()) {
//            // 如果为空,则根据权重直接过滤所有服务列表
//            sameClusterNameInst = getHostByRandomWeight(instances);
//        } else {
//            // 如果不为空,则根据权重直接过滤所在集群下的服务列表
//            sameClusterNameInst = getHostByRandomWeight(sameClusterNameInstList);
//        }

        // 随机选择一个节点
        int size = instances.size();
        int index = new Random().nextInt(size);  // 如果size为2,生成 0-1之间的随机数;如果为10,生成0-9之间的随机数
        System.out.println("生成的随机数为:" + index);
        ServiceInstance serviceInstance = instances.get(index);
        return new DefaultResponse(serviceInstance);
    }
}
8、父工程加入 module

在 父项目(spring-cloud-alibaba-2021)加入以下内容

xml 复制代码
    <modules>
        <module>spring-cloud-alibaba-2021-loadbalancer</module>
    </modules>
9、搭建完成

此时工程目录如下图

测试

在浏览器输入http://127.0.0.1:7001/ribbon/getRibbon后回车,显示以下内容,表示测试成功

相关推荐
吃饱了得干活1 天前
Spring Cloud Gateway 微服务网关:路由、断言、过滤器
java·spring cloud
蝎子莱莱爱打怪5 天前
XZLL-IM干货系列 04|Netty 长连接实战:Pipeline 怎么排、心跳怎么跳、连接怎么管
后端·微服务·面试
SamDeepThinking6 天前
Java微服务练习方式
java·后端·微服务
米丘9 天前
微前端之 Web Components 完全指南
微服务·html
霸道流氓气质12 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
慧一居士12 天前
Feign的GET请求如何传递对象参数?
java·spring cloud
我登哥MVP12 天前
SpringCloud Alibaba 核心组件解析:服务链路追踪
java·spring boot·后端·spring·spring cloud·java-ee·maven
风吹夏回12 天前
RabbitMQ 核心术语 + Python pika 方法完整讲解
分布式·python·rabbitmq
慧一居士12 天前
SpringCloud 微服务Feigin 用的完整调用端和被调用的示例
java·spring cloud
风吹夏回12 天前
RabbitMQ 三种模式入门:HelloWorld、WorkQueue、PubSub
分布式·rabbitmq·ruby