目录
2.3 在resource目录下新建一个bootstrap.yml文件
官网地址:Spring Cloud Consul
1. 服务注册
1.1普通调用存在的问题
- 当我们采用硬编码的方式(在代码中写死请求路径及访问地址)时,会产生一个问题:单独给我们被调用的服务的端口或者IP地址发生变化的时候,则需要手动修改代码中的内容
- 如果有多个同一服务:如多个订单微服务,普通的远程调用则无法实现负载均衡功能
- 如果系统需要支持更高的并发,则需要部署很多的微服务,在后续的维护过程中会变得很复杂
1.2.为什么要引入Consul服务注册
- 当我们端口或者IP地址变化时不需要手动去修改代码中的内容依旧能够正常访问(前提是微服务的服务名不变,通常是不会变的)
- Consul可以实现负载均衡的功能,极大程度的提高了我们系统的高可用性和稳定性
- 便于后期的维护
1.3.Consul的下载与安装
- 下载地址:Consul下载地址
特别说明:
Windows系统的386指的是
i386
指的是intel80386
,32位架构,amd64指的是amd的64位架构
,新的指令集,支持64位系统,现在大部分电脑都是64位,所以直接下载AMD64即可
2. 下载成功之后是一个压缩包,解压即可
3. 解压之后里面是一个exe文件
4. 在这里打开命令控制台输入以下命令即可查看consul版本以及启动consul服务
consul -v
consul agent -dev
3. 为了方便可以在Path环境变量中配置consul的安装路径,即可在任意位置通过cmd打开consul
4. 成功启动后在浏览器输入http://localhost:8500/
http://localhost:8500/
出现以下界面就表明consul安装成功
1.4 在SpringBoot中引入Consul
- 引入依赖,需要远程调用的微服务都需要引入该依赖
<!--SpringCloud consul discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
2. 在application.yml中编写配置
服务A cloud-payment-service
server:
port: 8001
spring:
application:
name: cloud-payment-service
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
服务B cloud-consumer-order
server:
port: 80
spring:
application:
name: cloud-consumer-order
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
3. 服务B调用服务A
我这里是使用的RestTemplate来进行远程调用
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced //必须要加因为Consul默认有多个微服务
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
4. 服务A cloud-payment-service
@RestController
@Tag(name = "支付微服务模块", description = "支付crud")
public class PayController {
@GetMapping("/test")
public String test() {
return "hello consul";
}
}
5. 服务B cloud-consumer-order
@RestController
public class OrderController {
// public static final String PaymentSrv_URL = "http://localhost:8001";//先写死,硬编码
public static final String PaymentSrv_URL = "http://cloud-payment-service";//服务注册中心上的微服务名称
@Resource
private RestTemplate restTemplate;
@GetMapping(value = "/test")
public String test() {
return restTemplate.getForObject(PaymentSrv_URL + "/test", String.class);
}
}
6. 分别启动服务A和服务B,必须要把consul给启动成功,启动成功之后可以在consul的web面板看到两个服务
7. 在浏览器输入 http://localhost/test
8. 至此我们服务注册就大功告成了
2. 配置中心
2.1 简述
微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。比如某些配置文件中的内容大部分都是相同的,只有个别的配置项不同。就拿数据库配置来说吧,如果每个微服务使用的技术栈都是相同的,则每个微服务中关于数据库的配置几乎都是相同的,有时候主机迁移了,我希望一次修改,处处生效。
2.2 引入依赖
<!--SpringCloud consul config-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
2.3 在resource目录下新建一个bootstrap.yml文件
bootstrap.yml的优先级要高于application.yml,当项目启动时会先加载bootstrap.yml中的配置,并且不会application.yml覆盖,所以需要同一管理的配置放在bootstrap.yml中。我这里只是为了演示就写得比较简单
bootstrap.yml
spring:
application:
name: cloud-payment-service
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
config:
profile-separator: '-' # consul默认是使用','分割,这里修改成'-'
format: YAML
# watch:
# wait-time: 1 # consul默认动态刷新配置时间是55秒(当我们在consul中修改了配置之后需要等待55秒钟),不建议修改
application.yml
server:
port: 8002
# ==========applicationName + druid-mysql8 driver===================
spring:
profiles:
active: dev # 后缀为dev
2.4 打开Consul web管理面板建立相关配置
consul当中配置文件需要存放在以下目录
config是文件夹,testApp,dev也是文件夹,consul默认是以‘,'分割,我在bootstrap.yml配置中修改成了‘-’,而testapp或者application就是我们项目名称,注意不是服务名称而是配置文件中的spring.application.name:,因为我们是激活的dev配置文件所以我们需要在consul中建立cloud-
config/payment-service-dev/文件夹
①建立config文件夹
②在config文件夹中建立cloud-payment-service-dev文件夹
③接下来就是需要在cloud-payment-service-dev文件夹中建立data的key-value键值对了key为data,value就是我们的配置信息
study:
consul: very happy
配置就到此写完了,接下来我们就需要在服务中获取配置了
2.5 获取Consul中的配置
@RestController
@Tag(name = "支付微服务模块", description = "支付crud")
public class PayController {
@GetMapping("/testGetInfo")
public String testGetInfo(@Value("${study.consul}") String info) {
return info;
}
}
成功获取到配置信息
修改consul中的配置信息
然后等待55秒,或者修改bootstrap.yml中的
spring:
application:
name: cloud-payment-service
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
config:
profile-separator: '-' # default value is ",",we update '-'
format: YAML
watch:
wait-time: 1 # consul默认动态刷新配置时间是55秒(当我们在consul中修改了配置之后需要等待55秒钟),不建议修改
然后再此访问即可看到动态更新了我们的配置信息(不需要重启我们的微服务)
至此我们配置中心就搭建好了
2.6 Consul 配置持久化和注册服务
当我们重启Consul服务的时候,会发现我们的配置全消失了,这肯定不是我们想要的那么如何将我们的配置进行持久化呢?如何让我们的Consul开机就启动呢?详细内容请见博主的另外一篇博客:
Consul 详细安装教程,以及配置持久化以及注册为服务详细教程-CSDN博客
3. Consul+LoadBalancer实现负载均衡
3.1 LoadBalancer介绍
LoadBalancer官网网址:Spring Cloud LoadBalancer :: Spring Cloud Commons
LoadBalancer是一个本地负载均衡客户端,Nginx是一个服务端负载均衡,他俩有什么区别呢?
- Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求,即负载均衡是由服务端实现的。
loadbalancer本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。
3.2 LoadBalancer支持的远程调用方式
LoadBalancer支持以下4中远程调用方式分别是
- RestTemplate
- RestClient
- WebClient
- WebFlux WebClient
3.3 引入依赖
在我们的调用者服务B中引入依赖(因为是客户端的负载均衡,我们这里的服务B cloud-consumer-order 就是我们的客户端
<!--loadbalancer-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
3.4 搭建多个微服务
在我们的项目中搭建两个服务A,这里就不一一演示了,把搭建的服务A复制一份改一下端口号即可,注意不能修改配置文件中的服务名称,不然就不认为是同一个服务了
3.4 创建测试
在两个服务A中添加以下代码
@RestController
@Tag(name = "支付微服务模块", description = "支付crud")
public class PayController {
@Value("${server.port}")
private String port; //因为两个服务A的端口不一样则可以判断是调用了哪一个服务
@GetMapping("/testGetPort")
public String testGetPort() {
return port;
}
}
在服务B中添加以下代码
@RestController
public class OrderController {
// public static final String PaymentSrv_URL = "http://localhost:8001";//先写死,硬编码
public static final String PaymentSrv_URL = "http://cloud-payment-service";//服务注册中心上的微服务名称
@Resource
private RestTemplate restTemplate;
@GetMapping("/testGetPortOrder")
public String testGetPort() {
return restTemplate.getForObject(PaymentSrv_URL+ "/testGetPort", String.class);
}
}
RestTemplateConfig.java
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced //负载均衡算法 //默认轮询
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
多次请求 http://localhost:80/testGetPortOrder 发现一次8001端口,一次是8002端口这样轮询。因为LoadBalancer默认采用轮询的负载均衡算法
3.5 手动切换负载均衡算法
将轮询负载均衡算法切换成随机算法
修改RestTemplateConfig
@Configuration
@LoadBalancerClient(value = "cloud-payment-service",configuration = RestTemplateConfig.class)
public class RestTemplateConfig {
//负载均衡算法
//默认轮询
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
//随机
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
重复3.4的操作发现每次返回的端口号是随机的,表明是随机访问两个服务A的
3.6 负载均衡算法原理
3.6.1 轮询算法
接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标 ,每次服务重启动后rest接口计数从1开始。
List<ServiceInstance> instances = discoveryClient.getInstances("cloud-payment-service");
如: List [0] instances = 127.0.0.1:8002
List [1] instances = 127.0.0.1:8001
8001+ 8002 组合成为集群,它们共计2台机器,集群总数为2, 按照轮询算法原理:当总请求数为1时: 1 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001
当总请求数位2时: 2 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002
当总请求数位3时: 3 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001
当总请求数位4时: 4 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002
如此类推......
3.6.2 随机算法
随机算法的实现通常涉及使用随机数生成器来选择服务器。例如,可以通过生成一个随机数,然后使用这个数作为索引来从服务器列表中选择一个服务器。随机算法是一种简单的负载均衡策略,但需要通过改进,如引入服务器权重,来更有效地处理不同性能的服务器,以达到更均衡的负载分布。
立志用功如种树然,方其根芽,犹未有干;及其有干,尚未有枝;枝而后叶,叶而后花!!!!