重学SpringCloud系列五之服务注册与发现---中

x33g5p2x  于2022-02-07 转载在 Spring  
字(24.3k)|赞(0)|评价(0)|浏览(794)

BUS消息总线

bus消息总线简介

在我前面的系列已经为大家介绍了Spring Cloud Config,可以实现将分布式微服务的配置进行集中管理。但是,就目前而言,我们目前仅限于在应用程序启动时更新此类配置。将属性的新值推送到Git之后,我们将需要手动重新启动每个应用程序进程以获得新值,或者通过手动发送“/actuator/refresh”请求的方式实现配置刷新。如果我们想要实现应用配置的热更新,单纯依靠Spring Cloud Config就无能为力了,那就需要结合我们本节开始为大家讲的Spring Cloud Bus才能够实现。

Spring Cloud Bus简介

Spring Cloud Bus将分布式系统的微服务通过轻量级的消息中间件连接起来,通过消息中间件广播状态更改(例如配置更改)或其他管理指令。总线就像是横向扩展的Spring Boot应用程序的分布式的actuator,它也可以用作微服务之间的通信渠道。

  • 消息总线的核心工作逻辑:将收到的消息(事件)批量分发到所有或部分的微服务节点应用,微服务应用在接到消息之后做出相应的业务处理相应。
  • Spring Cloud Bus不仅可以分发配置刷新消息(事件),还可以应用到整个微服务系统中的其他业务
  • Spring Cloud Bus是基于消息队列产品实现的,从官网来看目前支持的消息队列有两种:rabbitMQ和kafka
  • Spring Cloud Bus的消息分发机制,是基于消息队列的:发布/订阅模式实现的。

Spring Cloud Bus与Config实现应用配置热加载原理

  • 如果消息总线收到并分发的消息(事件)是配置刷新的消息,微服务收到该消息之后就会主动的去config server加载最新的配置。从而达到微服务配置热更新的效果(如下图所示)
  • Bus是通过eureka找到配置分发刷新目标服务的,为了降低核心架构图复杂度,下图没有体现eureka
  • 第1步发送“/bus/refresh”请求,可以在git仓库代码修改后由git仓库webhook自动执行。但基于之前系列已经说过的原因,这样做的可操作性不强。不建议使用。

笔者要说明的是:目前互联网上的很多关于Spring Cloud Bus的架构图都有一个错误,那就是“/bus/refresh”数据刷新请求是由config server接收的。实际这种理解是错误的,接收“/bus/refresh”请求的是Spring Cloud Bus。比如下面的这张图就是错误的。

在Spring Boot2.0中“/bus/refresh”服务端点不再被开放,而是使用“/actuator/bus-refresh”代替。需要结合Spring Cloud Bus与spring-boot-starter-actuator一起使用。

上面这种图之所以会画错,我猜可能是因为这个原因:我们在使用Spring cloud微服务架构的时候,为了降低微服务组件的复杂度,Spring cloud Bus和Spring CLoud Config通常是一起使用的,也就是说将Spring Cloud Bus是集成到Spring Cloud Config Server里面的,使用同一个JVM进程实例。导致Config Server看上去就是Spring Cloud Bus,二者是物理集成,逻辑分离。

架构图如下所示:

docker安装rabbitMQ

这里注意获取镜像的时候要获取management版本的,不要获取last版本的,management版本的才带有管理界面。
本文使用docker快速安装部署一个RabbitMQ镜像,只是为了方便学习,不适用于生产。RabbitMQ等消息中间件知识不是本文核心内容,后文我们结合RabbitMQ单节点来为大家讲解Spring Cloud Bus。所以本节我们需要先安装RabbitMQ

拉取docker镜像

前提是你的服务器已经成功安装了docker。

docker pull rabbitmq:management

运行镜像

使用rabbitMQ镜像运行一个实例,暴露5672和15672端口。

docker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:management

看到如下结果,容器启动成功了:

可以用docker ps查看正在运行的docker 容器

开放端口。因为使用到了5672和15672端口对外提供服务,所以需要在防火墙上开放端口(以CentOS7防火墙为例)

firewall-cmd --zone=public --add-port=5672/tcp --permanent;
firewall-cmd --zone=public --add-port=15672/tcp --permanent;
firewall-cmd --reload

访问管理界面

访问管理界面的地址就是 http://[宿主机IP]:15672,可以使用默认的账户登录,用户名和密码都guest,如:

到这里就完成安装部署了。

基于rabbitMQ的消息总线

config server集成Bus

通过maven坐标引入必须依赖

<!--添加基于RabbitMQ的Spring Cloud Bus的支持 -->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<!--在Spring Boot2.0版本需要actuator提供刷新请求接口:“/actuator/bus-refresh” -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

加上如下配置:指定RabbitMQ消息中间件的服务地址。因为我们的Spring Cloud Bus作为消息发布者向rabbitMQ中放入“配置刷新”消息,所以需要rabbit的地址及认证信息。

spring:
    rabbitmq:
      host: 192.168.128.1
      port: 5672
      username: guest
      password: guest

加上如下配置:暴露配置刷新服务端点。

笔者在使用当前版本做实验的时候单独暴露bus-refresh是不生效的(访问结果是405),笔者在之前版本实验过是生效的(下文代码注释掉的部分)。找不到任何理由,我猜想可能是版本bug,所以我是用的是include:"*"来完成任务。虽然开放了actuator的所有服务端点,但是我们加上Spring security认证也提升安全性。

