白话服务注册与发现
公益图书馆例子服务注册与发现
服务注册客户端负载均衡 Spring Cloud常用的服务注册中心 DiscoveryClient服务发现
DiscoveryClient测试用例两种服务发现注解的区别 Eureka集群环境构建(linux)
CentOS7(linux)环境准备profile配置文件修改为eureka server加入actuatorCentOS7环境下部署eureka集群访问测试其他需要注意的点: Eureka集群多网卡环境ip设置
配置实现多网卡ip选择配置方法总结归纳
方法一:直接配置eureka.instance.ip-address方法二:增加inetutils相关配置 通过启动命令行传递配置源码解析 Eureka集群服务注册与安全认证
服务注册配置Eureka安全认证(服务端)Eureka安全认证(客户端) Eureka自我保护与健康检查
Eureka的健康检查Eureka自我保护模式关闭 Eureka 的自我保护模式 主流服务注册中心对比(含nacos)
垂直扩展与水平扩展CAP理论
强数据一致性(High Consistency )高可用性(High Availability)分区容忍性(Partition Tolerance) 服务注册中心的CAP主流注册中心产品 zookeeper概念及功能简介
zookeeper简介集群节点必须是奇数个
容错率防脑裂 临时节点与持久节点
持久znode临时znode zookeeper-linux集群安装
环境准备安装 zookeeper
解压修改配置文件 zoo.cfg创建 myid 文件配置环境变量开启防火墙端口启动zookeeper服务 zookeeper服务注册与发现
微服务向zookeeper注册
配置zookeeper打开discovery开关 验证服务注册结果FeignClient远程服务名称调整访问测试 consul概念及功能介绍
Consul是什么?Spring Cloud服务与consulconsul集群 consul-linux集群安装
环境准备consul 端口说明agent启动参数安装过程
consul下载、解压和目录创建consul配置文件linux consul systemd服务配置consul集群安装结果验证web UI consul命令详解(用到查一下、不用记) consul服务注册与发现
微服务向consul注册
引入依赖包配置consul打开discovery开关启动日志 验证服务注册结果FeignClient远程服务名称调整接口访问测试 通用-auatator导致401问题
问题描述原因解决方案一解决方案二解决方案三解决方案四解决方案五
白话服务注册与发现 公益图书馆例子
笔者不想直接用专业的术语来说明“微服务注册与发现”,所以我们来看生活中的一个案例:“公益图书馆”。
随着人们生活水平的不断提高,追求精神食粮的朋友也越来越多。笔者曾经在一些城市看见过公益图书馆,其运行逻辑是:一些公益组织和个人提供一块场所,然后由组织内的人向图书馆内捐书。捐出的书越多,一段时间内能够借阅的书也就越多。这种做法有助于大家分享图书、节约资金、交流读书心得。那我们来看一下几个关键环节:
其实上面的这个“公益图书馆的例子”就是典型的服务注册与发现:
与客户端负载均衡相对的方法就是服务端负载均衡,如果上面的例子中借书过程一本书有多个副本,由图书管理员或系统决定借书者借其中的哪一个副本,这个就是服务端负载均衡。如:nginx、haproxy等就是服务端负载均衡。
服务注册与发现
服务注册
当一个微服务启动的时候,必须主动向服务注册中心注册其服务地址,以供其他微服务查询调用。图中橘黄色为服务注册中心,绿色为微服务节点。
客户端负载均衡Spring Cloud常用的服务注册中心
如果你的应用已经使用到了hadoop、kubernetes、docker,在Spring Cloud实施过程中可以考虑使用其关系户组件,避免搭建两套注册中心,节省资源。但是二者兼容使用说说容易,真正用起来还需要功夫。目前看,笔者觉得最佳选择应该是Nacos。
这里可以先简单的了解一下常见的这些服务注册中心,后面的章节我们会逐步的详细介绍。
DiscoveryClient服务发现
在前面的章节,我们已经为大家介绍了
服务消费者调用微服务之前,需要向服务注册中心,获取注册服务列表及服务信息。这个过程就是“服务发现”,那么服务发现是通过什么类实现的?服务列表及服务信息又包含哪些内容?本节就带着大家来解开这样的疑惑!
DiscoveryClient测试用例
DiscoveryClient 代表的就是:服务发现操作对象。
public interface DiscoveryClient extends Ordered {
int DEFAULT_ORDER = 0;
String description();
List getInstances(String serviceId);
List getServices();
default int getOrder() {
return 0;
}
}
它有两个核心方法:
下面是一个基于Spring、Junit的测试用例,使用上面两个方法来实现服务发现。
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class DiscoveryClientTest {
@Resource
private DiscoveryClient discoveryClient; // 进行eureka的发现服务
@Test
void discoveryClientTest() {
//获取服务Id
List services = discoveryClient.getServices();
services.forEach(System.out::println);
//获取每个服务的多个启动实例的注册信息。
for (String service:services){
discoveryClient.getInstances(service)
.forEach(s -> {
System.out.println("InstanceId=" + s.getHost() + ":" + s.getPort());
System.out.println("Host:Port="+ s.getHost() + ":" + s.getPort());
System.out.println("Uri=" + s.getUri());
System.out.println("InstanceId=" + s.getInstanceId());
System.out.println("Schema=" + s.getScheme());
System.out.println("ServiceId=" + s.getServiceId());
System.out.println("metadata="+ s.getmetadata());
});
}
}
}
结合测试结果的打印,可以更清楚的知道服务注册及发现相关的信息。理解DiscoveryClient 及其方法的作用。控制台打印结果如下:
两种服务发现注解的区别
在Spring Cloud中实现服务发现可以使用两种注解:@EnableDiscoveryClient和@EnableEurekaClient,两者的用法基本上是一样的。但存在区别,简单地说:
在Hello-microservice章节,实现微服务向服务注册中心注册的时候,我们使用了@EnableEurekaClient,是因为我们当时搭建的服务注册中心是基于eureka搭建的。Spring Cloud中还有很多的其他服务注册中心的选项,比如:consul、zookeeper、nacos,这时就不能使用@EnableEurekaClient注解了,需要使用@EnableDiscoveryClient注解。
Eureka集群环境构建(linux)
服务注册中心在整个微服务体系中,至关重要!如果服务注册中心挂了,整个系统都将崩溃。所以服务注册中心通常不会被部署为单点应用,而是采用集群的部署方式,其中个别节点挂掉不影响整个系统的运行。
下面,我们就来为大家介绍,如何基于CentOS7服务器构建eureka服务注册中心。
CentOS7(linux)环境准备
| 主机名称 | 主机ip |
|---|---|
| peer1 | 192.168.161.3 |
| peer2 | 192.168.161.4 |
| peer3 | 192.168.161.5 |
并且在安装eureka服务注册中心之间,需要将服务器时间同步,不能相差太多,否则eureka服务有可能启动失败。可以使用ntp进行时间同步。如:
ntpdate ntp.api.bz
profile配置文件修改
application-peer1.yml
这两个值之所以设置为true,目的是让eureka集群之间实现互相注册,互相心跳健康状态,从而达到集群的高可用。
#是否从其他实例获取服务注册信息,因为这是一个单节点的EurekaServer,不需要同步其他的EurekaServer节点的数据,所以设置为false;
fetch-registry: false
#表示是否向eureka注册服务,即在自己的eureka中注册自己,默认为true,此处应该设置为false;
register-with-eureka: false
把它们设置为false,是能解决你可能遇到的一些集群环境问题。这就好比你腿疼,你把腿砍了是不疼了,但你还能走路么。我们要的是让腿不疼,而不是把腿砍掉。“把腿砍了”这就不是“高可用”集群了,相当于你搭建了多个eureka server单点,这是“掩耳盗铃”的做法。
server:
port: 8761
servlet:
context-path: /eureka
spring:
application:
name: eureka-server
eureka:
instance:
hostname: peer1
health-check-url: http://${eureka.instance.hostname}:${server.port}/${server.servlet.context-path}/actuator/health
client:
#从其他两个实例同步服务注册信息
fetch-registry: true
#向其他的两个eureka注册当前eureka实例
register-with-eureka: true
service-url:
defaultZone: http://peer2:8761/eureka/eureka/,http://peer3:8761/eureka/eureka/
application-peer2.yml
server:
port: 8761
servlet:
context-path: /eureka
spring:
application:
name: eureka-server
eureka:
instance:
hostname: peer2
health-check-url: http://${eureka.instance.hostname}:${server.port}/${server.servlet.context-path}/actuator/health
client:
#从其他两个实例同步服务注册信息
fetch-registry: true
#向其他的两个eureka注册当前eureka实例
register-with-eureka: true
service-url:
defaultZone: http://peer1:8761/eureka/eureka/,http://peer3:8761/eureka/eureka/
application-peer3.yml
server:
port: 8761
servlet:
context-path: /eureka
spring:
application:
name: eureka-server
eureka:
instance:
hostname: peer3
health-check-url: http://${eureka.instance.hostname}:${server.port}/${server.servlet.context-path}/actuator/health
client:
#从其他两个实例同步服务注册信息
fetch-registry: true
#向其他的两个eureka注册当前eureka实例
register-with-eureka: true
service-url:
defaultZone: http://peer1:8761/eureka/eureka/,http://peer2:8761/eureka/eureka/
为eureka server加入actuator
spring-boot-starter-actuator是为Spring Boot服务提供相关监控信息的包。因为我们的eureka server要互相注册,并检查彼此的健康状态,所以这个包必须带上。
org.springframework.boot >spring-boot-starter-actuator
CentOS7环境下部署eureka集群
如果你的网络环境内没有DNS,需要配置/etc/hosts文件,将主机名称与ip地址关联。这一步必须要做,否则linux主机之间通过hostname访问eureka服务将无效,每台eureka server主机上都要执行。
192.168.161.3 peer1 192.168.161.4 peer2 192.168.161.5 peer3
开放防火墙端口(CentOS7),每台eureka server主机上都要执行。
firewall-cmd --zone=public --add-port=8761/tcp --permanent firewall-cmd --reload
将dhy-server-eureka通过maven打包,然后上传CentOS主机,启动Eureka服务注册中心集群
# 在peer1主机执行 nohup java -jar -Dspring.profiles.active=peer1 dhy-server-eureka-1.0.jar & # 在peer2主机执行 nohup java -jar -Dspring.profiles.active=peer2 dhy-server-eureka-1.0.jar & # 在peer3主机执行 nohup java -jar -Dspring.profiles.active=peer3 dhy-server-eureka-1.0.jar &
访问测试
访问http://192.168.161.3:8761/eureka/,即:访问peer1的eureka服务。可以见到DS Replicas中已经注册了peer3、peer2。
同理:
出现上面的这种eureka server之间互相注册的效果,表示我们的eureka服务注册中心集群模式搭建成功了!那么恭喜你,你是一个幸运儿。在实际的生产环境中,网络及主机环境往往更复杂,搭建过程的参数调整也更加复杂。
其他需要注意的点:
出现 unavailable-replicas 问题,首先要去检查一下你的health-check-url是否能正常响应。如果没有设置context-path,默认是:http://ip:端口/actuator/health。UP状态表示处于可用状态。
如果健康检查没有问题:
1.是否开启了register-with-eureka=true和fetch-registry=true
2.eureka.client.serviceUrl.defaultZone配置项的地址,不能使用localhost,要使用ip或域名。或者可以通过hosts或者DNS解析的主机名称hostname。
3.spring.application.name要一致,不配置也可以,配置了要一致
4.默认情况下,Eureka 使用 hostname(如:peer1、peer2、peer3) 进行服务注册,以及服务信息的显示(eureka web 页面),那如果我们希望使用 IP 地址的方式,该如何配置呢?答案就是eureka.instance.prefer-ip-address=true。当设置prefer-ip-address: true时 ,修改配置defaultZone:http://你的IP:9001/eureka/。如果此时你仍然使用http://peer1:8761/eureka会导致健康检查失败。
Eureka集群多网卡环境ip设置
在上一小节,我们为大家讲解了如何在linux环境下搭建集群式Eureka服务注册中心。有的朋友可能会遇到下面的问题(导致服务注册失败、健康检查失败):
上图中蓝色部分:大家可以明确的看到eureka server的服务绑定的ip是10.0.2.15?这是为什么?我们上一节中,也没有使用过这个ip啊,我们使用的是192.168.161.3。这是因为我的CentOS服务器上有多个网卡,还有一些docker相关的虚拟网卡。“多网卡”在生产环境上是非常常见的情况。怎么让eureka server服务绑定实例我们期望它绑定的网卡?
配置实现
首先来看一下,我的服务器(虚拟机)上面的网卡设备,一共五个(虚拟的)。
那我们现在要做的就是通知spring cloud,我们部署的微服务希望ip是192.168的本地网段。不要使用docker0和enp0s3的网段。
server:
port: 8761
servlet:
context-path: /eureka
spring:
application:
name: eureka-server
cloud:
inetutils:
preferredNetworks:
- 192.168
ignoredInterfaces:
- enp0s3
- docker0
eureka:
instance:
hostname: peer1
instance-id: ${spring.application.name}-${eureka.instance.hostname}:${server.port}
health-check-url: http://${eureka.instance.hostname}:${server.port}/${server.servlet.context-path}/actuator/health
client:
#从其他两个实例同步服务注册信息
fetch-registry: true
#向其他的两个eureka注册当前eureka实例
register-with-eureka: true
service-url:
defaultZone: http://peer2:8761/eureka/eureka/,http://peer3:8761/eureka/eureka/
多网卡ip选择配置方法总结归纳
除去上面的配置方法,还有其他能实现多网卡ip选择的方式,可以根据自己的网络环境情况选择使用。归纳如下:
方法一:直接配置eureka.instance.ip-addresseureka.instance.ip-address=192.168.1.7
直接配置一个完整的ip,一般适用于环境单一场景,对于复杂场景缺少有利支持。比如:你的eureka环境是结合docker容器部署的,就会有问题。因为docker容器的ip是动态的不固定的,所以你很难为docker容器中的服务指定ip。所以这种方式通常不建议使用。
方法二:增加inetutils相关配置
配置对应org.springframework.cloud.commons.util.InetUtilsProperties,其中包含:
| 配置 | 说明 |
|---|---|
| spring.cloud.inetutils.default-hostname | 默认主机名,只有解析出错才会用到 |
| spring.cloud.inetutils.default-ip-address | 默认ip地址,只有解析出错才会用到 |
| spring.cloud.inetutils.ignored-interfaces | 配置忽略的网卡地址 |
| spring.cloud.inetutils.preferred-networks | 期望优先匹配的网卡,正则匹配的ip地址或者ip前缀 |
| spring.cloud.inetutils.timeout-seconds | 计算主机ip信息的超时时间,默认1秒钟 |
| spring.cloud.inetutils.use-only-site-local-interfaces | 只使用内网ip |
上面已经为大家介绍了ignored-interfaces和preferred-networks用法,其他的配置举例说明如下:
使用/etc/hosts中主机名称映射的ip,这一种在docker swarm环境中比较好用。
# 随便配置一个不可能存在的ip,会走到InetAddress.getLocalHost()逻辑。 spring.cloud.inetutils.preferred-networks=none
当所有的网卡遍历逻辑都没有找到合适的网卡ip,会走JDK的InetAddress.getLocalHost()。该方法会返回当前主机的hostname, 然后会根据hostname解析出对应的ip。
# 只使用内网地址,遵循 RFC 1918 # 10/8 前缀 # 172.16/12 前缀 # 192.168/16 前缀 spring.cloud.inetutils.use-only-site-local-interfaces=true
通过启动命令行传递配置
java -jar xxx.jar --spring.cloud.inetutils.preferred-networks= #需要设置的IP地址 或者 java -jar xxx.jar --spring.cloud.inetutils.ignored-interfaces= #需要过滤掉的网卡
源码解析
为了说明这个问题的解决方案,我们需要翻看一下Eureka Client的源码。com.netflix.appinfo包下的InstanceInfo类封装了本机信息,其中就包括了IP地址。在 Spring Cloud 环境下,Eureka Client并没有自己实现探测本机IP的逻辑,而是交给Spring的InetUtils工具类的findFirstNonLoopbackAddress()方法完成的:
public InetAddress findFirstNonLoopbackAddress() {
InetAddress result = null;
try {
// 记录网卡最小索引
int lowest = Integer.MAX_VALUE;
// 获取主机上的所有网卡
for (Enumeration nics = NetworkInterface
.getNetworkInterfaces(); nics.hasMoreElements();) {
NetworkInterface ifc = nics.nextElement();
if (ifc.isUp()) {
log.trace("Testing interface: " + ifc.getDisplayName());
if (ifc.getIndex() < lowest || result == null) {
lowest = ifc.getIndex(); // 记录索引
}
else if (result != null) {
continue;
}
// 判断是否是被忽略的网卡
if (!ignoreInterface(ifc.getDisplayName())) {
for (Enumeration addrs = ifc
.getInetAddresses(); addrs.hasMoreElements();) {
InetAddress address = addrs.nextElement();
if (address instanceof Inet4Address
&& !address.isLoopbackAddress()
&& !ignoreAddress(address)) {
log.trace("Found non-loopback interface: "
+ ifc.getDisplayName());
result = address;
}
}
}
// @formatter:on
}
}
}
catch (IOException ex) {
log.error("Cannot get first non-loopback address", ex);
}
if (result != null) {
return result;
}
try {
// 如果以上逻辑都没有找到合适的网卡,则使用JDK的InetAddress.getLocalhost()
return InetAddress.getLocalHost();
}
catch (UnknownHostException e) {
log.warn("Unable to retrieve localhost");
}
return null;
}
Eureka集群服务注册与安全认证 服务注册配置
目前我们的项目中有两个微服务,我们需要将它们注册到服务注册中心。
由于我们将eureka server从单节点升级为集群,所以响应的注册配置也要修改,但总体上大同小异。主要修改的内容如下:
spring:
application:
name: aservice-sms
cloud:
inetutils:
preferredNetworks:
- 192.168
ignored-interfaces:
- .*VirtualBox.*
eureka:
client:
service-url:
defaultZone: http://dhy:centerpwd@peer1:8761/eureka/eureka/,http://dhy:centerpwd@peer2:8761/eureka/eureka/,http://dhy:centerpwd@peer3:8761/eureka/eureka/
FTP地址格式如下:“ftp://用户名:密码@FTP服务器IP”
对应下面一会讲到的spring security加密设置
因为defaultZone中使用主机名称访问注册服务,所以需要配置hosts文件。在windows中该文件C:windowsSystem32driversetchosts,在linux中该文件是/etc/hosts。
192.168.161.3 peer1 192.168.161.4 peer2 192.168.161.5 peer3
Eureka安全认证(服务端)
在此之前,我们的微服务向服务注册中心注册都是使用的公开访问权限,在实际的生产应用中,这很危险。因为随便的一个什么人,知道这个服务注册地址,都可以写一个服务注册到上面。通常我们需要使用eureka server对注册服务进行一个Spring Security的HttpBasic安全认证,虽然这个认证很简陋,但是能起到“防君子不防小人的”作用。
在dhy-server-eureka项目的pom.xml 中添加 Spring-Security 的依赖包
org.springframework.boot spring-boot-starter-security
然后在dhy-server-eureka项目的 application.yml中加上认证的配置信息(用户名和密码):
spring:
security:
user:
name: dhy
password: centerpwd
然后在dhy-server-eureka项目增加Spring Security 配置类:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 关闭csrf
http.csrf().disable();
// 支持httpBasic认证方式
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}
Eureka安全认证(客户端)
在 Eureka Server开启认证后,所有的服务(包括eureka-server本身)向服务注册中心注册的时候也要加上认证的用户名和密码信息:
defaultZone: http://dhy:centerpwd@peer1:8761/eureka/eureka/,http://dhy:centerpwd@peer2:8761/eureka/eureka/,http://dhy:centerpwd@peer3:8761/eureka/eureka/
不只是aservice-rbac和aservice-sms的defaultZone需要加上用户名密码,所有需要向服务注册中心注册的微服务都要加上,否则无法正确的注册,会被安全策略拦截。即:eureka-server的defaultZone配置都要修改。
Eureka自我保护与健康检查 Eureka的健康检查
#开启健康检查(需要spring-boot-starter-actuator依赖)(默认就是开启的) eureka.client.healthcheck.enabled = true
如果需要更细粒度健康检查,可实现com.netflix.appinfo.HealthCheckHandler接口 。 EurekaHealthCheckHandler 已实现了该接口。
正常情况下,当微服务的心跳消失,健康检查失败后,eureka server会将该服务从服务列表中剔除。表示该服务下线了。 但是一些异常情况下,eureka不会剔除服务,比如:eureka自我保护模式被开启的情况下。
Eureka自我保护模式
访问Eureka主页时,如果看到这样一段大红色的句子,那么表明Eureka的自我保护模式被启动了。
当个别服务健康检查失败,eureka server认为是该服务正常下线了,将其服务从列表中剔除。但是当85%以上服务都收不到心跳了,eureka server会认为是自己出了问题,就开启保护模式,不再将服务从列表中剔除。从而保障微服务系统的可用性。
eureka:
server:
#自我保护模式,当出现网络分区故障、频繁的开启关闭客户端、eureka在短时间内丢失过多客户端时,会进入自我保护模式,即一个服务长时间没有发送心跳,eureka也不会将其删除,默认为true
enable-self-preservation: true
#eureka server清理无效节点的时间间隔,默认60000毫秒,即60秒
eviction-interval-timer-in-ms: 60000
#阈值更新的时间间隔,单位为毫秒,默认为15 * 60 * 1000
renewal-threshold-update-interval-ms: 15 * 60 * 1000
#阈值因子,默认是0.85,如果阈值比最小值大,则自我保护模式开启
renewal-percent-threshold: 0.85
#清理任务程序被唤醒的时间间隔,清理过期的增量信息,单位为毫秒,默认为30 * 1000
delta-retention-timer-interval-in-ms: 30000
关闭 Eureka 的自我保护模式
可以使用eureka.server.enable-self-preservation=false来禁用自我保护模式,
关闭自我保护模式,需要在服务端和客户端配置。
服务端配置:
eureka:
server:
enable-self-preservation: false
#eureka server清理无效节点的时间间隔,默认60000毫秒,即60秒
eviction-interval-timer-in-ms: 60000 # 单位毫秒
客户端配置:
# 心跳检测检测与续约时间
# 测试时将值设置设置小些,保证服务关闭后注册中心能及时踢出服务
eureka:
instance:
lease-renewal-interval-in-seconds: 5 #默认30秒
lease-expiration-duration-in-seconds: 10 #默认90秒
配置说明
lease-renewal-interval-in-seconds 每间隔5s,向服务端发送一次心跳,证明自己依然”存活“。 lease-expiration-duration-in-seconds 告诉服务端,如果我10s之内没有给你发心跳,就代表我“死”了,请将我踢掉。
主流服务注册中心对比(含nacos)
在开始为大家介绍主流的服务注册中心之前,先给大家介绍一些分布式系统的常用理论知识,这样我们在后文为大家介绍主流的服务注册中心的时候,才能更好的理解它们之间的差异
垂直扩展与水平扩展先为大家说明一下,应用的垂直扩展与水平扩展之间的区别。
垂直扩展:将更多资源(CPU,内存)添加到现有的应用程序所在的服务器上。好处在于:我们不需要进行额外的开发,即可完成应用的处理能力的升级。但是,每次扩展服务器/主机时,垂直扩展的成本几乎都呈指数增长。并且垂直扩展很容易触及硬件资源的上限。
水平扩展:是一种添加更多具有标准容量的服务器并同时运行多个应用程序副本的方法。与垂直扩展相比,此方法的最大优点是由于同一应用程序部署多个副本,提升了应用本身的容错能力(其中一个副本出现问题,其他的副本还能工作)。但是水平扩展也带来了一些挑战,比如应用程序之间的网络更多,架构设计更复杂。
CAP理论
CAP理论是埃里克·布鲁尔(Eric Brewer) 提出的理论。在分布式系统领域,特别是大数据应用、NOSQL数据库等分布式复杂系统的设计方面给出了很好的指导方针。CAP理论的核心思想就是:强数据一致性、高可用、分区容忍性之间,只能三选二。也就是说:当你选择其中的两项作为来满足你的系统需求,就必须舍弃第三项,三项无法同时满足。
下面为大家介绍一下强数据一致性、高可用、分区容忍性的具体含义。
强数据一致性(High Consistency )国内很多文章,在提到CAP的C时都是说“一致性”,这是不对的,这个C必须是强数据一致性。如果应用仅仅满足了弱一致性或者最终一致性,它并不满足CAP的C特性。
记住:CAP中的C是High C,也就是强数据一致性。 我们常用的Eureka是一种弱一致性分布式系统,所以它是满足了CAP中的AP特性,舍弃了强数据一致性C。
高可用性(High Availability)
高可用通俗的说,就是任何时间都可用,任何请求都可以得到响应。怎么保障高可用?
应用水平扩展n个节点,其中1个或者几个节点挂了,其他的节点仍然可以支撑整体服务的运行。
什么样的系统是CA系统?
比如:我们的关系型数据库Oracle、MySQL,都是可以搭建高可用环境的,也就是满足A特性。
那么关系型数据库是如何保证强数据一致性的?
假如:我们现在有一个订单库DB、一个产品库DB,如何保障一个事务中操作这两个库,同时保证强数据一致性?
答案通常是:分布式事务,如:两阶段提交等。
分区容忍性(Partition Tolerance)
笔者之前曾经给一些朋友讲分区容忍性,发现他们理解这个概念有点困难。那我们就先去讲什么是AP,什么是CP,再讲什么是P。
那么,什么是分区容忍性?
通常是指分布式系统部分节点由于网络故障导致不可达,仍然可以对外提供满足一致性或者高可用的服务。
服务注册中心的CAP
由上面的讲解,大家可以知道eureka是AP系统,consul和zookeeper是CP系统,我们后面章节要重点为大家介绍的nacos支持CP或AP。笔者认为,就服务注册中心而言,AP要好于CP。
主流注册中心产品
| 核心对比 | Nacos | Eureka | Consul | Zookeeper |
|---|---|---|---|---|
| 一致性协议 | CP或AP | AP | CP | CP |
| 版本迭代 | 迭代升级中 | 不再升级 | 迭代升级中 | 迭代升级中 |
| SpringCloud集成 | 支持 | 支持 | 支持 | 支持 |
| Dubbo集成 | 支持 | 不支持 | 不支持 | 支持 |
| K8S集成 | 支持 | 不支持 | 支持 | 不支持 |
zookeeper概念及功能简介 zookeeper简介
zookeeper是一个分布式应用的协调服务。用于对分布式系统进行配置管理、节点管理、leader选举、分布式锁等。ZooKeeper最初是由“ Yahoo!”开发的。后来,Apache ZooKeeper成为Hadoop,Hbase和其他分布式框架常用的服务配置管理的标准。
zookeeper 将集群内的节点进行了这样三种角色划分(上图中的Client不属于zookeeper集群):
zookeeper集群是一个强调保障数据一致性的分布式系统(CP),客户端发起的每次查询操作,集群节点都能返回同样的结果。
那么问题来了:对于客户端发起的修改、删除等能改变数据的操作呢?
集群中那么多台机器,如果是你修改你的,我修改我的,没有统一管理,最后查询返回集群中哪台机器的数据呢?
如果随意操作,就不能保证数据一致性了。于是在zookeeper集群中,leader的作用就体现出来了,
这样就保障了zookeeper内各个节点的数据一致性。所以zookeeper集群中leader是不可缺少的,但是 leader 节点是怎么产生的呢?
其实就是由所有follower 节点选举产生的,且leader节点只能有一个。当前leader如果因为某些原因挂掉了,集群内剩余的节点会重新选举leader。
集群节点必须是奇数个
我们要搭建服务注册中心zookeeper集群,必须搭建奇数个节点,这是为什么呢?
容错率首先从容错率来说明:(需要保证集群能够有半数进行投票)
单节点服务器,2台服务器还有两个单点故障,所以直接排除了。
脑裂集群的脑裂通常是发生在节点之间通信不可达的情况下,集群会分裂成不同的小集群,小集群各自选出自己的leader节点,导致原有的集群出现多个leader节点的情况,这就是脑裂。
以上分析,我们从容错率以及防止脑裂两方面说明了3台服务器是搭建集群的最少数目,4台服务器发生脑裂时会造成没有leader节点的错误。
临时节点与持久节点
zookeeper中的数据是以树形结构组织的,类似于树形文件系统。树中的节点称为znode,znode按持久化类型分类可以分为:
即使在创建该特定znode的客户端断开连接后,持久znode数据仍然存在。默认情况下,所有znode都是持久的。
临时znode当客户端处于活跃状态时,该客户端创建的临时znode就是有效的。当客户端与ZooKeeper集合断开连接时,临时znode会自动删除。
Spring Cloud 微服务向zookeeper注册的过程就是在zookeeper增加临时znode节点,znode节点中保存了服务的注册信息。也正是利用了临时客户端断开连接后删除znode的的特性,实现了服务的自动下线。
zookeeper除了上面的这些内容,还能做很多事情,如:分布式锁。还有很多应用、运维的知识可以学习。本文只为大家介绍与“服务注册中心”相关的理论基础知识。下一节我们就手动搭建一个zookeeper集群。
zookeeper-linux集群安装 环境准备
zookeeper官网下载地址:http://mirror.bit.edu.cn/apache/zookeeper/stable/。一定要注意从3.5.5开始,带有bin名称的包才是我们想要的下载可以直接使用的里面有编译后的二进制的包,而之前版本的普通的tar.gz的包里面是只是源码的包无法直接使用。
安装JDK:由于zookeeper集群的运行需要Java运行环境,所以需要首先安装 JDK
| 主机名称 | 主机ip |
|---|---|
| peer1 | 192.168.161.3 |
| peer2 | 192.168.161.4 |
| peer3 | 192.168.161.5 |
在 /usr/local 目录下新建 software 目录,然后将 zookeeper 压缩文件上传到该目录中,然后通过如下命令解压。
mkdir -p /opt/zookeeper; #创建zookeeper目录 mkdir -p /opt/zookeeper/data; #创建zookeeper数据存放目录 mkdir -p /opt/zookeeper/log; #创建zookeeper日志存放目录 tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz -C /opt/zookeeper #将zookeeper解压到-C指定的目录
这里我用的是zookeeper最新的stable版本3.5.7,根据你自己的下载版本及安装包调整上面的命令。
修改配置文件 zoo.cfg
将zookeeper压缩文件解压后,我们进入到 apache-zookeeper-3.5.7/conf目录:
将 zoo_sample.cfg 文件复制并重命名为 zoo.cfg 文件。
cp /opt/zookeeper/apache-zookeeper-3.5.7-bin/conf/zoo_sample.cfg /opt/zookeeper/apache-zookeeper-3.5.7-bin/conf/zoo.cfg
然后通过 vim zoo.cfg 命令对该文件进行修改:
vim /opt/zookeeper/apache-zookeeper-3.5.7-bin/conf/zoo.cfg
上面红色框住的内容即是我们修改的内容,蓝色框是我们新增的内容:
A:其中 A 是一个数字,表示这个是服务器的编号id;
B:是这个服务器的 ip 地址;
C: Leader选举的端口;
D: Zookeeper服务器之间的通信端口。
我们需要修改的第一个是 dataDir ,在指定的位置处创建好目录。
第二个需要新增的是 server.A=B:C:D 配置,其中 A 对应下面我们即将介绍的myid 文件。B是集群的各个IP地址,C:D 是端口配置。
创建 myid 文件在 上一步 dataDir 指定的目录下,创建 myid 文件。然后在该文件添加上一步 server 配置的对应 A 数字,服务器的编号id。
touch /opt/zookeeper/data/myid; #创建文件 echo "0" > /opt/zookeeper/data/myid; #写入id
上面的命令是在192.168.161.3上面执行的,所以写入的id为0。后面的机器依次在相应目录创建myid文件,写上相应配置数字即可。
配置环境变量
为了能够在任意目录启动zookeeper集群,我们需要配置环境变量。
你也可以不配,这不是搭建集群的必要操作,只不过如果你不配置环境变量,那么每次启动zookeeper需要到安装文件的 bin 目录下去启动。
首先进入到 /etc/profile 目录,添加相应的配置信息:
#set zookeeper environment export ZK_HOME=/opt/zookeeper/apache-zookeeper-3.5.7-bin export PATH=$PATH:$ZK_HOME/bin
然后通过如下命令使得环境变量生效:
source /etc/profile
开启防火墙端口
firewall-cmd --zone=public --add-port=2181/tcp --permanent; firewall-cmd --zone=public --add-port=2888/tcp --permanent; firewall-cmd --zone=public --add-port=3888/tcp --permanent; firewall-cmd --reload
启动zookeeper服务
启动命令:
zkServer.sh start
停止命令:
zkServer.sh stop
重启命令:
zkServer.sh restart
查看集群节点状态:
zkServer.sh status
我们分别对集群三台主机执行状态查看命令,其中一台是leader,其他两台是follwer。
出现上面的状态,基本可以认定,我们的集群安装是成功的。启动日志可以通过如下的文件查看:
启动日志中会有一些异常,如果是防火墙端口已经正确打开,通常是因为启动顺序的原因导致的,无关紧要。A连接B,B还没启动,所以抛出“拒绝连接”异常。等B启动了就好了。
zookeeper服务注册与发现 微服务向zookeeper注册
引入依赖包
在之前的章节已经为大家介绍过微服务向eureka注册的实现过程,大同小异。首先我们需要通过maven坐标引入zookeeper的包。
如果项目pom之前存在spring-cloud-starter-netflix-eureka-client,与eureka相关的要删掉。
配置zookeeperorg.springframework.cloud spring-cloud-starter-zookeeper-discovery
有过eureka和我们安装zookeeper集群的经验,下面的配置都很好理解。
spring:
cloud:
zookeeper:
connectString: 192.168.161.3:2181,192.168.161.4:2181,192.168.161.5:2181
discovery:
register: true
prefer-ip-address: true
enabled: true
特别说一下 prefer-ip-address这个配置,如果不设置为true。服务注册的时候注册的访问地址是当前服务运行所在的主机的hostname(所以通常需要配置/etc/hosts才能正确的实现ip与主机hostname映射,才能正确的进行远程服务调用)
设置 prefer-ip-address: true之后,服务注册的时候注册的访问地址是当前服务运行所在的主机的ip.(不用配置/etc/hosts,即可正确实现远程服务调用)
以上的步骤,在所有需要向zookeeper注册的服务上都要进行。如:aservice-rbac、aservice-sms
验证服务注册结果
zookeeper官方并不提供web管理界面(有一些第三方开发的)。所以我们通过命令行来查看服务注册的结果。
zkCli.sh -server 192.168.161.3:2181
我们之前已经给大家介绍过,zookeeper的数据存储结果是按目录的树形结构。所以,看下面的命令:
通过上面的命令进一步认证我们的zookeeper集群安装的正确性。
FeignClient远程服务名称调整
需要注意的是,zookeeper注册的服务名称是spring.application.name,但是并不会将服务名称转成大写,这点与eureka有所区别。所以在使用FeignClient的时候,不能写“ASERVICE-SMS”,而是写“aservice-sms”。
访问测试
访问aservice-rbac的“/pwd/reset”接口(该接口中远程调用了aservice-sms的“/sms/send”短信发送方法),得到正确结果。说明我们的集群安装及服务注册发现全部可以正确的使用。
consul概念及功能介绍 Consul是什么?
Consul 官方站点:https://www.consul.io/
Consul 是一个支持多数据中心分布式高可用的服务发现和配置共享的服务软件,由 HashiCorp 公司用 Go 语言开发, 基于 Mozilla Public License 2.0 的协议进行开源. Consul 支持健康检查,并允许 HTTP 和 DNS 协议调用 API 存储键值对. 一致性协议采用 Raft 算法,用来保证服务的高可用. 使用 GOSSIP 协议管理成员和广播消息, 并且支持 ACL 访问控制.。它具备以下特性:
Consul提供了简单易用的HTTP接口,结合其他工具可以实现动态配置、功能标记、领袖选举等等功能。
Spring Cloud服务与consul
consul作为服务注册中心,并没有与eureka、zookeeper在核心的流程上有区别。仍然遵循于服务注册与发现的基本实现逻辑。
很多人看到上面的这张图会晕掉,它并不像我们之前讲过的Eureka和zookeeper的集群部署拓扑那样好理解。下面就带大家一步一步剖析这张图
上面这张图没有包含Spring Boot(Cloud)的服务。对于Spring Boot服务而言,consul的server和client都是服务端。所以这里的client和server都是对于consul集群内部而言。
| Consul Agent节点运行模式(大使馆) | 特性 |
|---|---|
| Client(工作人员) | consul client不存储任何信息,当接收到服务注册请求的时候,会将服务注册及查询请求转发给Server进行处理。不参与集群Leader的选举,无状态节点不做数据存储 |
| Server Follower(参赞) | 参与Leader选举,维护集群状态,存储服务注册数据,响应本地数据查询。与其他数据中心交互WAN gossip和转发查询给leader或者远程数据中心。 |
| Server Leader(大使) | 管理整个集群的数据同步,与consul集群内的其他节点维持心跳 |
consul-linux集群安装 环境准备
| IP | 节点名称 | Consul角色 |
|---|---|---|
| 192.168.161.3 | s1 | bootstrap Server |
| 192.168.161.4 | s2 | Server |
| 192.168.161.5 | s3 | Server |
| 192.168.161.6 | c1 | Client(UI) |
consul 端口说明bootstrap Server就是人为在consul server启动的时候指定Server Leader,不需要选举。参考下文中agent启动参数 -bootstrap参数说明。
| 端口 | 说明 |
|---|---|
| TCP/8300 | 8300 端口用于服务器节点。客户端通过该端口 RPC 协议调用服务端节点。服务器节点之间相互调用 |
| TCP/UDP/8301 | 8301 端口用于单个数据中心所有节点之间的互相通信,即对 LAN 池信息的同步。它使得整个数据中心能够自动发现服务器地址,分布式检测节点故障,事件广播(如领导选举事件)。 |
| TCP/UDP/8302 | 8302 端口用于单个或多个数据中心之间的服务器节点的信息同步,即对 WAN 池信息的同步。它针对互联网的高延迟进行了优化,能够实现跨数据中心请求。 |
| 8500 | 8500 端口基于 HTTP 协议,用于 API 接口或 WEB UI 访问。 |
| 8600 | 8600 端口作为 DNS 服务器,它使得我们可以通过节点名查询节点信息。 |
所以主机节点的防火墙开放如下端口:
firewall-cmd --zone=public --add-port=8300/tcp --permanent; firewall-cmd --zone=public --add-port=8301/tcp --permanent; firewall-cmd --zone=public --add-port=8302/tcp --permanent; firewall-cmd --zone=public --add-port=8500/tcp --permanent; firewall-cmd --zone=public --add-port=8600/tcp --permanent; firewall-cmd --reload
agent启动参数
consul agent 启动命令参数,consul agent --help
-advertise:通知展现地址用来改变我们给集群中的其他节点展现的地址,一般情况下-bind地址就是展现地址 -bootstrap:用来控制一个server是否在bootstrap模式,在一个datacenter中只能有一个server处于bootstrap模式,当一个server处于bootstrap模式时,可以自己选举为raft leader。 -bootstrap-expect:在一个datacenter中期望提供的server节点数目,当该值提供的时候,consul一直等到达到指定sever数目的时候才会引导整个集群,该标记不能和bootstrap公用。 -bind:该地址用来在集群内部的通讯,集群内的所有节点到地址都必须是可达的,默认是0.0.0.0。 -client:consul绑定在哪个client地址上,这个地址提供HTTP、DNS、RPC等服务,默认是127.0.0.1。 -config-file:明确的指定要加载哪个配置文件 -config-dir:配置文件目录,里面所有以.json结尾的文件都会被加载 -data-dir:提供一个目录用来存放agent的状态,所有的agent都需要该目录,该目录必须是稳定的,系统重启后都继续存在。 -dc:该标记控制agent的datacenter的名称,默认是dc1。 -encrypt:指定secret key,使consul在通讯时进行加密,key可以通过consul keygen生成,同一个集群中的节点必须使用相同的key。 -join:加入一个已经启动的agent的ip地址,可以多次指定多个agent的地址。如果consul不能加入任何指定的地址中,则agent会启动失败。默认agent启动时不会加入任何节点。 -retry-join:和join类似,但是允许你在第一次失败后进行尝试。 -retry-interval:两次join之间的时间间隔,默认是30s。 -retry-max:尝试重复join的次数,默认是0,也就是无限次尝试。 -log-level:consul agent启动后显示的日志信息级别。默认是info,可选:trace、debug、info、warn、err。 -node:节点在集群中的名称,在一个集群中必须是唯一的,默认是该节点的主机名。 -protocol:consul使用的协议版本。 -rejoin:使consul忽略先前的离开,在再次启动后仍旧尝试加入集群中。 -server:定义agent运行在server模式,每个集群至少有一个server,建议每个集群的server不要超过5个。 -syslog:开启系统日志功能,只在linux/osx上生效。 -ui-dir:提供存放web ui资源的路径,该目录必须是可读的。 -pid-file:提供一个路径来存放pid文件,可以使用该文件进行SIGINT/SIGHUP(关闭/更新)agent。
举例:启动Bootstrap Server,具体启动参数实例说明看下文CMD_OPTS
consul agent -server -bootstrap 其他启动参数
举例:启动Server,不加-server启动的agent就是cllient运行模式
consul agent -server 其他启动参数
安装过程
因为consul每次启动都需要配置很多的参数,为了将启动流程及参数固化下来,我们通常会将它写成一个linux 的systemd服务。
consul下载、解压和目录创建不了解systemd服务?白话说就是linux服务启动、停止、重启脚本的一个模板。详细学习的话可以参考https://www.cnblogs.com/jhxxb/p/10654554.html
Systemd 简介
wget https://releases.hashicorp.com/consul/1.7.2/consul_1.7.2_linux_amd64.zip
unzip consul_1.7.2_linux_amd64.zip
mv consul /usr/local/bin/
mkdir -p /opt/consul/{data,config} #创建数据目录和配置目录
consul配置文件
将以下的内容写入对应主机的配置文件
192.168.161.3:/etc/sysconfig/consul,-bootstrap指定了该agent为Server Leader,无需选举。
CMD_OPTS="agent -server -data-dir=/opt/consul/data -node=s1 -config-dir=/opt/consul/config -bind=192.168.161.3 -rejoin -client=0.0.0.0 -bootstrap"
192.168.161.4:/etc/sysconfig/consul,-join要求该agent启动之后加入192.168.161.3为leader的集群
CMD_OPTS="agent -server -data-dir=/opt/consul/data -node=s2 -config-dir=/opt/consul/config -bind=192.168.161.4 -rejoin -client=0.0.0.0 -join 192.168.161.3"
192.168.161.5:/etc/sysconfig/consul,-join要求该agent启动之后加入192.168.161.3为leader的集群
CMD_OPTS="agent -server -data-dir=/opt/consul/data -node=s3 -config-dir=/opt/consul/config -bind=192.168.161.5 -rejoin -client=0.0.0.0 -join 192.168.161.3"
192.168.161.6:/etc/sysconfig/consul,该节点没有指定-server,说明它是client运行模式。-ui说明该client提供web UI的管理界面。
CMD_OPTS="agent -ui -data-dir=/opt/consul/data -node=c1 -config-dir=/opt/consul/config -bind=192.168.161.6 -rejoin -client=0.0.0.0 -join 192.168.161.3"linux consul systemd服务配置
这一步可以不做,只是用来简化每次启动consul都输入一串consul命令的痛苦。如果这一小节内容无法理解,直接用consul <上文定义的CMD_OPTS> 启动consul实例即可
创建service配置文件
touch /usr/lib/systemd/system/consul.service
通过cat命令将如下内容写入文件
cat > /usr/lib/systemd/system/consul.service<服务配置完成之后需要重新加载systemd配置文件
systemctl daemon-reload如果希望consul服务开机启动,执行下面命令
systemctl enable consul #启动命令 journalctl -f #查看启动日志,都可以 journalctl -xe #查看启动日志,都可以手动启动执行下面的命令
systemctl start consul因为上面的CMD_OPTS中制定了-join参数,所以我们就不用手动的把启动后的agent加入集群。如果没有加-join参数,我们可以使用如下命令手动将新启动的agent加入集群。
consul joinconsul集群安装结果验证# 非Leader节点执行 join 通过consul members命令查看该集群agent的组成(分不出谁是leader)。使用consul info命令可以查看更详细的集群信息,可以分出leader。
consul members Node Address Status Type Build Protocol DC Segment s1 192.168.161.3:8301 alive server 1.7.2 2 dc1 s2 192.168.161.4:8301 alive server 1.7.2 2 dc1 s3 192.168.161.5:8301 alive server 1.7.2 2 dc1 c1 192.168.161.6:8301 alive client 1.7.2 2 dc1web UI访问192.168.161.6:8500/ui可以查看集群的agent节点组成,也可以表示我们安装成功。
consul命令详解(用到查一下、不用记)agent指令是consul的核心,它运行agent来维护成员的重要信息、运行检查、服务宣布、查询处理等等。join指令告诉consul agent加入一个已经存在的集群中,一个新的consul agent必须加入一个已经有至少一个成员的集群中,这样它才能加入已经存在的集群中,如果你不加入一个已经存在的集群,则agent是它自身集群的一部分,其他agent则可以加入进来。agent可以加入其他agent多次。如果你想加入多个集群,则可以写多个地址,consul会加入所有的地址。leave指令触发一个优雅的离开动作并关闭agent,节点离开后不会尝试重新加入集群中。运行在server状态的节点,节点会被优雅的删除,这是很严重的,在某些情况下一个不优雅的离开会影响到集群的可用性。members指令输出consul agent目前所知道的所有的成员以及它们的状态,节点的状态只有alive、left、failed三种状态。info指令提供了各种操作时可以用到的debug信息,对于client和server,info有返回不同的子系统信息,目前有以下几个KV信息:agent(提供agent信息),consul(提供consul库的信息),raft(提供raft库的信息),serf_lan(提供LAN gossip pool),serf_wan(提供WAN gossip pool)。event命令提供了一种机制,用来fire自定义的用户事件,这些事件对consul来说是不透明的,但它们可以用来构建自动部署、重启服务或者其他行动的脚本。exec指令提供了一种远程执行机制,比如你要在所有的机器上执行uptime命令,远程执行的工作通过job来指定,存储在KV中。agent使用event系统可以快速的知道有新的job产生,消息是通过gossip协议来传递的,因此消息传递是最佳的,但是并不保证命令的执行。事件通过gossip来驱动,远程执行依赖KV存储系统(就像消息代理一样)。force-leave可以强制consul集群中的成员进入left状态(空闲状态),记住,即使一个成员处于活跃状态,它仍旧可以再次加入集群中,这个方法的真实目的是强制移除failed的节点。如果failed的节点还是网络的一部分,则consul会周期性的重新链接failed的节点,如果经过一段时间后(默认是72小时),consul则会宣布停止尝试链接failed的节点。force-leave指令可以快速的把failed节点转换到left状态。keygen指令生成加密的密钥,可以用在consul agent通讯加密。monitor指令用来链接运行的agent,并显示日志。monitor会显示最近的日志,并持续的显示日志流,不会自动退出,除非你手动或者远程agent自己退出。reload指令可以重新加载agent的配置文件。SIGHUP指令在重新加载配置文件时使用,任何重新加载的错误都会写在agent的log文件中,并不会打印到屏幕。version指令用作打印consul的版本watch指令提供了一个机制,用来监视实际数据视图的改变(节点列表、成员服务、KV),如果没有指定进程,当前值会被dump出来。
consul服务注册与发现 微服务向consul注册 引入依赖包在之前的章节已经为大家介绍过微服务向eureka、zookeeper注册的实现过程,大同小异。首先我们需要通过maven坐标引入consul的包。
如果项目pom之前存在spring-cloud-starter-netflix-eureka-client,与eureka相关的要删掉,spring-cloud-starter-zookeeper-discovery要删掉。
org.springframework.cloud spring-cloud-starter-consul-discovery org.springframework.boot spring-boot-starter-actuator 因为consul的健康检查端点为’/actuator/health’,依赖于spring-boot-starter-actuator,所以对应的maven坐标要一并引入。
配置consul将host指向consul集群内的任意一个server即可。
spring: cloud: consul: #Consul服务注册发现配置 host: 192.168.161.3 port: 8500 discovery: prefer-ip-address: true service-name: ${spring.application.name}打开discovery开关 启动日志出现如下的一些日志,表示服务注册成功
验证服务注册结果
可以通过consul的WEB UI界面查看。也可以通过命令行,获取aservice-sms的注册信息,可以使用如下命令。
curl -s 127.0.0.1:8500/v1/catalog/service/aservice-smsFeignClient远程服务名称调整接口访问测试
需要注意的是,和zookeeper一样,consul注册的服务名称’spring.application.name’,但是并不会将服务名称转成大写,这点与eureka有所区别。所以在使用FeignClient的时候,不能写“ASERVICE-SMS”,而是写“aservice-sms”。访问aservice-rbac的“/pwd/reset”接口(该接口中远程调用了aservice-sms的“/sms/send”短信发送方法),得到正确结果。说明我们的集群安装及服务注册发现全部可以正确的使用。
如果出现401的错误,请看下一节内容。
通用-auatator导致401问题 问题描述有的同学可能会遇到:当项目引入如下的spring-boot-starter-actuator坐标之后,项目的服务如“/user/”和“/role/”无法被访问到,返回401无权访问的问题。
原因org.springframework.boot spring-boot-starter-actuator 这是因为你的项目同时引入了spring-boot-starter-security,二者同时引入,就会默认开启basicAuth的权限验证,导致本来公开访问的接口需要携带用户名密码才能访问。
org.springframework.boot spring-boot-starter-security 笔者根据经验,为您准备了如下的一系列方案,说实话基于不同的场景,需要使用不同的方法。根据版本差异也有可能部分解决方案失效,可以根据自己项目情况选择适用。
解决方案一spring: security: basic: enabled: false #关闭Basic认证解决方案二如果你的项目没有用到Spring Security,就将spring-boot-starter-security删除或者我的项目暂时没有用到Spring Security管理接口权限(当前章节没用到,后面会结合网关使用Spring Security)。只是用到了Spring Security进行密码加密。我们就引入对应的子包即可,不要引入spring-boot-starter-security。因为spring-boot-starter-security有自动装配机制,自动就把默认的安全权限给你配置上了。
比如引入子包spring-security-core
解决方案三org.springframework.security spring-security-core 5.2.2.RELEASE 如果你的项目确实使用到了Spring Security,请通过Spring Security的配置,正确的将项目接口访问权限开放。需要你掌握Spring Security的配置方法,下面只是一个例子。自己还需要根据自己项目情况配置权限。去学Spring Security。既然用到,就要学到
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/actuator/**").permitAll() .anyRequest().authenticated(); } }解决方案四这种方法很简单,但经过笔者测试在当前版本下不生效。网上有人使用是可以生效的,应该是版本差异,所以记在这里备选。
management: security: enabled: false解决方案五这是一种比较通用的,防止Spring Security自动装配的方法。但是笔者测试,加入spring-boot-starter-actuator后,该配置失效。可能也是版本差异的问题,作为一种解决方案提供大家实验。



