【SpringCloud】3.0 负载均衡,常见的负载均衡方案

微服务架构

  • SpringCloud
    服务注册发现:Nacos
    服务限流降级:Sentinel
    分布配置中⼼:Nacos
    分布式事务保证数据一致性:Seata
    分布式定时任务:Spring Scheduling Tasks
    服务⽹关:SpringCloud Gateway
    异步消息削峰填谷:RocketMQ
    服务之间调⽤:OpenFeign、Ribbon
    链路追踪:Sleuth+Zipkin

技术栈版本控制

序号 名称 版本 用途 备注
1 openJDK 21 Java JDK
2 postgresSQL 17.4 基础数据库
3 Maven 3.9.9 Java第三方依赖包管理工具
4 springboot 3.4.3 Java MVC框架
5 springcloud 2024.0.1 微服务框架
6 springcloud Alibaba 2023.0.3.2 阿里巴巴微服务框架扩展

组件版本控制

序号 名称 版本 用途 备注
1 Nacos 2.5.1 服务注册发现、分布配置中⼼
2 Sentinel 1.8.8 服务限流降级 面向云原生微服务的高可用流控防护组件
3 Seata 2.3.0 分布式事务保证数据一致性

什么是负载均衡(Load Balance)

  • 分配访问具体哪个服务节点,就是负载均衡
  • 原理是将数据流量分摊到多个服务器执行,减轻每台服务器的压力,从而提高了数据的吞吐量

软硬件角度负载均衡的种类

  • 通过硬件来进行解决,常见的硬件有NetScaler、F5、Radware和Array等商用的负载均衡器,但比较昂贵的
  • 通过软件来进行解决,常见的软件有LVS、Nginx等,它们是基于Linux系统并且开源的负载均衡策略

从端的角度负载均衡有两种

  • 1.服务端负载均衡
    请求发起后 由负载均衡再决定分发到哪个服务节点
  • 2.客户端负载均衡
    谁请求谁决定访问哪个服务节点

常见的负载均衡策略(看组件的支持情况)

    1. 节点轮询
      简介:每个请求按顺序分配到不同的后端服务器
    1. weight 权重配置
      简介:weight和访问比率成正比,数字越大,分配得到的流量越高
    1. 固定分发
      简介:根据请求按访问ip的hash结果分配,这样每个用户就可以固定访问一个后端服务器
    1. 随机选择、最短响应时间等等

AlibabaCloud集成Ribbon实现负载均衡

什么是Ribbon

Ribbon是一个客户端负载均衡工具,通过Spring Cloud封装,可以轻松和AlibabaCloud整合

用法: 加了@LoadBalanced注解,就有负载均衡的功能。

修改online-edu-order-service/src/main/java/org/online_edu/OrderApplication.java

@SpringBootApplication()
@MapperScan("org.online_edu.dao")
@EnableDiscoveryClient
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

测试验证(客户端负载均衡)

2个实体类增加一个测试字段:
Video.java和VideoOrder.java

    //测试
    private String serveInfo;

    public String getServeInfo() {
        return serveInfo;
    }

    public void setServeInfo(String serveInfo) {
        this.serveInfo = serveInfo;
    }

修改VideoController.java

@RestController
@RequestMapping("api/v1/video")
public class VideoController {
    @Autowired
    private VideoService videoService;


    @RequestMapping("/find_by_id")
    public Video findById(@RequestParam(name = "videoId") int videoId, HttpServletRequest request) {
        Video video = videoService.findById(videoId);
        // 拿到服务端的id+端口
        video.setServeInfo(request.getServerName() + ":" + request.getServerPort());
        return video;
    }
}

修改VideoOrderController.java

@RestController
@RequestMapping("/api/v1/video_order")
public class VideoOrderController {

    /**
     * 服务对象
     */
    @Autowired
    private VideoOrderService videoOrderService;

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 可以拉到注册服务的列表
     */
    @Autowired
    private DiscoveryClient discoveryClient;

    @RequestMapping("save")
    public Object save(@RequestParam(name = "videoId") int videoId) {
//        Video video = restTemplate.getForObject(
//                "http://localhost:9000/api/v1/video/find_by_id?videoId=" + videoId, Video.class);
        // 传入在nacos注册的服务名
//        List<ServiceInstance> list = discoveryClient.getInstances("online-edu-video-service");
//        ServiceInstance serviceInstance = list.get(0);
//
//
//        Video video = restTemplate.getForObject(
//                "http://" + serviceInstance.getHost() +":"+serviceInstance.getPort()+ "/api/v1/video/find_by_id?videoId=" + videoId, Video.class);


        Video video = restTemplate.getForObject(
                "http://online-edu-video-service/api/v1/video/find_by_id?videoId=" + videoId, Video.class);
        VideoOrder videoOrder = new VideoOrder();
        if (video != null) {
            videoOrder.setServeInfo(video.getServeInfo());
            videoOrder.setVideoId(video.getId());
            videoOrder.setVideoTitle(video.getTitle());
            videoOrder.setCreateTime(new Date());
        }
        return videoOrder;
    }

}