如果你的config server和我一样引入了spring-boot-starter-security,把下面的代码配置加入。否则通过“/actuator/bus-refresh”访问将会报错:401,没有权限访问。

@Configuration
public class ConfigServerWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 都是无状态请求,不需要session,节省资源
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
        //关闭csrf跨站请求防御
        http.csrf().disable();
        // 所有的请求必须登录认证后才能访问
        http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
    }
}

集成结果认证

登录RabbitMQ管理界面,有一个channel表示我们已经成功集成rabbitMQ

显示如下topic,表示我们成功集成了Spring Cloud Bus

PostMan访问:“http://localhost:8771/actuator/bus-refresh” ,响应结果为204(虽然暂时我们没有RabbitMQ的消息接收端,但是不影响请求测试)

bus实现批量配置刷新

前提

之前的章节,我们先后为大家介绍了netflix公司为Spring Cloud社区贡献的一系列技术方案:

  • eureka实现服务注册中心
  • spring cloud config 为我们提供了配置集中管理的能力
  • spring cloud bus为我们提供了配置属性在全量实例或者部分实例刷新的能力(我们还没实验,本节就来实验)

所以在正确的完成了eureka client相关配置,config client相关配置,我们的微服务想具备spring cloud bus提供的配置刷新能力,需要做如下的配置

为微服务集成Bus提供的配置刷新能力

比如:dhy-service-rbac和dhy-service-sms,集成Bus提供的配置刷新能力

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

加上RabbitMQ消息队列的配置(加到git仓库对应微服务的配置文件中)

spring:
    rabbitmq:
      host: 192.168.161.3
      port: 5672
      username: guest
      password: guest

测试方法

之前是访问单节点http://localhost:8401/actuator/refresh进行单节点配置刷新,现在访问Bushttp://localhost:8771/actuator/bus-refresh进行所有实例的配置刷新。

  • 先启动多个dhy-service-rbac实例
  • 然后修改配置
  • 之后请求Bus的http://localhost:8771/actuator/bus-refresh进行配置刷新
  • 最后看看配置刷新后的配置是不是已经热更新。(不需要重启)

局部刷新方式

之前我们讲的/actuator/bus-refresh是刷新所有在eureka上注册,并且连接了config server微服务实例的配置。假如我们希望局部刷新实例需要使用:/actuator/bus-refresh/{destination},这样消息总线上的微服务实例就会根据destination参数的值来判断某个微服务实例是否需要刷新。

  • /actuator/bus-refresh/aservice-rbac:8401表示启动端口为8401的aservice-rbac微服务实例的配置将被刷新。
  • /actuator/bus-refresh/aservice-rbac:**,这样就可以触发aservice-rbac微服务所有实例的配置刷新。

这种刷新方式虽然提供了一定的灵活性,但远远达不到灰度发布的要求。所以Spring Cloud Config整体上还是有一定的局限性。
默认情况下,ApplicationContext IDspring.application.name:server.port,详见org.springframework.boot.context.ContextIdApplicationContextInitializer.getApplicationId(ConfigurableEnvironment)方法。

alibaba-nacos

nacos介绍与单机部署

本章我们开始为大家介绍nacos,nacos是alibaba开发的是用于微服务管理的平台,其核心功能是服务注册与发现、集中配置管理。有了之前的一系列相关的技术的学习,nacos学习起来还是非常简单的,使用也很简单。

  • Nacos作为服务注册发现组件,可以替换Spring Cloud应用中传统的服务注册与发现组件,如:Eureka、consul等,支持服务的健康检查。
  • Nacos作为服务配置中心,可以替换apollo、Spring Cloud Config和Bus。

当然Nacos作为一个微服务管理平台,除了面向spring Cloud,还支持很多其他的微服务基础设施,如:docker、dubbo、kubernetes等。除了核心的服务注册与发现和配置管理功能,还提供了各种服务管理的功能特性,如:动态DNS、服务元数据管理等。

官方资料

Nacos单机部署

虽然nacos现在已经升级到2.0版本,但是部署方式没有发生变化.本文使用的1.0版本的安装部署方式仍然适用。

Nacos支持单点部署的模式,搭建过程非常简单,实际上nacos的standalone模式没有所谓的安装过程,就是下载和启动。但是这种情况没有高可用支持,所以只适合测试或学习使用。

下载解压

首先去nacos的github地址下载release安装包。当然你也可以自己下载源码之后进行编译打包,nacos是使用java开发的,使用maven进行编译打包。这里我们就不自己打包了,使用release安装包。下载地址是:https://github.com/alibaba/nacos/releases。在linux系统下可以使用如下的命令下载和解压缩。

#下载nacos
wget https://github.com/alibaba/nacos/releases/download/1.2.1/nacos-server-1.2.1.tar.gz;
# 解压nacos
tar -xvf nacos-server-1.2.1.tar.gz

启动服务

进入到nacos/bin目录下面,startup命令用于启动nacos,shutdown命令用于停掉nacos。

  • 如果你是linux/unix系统,使用sh startup.sh -m standalone脚本启动方式。
  • 如果你是windows系统,双击startup.cmd启动nacos。

