当前位置: 首页 > news >正文

负载均衡 LoadBalance

问题引入

我们一个服务可能会进行多机部署,也就说多台服务器组成的集群共同对外提供一致的服务,那么我们的微服务的代码就需要拷贝多份,部署到不同的机器上。

我们使用 IDEA 来开启多个相同的服务
这里以 product-service 为例:

找到 services 按键,点开来:

在这里插入图片描述

找到我们需要多机部署的服务,右键然后点击 Copy Configuration ,复制这个服务的所有配置。

在这里插入图片描述

之后就是给我们新的服务命名,然后点击 Modify options 修改配置信息。

在这里插入图片描述

点击 Add VM options
在这里插入图片描述

在 VM options 添加端口信息:

-Dserver.port=端口号

注意由于我们是在本机部署多个服务,所以端口号需要修改,避免端口的冲突

在这里插入图片描述

最后点击 Apply ,然后 OK,就可以创建成功了。


然后我们启动所有的服务,在 Eureka 界面可以看到我们的 product-service 有多个注册信息

在这里插入图片描述

这里使用 order-service 来注册发现 product-service:

@Slf4j
@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate DiscoveryClient discoveryClient;public OrderInfo getOrderById(Integer orderId) {OrderInfo orderInfo = orderMapper.selectById(orderId);
//        String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();//从 eureka 中获取服务信息List<ServiceInstance> instances = discoveryClient.getInstances("product-service");String uri = instances.get(0).getUri().toString();String url = uri + "/product/" + orderInfo.getProductId();log.info("访问的资源路径:" + url);ProductDetailInfo productDetailInfo = restTemplate.getForObject(url, ProductDetailInfo.class);orderInfo.setProductDetailInfo(productDetailInfo);return orderInfo;}
}

但是我们发现多个请求,即使 product-service 有三个一样的服务,但是使用的都是 9092,如果我们的请求类一旦上升,就可能会导致 9092这个服务器崩溃,我们应该要做到均衡地将这些请求发送给 product-service 的三个不同的服务器中,这就是我们本章要提到的负载均衡

在这里插入图片描述

负载均衡

概念

负载均衡(Load Balance,简称LB),是高并发, 高可用系统必不可少的关键组件.
当服务流量增大时,通常会采用增加机器的方式进行扩容,负载均衡就是用来在多个机器或者其他资源中,按照一定的规则合理分配负载.

分类

负载均衡分为服务端负载均衡和客户端负载均衡

服务端负载均衡,主要使用的是负载均衡器 Nginx,请求先到达负载均衡器,然后通过负载均衡算法在多个服务器之间选择一个进行访问

在这里插入图片描述


客户端负载均衡:
将负载均衡的功能以库的方式集成到客户端中,而不是由一台负载均衡设备集中提供

在这里插入图片描述

模拟实现

这里我们使用原子类,避免发生线程不安全,通过原子类的数值和我们获取到的服务注册列表的长度进行取余获取下标,以轮询的方式来访问 product-service 服务端。

@Slf4j
@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate DiscoveryClient discoveryClient;private static final AtomicInteger count = new AtomicInteger(1);private List<ServiceInstance> instances;@PostConstructpublic void init() {instances = instances = discoveryClient.getInstances("product-service");}public OrderInfo getOrderById(Integer orderId) {OrderInfo orderInfo = orderMapper.selectById(orderId);
//        String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();int index = count.getAndIncrement() % instances.size();String uri = instances.get(index).getUri().toString();String url = uri + "/product/" + orderInfo.getProductId();log.info("访问的资源路径:" + url);ProductDetailInfo productDetailInfo = restTemplate.getForObject(url, ProductDetailInfo.class);orderInfo.setProductDetailInfo(productDetailInfo);return orderInfo;}
}

但是这个实现方式也有缺陷,就是如果后续有新的服务注册或者旧的服务崩溃的话,我们的 order-service 就不会获得到最新的注册列表,导致后续出现 bug

即使你采用下面的方式,每次 order-service 处理请求都要进行重新获取服务列表,也还是会出现 bug ,那就是如果旧的服务崩溃,可能无法即使获取,导致出现ConnectException

java.net.ConnectException: Connection refused: connect
@Slf4j
@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate DiscoveryClient discoveryClient;private static final AtomicInteger count = new AtomicInteger(1);//    private List<ServiceInstance> instances;
//
//    @PostConstruct
//    public void init() {
//        instances = instances = discoveryClient.getInstances("product-service");
//    }public OrderInfo getOrderById(Integer orderId) {OrderInfo orderInfo = orderMapper.selectById(orderId);
//        String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();List<ServiceInstance> instances = discoveryClient.getInstances("product-service");int index = count.getAndIncrement() % instances.size();String uri = instances.get(index).getUri().toString();String url = uri + "/product/" + orderInfo.getProductId();log.info("访问的资源路径:" + url);ProductDetailInfo productDetailInfo = restTemplate.getForObject(url, ProductDetailInfo.class);orderInfo.setProductDetailInfo(productDetailInfo);return orderInfo;}
}

