【SpringCloud】(一文通)多机部署, 负载均衡-LoadBalance

news2024/12/23 18:30:12

目 录

  • 一. 负载均衡介绍
    • 1.2 什么是负载均衡
    • 1.3 负载均衡的⼀些实现
  • 二. Spring Cloud LoadBalancer
    • 2.1 快速上手
      • 2.1.1 使用 Spring Cloud LoadBalancer 实现负载均衡
      • 2.1.2 启动多个product-service实例
    • 2.2 负载均衡策略
    • 2.3 LoadBalancer 原理
  • 三. 服务部署(Linux)
    • 3.2 服务构建打包
    • 3.3 启动服务
    • 3.4 开放端口号

一. 负载均衡介绍

1.1 问题描述

观察上个文章远程调用的代码

List<ServiceInstance> instances = discoveryClient.getInstances("productservice");
//服务可能有多个, 获取第⼀个
EurekaServiceInstance instance = (EurekaServiceInstance) instances.get(0);
  1. 根据应用名称获取了服务实例列表
  2. 从列表中选择了⼀个服务实例

思考: 如果⼀个服务对应多个实例呢? 流量是否可以合理的分配到多个实例呢?

现象观察:

我们再启动 2 个 product-service 实例

选中要启动的服务, 右键选择 Copy Configuration…

在这里插入图片描述

在弹出的框中, 选择 Modify options -> Add VM options

在这里插入图片描述

添加 VM options : -Dserver.port=9091

9091 为服务启动的端⼝号, 根据自己的情况进行修改

在这里插入图片描述

现在 IDEA 的 Service 窗口就会多出来⼀个启动配置, 右键启动服务就可以

在这里插入图片描述

同样的操作, 再启动1个实例, 共启动3个服务

在这里插入图片描述

观察 Eureka, 可以看到 product-service下有三个实例:

在这里插入图片描述

访问结果:

访问: http://127.0.0.1:8080/order/1

11:46:05.684+08:00 INFO 23128 --- [nio-8080-exec-1] 
com.bite.order.service.OrderService : LUCF:product-service:9090
11:46:06.435+08:00 INFO 23128 --- [nio-8080-exec-2] 
com.bite.order.service.OrderService : LUCF:product-service:9090
11:46:07.081+08:00 INFO 23128 --- [nio-8080-exec-3] 
com.bite.order.service.OrderService : LUCF:product-service:9090

通过日志可以观察到, 请求多次访问, 都是同⼀台机器.

这肯定不是我们想要的结果, 我们启动多个实例, 是希望可以分担其他机器的负荷, 那么如何实现呢?

解决方案:

我们可以对上述代码进行简单修改:

private static AtomicInteger atomicInteger = new AtomicInteger(1);

private static List<ServiceInstance> instances;

@PostConstruct
public void init(){
	//根据应⽤名称获取服务列表
	instances = discoveryClient.getInstances("product-service");
}

public OrderInfo selectOrderById(Integer orderId) {
	OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
	//String url = "http://127.0.0.1:9090/product/"+ orderInfo.getProductId();
	//服务可能有多个, 轮询获取实例
	int index = atomicInteger.getAndIncrement() % instances.size();
	ServiceInstance instance =instances.get(index);
	log.info(instance.getInstanceId());
	//拼接url
	String url = instance.getUri()+"/product/"+ orderInfo.getProductId();
	ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
	orderInfo.setProductInfo(productInfo);
	return orderInfo;
}

观察日志:

12:02:13.245+08:00 INFO 1800 --- [nio-8080-exec-1] 
com.bite.order.service.OrderService : LUCF:product-service:9091
12:02:15.723+08:00 INFO 1800 --- [nio-8080-exec-2] 
com.bite.order.service.OrderService : LUCF:product-service:9090
12:02:16.534+08:00 INFO 1800 --- [nio-8080-exec-3] 
com.bite.order.service.OrderService : LUCF:product-service:9092
12:02:16.864+08:00 INFO 1800 --- [nio-8080-exec-4] 
com.bite.order.service.OrderService : LUCF:product-service:9091
12:02:17.078+08:00 INFO 1800 --- [nio-8080-exec-5] 
com.bite.order.service.OrderService : LUCF:product-service:9090
12:02:17.260+08:00 INFO 1800 --- [nio-8080-exec-6] 
com.bite.order.service.OrderService : LUCF:product-service:9092
12:02:17.431+08:00 INFO 1800 --- [nio-8080-exec-7] 
com.bite.order.service.OrderService : LUCF:product-service:9091

