当前位置: 首页 > 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.lryc.cn/news/601666.html

相关文章:

  • 阻止网页重定向
  • 6、企业信息化
  • 齐护Ebook科技与艺术Steam教育套件 可图形化micropython Arduino编程ESP32纸电路手工
  • 装修独栋别墅需要注意的细节有哪些?
  • 像素农场播种机-作物模拟器HTML+CSS+JavaScript
  • Linux 系统网络配置及 IP 地址相关知识汇总
  • JVM terminated. Exit code=1
  • 通俗理解主机的BIOS和UEFI启动方式
  • SpringBoot 整合 Langchain4j AIService 深度使用详解
  • uniapp input 聚焦时键盘弹起滚动到对应的部分
  • Python入门构建网页
  • Python爬虫实战:研究netaddr库相关技术构建IP地址信息采集分析系统
  • r0env2024:开箱即用的AI工具集成Kali发新版
  • Java学习-------外观模式
  • 不坑盒子:Word里1秒制作“花括号”题目,多音字组词、形近字组词……
  • J3160迷你小主机 性能测试 对比i3-4170 以及服务器
  • 【Linux | 网络】传输层(UDP和TCP)
  • Word和WPS文字如何制作分栏试卷?想分几栏分几栏
  • 使用uni-app开发一个点餐收银台系统前端静态项目练习
  • Netty中 ? extends Future<? super V>这种的写法的理解
  • 使用GPU训练模型
  • MyBatis-Plus高效开发实战
  • 关于GRPC的相关知识。
  • 编程语言Java——核心技术篇(五)IO流:数据洪流中的航道设计
  • 点击劫持:潜藏在指尖的安全陷阱
  • 【Unity3D实例-功能-移动】角色移动-通过WSAD(Transform方式)
  • 《频率之光:共振之恋》
  • 益莱储:明智地投资测试仪器
  • 数据结构的基本知识
  • [STM32][HAL]stm32wbxx 超声波测距模块实现(HY-SRF05)