nacos的默认服务端口是8848,启动完成之后通过浏览器访问nacos:http://ip:8848/nacos/。看到如下界面,需要登陆,默认的用户名密码都是nacos,登陆之后看到如下界面:

如果你访问不到上面的界面,请检查你部署的主机操作系统的防火墙设置。以下是为CentOS7系统防火墙开放8848端口的命令,其他系统请自行解决。

firewall-cmd --zone=public --add-port=8848/tcp --permanent    
firewall-cmd --reload

如果仍然访问不到,可能是服务器多网卡导致的。可以参考下一节“集群安装”中的处理方法。

nacos的单机standalone模式安装对于新人可以说是非常的友好,几乎不需要的更多的操作就可以搭建nacos单节点,方便大家学习使用。另外,单机standalone模式安装默认是使用了nacos应用本身的嵌入式数据库apache derby,我们可以使用MySQL替换它。使用外置MySQL数据库,和“集群安装”对于数据库的要求是一样的,我们下节再讲。

nacos集群部署方式(linux)

安装nacos集群

我准备了三台服务器(虚拟机),192.168.161.3、192.168.161.4、192.168.161.5。在三台服务器上分别下载、解压nacos安装包,并在防火墙开放8848端口(参考单机部署standalone部署的模式的操作)。然后我们开始nacos集群模式的安装。

MySQL数据库初始化
  • 在生产环境中MySQL至少是主备模式,或者采用高可用模式。
  • 为了将服务注册及配置相关的数据持久化存储,nacos实例之间共享数据并且方便查看,集群安装模式把数据存储到Mysql数据库里面。
  • 建立Mysql数据库实例,并执行sql源文件是在nacos/conf解压目录下面的nacos-mysql.sql文件。sql语句源文件。
    数据库db、用户需要自己去创建,nacos-mysql.sql文件只有建表语句和初始化用户nacos的INSERT语句。
在nacos/conf/application.properties中增加mysql配置

在192.168.161.3、192.168.161.4、192.168.161.5都要配置。

spring.datasource.platform=mysql

db.num=1
db.url.0=jdbc:mysql://192.168.161.3:3306/nacosdb?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=test
db.password=
  • spring.datasource.platform说明支持数据持久化的数据库类型,已知目前只支持mysql
  • db.num数据库实例的数量,我们实验环境只有一个mysql数据库实例,所以是1
  • db.url.0表示第一个mysql数据库的jdbc url连接。如果还有第二个、第三个,请增加db.url.n中的n。
在nacos/conf/cluster.conf中加入ip配置

在192.168.161.3、192.168.161.4、192.168.161.5都要配置。如果没有这个文件,就新建一个。

#ip:port
192.168.161.3:8848
192.168.161.4:8848
192.168.161.5:8848
启动服务

在192.168.161.3、192.168.161.4、192.168.161.5执行nacos/bin/startup.sh,在三台服务器上分别启动nacos。正常情况下,我们的集群节点就安装成功了。三个节点都可以提供服务,并且三个节点之间可以通信。通过浏览器访问nacos:http://ip:8848/nacos/可以正常的访问nacos服务。
注意:启动单机模式的用的是sh startup.sh -m standalone,而启动集群模式不需要-m standalone参数。

多网卡Ip选择问题

正常情况下,上面的步骤就可以安装成功了,三台服务器上的nacos可以对外提供服务,彼此间可以通信。但是,通常在生产环境下的服务器是多网卡的,nacos该如何正确的绑定网卡?

其网卡的选择配置和Spring Cloud系列配置是差不多的,可以参考《eureka集群多网卡环境ip设置》学习,但有些许的不同,下文会讲到。

查看我的服务器网卡:

使用ip addr命令查看linux主机的网卡。

  • 第一个lo网络ip是回路ip,127.0.0.1,这个是标配
  • 第二个enp0s3网络设备,ip是10.0.2.15,这个是因为我的服务器网络使用了双网卡:桥接+HostOnly模式。正常的服务器是没有的。
  • 第三个enp0s8就是本服务器真正的使用到提供服务的网络ip,如:192.168.161.4。
  • 第四个网络设备是因为我在这台虚拟机上安装过docker,所以有一个docker0的网络设备。

因为较多的网络设备,导致我在后续安装过程出现问题。这里先卖个关子。

问题: 解决网卡获取的不是我们希望绑定的网卡的问题:当我们配置完成之后,使用startup.sh命令启动。发现集群节点列表中并没有任何记录。而且后台服务日志报错,内容如下:

解决方案一(较新的nacos版本):

针对我的网卡和期望ip,我的application.properties配置如下:

nacos.inetutils.ip-address=192.168.161.5

解决方案二(较旧的nacos版本):

针对我的网卡和期望ip,我的application.properties增加配置如下:

期望被忽略掉的网卡名称

nacos.inetutils.ignored-interfaces[0]=enp0s3
nacos.inetutils.ignored-interfaces[1]=docker0

期望匹配的网卡ip前缀

nacos.inetutils.preferred-networks[0]=192.168.161.

需要注意的是这里的inetutils配置的前缀是nacos,不是我们之前学过的spring.cloud,这点区别要注意,别配置错了

当以上工作都完成之后,我们通过浏览器分别访问nacos服务,看到如下界面。集群管理的节点列表里面已经有三各节点,ip分别是192.168.161.3:8848、192.168.161.4:8848、192.168.161.5:8848。

nacos集群架构