通过日志可以看到, 请求被均衡的分配在了不同的实例上, 这就是负载均衡.

1.2 什么是负载均衡

负载均衡(Load Balance,简称 LB) , 是⾼并发, 高可用系统必不可少的关键组件

当服务流量增大时, 通常会采用增加机器的方式进行扩容, 负载均衡就是用来在多个机器或者其他资源中, 按照⼀定的规则合理分配负载

1.3 负载均衡的⼀些实现

上面的例子中, 我们只是简单的对实例进行了轮询, 但真实的业务场景会更加复杂. 比如根据机器的配置进行负载分配, 配置高的分配的流量高, 配置低的分配流量低等.

服务多机部署时, 开发⼈员都需要考虑负载均衡的实现, 所以也出现了⼀些负载均衡器, 来帮助我们实现负载均衡.

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

服务端负载均衡

在服务端进行负载均衡的算法分配.比较有名的服务端负载均衡器是 Nginx. 请求先到达 Nginx 负载均衡器, 然后通过负载均衡算法, 在多个服务器之间选择⼀个进行访问.

在这里插入图片描述

客户端负载均衡

在客户端进行负载均衡的算法分配

把负载均衡的功能以库的⽅式集成到客⼾端, ⽽不再是由⼀台指定的负载均衡设备集中提供.

比如Spring Cloud的Ribbon, 请求发送到客户端, 客户端从注册中心(比如Eureka)获取服务列表, 在发送请求前通过负载均衡算法选择⼀个服务器,然后进行访问.

Ribbon是Spring Cloud早期的默认实现, 由于不维护了, 所以最新版本的Spring Cloud负载均衡集成的是Spring Cloud LoadBalancer(Spring Cloud官方维护)

在这里插入图片描述

客户端负载均衡和服务端负载均衡最大的区别在于服务清单所存储的位置

二. Spring Cloud LoadBalancer

2.1 快速上手

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

2.1.1 使用 Spring Cloud LoadBalancer 实现负载均衡

  1. RestTemplate 这个Bean 添加 @LoadBalanced 注解就可以
@Configuration
public class BeanConfig {
	@Bean
	@LoadBalanced
	public RestTemplate restTemplate(){
		return new RestTemplate();
	}
}
  1. 修改IP端口号为服务名称
public OrderInfo selectOrderById(Integer orderId) {
	OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
	//String url = "http://127.0.0.1:9090/product/"+ orderInfo.getProductId();
	String url = "http://product-service/product/"+ orderInfo.getProductId();
	ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
	orderInfo.setProductInfo(productInfo);
	return orderInfo;
}

2.1.2 启动多个product-service实例

按照上⼀文章的方式, 启动多个product-service实例

在这里插入图片描述

2.1.3 测试负载均衡

连续多次发起请求: http://127.0.0.1:8080/order/1

观察 product-service 的日志, 会发现请求被分配到这3个实例上了

在这里插入图片描述

2.2 负载均衡策略

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

  1. 轮询(Round Robin): 轮询策略是指服务器轮流处理用户的请求. 这是⼀种实现最简单, 也最常用的策略. 生活中也有类似的场景, 比如学校轮流值日, 或者轮流打扫卫生.
  2. 随机选择(Random): 随机选择策略是指随机选择⼀个后端服务器来处理新的请求

自定义负载均衡策略

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

  1. 定义随机算法对象, 通过 @Bean 将其加载到 Spring 容器中

此处使用 Spring Cloud LoadBalancer 提供的 RandomLoadBalancer

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
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;

public class LoadBalancerConfig {
	@Bean
	ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {
		String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
		System.out.println("=============="+name);
		return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
	}
}

注意: 该类需要满足:

  1. 不用 @Configuration 注释
  2. 在组件扫描范围内
  1. 使用 @LoadBalancerClient 或者 @LoadBalancerClients 注解

在 RestTemplate 配置类上方, 使用 @LoadBalancerClient 或@LoadBalancerClients 注解, 可以对不同的服务提供方配置不同的客户端负载均衡算法策略.

由于此项目中只有⼀个服务提供者, 所以使用@LoadBalancerClient

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