运行2个online-edu-video-service服务(idea右上角 ,进入编辑详情可以copy)


image.png

然后重新调用接口

http://localhost:8000/api/v1/video_order/save?videoId=40
image.png

+

image.png

ribbon服务间调用负载均衡源码分析

idea搜索类的快捷键是 ctrl+N
@LoadBalanced

1)首先从注册中心获取provider的列表
2)通过一定的策略选择其中一个节点
3)再返回给restTemplate调用

Ribbon支持的负载均衡策略介绍

策略类 命名 描述
RoundRobinRule 线性轮询策略 按照顺序选择server,简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。
AvailabilityFilteringRule 可用过滤策略 过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server(active connections超过配置的阈值)。

对以下两种服务器进行忽略:
(1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。
(2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的..ActiveConnectionsLimit属性进行配置。
WeightedResponseTimeRule 响应时间加权重策略 根据server的响应时间分配权重,以响应时间作为权重,响应时间越短的服务器被选中的概率越大,综合了各种因素,比如:网络,磁盘,io等,都直接影响响应时间
ZoneAvoidanceRule 区域权重策略 综合判断server所在区域的性能,和server的可用性,轮询选择server
BestAvailableRule 最空闲策略 忽略那些短路的服务器,并选择并发数较低的服务器。
RandomRule 随机策略 随机选择server
RetryRule 重试策略 当选择server不成功,短期内尝试选择一个可用的server

怎么配置

通过定义IRule实现可以修改负载均衡规则,有两种方式:

(这种方式是作用于全局的,在order服务访问任何一个微服务,都是随机的)

1.代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:

  @Bean
  public IRule randomRule() {
    return new RandomRule();
  }

2.配置文件方式:在online-edu-order-service的application.yml文件中,添加新的配置也可以修改规则:

这种方式会先指定服务名称,再去指定负载均衡的规则,所以这种配置是针对某个微服务而言的。

online-edu-order-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule # 负
载均衡规则 

服务器性能有差异时用WeightedResponseTimeRule,没有则默认即可。

demo(客户端负载均衡)

因为我的服务总是报java.net.UnknownHostException: online-edu-video-servicec错误。

image.png

最后百度的原因是

因为Spring Cloud 2020版本以后,默认移除了对Netflix的依赖,其中就包括Ribbon,官方默认推荐使用Spring Cloud Loadbalancer 正式替换Ribbon,并成为了Spring Cloud负载均衡器的唯一实现,因此要在原有依赖的基础上添加 下面的Loadbalancer依赖。

按照且感谢 春.生——https://blog.csdn.net/Funny54?type=blog 的博客指导,增加依赖(父项目引入依赖和版本,子项目引入依赖)

            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-loadbalancer -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-loadbalancer</artifactId>
                <version>4.1.4</version>
            </dependency>

online-edu-order-service/src/main/resources/application.yml

增加依赖:


image.png

online-edu-order-service/src/main/java/org/online_edu/controller/VideoOrderController.java

@RestController
@RequestMapping("/api/v1/video_order")
public class VideoOrderController {

    /**
     * 服务对象
     */
    @Autowired
    private VideoOrderService videoOrderService;

    @Autowired
    private RestTemplate restTemplate;

//    /**
//     * 可以拉到注册服务的列表
//     */
//    @Autowired
//    private DiscoveryClient discoveryClient;

    @RequestMapping("save")
    public Object save(@RequestParam(name = "videoId") int videoId) {
//        Video video = restTemplate.getForObject(
//                "http://localhost:9000/api/v1/video/find_by_id?videoId=" + videoId, Video.class);
        // 传入在nacos注册的服务名
//        List<ServiceInstance> list = discoveryClient.getInstances("online-edu-video-service");
//        ServiceInstance serviceInstance = list.get(0);
////
////
//        Video video = restTemplate.getForObject(
//                "http://" + serviceInstance.getHost() +":"+serviceInstance.getPort()+ "/api/v1/video/find_by_id?videoId=" + videoId, Video.class);

        Video video = restTemplate.getForObject(
                "http://online-edu-video-service/api/v1/video/find_by_id?videoId=" + videoId, Video.class);
        VideoOrder videoOrder = new VideoOrder();
        if (video != null) {
            videoOrder.setServeInfo(video.getServeInfo());
            videoOrder.setVideoId(video.getId());
            videoOrder.setVideoTitle(video.getTitle());
            videoOrder.setCreateTime(new Date());
        }
        return videoOrder;
    }

}

image.png

这里online-edu-video-service起3个服务。
image.png

我这里用的Apipost软件。

发起多次请求,可以看到再访问到video服务分布式节点。

image.png

image.png

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容