前言
本文主要描述Spring Cloud Alibaba Nacos组件的单机部署以及集群部署以及如何使用,比如如何使用nacos去作为我们的服务注册中心或者如何使用nacos去作为我们的配置中心,他们可以解决什么样的问题等,虽然官方有一些文档,但是其中还是有很多坑需要注意的 : 官方文档V2版 , 还有就是需要注意一下,我们本文是基于nacos2.x往上版本进行叙述了,因为nacos1.x和2.x在集群部署和单机使用上还是有些许不一样的!
还需要注意一点就是本文是使用springcloud为基础然后去集成的Nacos,而不是直接使用了SpringCloudAlibaba全家桶,这样子可以更方便我们根据业务需求去自定义我们cloud项目的各组件!
本文实例环境 :
- centOS7.9
- java1.8.0_281
- nginx1.20.1 (1.9.0以下版本没stream模组,无法于Nacos2.x集群结合实现高可用)
- maven 3.2.1
Nacos服务端安装
Nacos分为Service(服务)端和Client(客户)端,服务端主要就是图形化界面去管理我们的一些动态配置或者可以查看我们的微服务实例,以及可以新增命名空间去管理我们各个环境的微服务并可以形成隔离
nacos服务端依靠JAVA环境来运行,所以需要确保你服务器中有以下环境
- JDK1.8
- Maven 3.2.x+
- 下载nacos服务压缩包
下载地址 : https://github.com/alibaba/nacos/releases
下载然后通过ftp上传到linux服务器并解压,我把nacos压缩包放在了/usr下
cd /usr
tar -zxvf tar -axvf nacos-server-2.2.2.tar.gz
鉴权配置
然后修改nacos配置,配置文件在nacos文件夹下的config下的一个叫application.properties文件
cd /usr/nacos/config
vim application.properties
首先我们要开启鉴权功能,因为2.2.0后的版本鉴权功能时默认关闭的,也就是访问你的nacos不需要登录就可以进去
nacos.core.auth.enabled=true
#下面这俩个配置的主要作用就是,如果你使用nacos的api进行服务信息拉取或者推送的话,只需要在请求头中存放以下参数那就可不通过登录去获取jwt也可以去进行服务拉取和推送了,例如我下述这么配置的话那你的请求头中存放的数据就是 key:yu101408 value:yu101408 ,
nacos.core.auth.server.identity.key=yu101408
nacos.core.auth.server.identity.value=yu101408
#下面是2.2.0之前官方的默认密钥 , 但不推荐使用有安全风险,我们需要自己自定义一个,其实可以直接根据下面这个值然后随便改上其中几个数字即可
nacos.core.auth.plugin.nacos.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789
数据库配置
然后我们配置nacos的数据库
然后我们nacos的config包下有一个sql文件,我们需要再自己的mysql数据库中创建一个名为 'nacos_config' 的数据库,然后执行该sql文件
端口号配置
这里需要提一下端口号的问题,nacos的默认端口号是8848,如果我们服务器中有应用程序占用的该端口也可以修改以下配置来更改nacos的端口
server.port=10010
注意 : 在nacos2.X之后的版本加入了GRPC链接,用于服务提供端与nacos的长链接信息同步,GRPC用的端口是你nacos主端口的基础上进行一定偏移量生成的
就比如你把cacos端口设置成了10010,那gRPC就会占用你11010和11011,同时jraft会占用你的9010端口,所以我们要保证这些端口都是空闲状态
nacos服务端启动
cd /usr/nacos/bin
# -m standalone : 作为单机启动,集群启动后面我们来搞
./startup.sh -m standalone
然后我们访问 ip:端口号/nacos 访问nacos的服务端,默认账号 : nacos 默认密码: nacos
Nacos作为服务注册中心配置
版本问题
nacos版本对应在nacos的官方文档上也记录有,但是感觉描述的不是很清晰,但凡如果版本对应不正确就会出现服务找不到的情况,所以我这里专门花一栏的空间去解释一下nacos与springboot与springcloud的一个对应情况,具体如下
nacos版本分为三个分支 , 一个是2022.x分支 一个是 2021.x分支 一个是2.2.x分支 , nacos版本信息可以在这个网址查看 https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-discovery
切记,并不是2022.x开头的版本是最新的,这三个分支都在进行更新,这三个分支唯一的区别就是兼容不同的springboot版本!切勿觉得2.2.x版本就是特别老的版本觉得功能会短缺,然后强制将自己的springboot版本改成了3.0 , 然后去使用了2022.x分支的nacos版本,最后你会发现和2.2.x分支版本其实一模一样!
2022.x分支
Spring Cloud Alibaba Version 版本其实就是对应的nacos的版本,意思就是 如果你项目是使用的springboot3.0那你就需要使用2022.0.0.0-RC2版本 如果你项目使用的springboo3.2 那你就需要使用 2022.0.0.0-RC1版本的nacos,而且你cloud版本也必须大于等于2022.x版本
2021.x分支
和上述一样,根据你项目的boot版本去选择对应的nacos版本
2.2.x分支
本文使用的环境
jdk = jdk8版本
nacos = 2021.1版本
springboot = 2.4.2版本
springcloud = 2020.0.1
maven = 3.6.1
大家如果是重新搭项目可以直接跟着我这个来,至少其中坑我是都踩完了!
服务注册
nacos客户端也就是我们的项目微服务,首先我们就是加入nacos的服务中心的pom依赖
<!--nacos 服务注册中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2021.1</version>
</dependency>
然后去启动类加入开启nacos服务中心的注解
@EnableDiscoveryClient
然后去application.yml配置nacos , 千万注意application.name 值不能带 下划线'_' , 否则ribbon负载均衡报会找不见服务异常!
server:
port: 10001 #微服务端口号
spring:
application:
name: xxx-goods #微服务名称,也是注册到nacos的服务名称
cloud:
nacos:
discovery:
server-addr: ***.**.239.99:10010 # nacos ip地址:端口号
username: nacos #nacos的账号
password: nacos #nacos的密码
我们启动该微服务,然后去nacos的服务端的服务列表看一下就会出现我们启动的该微服务信息了
负载均衡
这里配置的负载均衡是配置在服务消费端的,也就是如果你order访问goods,那下面的配置就需要配置在order服务中,然后再去请求goods如果goods注册了多个实例那就是进行负载均衡了,注意如果你goods去请求order那你goods里面也需要进行以下配置;
nacos并没有负载均衡策略,而是依据ribbon组件来实现的,所以我们需要先将ribbon的负载均衡的maven地址给注入到pom文件中,如果你springcloud使用的2020.x以下的版本也就是伦敦地铁站命名版本的话那你就不需要导入loadbalancer了,因为在2020.x以上版本springcloud移除了ribbon组件!
<!--ribbon 负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
轮询
当我们相同的服务启动了多个实例,那nacos就是使用默认的轮询负载均衡策略来进行管理我们的服务请求,接下来就让我们测试一下吧
我们将同一个项目进行修改启动参数然后让他生成三个不同的实例,然后启动
然后我们去nacos服务端看一下是否三个服务都注册成功 , 可以看见3个goods服务实例都已经注册到服务中心,同时我们也启动了一个消费者,然后我们使用消费者order服务去请求goods服务
我们请求 http://127.0.0.1:10002/order/getGoodsId , 然后就会返回一个goodsID和服务提供者的一个实例端口号,效果如下
可以发现,nacos是默认以轮询的方式在在请求我们的服务提供者(goods)服务;
权重
但是在项目开发中我们会经常发现,不是每一台服务器的配置都是一致的,有些服务器性能较好,有些服务器性能较差,那我们肯定希望性能好的服务器可以多承担一些请求,而服务器较差的少承担一些请求,所以我们可以使用ribbon的框架去集成nacos的权重算法来自定义一个负载均衡实现类,首先我们先在项目中创建一个配置类 NacosWeightLoadBalancerRule
然后我们把pom中的loadbalancer(ribbon负载均衡)依赖换成ribbon依赖,因为使用loadbalancer不能通过rule接口去自定义负载均衡端口,而且流程比较繁琐,所以我们这里使用ribbon的rule接口去实现nacos的权重负载均衡!
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
然后我们在类中继承AbstractLoadBalancerRule类,并重写他里面的choose方法,AbstractLoadBalancerRule类就是ribbon中每个负载均衡策略实现类的父类,可以看见除了我们自定义的,差不多有10个负载均衡规则,比如ribbon默认的RoundRobinRule(轮询规则),还有RandomRule(随机规则)等等
重写choose方法
public class NacosWeightLoadBalancerRule extends AbstractLoadBalancerRule {
@Resource
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object o) {
//1.获取服务名字
BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();//获取到我们url中服务名字
String serviceName = loadBalancer.getName();
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();//获取服务中心的地址 端口 还有当前服务的命令空间,其实就是去读配置文件去了,
try {
Instance instance = namingService.selectOneHealthyInstance(serviceName);//根据ribbon的权重算法筛选出一个服务地址
return new Server(instance.getIp(),instance.getPort()); //然后把那个服务地址给返回回去
} catch (NacosException e) {
e.printStackTrace();
}
return null;
}
}
然后我们再将这个配置类加载到springboot容器中,在启动类中加入以下代码
@Bean
public IRule ribbonRule() {
return new NacosWeightLoadBalancerRule();
}
然后我们重启项目就会发现我们写的权重负载均衡策略已经生效了,然后我们修改一个goods每个实例的权重值,然后让我们测试一下看一下效果
可以看出来,10001被请求的次数最多,而10004已经很少被请求了,那我们也就成功通过ribbon实现了根据权重去进行负载均衡了!
随机规则
我为什么这里要写一下随机规则呢,因为上述我讲了ribbon有自带的好多负载均衡机制,那我们如何去使用了,那我这里就使用ribbon自带的随机策略来进行一个演示,很简单,只需要在我们的启动类中增加以下代码即可
然后重启项目,你就会发现你请求其他服务都会是随机负载均衡策略了
Nacos成为注册配置中心配置
使用场景 :
通常我们配置文件都在项目中的,但是这样子有一些弊端,比如生产环境中的mysql账号密码以及redis密码都会在项目的配置文件中记录,那每一个开发者都是可见的,安全性会下降,还有就是比如我们会或多或少的在项目运行中开启一些功能,那我们就必须先把项目给停止下来然后修改一个配置中的开启变量然后再启动,这样子就会很麻烦而且也很不合理,这时候nacos就可以完美解决以上问题,把我们的配置文件都上传到配置中心,然后只需要在项目中配置nacos配置中心的一些连接参数即可,然后当我们项目启动的时候就会去nacos配置中心拉取配置进行启动,并当我们修改配置中心的配置的时候也可以同步到我们的项目中,所以当我们修改一些配置中的参数的时候就不需要停止项目了!
首先我们需要导入nacos-config的pom依赖
<!--nacos 服务配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2021.1</version>
</dependency>
同时我们也需要导入spring-cloud-starter-bootstrap的一个依赖,因为我们需要创建一个bootstrap.yml的配置文件来添加连接到配置中心的配置属性来加载外部配置中心的配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
那我们就可以新建一个bootstrap.yml的文件(和application.yml同级目录),然后将spring.cloud.nacos.config级别下的配置写到bootstrap.yml,还有一个就是spring.application.name和profiles.active 配置也需要写到bootstrap.yml中,那bootstrap.yml配置就如下
剩下的配置就还是放在application.yml中即可
然后我们打开nacos的服务端,进入配置列表,然后点击创建配置
编辑完成以后就可以点击发布了,然后我们去测试一下,在项目中编写以下代码,就是使用@Value去获取配置文件中的变量
然后启动项目,其实我们在启动项目的时候也可以看见我们的项目在启动的时候就会订阅我们配置中心配置
可以看见我们项目订阅了三种结构dataid的配置文件,他们三个有不同的用处
- xxx-goods.yaml+DEFAULT_GROUP 这个结构的配置文件他不会区分环境,也就是test环境也好还是dev环境也好都会共享该配置
- xxx-goods-dev.yaml+DEFAULT_GROUP 这个结构的配置文件他会区分环境,也就是只有dev环境才会读取该配置,也就是dev独有的配置,刚才我们演示的就是使用的该dataid结构
- xxx-goods+DEFAULT_GROUP 这个结构的配置文件他没有声明文件类型,他也会尝试着去读取一下
也就是说如果我们dev(开发)环境和(pro)线上环境用的同一个mysql不同redis,那你就可以把mysql配置写在xxx-goods.yaml中,然后把redis配置分别写在xxx-goods-dev.yaml配置和xxx-goods-pro.yaml配置中,一般这种用于spring-boot项目比较合适,如果是spring-cloud项目的话,每个环境配置文件都放在一块肯定很繁琐,一般会使用空间命名功能,下面我们也会讲到!
然后我们请求一下findgoods接口
可以看见,配置在nacos配置中心的data.goodsId这个参数生效了,当你实时修改配置中心参数的时候,项目也会同步过来的,比如我们把配置中心的data.goodsId 修改成 999999
点击发布以后,看我们的项目控制台就监听到了
因为我们在service中添加了@RefreshScope注解所以,service中的goodsId也会进行更新,那我们在请求一下findGoods接口看一下
命名空间
使用场景
通常我们开发一个项目会包含很多环境,比如说我们通常会有开发环境,测试环境还有生产环境,那这么多的环境我们可以使用一个nacos去管理么,那答案是肯定的,那就可以使用我们的命名空间对这些不同环境的服务进行隔离了,具体操作如下.
我们现在nacos服务端创建三个个命名空间,对应着dev(开发环境),test(测试环境),pro(生成环境)
然后我们把每个环境自动生成的命名空间id记录下来并填到我们对应环境的配置文件中,这个命名空间id也可以自定义,不填的话就会自动生成.
上面是修改的application.yml中discovery的配置,当我们服务启动以后他会根据不同的命名空间id注册到不同的命名空间中,我们可以启动看一下,我们根据active环境变量启动goods同项目不同的三个实例
然后我们看一下nacos服务端,会发现每个环境他会根据不同的命名空间id注册到对应的命名空间中,并且命名空间他是相互隔离的,就是不在同一空间内的服务是不能相互调用的,
以上是使用命名空间对环境进行隔离,可以让我们轻松的使用同一个nacos管理多个环境的服务,那同时我们的配置也是可以这样子做的,简单操作就是在bootstrap.yml的nacos.config下新增namespace参数
然后当我们启动项目的时候 , 项目就会根据配置的命名空间id去nacos的配置中心中对应的命名空间中查找配置
这样子配置和注册服务都就形成了隔离,你也可以只对注册服务进行隔离而配置中心还是进行耦合,也就是别再bootstrap.yml中填namespace参数,然后把配置都扔到nacos的public命名空间中,那所有的环境都会去public命名空间中读取!
这里还要提一个参数,就是shared-configs , 他的作用就是可以读取自己命名空间中任意的配置,一般用来读取一些共享配置,比如我们同一命名空间中的所有服务都使用的同一个redis,那就可以把redis的配置单独出来,然后再每个服务配置读取
那算下来,我们配置文件这么多,如果不同配置文件中配置了项目内容 , 那优先级是什么呢,如下
xxx-goods-dev.yaml > xxx-goods.yaml > shared-configs(共享配置) > 本地application.yml配置 > 本地 bootstrap.yml
其实可以分析出来这些配置都是依据覆盖来实现的,最先读取的配置也就优先级最低,因为他会被后续的同配置进行覆盖操作!
总结 : 当我们一个服务在不同环境要需要不同的配置那就需要把这一部分配置写到xxx-goods-dev.yaml中,应为这个配置只有dev环境的xxx-goods服务才会读取,而如果你有部分配置是同一服务在所有环境都通用那你就把这部分配置写到xxx-goods.yaml,因为这个配置文件只要是xxx-goods服务他都会读取不会区分环境,那如果你有一部分配置是全命名空间通用的那你就写在一个共享配置中,然后使用shared-config进行引入即可!
Nacos集群搭建
nacos集群主要作用就是保证nacos的高可用,每个nacos实例之间会进行数据同步,然后我们启动多个nacos实例然后通过nginx反向代理帮我们去请求其中一个nacos实例,具体部署操作如下 :
首先我们先来改一下nacos的集群配置
进入nacos配置文件夹
cd /usr/nacos/conf
默认的集群配置文件是一个副本,我们复制一个副本出来并重命名为cluster.conf
cp /usr/nacos/conf/cluster.conf.example /usr/nacos/conf/cluster.conf
编辑集群配置文件
vim cluster.conf
切记 : 如果你搭建的nacos集群在一台服务器上,那端口号不可以连续,因为在上述构建单机的时候说过,nacos2.x版本之后会进行gRPC进行长连接来保存服务同步,而这个gRPC端口是根据你每个nacos实例的端口+1000和+1001来算的,如果当你第一个nacos实例的端口号是10010那你这台实例就会占用10010和11010和11011 这三个端口号, 然后你在启动的第二个nacos实例的端口号是10011,那他占用的端口号就是10011和11011和11012了,发现没这两个实例都用到了11011端口号,所以第二个实例肯定会启动失败,错误原因就是11011端口号被占用无法绑定!如果你使用的nacos1.x的话可以忽略,因为1.0x没有用到grpc;
在末尾新增我们要准备部署的集群各实例ip地址:端口号 , 然后保存!
如果你服务器内存地址不是很大的话推荐你在nacos的启动脚本中修改一下nacos jvm的内存占用,
cd /usr/nacos/bin
vim startup.sh
- Xms : 堆内存初始大小
- Xmx : 堆内存最大扩展内存
- Xmn : 堆内新生代的大小
修改完以后,然后我们在将nacos实例复制两份出来
cp -r /usr/nacos /usr/nacos-10015/
cp -r /usr/nacos /usr/nacos-10020/
并修改每个实例中的端口号,修改第一个实例
vim /usr/nacos/conf/application.properties
修改第二个实例
vim /usr/nacos-10015/conf/application.properties
修改第三个实例
vim /usr/nacos-10020/conf/application.properties
然后我们分别启动这三个实例
启动第一个实例
cd /usr/nacos/bin
#如果你配置文件中配置了数据库,那就使用以下启动命令
./startup.sh
#如果你没配置数据库要使用内置数据库的话用以下启动命令
sh startup.sh -p embedded
启动第二个实例
cd /usr/nacos-10015/bin
#如果你配置文件中配置了数据库,那就使用以下启动命令
./startup.sh
#如果你没配置数据库要使用内置数据库的话用以下启动命令
sh startup.sh -p embedded
启动第三个实例
cd /usr/nacos-10020/bin
#如果你配置文件中配置了数据库,那就使用以下启动命令
./startup.sh
#如果你没配置数据库要使用内置数据库的话用以下启动命令
sh startup.sh -p embedded
然后我们使用浏览器分别访问这三个实例,查看这三个实例是否都正常启动了
都确保每个实例都启动成功以后我们在使用nginx对其集群进行反向代理,让nginx监听一个端口号,然后我们只需要访问这个端口号然后nginx就会将该请求随机转发到这三个其中一台nacos实例,如果请求错误那他就会更换实例继续请求直到请求成功,这样子我们就可以通过nacos集群完成了高可用,就算其中一个实例宕机了也无大碍!
如果大家服务器中没有安装nginx的话可以看我这篇文章Linux系列 CentOS7(yum)安装nginx,特别注意该文章最后的stream模块必须安装哦,因为我们就是基于nginx的该模块对nacos进行请求随机转发的!
首先我们先修改nginx的配置文件,一般如果你使用yum安装的话nginx的配置文件路径为 /etc/nginx/nginx.conf
vim /etc/nginx/nginx.conf
切记 : stream和http是同级的,千万别把stream流放到http域中,你可以直接划到nginx.conf的末尾进行编写
然后我们校验一下nginx的配置文件的正确性
nginx -t
如果报错 "stream" directive is not allowed here in 的话那就是你的stream位置写错了,一定要和http是同级!
校验通过以后重启nginx
systemctl restart nginx
测试
请求 : ip:100018/nacos
发现已经代理转发成功了,我们去看一下nginx的stream日志
我们前端请求的10018端口,实际到了服务器nginx都会随机转发到10015 10010 10020端口上去进行请求;那我们这做的目的就是为了nacos的高可用性,那如果我把10015和10020端口的两个实例都关掉,他会请求成功么
测试集群高可用
关闭第一个实例
cd /usr/nacos-10015/bin
./shutdown.sh
关闭第二个实例
cd /usr/nacos-10020/bin
./shutdown.sh
可以看见我们已经成功关闭了10015和10020实例,然后我们在多次请求 ip:10018/nacos 地址看看情况
发现,请求没有任何问题,然后我们在看一下stream日志
发现,nginx请求随机到10015端口的时候他会在次进行转发,直到转发到可用的10010端口!那nacos的高可用集群也就完美搭建完成了!
小结
原本想着在本章节进行nacos各个功能的原理进行一个分析的,但是感觉文章已经够大了,为了文章便于阅读所以准备将原理专门写成一个章节,毕竟我们每学习使用一个开源框架都要去了解以及理解他底层的原理实现,不仅仅是为了面试,当我们理解了一个框架的原理实现就算工作中遇见问题,我们也可以依据他的运行方式快速的发现发生错误的原因,以及我们更好的去处理错误!
文章由本人亲手编写,如果发现错误请评论区指正,或者有什么疑惑也可以评论区留言,我会及时回复帮住大家的!