LoadBalance

SpringCloud从2020.0.1版本开始,移除了Ribbon组件,使用Spring Cloud LoadBalancer组件来代替Ribbon实现客户端负载均衡

使用

添加注解 @LoadBalanced

在 RestTemplate 上添加 @LoadBalanced
将 RestTemplate 交给 LoadBalance 管理

@Configuration
public class BeanConfig {@Bean@LoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}
}

修改远程调用的代码

修改 String url = “http://product-service/product/” + orderInfo.getProductId();
将 ip 和端口号修改为 服务名称【product-service】,这样 LoadBalance会自动为我们提供服务端

@Slf4j
@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate DiscoveryClient discoveryClient;public OrderInfo getOrderById(Integer orderId) {OrderInfo orderInfo = orderMapper.selectById(orderId);String url = "http://product-service/product/" + orderInfo.getProductId();log.info("访问的资源路径:" + url);ProductDetailInfo productDetailInfo = restTemplate.getForObject(url, ProductDetailInfo.class);orderInfo.setProductDetailInfo(productDetailInfo);return orderInfo;}
}

负载均衡策略

负载均衡策略是一种思想,无论是哪种负载均衡器,它们的负载均衡策略都是相似的 SpringCloud
LoadBalancer仅支持两种负载均衡策略:轮询策略和随机策略

1.轮询(RoundRobin):轮询策略是指服务器轮流处理用户的请求.这是一种实现最简单,也最常用的策略.生活中也有类似的场景,比如学校轮流值日,或者轮流打扫卫生.

2.随机选择(Random):随机选择策略是指随机选择一个后端服务器来处理新的请求

自定义负载均衡策略

SpringCloud LoadBalancer默认负载均衡策略是轮询策略,实现是RoundRobinLoadBalancer,如果服务的消费者如果想采用随机的负载均衡策略,也非常简单。

官方链接:SpringCloud LoadBalancer

public class CustomLoadBalancerConfiguration {@BeanReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),name);}
}

CustomLoadBalancerConfiguration 这个类不用加类注解【@Configuration】
因为这个类是在组件的扫描范围内

@LoadBalancerClient(name = "product-service", configuration = CustomLoadBalancerConfiguration.class)
@Configuration
public class BeanConfig {@Bean@LoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}
}

我们在 RestTemplate 上添加 @LoadBalancerClient 注解,将服务名称和负载策略填写进去

http://www.dtcms.com/a/300432.html

相关文章:

  • Spring Boot音乐服务器项目-查询音乐模块
  • 《Foundation 面板:设计、功能与最佳实践解析》
  • Java学习-------序列化与反序列化
  • UV: 下一代 Python 包管理工具
  • golang--虚拟地址空间
  • 阿里 Qwen3 四模型齐发,字节 Coze 全面开源,GPT-5 8 月初发布!| AI Weekly 7.21-7.27
  • 批量重命名带编号工具,附免费地址
  • Android网络框架封装 ---> Retrofit + OkHttp + 协程 + LiveData + 断点续传 + 多线程下载 + 进度框交互
  • linux根据pid获取服务目录
  • 一场关于电商零售增长破局的深圳探索
  • Vulnhub red靶机渗透攻略详解
  • PHP框架之Laravel框架教程:2. 控制器、路由、视图简单介绍
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现标签条码一维码的检测(C#代码,UI界面版)
  • WPFC#超市管理系统(2)顾客管理、供应商管理、用户管理
  • MySQL ROUTER安装部署
  • EasyExcel使用(二:写出)
  • git 提交时排除一个或多个文件
  • mac系统彻底删除mysql并重装
  • 【LeetCode】LRU 缓存 题解
  • 在Podman/Docker容器中为Luckfox Lyra Zero W编译SDK:终极排错指南
  • C 语言第 10 天学习笔记:字符串基础操作与相关函数
  • 在docker中安装frp实现内网穿透
  • Libevent(4)之使用教程(3)配置
  • 比特币运行机制全解析:区块链、共识算法与数字黄金的未来挑战
  • 【micro:bit】从入门到放弃(八):超声波测距、小车巡线、红外避障
  • Redis对象机制详解
  • vue3.6更新哪些内容
  • 如何在 InsCodeAI 上搭建并使用 Jupyter Notebook 环境?
  • spring gateway 配置http和websocket路由转发规则
  • 零基础学习性能测试第五章:JVM性能分析与调优-GC垃圾分代回收机制与优化