@LoadBalancerClient 注解说明

  1. name: 该负载均衡策略对哪个服务生效(服务提供方)
  2. configuration : 该负载均衡策略 用哪个负载均衡策略实现

2.3 LoadBalancer 原理

LoadBalancer 的实现, 主要是 LoadBalancerInterceptor , 这个类会对 RestTemplate 的请求进行拦截, 然后从 Eureka 根据服务 id 获取服务列表,随后利用负载均衡算法得到真实的服务地址信息,替换服务id

来看看源码实现:

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
 //...
 public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
	 URI originalUri = request.getURI();
	 String serviceName = originalUri.getHost();
	 Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
	 return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
 	}
}

可以看到这里的 intercept 方法, 拦截了用户的 HttpRequest 请求,然后做了几件事:

  1. request.getURI() 从请求中获取 uri, 也就是 http://product-
  2. service/product/1001 originalUri.getHost() 从 uri 中获取路径的主机名, 也就是服务 id, product-service
  3. loadBalancer.execute 根据服务 id, 进行负载均衡, 并处理请求

点进去继续跟踪

public class BlockingLoadBalancerClient implements LoadBalancerClient {
 
	 public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
		 String hint = this.getHint(serviceId);
		 LoadBalancerRequestAdapter<T, TimedRequestContext> lbRequest = new LoadBalancerRequestAdapter(request,this.buildRequestContext(request, hint));
		 Set<LoadBalancerLifecycle> supportedLifecycleProcessors = this.getSupportedLifecycleProcessors(serviceId);supportedLifecycleProcessors.forEach((lifecycle) -> {
		 	lifecycle.onStart(lbRequest);
	 	});
		 //根据serviceId,和负载均衡策略, 选择处理的服务
		 ServiceInstance serviceInstance = this.choose(serviceId, lbRequest);
		if (serviceInstance == null) {
			supportedLifecycleProcessors.forEach((lifecycle) -> {
				lifecycle.onComplete(new CompletionContext(Status.DISCARD, lbRequest, new EmptyResponse()));
		});
		throw new IllegalStateException("No instances available for " + serviceId);
		} else {
			return this.execute(serviceId, serviceInstance, lbRequest);
		}
	}
	
	
		/**
		* 根据serviceId,和负载均衡策略, 选择处理的服务
		*
		*/
	public <T> ServiceInstance choose(String serviceId, Request<T> request) {
	//获取负载均衡器
		ReactiveLoadBalancer<ServiceInstance> loadBalancer = this.loadBalancerClientFactory.getInstance(serviceId);
		if (loadBalancer == null) {
			return null;
		} else {
		//根据负载均衡算法, 在列表中选择⼀个服务实例
			Response<ServiceInstance> loadBalancerResponse = (Response)Mono.from(loadBalancer.choose(request)).block();
			return loadBalancerResponse == null ? null : (ServiceInstance)loadBalancerResponse.getServer();
		}
	}
}

三. 服务部署(Linux)

接下来我们把服务部署在Linux系统上

安装 mysql:参考后面文章

数据初始化

参考前面初始化数据-数据准备SQL

修改配置文件

修改配置文件中, 数据库的密码

3.2 服务构建打包

采用 Maven 打包, 需要对 3 个服务分别打包:

eureka-server, order-service, product-service

  1. 打包方式和SpringBoot项目⼀致, 依次对三个项目打包即可

在这里插入图片描述

3.3 启动服务

  1. 上传 Jar 包到云服务器

第⼀次上传需要安装 lrzsz
 
apt install lrzsz

直接拖动文件到 xshell 窗口, 上传成功.

  1. 启动服务
#后台启动eureka-server, 并设置输出⽇志到logs/eureka.log
nohup java -jar eureka-server.jar >logs/eureka.log &

#后台启动order-service, 并设置输出⽇志到logs/order.log
nohup java -jar order-service.jar >logs/order.log &

#后台启动product-service, 并设置输出⽇志到logs/order.log
8 nohup java -jar product-service.jar >logs/product-9090.log &

再多启动两台 product-service 实例

#启动实例, 指定端⼝号为9091
nohup java -jar product-service.jar --server.port=9091 >logs/product-9091.log &

#启动实例, 指定端⼝号为9092
nohup java -jar product-service.jar --server.port=9092 >logs/product-9092.log &

