一.负载均衡
1.1问题描述
//根据应用名称获取服务列表
List<ServiceInstance> instances=discoveryClient.getInstances("product-service");
//一个微服务可能有多个实例,获取第一个
EurekaServiceInstance instance=(EurekaServiceInstance)instances.get(0);
1.根据应用名称获取服务实例列表,然后从实例列表中选择一个其中一个实例
那要是一个服务对应多个实例,流量能否合理的分配到多个实例呢?
如下操作,一个服务弄出多个实例来:
我们在启动两个product-service实例:选中要启动的实例,右键选择copy configuration
在弹出的框中选择Modify options->Add VM options(添加虚拟机参数)
添加VM options:-Dserver.poort=9091(9091是服务启动的端口号,可以自己根据情况进行修改)(意思是在 Java 虚拟机启动参数中设置一个系统属性,这里具体是将服务器的端口设置为 9091。)
像这样一共启动三个服务(把原来的那个改名为ProductServicrApplication-9090:
启动服务,访问eureka会发现product-service有三个实例,如下:
访问127.0.0.1:8080/order/1,访问结果为:
多次访问,再观察日志:
通过日志可以观察到,请求多次访问,都是同一台机器(这个信息是下面这个截图看到的)
这肯定不是我们想要看到的,我们希望三个实例可以分担请求的负荷,那么如何实现呢?
解决方案:
使用原子类中的整数,防止多线程下的线程安全问题
这里将atomicinteger提出来当成静态成员变量,那么同一个工程中就是共用一个integer,每访问一次就加一,而不是每次访问都是从一开始。
同理,将instances列表拿出来当成成员变量,就是防止每一次访问都重新获取服务实例列表。而对于getInstances方法,它的返回结果不是按顺序的,而是每一次都不一样,如果将getInstances方法放到selectOrderInfoById中,那么每一次发起请求就会重新获取服务实例列表,前三次获取的实例顺序可能是a b c,可能是b a c,也可能是a c b.然后每次都对原子整数取模,得到的下标是按顺序的,也就是1 2 3,那么就不是均衡的获取了
在方法中进行实例获取时,可以使用轮询获取
这次进行多次访问,再观察日志,就会发现这三个服务实例是轮着来的
1.2什么是负载均衡
负载均衡(LoadBalance,简称LB),是高并发,高可用系统必不可少的关键组件。
当服务流量增大时,通常会采用增加机器的方式进行扩容,负载均衡就是用来在多个机器或者其他资源中,按照一定的规则合理分配负载。
1.3负载均衡的一些实现
上述例子只是简单的轮询机制,但真实场景会更加复杂,比如根据机器的配置进行负载分配,配置高端分配的流量高,配置低的分配的流量低。
类似于企业员工:能力强的员工可以多承担一些工作。
服务多机部署时,开发人员都需要考虑负载均衡的实现,所以也出现了一些负载均衡器来帮我们实现负载均衡。
负载均衡分为服务端负载均衡和客户端负载均衡。
服务器端负载均衡
订单服务调用商品服务,商品服务就是服务端,服务端这里通过一定的算法将收到的请求分配到莫一台机器上,这就是服务端负载均衡。最常见的服务器端负载均衡器就是Nginx负载均衡器,然后通过负载均衡算法,在多个服务器之间选择一个进行访问。
客户端负载均衡器
在客户端负载均衡中,负载均衡的逻辑由客户端自己实现。客户端知道所有可用的服务实例,并根据一定的算法选择一个合适的服务实例来发起请求。
二.SpringCloud LoadBalancer
2.1给RestTemplate这个Bean添加@LoadBalancer
这里使用了@LoadBalanced注解,是的该RestTemaplate对象有了负载均衡功能,并且默认的负载均衡算法是轮询的负载均衡
2.2修改端口号为服务名称
2.3启动多个product-service实例并测试负载均衡
连续多次发起请求:127.0.0.1:8080/order/1
观察product-service的日志,就会发现请求被分配到了这三个实例上
2.4负载均衡策略
1.轮询:轮询策略是指服务器轮流处理用户的请求。这是最简单最常用的策略
2.随机选择:随机选择一个后端服务器来处理请求。
三.自定义负载均衡器
SpringCloud LoadBalancer默认的负载均衡策略是轮询策略,是现实RoundRobinLoadBalancer。现在我们来实现一个采用随机策略的负载均衡器
3.1定义随机算法对象,通过@Bean将其加载到Spring中
此处使用SpringCloudLoadBalancer提供的RandomLoadBalancer
package com.bite.order.config;
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> randomLoadBanlancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory){
String name=environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
System.out.println("============="+name);
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),name);
}
}
@Bean注解:适用于告诉Spring容器,该方法将要返回一个bean对象,可以被其他组件注入和使用。在这里,该方法是返回一个ReactorLoadBalancer<ServiceInstance>接口的负载均衡器bean
可以看到,有两个类实现了这个接口,这两个类也就是我们所说的随机负载均衡器和轮询负载均衡器。这个接口的ServiceInstance表示该负载均衡器的操作对象是服务实例列表
Environment:Spring 的Environment
接口提供了对应用程序环境的访问。在这里,可以通过它来获取应用程序的配置属性。getProperty就是获取属性的意思
LoadBalanceClientFactory:是一个用于创建负载均衡客户端的工厂类。它可以提供一些方法来配置负载均衡器
最后返回的是一个随机负载均衡器的实例对象
总的来说,这段代码定义了一个方法,该方法在 Spring 容器中创建并返回一个随机负载均衡器 bean。这个负载均衡器可以根据服务实例列表选择一个随机的服务实例来处理请求,并且可以通过配置属性进行定制。
注意:该类要满足:1.不使用@Configuration注释 2.在组件扫描范围内
为什么不使用@Configuration注释?首先,如果使用了@Configuration注释,那么就会在Spring启动时将该类中被@Bean标记的方法中的bean对象创建出来,而不是在什么时候使用时再创建,这样就不具有灵活性。其次,假设有两个类都有@Configuration注释,而且两个类中都有使用@Bean注入同类型的负载均衡器,那么俩个负载均衡器都会在Spring启动时创建出来,当在某个地方使用时,编译器就不知道要使用哪一个负载均衡器了
组件扫描范围指什么:设置端口参数 - 豆包
只有放到组件扫描范围内了,这个类才能被Spring容器发现并且管理。其中定义的@Bean方法才有可能被拿出来创建Bean。如果这个类不在组件扫描范围内,这个bean的定义就没办法被发现,就无法创建Bean
3.2使用@LoadBalancerClient或者@LoadBalancerClients注解
在RestTemplate配置类上方,使用@LoadBalancerClinet或@LoadBalancerClients注解,可以对不同的服务方提供配置不同的客户端负载均衡算法策略
这样的话,就能够返回一个带有负载均衡功能的http处理工具RestTemplate实例对象
上面的@LoadBalancerClient中name制定了该负载均衡算法对哪个服务起作用,configuration标识了使用哪个类的对象作为负载均衡器。
四.服务部署
3.1准备数据
在linux终端登录mysql进行建库建表操作
修改配置文件中数据库的密码,改成linux系统中的数据库密码
product和order的都要改
3.2服务构建打包
要分别对eureka-server、product-service、order-service打包
1.pom.xml配置
<properties>项目属性和变量:定义项目中可能用到的常量或变量值,可在整个文件中被引用,也可在其他配置文件中被引用。
<profiles>元素用于定义不同的构建配置文件,构建配置文件通常用于定义软件项目在构建过程中的各种参数和设置,以实现不同环境下的定制化构建。可以针对开发(dev)、测试(test)、生产(prod)等不同环境设置不同的构建参数。
每个配置文件都有一个唯一的 ID 和一组属性。在这个例子中,每个配置文件只有一个属性
profile.name
,分别设置为 “dev” 和 “prod”。在 Maven 项目中,配置文件可以用于在不同的环境中设置不同的构建参数、资源过滤、插件配置等。例如,可以根据不同的环境设置不同的数据库连接信息、服务器地址等。
由于product-service和order-service都涉及到数据库密码的修改,所以这段配置要添加到这两个子项目的pom文件中
2.备份并修改yml配置文件
将prod环境的yml文件中的数据库密码改成linux文件下的MySQL密码
然后在每个项目的主配置文件application.yml加下面的配置:
spring:
profiles:
active: @profile.name@
这里的profile.name就是去maven中读取这个变量
项目启动后,并不是只选择其中一个配置文件进行操作。首先,application.yml通常作为基础配置文件,其中可以包含一些通用的配置属性
然后,根据激活的环境(通过spring.profiles.active属性指定),会加载相应的特定环境配置文件。例如:如激活的是开发环境,那么application-dev.yml中的配置会与application.yml中的配置合并,共同生效。
那要是在多个配置文件中有相同的属性,特定环境配置文件中的属性会覆盖application.yml中的属性
总之,在SpringBoot项目中,项目启动时多个配置文件(application.yml,application-dev.yml,application-prod.yml等)都会被扫描,但会根据环境选择特定的配置文件与基础配置文件合并生效,以满足不同环境下的配置需求。
pom.xml与applicatiom.yml的区别与用途:https://www.doubao.com/thread/wc88d3dc72ec47658
3.依次对三个项目打包
和SpringBoot项目的打包方式一样,依次对三个项目进行package即可
先点一下clean(当执行 “clean” 阶段时,Maven 会删除项目的 target 目录。这个目录通常包含编译后的字节码文件、打包生成的文件(如 JAR、WAR 文件)、测试报告等在构建过程中生成的临时文件。)“clean” 阶段为后续的构建阶段创造了一个干净的环境。这意味着在执行其他构建阶段(如 compile、test、package 等)之前,项目的构建状态被重置,确保构建结果的准确性和一致性。例如,如果在上一次构建中出现了错误或者需要进行重大的更改,执行 “clean” 阶段可以确保所有的临时文件和旧的构建产物都被删除,以便进行新的构建。
clean完再打包
3.3启动服务
1.上传Jar包到云服务器上
第一次上传需要安装lrzsz(lrzsz
是一款在 Linux 系统中常用的文件传输工具。)
apt install lrzsz
直接拖动文件到xshell窗口,上传成功(上传之前可以建一个springcloud的文件夹,具体操作如下)
2.启动服务
启动服务后,我们希望看到日志,所以可以先创建一个日志目录:
进行eureka-server,product-service,order-service的启动
#后台启动eureka-server,并设置输出日志到logs/erueka.log
nohup java -jar eureka-server-1.0-SNAPSHOT.jar >logs/eureka.log &
#后台启动product-service,并设置输出日志到logs/product.log
nohup java -jar product-serivce-1.0-SNAPSHOT.jar >logs/product.log &
#后台启动order-service,并设置输出日志到logs/order.log
nohup java -jar order-service-1.0-SNAPSHOT.jar >logs/order.log &