在完成nacos集群的配置之后,我们可以通过三个入口分别访问集群内的nacos服务,那下面的问题就是如何将三个入口转成一个入口,实现nacos server对外服务的负载均衡和高可用。

方案一

目前许多个人开发者写的博客或教程中的方法就是在三个nacos服务的前端加一个负载均衡器,如:nginx、haproxy。然后号称是生产级别的搭建方法,但这种方法是绝对不能用于生产的,因为你的nginx和haproxy是单点,一旦nginx挂了,整个服务就挂了。

方案二

nacos官网推荐的方法是使用虚拟ip的方法,如下:

  • 最开始虚拟ip192.168.161.6可能与192.168.161.3的主机绑定在一起,通过这两个ip都可以访问192.168.161.3主机的nacos服务。
  • 一旦192.168.161.3主机宕机或者其他网络故障,192.168.161.6会自动切换到与192.168.161.4或者192.168.161.5主机绑定在一起。这个过程被叫做虚拟ip的漂移。

这种虚拟ip的方法问题就是没有使用到负载均衡,访问的仍然是某一个节点的nacos服务,只不过形成了主从备份,提供了高可用。对于微服务量级不大的,这种就已经完全够用了。

方案三(我的方案)

那既可以提供高可用,又可以提供负载均衡的办法,可能有的朋友已经想到了,如下图:

  • 在nacos服务的前端加上nginx或者haproxy的负载均衡器(多节点)
  • 然后对负载均衡器的部署服务器使用虚拟ip,即:通过keepalived实现虚拟ip的漂移
  • 用户访问负载均衡器实现对nacos服务的访问,主nginx挂掉,虚拟ip漂移到从nginx负载均衡提供服务
  • 任何一个nginx负载均衡都可以将请求负载,均衡的分流到nacos实例上面

nacos集群(虚拟ip漂移)

我们就拿官网中推荐的方法(方案二),使用虚拟ip访问nacos集群的方式做个例子讲解一下。为什么不讲第三种?一般系统架构水平到了的人听懂这种方式就知道第三种方式怎么做,水平不到的人听了第三种仍然还是不懂。

安装配置keepalived

在三台服务器上分别安装keepalived

yum install -y keepalived

在三台服务器上分别修改/etc/keepalived/keepalived.conf

vrrp_instance VI_1 {
    state MASTER
    interface enp0s8
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 123456
    }
    virtual_ipaddress {
        192.168.161.6
    }
}
  • 一台服务器是MASTER主节点,其他的服务器为BACKUP节点
  • interface 配置为我们刚刚查看的网卡的名称
  • virtual_router_id 必须一致,表示这三台服务器抢用一个虚拟ip。
  • 修改priority优先级,三台服务器要不一样,比如:100、101、102,优先级最高的优先使用虚拟ip。MASTER的优先级一定要高于BACKUP主机
  • advert_int 是几台服务器之间的同步检查时间,1秒
  • authentication 的设置必须一致,这样这几台服务器才能通信
  • 修改virtual_ipaddress为三台服务器所在网段内未被占用的IP地址,比如:192.168.161.6
修改防火墙

CentOS7必须开放防火墙配置,否则三台主机无法就虚拟ip的使用优先级通信,将都是MASTER,都配置虚拟ip。

firewall-cmd --direct --permanent --add-rule ipv4 filter INPUT 0  --protocol vrrp -j ACCEPT;
firewall-cmd --reload;
启动keepalived服务
sudo systemctl restart keepalived.service

这时我们通过http://192.168.161.6:8848/nacos,访问的服务实际上是keepalived的MASTER节点192.168.161.3。如果我们把161.3的服务器停了,161.6的ip就漂移到161.4服务器上(按优先级)。总之可以保证192.168.161.6虚拟VIP提供的服务一直是可用的,除非nacos所有服务器节点都挂了。

nacos服务注册与发现

本节就为大家介绍如何使用nacos作为服务注册中心。

也就是说我们使用Ribbon、OpenFeign作为远程调用的基础组件,然后将eureka服务注册中心替换为nacos。

父项目依赖更换

因为nacos属于Spring Cloud Alibaba成员,为了规范相关版本与Spring Cloud、Spring Boot版本之间的兼容性,我们在父项目pom文件中引入spring-cloud-alibaba-dependencies
dependencyManagement的作用多次讲过了,通过dependencyManagement管理的dependency通常是多个子项目的父项目,我们通过import其pom信息,从而进行其子项目的版本号管理。一个父项目带多个子项目,父项目规定了子项目的版本号,从而个子项目之间的兼容性会更好。

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.5.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
         <!-- 在父项目中加入alibaba项目版本管理依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.2.1.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

在dependencyManagement中我们维护了三个核心依赖项目的版本,这个版本号之间关系是Spring Cloud Alibaba官方推荐的,能够体现更好的兼容性。spring-cloud-alibaba与spring-cloud和spring-boot之间的版本说明

Spring Cloud VersionSpring Cloud Alibaba VersionSpring Boot Version
Spring Cloud Hoxton.SR32.2.1.RELEASE2.2.5.RELEASE
Spring Cloud Hoxton.RELEASE2.2.0.RELEASE2.2.X.RELEASE
Spring Cloud Greenwich2.1.1.RELEASE2.1.X.RELEASE
Spring Cloud Finchley2.0.1.RELEASE2.0.X.RELEASE
Spring Cloud Edgware1.5.1.RELEASE1.5.X.RELEASE