3.4 开放端口号

根据自己项目设置的情况, 在云服务器上开放对应的端口号,不同的服务器厂商, 开放端口号的入口不同, 需要自行找⼀找或者咨询对应的客服人员.

以腾讯云服务器举例:

  1. 进入防火墙管理页面

在这里插入图片描述

  1. 添加规则

在这里插入图片描述

端口号写需要开放的端口号, 多个端口号以逗号分割.

3.5 测试

  1. 访问 Eureka Server:

在这里插入图片描述

  1. 访问订单服务接口: http://110.41.51.65:8080/order/1

在这里插入图片描述

远程调用成功

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2062127.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(24)(24.4) MultiWii/DJI/HDZero OSD (version 4.2 and later)(一)

文章目录 前言 1 基于遥测的OSD 前言 ArduPilot 使用基于MSP&#xff08;MultiWii 串行协议&#xff09;的协议支持多种类型的屏幕显示&#xff08;OSD&#xff09;&#xff1a; 基于 MSP 遥测的操作系统&#xff0c;如大疆 FPV 护目镜 V1/V2、大疆护目镜 RE、FatShark Byte…

QML----QML+VTK+CMake示例

为了创建一个使用QML和VTK的示例项目,并使用CMake进行构建,将遵循以下步骤: 创建C++类以集成VTK和QML:这个类将负责设置VTK渲染数据,并与QML进行交互。设置CMake配置:配置CMake以找到并链接Qt和VTK库。创建QML界面:设计QML界面以显示VTK渲染的内容。编写主函数:初始化应…

C语言实现通讯录-动态版本与文件版本

C语言实现通讯录-动态版本与文件版本 1.前言2.动态版本2.1联系人信息之前的&#xff1a;改版: 2.2初始化之前的&#xff1a;改版&#xff1a; 2.3自动扩容 3.文件版本3.1自动保存函数实现&#xff1a;效果&#xff1a; 3.2打开时加载信息函数实现&#xff1a;效果&#xff1a; …

day37-https实战

1.知识点补充: 四层代理转发数据库 目的: 通过10.0.0.4 使用ssh远程连接到web01 ssh--->10.0.0.4:2222 ---------->172.16.1.7:22 xshell-->创建会话-->10.0.0.4 端口 2222 Linux系统 ssh 10.0.0.4 -p2222作业: 目的: 实现连接10.0.0.4的5555端口转发到后端数据库…

【hot100篇-python刷题记录】【只出现一次的数字】

R5-技巧篇 思路&#xff1a;使用哈希记录次数即可 class Solution:def singleNumber(self, nums: List[int]) -> int:dictdefaultdict(int)for num in nums:dict[num]1for num in nums:if dict[num]1:return num

【hot100篇-python刷题记录】【爬楼梯】

R5-真正的动态规划 动态规划核心&#xff1a; 第i步是怎么来的&#xff08;即动态规划公式&#xff09; 走到第i步阶梯的总方法数sum(走到第i-1步阶梯的总方法数&#xff0c;走到第i-2步阶梯的总方法数) class Solution:def climbStairs(self, n: int) -> int:if n<2:r…

云原生系列 - Nginx(高级篇)

前言 学习视频&#xff1a;尚硅谷Nginx教程&#xff08;亿级流量nginx架构设计&#xff09;本内容仅用于个人学习笔记&#xff0c;如有侵扰&#xff0c;联系删学习文档&#xff1a; 云原生系列 - Nginx(基础篇)云原生系列 - Nginx(高级篇) 一、扩容 通过扩容提升整体吞吐量…

OpenCV resize 的各插值方式的区别与用途

一、resize 函数中 interpolation 参数的区别和用途 cv2.resize 函数中的 interpolation 参数用于指定图像缩放时使用的插值方法。不同的插值方法会影响缩放后图像的质量和处理速度。以下是cv2.INTER_AREA、cv2.INTER_CUBIC、cv2.INTER_NEAREST、cv2.INTER_LINEAR 和 cv2.INTE…

有关应用层面试题有关库的思维导体

面试题目&#xff1a; TCP通信中3次握手和四次挥手&#xff1f; 答&#xff1a; 第一次握手&#xff1a;客户端发送SYN包&#xff08;SYN1, seq0&#xff09;给服务器&#xff0c;并进入SYN_SENT状态&#xff0c;等待服务器返回确认包。第二次握手&#xff1a;服务器接收到S…

