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

负载均衡 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.xdnf.cn/news/16413.html

相关文章:

  • web刷题
  • c++11--static_assert
  • Linux->自定义shell
  • FPGA IP升级
  • 网络服务综合项目
  • Oracle 数据库报 ora-00257 错误并且执行alter system switch logfile 命令卡死的解决过程
  • XSS利用
  • 02人工智能中优雅草商业实战项目视频字幕翻译以及声音转译之以三方AI模型API制作方式预算-卓伊凡|莉莉
  • linux 板卡实现vxi11服务
  • 阿里 Qwen3 四模型齐发,字节 Coze 全面开源,GPT-5 8 月初发布!| AI Weekly 7.21-7.27
  • 初识 docker [上]
  • 《 接口日志与异常处理统一设计:AOP与全局异常捕获》
  • P图太假?AI一键融入背景!
  • vLLM 的“投机取巧”:Speculative Decoding 如何加速大语言模型推理
  • 【优选算法】BFS解决FloodFill算法
  • 零基础学习性能测试第五章:JVM性能分析与调优-GC垃圾分代回收机制与优化
  • 死锁出现的原因
  • 《计算机组成原理与汇编语言程序设计》实验报告四 Debug及指令测试
  • #影·数学计划# N1 一元一次方程讲解 未完待续
  • 基于STM32的智能康养木屋监测系统
  • vector使用和模拟
  • 在本地环境中运行 ‘dom-distiller‘ GitHub 库的完整指南
  • openshift AI 2.22安装的需求
  • 人工智能与城市:城市生活的集成智能
  • 基于 LSTM 与 SVM 融合的时间序列预测模型:理论框架与协同机制—实践算法(1)
  • Wireshark TS | 发送数据超出接收窗口
  • Frontiers in Psychology投稿LaTeX(三)
  • 元宇宙中的“虫洞“:技术实现、应用场景与未来挑战
  • J3160迷你小主机 性能测试 对比i3-4170 以及服务器
  • Python Pandas.qcut函数解析与实战教程