微服务整合nacos服务发现

  • spring-cloud-starter-alibaba-nacos-discovery是spring-cloud-alibaba-dependencies子项目。所以它们的版本号都不需要我们手动维护,继承自父项目dependencyManagement中的定义。
  • 因为我们之前使用了eureka,所以用nacos的spring-cloud-starter-alibaba-nacos-discovery将spring-cloud-starter-netflix-eureka-client在pom文件中替换掉
  • spring-cloud-starter-alibaba-nacos-discovery也默认包含了spring-cloud-starter-netflix-ribbon,不需要单独引入。我们之前学习的所有的ribbion和openfeign相关的负载均衡、远程服务调用的知识在nacos下依然适用。
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

在application.yml中加入必要的服务注册中心信息配置我们上一篇文章中搭建的nacos的服务注册中心的地192.168.161.6:8848。(删掉eureka配置)

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.161.6:8848

加上EnableDiscoveryClient注解(去掉EnableEurekaClient注解),开启Spring Cloud的服务注册与发现功能。

在aservice-sms服务提供者和aservice-rbac服务调用者中将以上的三个动作完成,我们就成功的将eureka客户端替换为nacos-discovert客户端了。我们访问nacos服务,通过服务管理->服务列表,看到我们注册的服务已经在列表中。

结果验证

服务注册成功了,剩下的就是服务之间实现远程调用的实现及测试了。Ribbon和OpenFeign现在已经做到了服务注册中心的无关性,也就是说:不论你用nacos、zookeeper、consul、eureka哪一个服务注册中心,OpenFeign和Ribbon的使用方式几乎都没有区别。
需要注意的是:我们使用openfeign进行远程服务调用,服务名称应该是和spring.applicatopn.name定义一致的。(这点与Eureka不同,eureka是将服务名称转成大写字母)

//@FeignClient("ASERVICE-SMS")
@FeignClient("aservice-sms")
public interface SmsService {

  @PostMapping(value = "/sms/send")
  AjaxResponse send(@RequestParam("phoneNo") String phoneNo,
                    @RequestParam("content") String content);

}

nacos服务注册中心详解

查看服务列表

首先从最简单的信息开始看起:服务列表中aservice-rbac启动了一个实例,aservice-sms启动两个实例。并且所有实例都通过了nacos服务注册中心的健康检查。点击“详情”可以查看服务状态,以及服务所有启动实例的状态。

集群的划分

某些大型企业,为了满足异地容灾的需要,通常将应用部署在不同的机房。不同机房的服务器组通常被称为一个“集群”。但是nacos的集群与apollo(系列六会讲)有所不同:

  • apollo的集群信息是需要我们手动添加的,一个微服务在A集群使用一套配置,在B集群使用另一套配置。
  • 而nacos集群理论上指的是nacos server所组成的集群。通过Nacos-Sync将服务信息双向同步。

对于注册在nacos server(集群A)上面的微服务实例,就是集群A的实例。同理,注册在nacos server (集群B)上面的微服务实例就是集群B的实例。所以nacos的“集群”信息是不需要我们手动添加的,微服务注册在哪个集群的nacos上,它逻辑上就属于那个集群。

微服务group分组

nacos的微服务分组概念,有两层含义:

  • 不同分组的微服务,彼此之间不能发现对方,也就不能进行远程服务调用。所以一个公司内部根据项目的逻辑独立性进行分组是最好的,不存在彼此的服务调用一组项目成为一个group。
  • 将微服务分组,方便我们查看,以及方便我们后面章节会为大家介绍的配置管理分类。

可以通过如下属性对微服务分组进行配置。

spring.cloud.nacos.discovery.group=

健康保护阈值

  • 设置健康保护阈值的目的:为了防止过多微服务实例挂掉之后,导致流量全部流向健康实例 ,继而造成流量压力把健康实例也压垮,并形成雪崩效应,导致整个集群服务崩溃。
  • 健康保护阈值是一个 0 到 1之间的浮点数。当健康实例占总服务实例的比例小于该值时,无论被请求的实例是否健康,都会将这个实例返回给客户端。这样做虽然导致一部分请求失败,但是保证了集群的剩余的健康实例能一定程度上维持系统的整体服务。
    这就好像一些公司的人力分配,有些员工能力就是不行,领导也知道你不行,但是领导还是让他去领衔做一部分工作。因为这部分工作得有人做,不能所有工作全交给“原本行”的人去做,就把"原本行"的人压垮了。导致整个公司所有业务瘫痪。

附录:nacos-discovery客户端配置项

