重学SpringCloud系列五之服务注册与发现---中
Posted 大忽悠爱忽悠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重学SpringCloud系列五之服务注册与发现---中相关的知识,希望对你有一定的参考价值。
重学SpringCloud系列五之服务注册与发现---中
- BUS消息总线
- alibaba-nacos
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
进行单节点配置刷新,现在访问Bus
的http://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 ID
是spring.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、服务元数据管理等。
官方资料
- 官方网站:home (nacos.io)
- github:https://github.com/alibaba/Nacos
- spring cloud alibaba:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html
为什么叫nacos?Naming与Configuration的前两个字母的组合,最后的s代表service。从其命名也能看出其核心功能。
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 Version | Spring Cloud Alibaba Version | Spring Boot Version |
---|---|---|
Spring Cloud Hoxton.SR3 | 2.2.1.RELEASE | 2.2.5.RELEASE |
Spring Cloud Hoxton.RELEASE | 2.2.0.RELEASE | 2.2.X.RELEASE |
Spring Cloud Greenwich | 2.1.1.RELEASE | 2.1.X.RELEASE |
Spring Cloud Finchley | 2.0.1.RELEASE | 2.0.X.RELEASE |
Spring Cloud Edgware | 1.5.1.RELEASE | 1.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配置)
以上是关于重学SpringCloud系列五之服务注册与发现---中的主要内容,如果未能解决你的问题,请参考以下文章