什么是Redis大key问题?如何解决?

目录 Key多大算大呢&#xff1f; 识别big key 处理big key Big Key是Redis中存储了大量的数据的Key&#xff0c;不要误以为big key只是表示Key的值很大&#xff0c;他还包括这个Key对应的value占用空间很多的情况&#xff0c;通常在String、list、hash、set、zset等类型中出…

三、Socket多路复用介绍

一、Socket连接方式 二、多路复用 三、Socket连接包含的内容

搭建TestBench,收藏这几条基本框架就够了

Verilog功能模块HDL设计完成后&#xff0c;并不代表设计工作的结束&#xff0c;还需要对设计进行进一步的仿真验证。掌握验证的方法&#xff0c;即如何调试自己的程序非常重要。在RTL逻辑设计中&#xff0c;要学会根据硬件逻辑来写测试程序即写Testbench。Verilog测试平台是一个…

MATLAB中qr函数用法

目录 语法 说明 示例 Q-Less QR 分解 矩阵的完整 QR 分解 置换 QR 分解 用精简 QR 因子求解线性系统 求解稀疏线性系统 求解矩形稀疏线性系统 提示 qr函数的功能是对矩阵进行QR 分解。 语法 R qr(A) [Q,R] qr(A) [Q,R,P] qr(A) [___] qr(A,"econ") […

sklearn转换器和估计器

转换器 实例化一个转换器类 调用fit_transform() 转换器调用有以下几种形式&#xff1a; fit_transform fit transform估计器&#xff08;sklearn机器学习算法的实现&#xff09; 在sklearn中&#xff0c;估计器是一个重要的角色&#xff0c;是一类实现了算法的API 1、用于…

深入理解 C# 中的 dynamic 类型详解与示例

文章目录 1. 什么是 dynamic 类型&#xff1f;2. dynamic 的工作原理3. dynamic 类型的使用4. 使用 dynamic 的场景5. dynamic 的优缺点6. dynamic 类型的注意事项7. 总结 在 C# 编程中&#xff0c;dynamic 类型是一个非常特殊的类型&#xff0c;它在编译时并不会进行类型检查&…

TCP协议中的建立连接机制

目录 客户端与服务器间的三次握手 1、关于SYN_RCVD状态 2、关于系统调用listen的第二个参数 3、为什么服务端操作系统内核中的全连接队列不能太长&#xff1f; 4、服务端操作系统内核中可以没有全连接队列&#xff1f; 客户端与服务器间的三次握手 要想成功创建连接需要客…

【Docker深入浅出】Docker镜像

文章目录 一. Docker镜像简介二. Docker镜像详解1. 镜像和容器的关系2. 镜像通常比较小3. 拉取镜像4. 镜像命名4.1. 镜像仓库服务4.2. 官方和非官方镜像仓库4.3. 镜像的命名和标签 5. 为镜像打多个标签6. 过滤镜像内容6.1. 虚空镜像6.2. 删除虚空镜像6.3. 过滤器与格式化输出 7…

【C++题解】1004 - 编程求1*2*3*...*n

欢迎关注本专栏《C从零基础到信奥赛入门级&#xff08;CSP-J&#xff09;》 问题&#xff1a;1004 - 编程求1 * 2 * 3 * … * n 类型&#xff1a;简单循环 题目描述&#xff1a; 编程求 123⋯n 。 输入&#xff1a; 输入一行&#xff0c;只有一个整数 n(1≤n≤10)&#xf…

监狱单位如何选择适合的FTP传输替代方案?

监狱单位是我国司法体系中的重要组成部分&#xff0c;监狱对于维持社会稳定也有重要作用。监狱的正常运作中&#xff0c;少不了文件的传输。由于监狱的封闭性和特殊性&#xff0c;所有传输到监狱的文件都需要经过严格的审核和登记手续&#xff0c;以确保文件的安全性和合法性。…

Linux 进程介绍

今天给伙伴们分享一下Linux 进程介绍&#xff0c;希望看了有所收获。 我是公众号「想吃西红柿」「云原生运维实战派」作者&#xff0c;对云原生运维感兴趣&#xff0c;也保持时刻学习&#xff0c;后续会分享工作中用到的运维技术&#xff0c;在运维的路上得到支持和共同进步&am…