配置项Key默认值说明
服务端地址spring.cloud.nacos.discovery.server-addrNacos Server 启动监听的ip地址和端口
服务名spring.cloud.nacos.discovery.service${spring.application.name}给当前的服务命名
服务分组spring.cloud.nacos.discovery.groupDEFAULT_GROUP设置服务所处的分组
权重spring.cloud.nacos.discovery.weight1取值范围1到100,数值越大,权重越大
网卡名spring.cloud.nacos.discovery.network-interface当IP未配置时,注册的IP为此网卡所对应的IP地址,如果此项也未配置,则默认取第一块网卡的地址
注册的IP地址spring.cloud.nacos.discovery.ip优先级最高
注册的端口spring.cloud.nacos.discovery.port-1默认情况下不用配置,会自动探测
命名空间spring.cloud.nacos.discovery.namespace常用场景之一是不同环境的注册的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
AccessKeyspring.cloud.nacos.discovery.access-key当要上阿里云时,阿里云上面的一个云账号名
SecretKeyspring.cloud.nacos.discovery.secret-key当要上阿里云时,阿里云上面的一个云账号密码
Metadataspring.cloud.nacos.discovery.metadata使用Map格式配置,用户可以根据自己的需要自定义一些和服务相关的元数据信息
日志文件名spring.cloud.nacos.discovery.log-name
接入点spring.cloud.nacos.discovery.enpointUTF-8地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址
是否集成Ribbonribbon.nacos.enabledtrue一般都设置成true即可
是否开启NacosWatchspring.cloud.nacos.discovery.watch.enabledtrue可以设置成false来关闭watch

nacos客户端配置加载

nacos作为配置管理中心,实现的核心功能就是配置的统一管理,本节我们就以aservice-rbac为例读取nacos配置中心的配置数据,配置读取正确之后才能正确的启动aservice-rbac服务。

项目代码配置工作

在aservice-rbac服务的pom文件中引入如下配置

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

新建配置文件bootstrap.yml,新增spring.cloud.nacos.config段配置,将服务指向正确的nacos服务端。该配置文件中只保留nacos相关的配置即可,其他的配置放到nacos中统一管理(配置的分类后文介绍)。

spring:
  application:
    name: aservice-rbac
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.161.6:8848
      config:
        server-addr: ${spring.cloud.nacos.discovery.server-addr}
        file-extension: yaml   #nacos配置文件后缀,下文配置发布的时候会用到。注意是yaml,不是yml
        group: DHY_GROUP #配置分组,默认分组是DEFAULT_GROUP

这里需要格外注意:上面这些属性必须配置在bootstrap.yml或properties文件中,而不是application.yml中,config配置内容才能被正确加载。因为bootstrap.yml加载优先级高于application.yml,保证在应用一起动时就去加载配置,对于Spring 中一些自动装载类来说这很重要。

nacos配置中心发布配置

配置分类

目前,我们把配置分为两类:

  • 第一类:上文中的nacos配置(包括spring.application.name)放在本地服务中,因为需要根据nacos配置去nacos配置管理中心加载其他配置。
  • 第二类:除了nacos之外的其他配置(包括数据库配置等),放入nacos配置管理中心,在本地服务中注释掉或删除。

第二类配置,比如下面的这些配置:

server:
  port: 8401

spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  datasource:
    url: jdbc:mysql://123.56.169.21:3306/xxx?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: xxx
    password: xxx
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      minimum-idle: 5
      maximum-pool-size: 15
      auto-commit: true
      idle-timeout: 30000
      pool-name: RBACHikariCP
      max-lifetime: 120000
      connection-timeout: 30000
      connection-test-query: SELECT 1
  cloud:
    inetutils:
      preferredNetworks:
        - 192.168
      ignored-interfaces:
        - .*VirtualBox.*

mybatis:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

ribbon:
  eager-load:
    enabled: true
    clients: aservice-sms

feign:
  okhttp:
    enabled: true

logging:
  level:
    com.dhy.cloud.aservice.rbac.feign.SmsService: debug
发布配置

通过配置列表右侧的“+”按钮添加配置文件

点击“+”按钮之后,页面内容如下:

Data Id:该配置文件在nacos系统内的唯一标识,在 Nacos Spring Cloud 中,dataId的完整格式如:

${prefix}-${spring.profile.active}.${file-extension}
  • prefix 默认为 spring.application.name 的值,也可以通过配置项spring.cloud.nacos.config.prefix来自定义配置。
  • spring.profile.active 即为当前环境对应的profile,如:aservice-rbac-dev.yaml中的dev就是指开发环境。注意:当spring.profile.active为空时,对应的环境定义字符将不存在,如:aservice-rbac.yaml
  • file-exetension 为配置内容的数据格式,可以通过配置项spring.cloud.nacos.config.file-extension来配置。注意我们使用的是yaml类型,不是yml。虽然二者是一个意思,但是nacos只认yaml。

Group:同spring.cloud.nacos.config.group配置,界面填写的内容与项目中的配置二者一定要统一,否则无法正确读取配置,Group起到配置“隔离”的作用。

配置内容:将上文中分类完成的第二类配置,粘贴到里面。(在本地服务或项目中把这部分配置删掉)

最后将nacos配置发布、确定,如下图:

启动服务验证配置

启动服务的时候,加上-Dspring.profiles.active=dev参数,这个参数是和DataId匹配的,如:

  • -Dspring.profiles.active=dev 会去找xxxxx-dev.yaml配置文件
  • -Dspring.profiles.active=pro 会去找xxxxx-pro.yaml配置文件
# 启动服务并加载`aservice-rbac-dev.yaml`配置文件
java -jar aservice-rbac.jar -Dspring.profiles.active=dev

服务启动后,我们可以验证一下aservice-rbac是否正确的读取到了nacos配置中心的配置:

可以看日志:

看看数据库连接池

因为我们的数据库配置是在nacos配置中心管理的,本地已经删除了。所以正确的初始化了数据库连接池,也能证明服务已经正确的读取到了nacos配置中心的配置。

