Spring Cloud 本身并不是一个拿来即用的框架,而是一套规范。主流的 Spring Cloud Netfix 和 Spring Cloud Alibaba 实现了这一套规范
微服务常用技术栈:
技术栈 | 落地实现 |
---|---|
服务注册与返现 | Eureka、Zookeeper、Consul、Nacos |
服务熔断、限流、降级 | Hystrix、Reslience4j、Sentinel |
服务调用 | Ribbin、Loadbalancer、Feign、OpenFeign、Dubbo |
配置中心 | Config、Zookeeper、Consul、Nacos |
服务网关 | Zuul、Gateway |
消息总线 | Bus、Nacos |
由 Spring Cloud Alibaba 微服务解决方案提供的组件:
组件名 | 详情 |
---|---|
Nacos | 服务注册与发现、服务管理、配置中心 |
RocketMQ | 分布式消息中间件,主要提供可靠的消息发布与订阅消费服务 |
Dubbo Spring Cloud | 远程调用框架 |
Sentinel | 流程控制框架,提供服务限流、降级、熔断等功能,保证服务的稳定性 |
Seata | 分布式事务解决方案,保证数据一致性 |
OSS | (收费)高可用的文件存储服务 |
SchedulerX | (收费)分布式任务调度 |
SMS | (收费)短信服务 |
版本5.7.24
Spring Boot简化了Spring MVC:
创建 SpringBoot项目有很多种方式
https://start.spring.io
https://start.aliyun.com
这里选择到官网用网页创建
创建之前先查询版本依赖关系,选择一个
https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
![在这里插入图片描述](https://img-blog.csdnimg.cn/2734fe806aa0425d86dc0980b28747a6.png
这里选了springboot 2.6.11,(随便选)后面再改 ,依赖只加了个 Spring Web
添加依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
搭建 Spring Cloud Alibaba 需要基于 Spring Boot 项目
以父子项目的形式管理整个 Spring Cloud Alibaba 项目
先创建一个 Maven 项目作为父项目,再把之前生成的 smaple 项目加进去即可
再修改 pom 文件,做好基础依赖
父pom:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.echoogroupId>
<artifactId>parentartifactId>
<version>1.0.0version>
<name>echoo-cloudname>
<description>Parent For Spring Cloud Alibabadescription>
<modules>
<module>samplemodule>
modules>
<properties>
<java.version>1.8java.version>
<spring.boot.version>2.3.2.RELEASEspring.boot.version>
<spring.cloud.version>Spring Cloud Hoxton.SR9spring.cloud.version>
<spring.cloud.alibaba.version>2.2.6.RELEASEspring.cloud.alibaba.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>${spring.boot.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<version>${spring.boot.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<version>${spring.boot.version}version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring.cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>${spring.cloud.alibaba.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.7.0version>
<configuration>
<source>${java.version}source>
<target>${java.version}target>
<encoding>UTF-8encoding>
configuration>
plugin>
plugins>
build>
<repositories>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
repository>
<repository>
<id>spring-snapshotsid>
<name>Spring Snapshotsname>
<url>https://repo.spring.io/snapshoturl>
<snapshots>
<enabled>trueenabled>
snapshots>
repository>
repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
pluginRepository>
<pluginRepository>
<id>spring-snapshotsid>
<name>Spring Snapshotsname>
<url>https://repo.spring.io/snapshoturl>
<snapshots>
<enabled>trueenabled>
snapshots>
pluginRepository>
pluginRepositories>
project>
sample.pom:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>com.echoogroupId>
<artifactId>parentartifactId>
<version>1.0.0version>
<relativePath/>
parent>
<groupId>com.echoo.basegroupId>
<artifactId>sampleartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>samplename>
<description>base project for Spring Cloud Alibabadescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
dependencies>
project>
Nacos = Naming + Configuration +Service
= 服务命名管理 + 配置管理 + 服务注册中心
地址:https://github.com/alibaba/nacos/releases
根据推荐版本,这里选择下载 v1.4.2
我选择了用 docker 镜像装在自己的云服务器上,参考:https://blog.csdn.net/qq_31856061/article/details/123621927
安装后直接访问,修改密码即可。
下面正式开始编写代码,将会编写一个消费服务,两个服务供应,服务之间的调用暂时用 RestTemplate + Ribbon 实现,顺带测试一下负载均衡的效果。
在之前创建了一个父子项目 echoo-cloud
复制一份修改一下项目名称路径pom.xml文件即可
父 pom.xml
<groupId>com.echoo.cloud.nacosgroupId>
<artifactId>spring-cloud-alibaba-nacos-sampleartifactId>
<version>1.0.0version>
<name>spring-cloud-alibaba-nacos-samplename>
<description>nacos-sampledescription>
子 pom.xml
<parent>
<groupId>com.echoo.cloud.nacosgroupId>
<artifactId>spring-cloud-alibaba-nacos-sampleartifactId>
<version>1.0.0version>
<relativePath/>
parent>
<groupId>com.echoo.cloud.nacos.provicergroupId>
<artifactId>provider8031artifactId>
<version>1.0.0version>
<name>provider8031name>
<description>nacos-provider8031description>
修改项目的项目名和目录名
因为子项目的目录修改了,所以父项目的 pom.xml 对子项目的引用也要改
<modules>
<module>provider8031module>
modules>
在 Provider8031 的 pom.xml 中添加 Nacos 注册中心/服务发现和健康监控依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
配置文件:
server:
port: 8031 # 服务端口
spring:
application:
name: nacos-provider-one # 应用名
cloud:
nacos:
discovery:
server-addr: 124.221.89.209:8849 # Nacos 服务地址
management:
endpoints:
web:
exposure:
include: '*' # 公开所有端点
编写测试接口
@RestController
public class TestController {
@Value("${server.port}")
private String port;
@RequestMapping("/test")
public String test() {
return "信息返回自①号服务,服务端口:" + port;
}
}
复制一份 provider8031 项目,修改成 provider8032 ,添加到父项目中
此时就有两个服务供应了
启动后可以看到 Nacos 上注册了两个服务供应
服务接口通过 postman 或 curl 测试也是完全没问题
复制一份 provider 项目,修改成 consumer8041
至此有了三个子项目, 两个供应者一个消费者
注意,两个服务供应者的名字是一样的,端口不同(后续通过这个名字调取服务)
使用 Java Config 的方式配置一个负载均衡的 RestTemplate
@Configuration
public class GenericConfig {
@Bean
@LoadBalanced // 必须添加负载均衡注解
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
在测试接口处通过 RestTemplate 来远程调用
@RestController
public class TestController {
@Value("${server.port}")
private String port;
private final String SERVER_URL = "http://nacos-provider"; // 这里不写具体的某个服务的ip和端口,而是通过注册到 Nacos 的服务名代替
@Resource
private RestTemplate restTemplate;
@RequestMapping("/test")
public String test() {
String result = restTemplate.getForObject(SERVER_URL + "/test", String.class);
return "①号消费者(" + port + "):" + result;
}
}
调用消费者测试接口
可以看到消费者测试接口轮流调用两个供应服务的接口,实现了负载均衡
这个负载均衡是由 Ribbon 框架实现的,而 Nacos 整合了 Ribbon
RestTemplate 在Config类中已经被 Ribbon 代理, 当调 用 restTemplate的 postForObject()、getForObject() 时根据微服务名称,到注册中心查找对应的具体的服务地址。
通常我们在项目的 application.propertiesd 或 application.yml 文件里写配置,有个非常明显的缺点,就是每次修改配置后需要重启项目才能生效。如果对多个相同的服务做了集群,那么就有了很多个重复的配置文件,修改起来非常麻烦。为此,Nacos 提供了配置中心解决方案。
我们直接用消费者项目修改,引入 Nacos 配置中心。
修改 pom.xml : 引入 nacos 配置中心的依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
创建基础配置文件 bootstrap.yml 或 bootstrap.properties
在 SpringBoot 框架中,配置文件不止一种,名为 bootstrap 的配置文件 优先级 比名为 application 的配置文件高,启动时会先加载 bootstrap 配置文件。因此我们把启动项目的基本配置写在 bootstrap 中,待项目启动后再从 Nacos 配置中心拉取哪些次要配置。是这样一个逻辑
server:
port: 8041 # 服务端口
spring:
application:
name: nacos-consumer-one # 应用名
cloud:
nacos:
discovery:
server-addr: 124.221.89.209:8849 # Nacos 注册中心地址
config:
server-addr: 124.221.89.209:8849 # Nacos 配置中心地址(因为是同一个所以和注册中心地址一样)
file-extension: yml # 指定配置文件的数据格式
management:
endpoints:
web:
exposure:
include: '*' # 公开所有端点
DataId 目的是使项目快速切换多套配置内容。
DataId 格式:
${prefix}-${spring.profiles.active}.${file-extention}
注意:
可以通过 spring.cloud.nacos.config.prefix 配置项覆盖
注意:
当 spring.profiles.active 为空时,对应的连接符 “-” 也将不存在,dataId 的格式变成 p r e f i x . {prefix}. prefix.{file-extention}
注意:
通过 spring.cloud.nacos.config.file-extention 配置项来配置
有了基础认识后,我们开始用 DataId 创建配置
先在消费者的 application.yml 配置中:
spring:
profiles:
active: dev
在 Nacos 平台上添加配置
开发配置 DataId:
nacos-consumer-dev.yml
配置项:
custom:
info: nacos config dev
生产配置 DataId:
nacos-consumer-prod.yml
配置项:
custom:
info: nacos config prod
@RefreshScope // 配置自动更新
@RestController
public class TestController {
@Value("${custom.info}") // 从配置文件读取配置
private String config;
@RequestMapping("/getConfig")
public String getConfig() {
return config;
}
}
@RefreshScope 是 SpringCloud 的原生注解
测试:
成功拿到 开发配置 的配置项的值
修改开发配置
custom:
info: nacos config dev update
再测试看看有没有自动更新
group 是分组,表示配置归属于哪个组,默认是 DEFAULT_GROUP 组。
我们可以通过 spring.cloud.nacos.config.group 配置项 指定获取哪个组的配置
在 Nacos 配置中心新建一个配置
修改 bootstrap.yml 文件,增加 spring.cloud.nacos.config.group 配置项,指定 CUSTOM_GROUP 组
spring:
application:
name: nacos-consumer # 应用名
cloud:
nacos:
discovery:
server-addr: 124.221.89.209:8849 # Nacos 注册中心地址
config:
server-addr: 124.221.89.209:8849 # Nacos 配置中心地址(因为是同一个所以和注册中心地址一样)
file-extension: yml # 指定配置文件的数据格式
group: CUSTOM_GROUP # 指定组
完了拿到的就是 CUSTOM_GROUP 的配置了
namespace 命名空间,是一种粗粒度的控制。默认的命名空间是 public。
命名空间囊括了 Group、DataId。
创建自定义命名空间
回到配置列表,public 旁多出了个 fatst_team 命名空间
选择 fast_team 命名空间,添加一条配置
在消费者 bootstrap.yml 增加一项配置项,指定命名空间(必须是命名空间的 id)
spring:
application:
name: nacos-consumer # 应用名
cloud:
nacos:
discovery:
server-addr: 124.221.89.209:8849 # Nacos 注册中心地址
config:
server-addr: 124.221.89.209:8849 # Nacos 配置中心地址(因为是同一个所以和注册中心地址一样)
file-extension: yml # 指定配置文件的数据格式
group: CUSTOM_GROUP # 指定组
namespace: 19b1fec0-b937-457b-b322-171086446ba2 # 命名空间 id
测试取到的值就是 fast_team 命名空间下 dev 配置文件的值
三者都是用于将区分配置,其中 Namespace 为第一级,Group 为第二级,DataId 为第三级,粒度由粗到细。
比如用 Namespace 区分团队,以 Group 区分功能模块,以 DataId 做最后的环境区分。
Nacos 默认使用嵌入式数据库 - Derby 数据库,因为 存储容量限制,不方便查询、优化,做集群时不能将应用和数据分开 等原因,往往需要一个外部的数据库做为数据的持久化仓库。目前仅支持 MySql ,版本要求为 5.6.5+ 。
先创建一个数据库 nacos_config (任意名),并通过脚本文件 nacos-mysql.sql 导入对应的表
nacos-mysql.sql 脚本文件可以在源码找到(
nacos-1.4.2.zip\nacos-1.4.2\distribution\conf\nacos-mysql.sql
)
也可以在安装目录找到 (安装目录\conf\nacos-mysql.sql
)
然后修改 Nacos 的配置文件
我用的是 docker 部署 Nacos,所以先进入容器
docker exec -it 594c187f55f9 /bin/bash
编辑 /conf/application.properties 配置文件
spring.datasource.platform=mysql
db.url.0=jdbc:mysql://124.221.89.200:3305/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=xxxxx
重启容器
docker stop 594c187f55f9
docker start 594c187f55f9
重新访问 Nacos ,此时的 Nacos 是初始状态
刷新看看服务注册是否正常,至此完成对单机版 Nacos 的持久化。
重新创建命名空间和配置
custom:
info: nacos config namespace fast_team group CUSTOM_GROUP active dev repository with mysql
修改消费者项目 bootstrap.yml 中的 namespace 项并重启项目
测试没问题,完美。
上面实现的 Nacos 数据持久化,就是在为集群部署奠定基础。在实际生产中,如果以单机模式运行 Nacos 服务,一旦宕机,就会引发单点故障,而 Nacos 作为一个基础服务,影响非常大。
首先看看 Nacos 的集群架构
客户端指代所有连接到 Nacos 的服务,如浏览器
负载均衡常用 Nginx 、Keepalived、OpenResty 等
所有 Nacos 节点都用的同一数据库(集群),保证数据统一。
业界常用的负载均衡中间件是 Keepalived,它能解决使用静态路由地址单点故障的问题,保证99.99%高可用 。
但是 Keepalived 使用起来比较麻烦,这里先选择 OpenResty 实现,后面再引申到 Keepalived。
OpenResty:
有一个大名鼎鼎的中间件 Nginx,它有两个重要的功能:反向代理和负载均衡。OpenResty 就是基于 Nginx 开发的,集成了很多优秀的 Nginx模块,其中最出名的就是 Lua 了,OpenResty 相当于是 Nginx 的增强。
① 下载安装 nacos-server
Nacos 集群至少 3 个节点,我这里用自己的云服务器搭。
下载 nacos-server-1.4.2.tar.gz 压缩包 (地址:https://github.com/alibaba/nacos/releases
)
上传服务器、解压即可
② 配置文件配置 mysql数据库
修改 conf/application.properties
配置文件
#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://124.221.89.200:3305/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=true&serverTimezone=UTC
db.user.0=root
db.password.0=xxxxx
注意:
- url 中的默认 useSSL=false,在启动过程中报错 Public Key Retrieval is not allowed,改成 useSSL=true 后正常启动。
- 集群节点共用一个 数据库(集群)。
③ 添加集群配置
在 conf/
下添加一个集群配置文件 cluster.conf 。
内容如下:
124.221.89.200:8848
124.221.89.200:8847
124.221.89.200:8846
注意:分别是3个 Nacos (包括自己)的地址和端口
④ 复制 nacos 文件夹
因为我们需要3个 Nacos 服务,因此直接将 nacos 改名,再复制2份即可,分别命名 nacos1、nacos2,nacos3
mv nacos/ nacos1
cp -R nacos1/ nacos2
cp -R nacos1/ nacos3
⑤ 修改启动脚本
vi nacos1/bin/startup.sh
定位到启动命令行
nohup $JAVA ${JAVA_OPT} nacos.nacos >> ${BASE_DIR}/logs/start.out 2>&1 &
插入指定启动端口配置项: -Dserver.port=8848
nohup $JAVA -Dserver.port=8848 ${JAVA_OPT} nacos.nacos >> ${BASE_DIR}/logs/start.out 2>&1 &
其他两个 nacos 同理指定 8847,8846 端口即可
⑥ 启动三个服务
./nacos1/bin/startup.sh
./nacos2/bin/startup.sh
./nacos3/bin/startup.sh
分别查看启动日志观察是否有如下启动成功日志
2022-08-25 09:11:16,636 INFO Nacos started successfully in cluster mode. use external storage
至此 nacos 集群节点搭建好了,访问任意 nacos 服务平台可看到节点信息
下载 OpenResty 及其依赖的安装包
wget -O openresty-1.15.8.2.tar.gz -c https://sourceforge.net/projects/generic-software/files/php/openresty-1.15.8.2.tar.gz/download
wget -O openssl-1.0.2q.tar.gz -c https://sourceforge.net/projects/generic-software/files/php/openssl-1.0.2q.tar.gz/download
wget -O pcre-8.41.tar.gz -c https://sourceforge.net/projects/generic-software/files/php/pcre-8.41.tar.gz/download
wget -O zlib-1.2.11.tar.gz -c https://sourceforge.net/projects/generic-software/files/php/zlib-1.2.11.tar.gz/download
wget -O perl-5.30.1.tar.gz -c https://sourceforge.net/projects/generic-software/files/php/perl-5.30.1.tar.gz/download
安装 perl (安装路径随意)
tar -zxvf perl-5.30.1.tar.gz
cd perl-5.30.1
./Configure -des -Dprefix=$HOME/localperl
make
make test
make install
分别解压 openssl pcre zlib
tar -zxvf openresty-1.15.8.2.tar.gz
tar -zxvf openssl-1.0.2q.tar.gz
tar -zxvf pcre-8.41.tar.gz
tar -zxvf zlib-1.2.11.tar.gz
安装 OpenResty
cd openresty-1.15.8.2
./configure --prefix=/usr/openresty --with-pcre=/usr/pcre/pcre-8.41 --with-zlib=/usr/zlib/zlib-1.2.11 --with-http_ssl_module --with-openssl=/usr/ssl/openssl-1.0.2q
make & make install
–prefix : 指定 OpenResty 安装路径
–with-pcre : 指定 pcre 所在路径
–with-zlib : 指定 zlib 所在路径
–with-openssl : 指定 openssl 所在路径
–with-http_ssl_module : 是个 ssl 模块,支持配置 https
如果执行make & make install
的时候出现编译器错误:checking for gcc... cc sed: can't read conftest.err: No such file or directory
安装 gcc gcc-c++ 编译器即可
yum install -y gcc gcc-c++
启动 OpenResty 服务
/usr/openresty/bin/openresty
OpenResty 服务的默认端口是 80 端口
启动成功后直接访问,页面出现 Welcome to OpenResty! 即成功
接下来是把 nacos 集群挂载到 OpenResty 上
编辑 /usr/openresty/nginx/conf
配置文件
在 http{}
代码块中添加一个upstream (溯源流) 对象,给此对象起名为 nacosCluster (起名随意), 对象内容是指定 nacos 集群的服务节点地址,
http {
upstream nacosCluster{
server 127.0.0.1:8848;
server 127.0.0.1:8847;
server 127.0.0.1:8846;
}
}
在 server{}
代码块的 location / {}
对象中添加代理
server {
location / {
proxy_pass http://nacosCluster;
index index.html index.htm;
}
}
重载配置文件
/usr/openresty/bin/openresty -s reload
proxy_pass http://nacosCluster
nacosCluster 为溯源流对象名,意思是通过 nginx 代理 nacosCluster 中的 nacos 服务地址
例如:
当访问http://124.221.89.200:80/nacos
时,自动把124.221.89.200:80
替换成127.0.0.1:8847
,再拼接上资源路径/nacos
,即http://127.0.0.1:8847/nacos
, 至于负载均衡策略先忽略.
测试
修改消费之模块的 nacos 服务地址端口为 OpenResty 的服务地址
spring:
application:
name: nacos-consumer # 应用名
cloud:
nacos:
discovery:
server-addr: 124.221.89.200:80 # Nacos 注册中心地址
config:
server-addr: 124.221.89.200:80 # Nacos 配置中心地址(因为是同一个所以和注册中心地址一样)
file-extension: yml # 指定配置文件的数据格式
group: CUSTOM_GROUP # 指定组
namespace: 8f60737f-6b74-47f7-8f29-2c908d133fca # 命名空间 id
启动访问是否能获取配置数据
如果能,说明 OpenResty 算部署好了
接下来关掉一个 nacos 节点
此时只有两个可用节点.再测试是否能正常获取配置数据
如果能,则我们想要的 nacos 集群高可用就算实现了.
在4.6中实现了 Nacos 集群服务的基本高可用,短板此时来到了 OpenResty 身上,因为只有一个 OpenResty 服务,如果它挂了,下面的 Nacos 服务都废了,这就引发了单点故障。解决方案就是 OpenResty 也做一个集群,联合 Keepalived。
架构如下:
VIP 就是 Visual IP ,虚拟IP,也就是等下要用的 Keepalived 服务。我们主要用到 Keepalived 服务的 VRRP 功能。
VRRP:
虚拟路由器冗余协议功能,用来解决静态路由单点故障问题,保证服务高可用。
VRRP 协议为两台或以上设备提供一个或多个虚拟IP,内部节点分为 MASTER 主节点和 BACKUP 备用节点两种。假如有两台服务,一个 MASTER 一个 BACKUP 。首先会选举 MASTER 作为服务节点提供服务,当 MASTER 挂了,BACKUP 节点没有接受到 MASTER 节点的反馈时会重新选举(根据优先级)BACKUP 节点作提供服务。
因为上面已经部署了一台 OpenResty 服务器,已经有一个节点了。接下载再部署另外一台 OpenResty 服务器形成双节点集群即可。部署方式直接参考 安装 OpenResty
注意:
新部署的这台 OpenResty 服务器不需要部署 Nacos 服务了,直接配置现有的那 3 个 Nacos 服务即可,这次的 ip 就不是 127.0.0.1 了。
upstream nacosCluster{
server 124.221.89.200:8848;
server 124.221.89.200:8847;
server 124.221.89.200:8846;
}
两台服务器都需要部署 Keepalived 服务。
1.下载 Keepalived 安装包
下载地址:https://www.keepalived.org/download.html
上传、解压
2.配置安装路径
进入解压目录
直接配置,后面编译和安装就按默认的安装路径去装,比较不好找
./configure
指定目录,后面编译和安装就安装在自己指定的目录,更加清晰
./configure -- prefix=/usr/local/keepalived
如果配置或者后面编译的时候出现
这是因为缺少了一些头文件(库),装上即可:yum install -y openssl-devel
3.编译安装
make && make install
4.把 Keepalived 设置为系统服务
如果是没有配置指定目录安装的,需要先查看安装位置
whereis keepalived
/etc/keepalived /usr/local/sbin/keepalived /usr/local/etc/keepalived
三个路径分别是 启动目录 执行文件 配置文件目录
如果是自己指定安装目录的,创建一个系统配置文件目录/etc/
下keepalived
的配置文件存放目录
mkdir /etc/keepalived
把安装路径(注意自己的安装路径)下的配置文件复制一份到启动目录
# 没有指定目录
cp /usr/local/etc/keepalived/keepalived.conf /etc/keepalived/
# 指定安装目录
cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/
把执行文件拷贝一份到 /usr/sbin/ 目录下
# 没有指定目录
cp /usr/local/sbin/keepalived /usr/sbin/
# 指定安装目录
cp /usr/local/keepalived/sbin/keepalived /usr/sbin/
将源码包中的初始化脚本拷贝一份到系统初始化目录下,系统初始化的时候会初始化keepalived
cp /usr/keepalived-2.1.5/keepalived/etc/init.d/keepalived /etc/init.d/
给执行文件添加可执行权限
chmod +x /etc/init.d/keepalived
将源码包中的配置文件拷贝一份到系统配置目录中,系统初始化keepalived
的时候用
cp /usr/keepalived-2.1.5/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
加入开机自启动
chkconfig --add keepalived
chkconfig keepalived on
firewall-cmd --direct --permanent --add-rule ipv4 filter INPUT 0 --protocol vrrp -j ACCEPT
firewall-cmd --reload
编辑 MASTER /etc/keepalived/keepalived.conf 文件
vrrp_script chk_openresty {
script "/etc/keepalived/openresty_check.sh"
interval 2
weight -20
}
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
track_script {
chk_openresty
}
virtual_ipaddress {
192.168.1.115/24
}
}
编辑 BACKUP /etc/keepalived/keepalived.conf 文件
vrrp_script chk_openresty {
script "/etc/keepalived/openresty_check.sh"
interval 2
weight -20
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 50
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
track_script {
chk_openresty
}
virtual_ipaddress {
192.168.1.115/24
}
}
编写检测 OpenResty 服务的脚本
touch /etc/keepalived/openresty_check.sh
#!/bin/bash
echo "开始执行 openresty 进程检查脚本"
echo "当前 openresty 进程数:$n"
if [ $n -eq 0 ];then
echo "开始启动 openresty"
/usr/openresty/bin/openresty
sleep 2
if [ `/usr/openresty/bin/openresty` -eq 0 ];then
echo "openresty 启动失败,关闭 keepalived"
killall keepalived
fi
fi
注意 shell 脚本中命令行用的不是单引号
''
,而是反引号 ``
启动keepalived
service keepalived start
如下启动成功
[root@VM-16-13-centos bin]# service keepalived start
Starting keepalived (via systemctl): [ OK ]
看看虚拟地址有没有起来
ip addr
5.测试访问
用虚拟地址访问 nacos 服务
关掉一台服务模拟宕机再访问
如果也没问题就是成功啦
到这里 Nacos + OpenResty + Keepalived
的集群高可用就部署完成噜
CAP:
- 一致性(Consistency):集群所有节点同一时间的数据是一致的,每次读取要么是最新的数据,要么是一个错误。
- 可用性(Availability):client 在任何时刻的读写操作都能在限定的延迟内完成的,即每次请求都能获得一个响应(非错误),但不保证是最新的数据。
- 分区容错性(Partition tolerance):在分布式系统中,服务之间通过网络相互通信,但网络不一定实时可靠,出现网络故障时,就有可能造成数据不一致,也就是发生了网络分区,分区容错性就是系统应该能保证在这种情况下可以正常工作。
CAP理论:
对于C A P
这三个特性,我们只能三选二,无法同时满足这三个特性,而现大多数是分布式系统,P一般是必须存在的,所以一般是在CA之间做取舍。具体详情参考某大鸟博客https://blog.csdn.net/paolei/article/details/119912023
承上:无
启下:SpringCloudAlibaba系列微服务搭建笔记二_RestTemplate+Ribbon