后面的文章中还会为大家介绍配置动态刷新,可以进一步验证!

nacos客户端配置刷新

上一节给大家介绍了nacos客户端(也就是我们的业务微服务)在启动的时候,怎样实现配置从nacos配置中心加载。本节为大家介绍一下当配置发生变更的时候,如何动态的通知并刷新服务的配置数据。

若干已经讲过的问题(回顾):

  • 哪些配置可以刷新,那些配置不能刷新?参考spring cloud config系列
  • 支持使用哪些Spring 注解来实现配置的动态刷新?@Value和@ConfigurationProperties
  • 是不是我们讲过的所有配置管理中心平台下,@Value和@ConfigurationProperties都需要配合@RefreshScope注解才能实现动态刷新?大体上是的,但有例外。
配置管理平台ValueConfigurationProperties
nacos需要结合RefreshScope才能生效需要结合RefreshScope才能生效
apollo不需要结合RefreshScope就能生效(例外)需要结合RefreshScope才能生效
spring cloud config需要结合RefreshScope才能生效需要结合RefreshScope才能生效

下面两个例子都可以将nacos配置"user.init.password"键对应的值热更新到password和defaultPwd对象上。这两个注解需要结合@RefreshScope注解使用才能使配置动态更新生效。

@RefreshScope   //这里需要加上RefreshScope注解
@ConfigurationProperties(prefix = "user.init")
public class User{
    private String password;
}

下文中会针对这种@Value注解的方法为例进行讲解。

@RefreshScope   //这里需要加上RefreshScope注解
public class Xxxxx{
    @Value("${user.init.password}")
    private String defaultPwd;
}

使微服务客户端具备配置刷新能力

nacos发布配置

添加一个配置:user.init.password=12345678

修改代码实现配置动态刷新

在需要进行配置刷新的类上使用@RefreshScope,user.init.password对应的配置对象defaultPwd的值就具备了刷新的能力。

@Service
@RefreshScope
public class SysuserService {
   
  @Value("${user.init.password}")
  private String defaultPwd;

  public void pwdreset(Integer userId){
    if(userId == null){
      throw new CustomException(CustomExceptionType.USER_INPUT_ERROR,
              "重置密码必须带主键");
    }else{
      SysUser sysUser = sysUserMapper.selectByPrimaryKey(userId);

      //String defaultPwd = dbLoadSysConfig.getConfigItem("user.init.password");
      sysUser.setPassword(passwordEncoder.encode(defaultPwd));

      sysUserMapper.updateByPrimaryKeySelective(sysUser);
      smsService.send(sysUser.getPhone(),"您好,管理员已经将您的密码重置为" + defaultPwd);
    }
  }
}

配置动态刷新测试

使用postman向“/sysuser/pwd/reset”接口发送请求

端点如图所示

去nacos配置中心修改user.init.password的值为Abcd1234,再次通过postman发送请求

在以上的过程中,我们没有重启服务,就实现了配置在nacos中修改之后,服务自动更新配置所对应的对象的数据。

nacos服务配置隔离与共享

四种隔离级别

下面这张图是nacos官网讲解nacos数据模型的概念时给出的,用来理解nacos的隔离级别再好不过。

  • 一个namespace命名空间可以包含多个分组
  • 一个分组可以有多个子项目或者子模块的微服务
  • 每个子项目有自己的dataid,dataId命名规则带后缀,如:xxxx-dev.yaml、xxxx-pro.yaml,可以产生对某一个子项目部署环境配置文件加载隔离的效果。
    注意:学过apollo的同学不要把apollo的namespace和nacos的namespace搞混了,apollo的namespace是用于将多个服务或项目都需要使用的共有配置放到一个namespace中,方便多个项目公共加载使用。而nacos的namespace就是一个顶级的服务配置隔离级别。

其实还有一种隔离,是上面的这张图没有体现出来的,那就是:nacos可以增加多个用户(并为用户赋权)、在这些用户下可以建立namespace。不同用户建立的namespace里面的服务肯定是不能互相访问的,配置也是隔离的。

隔离级别的使用模式

namespace没有一定的必须的隔离模式要求,你可以根据自己的情况进行隔离,比如:

  • 一套部署环境建立一个namespace,如:开发环境、测试环境
  • 一个研发部门建立一个namespace,如:研发一部、研发二部
  • 一个分公司建立一个namespace,如:上海分公司、北京分公司
  • ……

根据自己的项目情况,公司项目、组织架构、外部客户等分组特征可以灵活决定namespace的隔离模式。下面为大家举两个常见的例子:

隔离级别举例一举例二
namespace(一级)一套部署环境一个namespace,如:DEV开发环境;PRO生产环境一个公司的一个部门建立一个namespace。如:开发一部,开发二部
group(二级)一个微服务综合项目一个组一个微服务综合项目一个组
dataid/service(三级)微服务配置文件xxxx.yaml、yyyy.yaml微服务配置文件xxxx-dev.yaml、xxxx-pro.yaml来区分部署环境

group的分组模式:通常是一个微服务综合项目作为一个group,因为微服务模块之间需要互相调用,放在不同的组会产生隔离,无法彼此远程调用。

添加并使用namespace

我们假设一个需求:为XX公司的“研发一部”建议一个namespace,该部门的所有项目都注册到这个namespace 下面,并且使用这个namespace下面的配置。

  • 首先在nacos中添加命名空间:

  • 添加完成之后效果如下:

微服务在没有明确指定配置的情况下, 默认使用的是Public命名空间。如果需要明确使用自定义的命名空间namespace,可以通过以下配置来实现:

# 配置隔离的namespace
spring.cloud.nacos.config.namespace=fd96613a-2bad-4288-806d-0a7c2b265267
# 服务隔离的namespace
spring.cloud.nacos.discovery.namespace=fd96613a-2bad-4288-806d-0a7c2b265267
  • 服务隔离配置:微服务属于不同的namespace,彼此之间不能远程调用
  • 配置隔离配置:同一个namespace下的配置文件可以为该namespace下的多个子项目共享(下文会讲),跨namespace则无法实现配置文件共享。

Group的配置使用

分组不需要在nacos管理界面中新建,只需要在nacos客户端配置即可。

# 服务隔离的分组group
spring.cloud.nacos.discovery.group=
# 配置隔离的分组group
spring.cloud.nacos.config.group=
  • 服务隔离配置:微服务属于不同的Group,彼此之间不能远程调用。
  • 配置隔离配置:同一个namespace下面的不同Group的配置文件可以共享(下文会讲到)

配置文件共享

实现思路

nacos的配置共享的思路是:一个项目可以使用多个配置文件,既然一个项目或者子项目可以使用多个配置文件,那么配置共享是不是就简单了。方法就是:我们把公有配置单独抽取为一个配置文件,如下文中的:common-datasource.yaml。

一个项目使用多个配置文件(共享配置文件)

准备工作:我们把原有的nacos上面的aservice-rbac-dev.yml配置文件拆分成两份。

  • 一份叫做common-datasource.yaml,放在DEFAULT_GROUP默认分组下,是数据库连接及连接池相关的配置,属于公有配置。
  • 一份仍然叫做aservice-rbac-dev.yml,除去数据库连接外的其他的所有配置,属于子项目(服务)内部的个性化配置。

现在配置文件被拆分成了两份,aservice-rbac服务该如何使用两份配置文件?不再使用默认的dataid规则加载配置文件,可以通过如下的方法加载多个配置文件:

  • 通过spring.cloud.nacos.config.extension-configs[n].data-id的配置方式来支持多个Data Id 的配置。
  • 通过spring.cloud.nacos.config.extension-configs[n].group的配置方式自定义Data Id 所在的组,不明确配置的话,默认是 DEFAULT_GROUP。
  • 通过spring.cloud.nacos.config.extension-configs[n].refresh的配置方式来控制该Data Id 在配置变更时,是否支持应用中可动态刷新, 感知到最新的配置值。默认是不支持的。

nacos配置Beta发布

灰度发布简介

灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式。AB test就是一种灰度发布方式,让一部分用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题。
灰度发布就是让配置先在部分实例生效,如果效果理想全量发布到所有实例,如果效果不理想就可以放弃当前的“灰度发布配置”。

nacos与灰度发布

在nacos配置管理里面,与灰度发布有相同效果的发布方式被称为beta发布。其实二者的核心内涵并没有什么实质上的差别,下文取自百度百科:

beta发布就是发布一个公测版本,公测结果较好,就全量发布。公测结果不理想,就放弃Beta发布的版本。

在nacos配置中心实现Beta发布

实验场景

aservice-rbac服务在192.168.161.3,192.168.161.4,192.168.161.5启动了三个实例。我们想把其中192.168.161.3,192.168.161.4实例的配置项"user.init.password"的值改为xxxxyyyy(即:Beta发布),192.168.161.5的配置不做更改。

配置实现步骤
  • 在配置列表页面,选择要进行配置变更灰度发布的配置文件进行"编辑"修改

  • 在配置编辑界面,修改配置项"user.init.password"的值改为xxxxyyyy。并勾选Beta发布,填写需要Beta发布的微服务实例运行的主机的ip,多个ip用逗号分隔。

  • 点击“发布beta”按钮们进行配置发布

  • beta配置发布之后,正常情况下,192.168.161.3,192.168.161.4上运行的实例的配置项会立刻动态刷新。
  • 配置编辑界面分Tab展示,一个Tab是正式版的配置,也就是192.168.161.5实例正采用的配置。一个Tab是Beta版的配置,也就是192.168.161.3,192.168.161.4实例动态刷新之后的配置。

Beta配置的测试

因为这种测试方法我们之前的章节已经多次用过,这里就不截图详细说明了,测试方法如下:

  • 用postman发送请求到192.168.161.3,192.168.161.4的“/sysuser/pwd/reset”接口,在SysuserService的defaultPassword的默认密码值是xxxxyyyy
  • 用postman发送请求到192.168.161.5的“/sysuser/pwd/reset”接口,在SysuserService的defaultPassword的默认密码值是Abcd234

出现以上结果,证明我们的Beta发布结果是正确的。

Beta配置回退与全量发布

beta发布的配置可以进行回退,也可以全量发布

  • 点击“停止beta”,192.168.161.3,192.168.161.4的配置就回退到正式版的配置。
  • 点击“发布”实际就是全量发布,将Beta配置在所有微服务实例(包括192.168.161.5)上生效。当前的“BETA配置”转成新的“正式版”配置。

相关文章

微信公众号

最新文章

更多

目录