上一章节,讲解了在单机模式下的服务注册与发现的相关知识点及简单示例。而在实际生产或者在这种微服务架构的分布式环境中,须要考虑发生故障时,各组件的高可用。而其实高可用,个人简单粗俗理解就是,经过系统的冗余进行高可用,或者是进行集群部署,保证一台服务不可用时,会进行自动转移至可用的服务中。今天的章节,就来讲说关于
Eureka
的高可用吧。html
讲解前,咱们先来聊聊在使用
Dubbo
时耳闻能详的Zookeeper
和Eureka
之间的区别吧。java
根据百度百科的定义,CAP定理又称CAP原则,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),最多只能同时三个特性中的两个,三者不可兼得。git
在分布式领域,你们应该对CAP
理论不陌生了吧(了解但也是没有深刻了解过⊙﹏⊙‖∣)。github
如下摘至百度百科:web
● 一致性(C):在分布式系统中的全部数据备份,在同一时刻是否一样的值。(等同于全部节点访问同一份最新的数据副本)spring
● 可用性(A):在集群中一部分节点故障后,集群总体是否还能响应客户端的读写请求。(对数据更新具有高可用性)api
● 分区容错性(P):以实际效果而言,分区至关于对通讯的时限要求。系统若是不能在时限内达成数据一致性,就意味着发生了分区的状况,必须就当前操做在C和A之间作出选择。浏览器
至于为什么不能同时知足,以上在分区容错性也有简单的说明了,通常来讲,分区容错没法避免,所以能够认为CAP
的P
老是成立,而对于一致性和可用性为什么不能同时知足,简单来讲就是:存在可能通讯失败状况,即:出现分区容错,至于更详细具体的,你们能够看下大佬阮一峰的这篇文章:CAP 定理的含义。这里就不阐述了,不是很了解~缓存
对于Eureka而言,其是知足
AP
的,而Zookeeper而言,是知足CP
的。安全
Eureka是知足AP
的:
Zookeeper是知足AP
的:
个人简单理解:因为Zookeeper
有loader选举策略,使其能够保证数据的一致性。而Eureka
,本事没有选举策略,各服务是独立运行的,至少在某一时刻,各服务之间的数据是不一致的,而在可用性方面,Zookeeper
也是因为选举策略缘由,在选举期间是,整个zookeeper
是不可用的,会形成短暂(看选举时长)的服务不可用。而对于Eureka
而言,服务是独立运行的,因此不会由于某台服务不可用致使了其余服务不可用状况。感受上面有点绕,简单来讲,就是Zookeeper
经过选举策略保证数据的一致性,但缺失了可用性,Eureka
因为服务独立运行,经过心跳等通讯策略进行数据同步,存在数据不一致性,但保证了服务的可用性。
因此,综上所述,做为服务注册中心而言,可用性原则是比数据一致性更重要的,同时上一章节也有说过,因为Eureka
有自我保护模式,可保护服务注册表中的信息不被剔除,因此Eureka
能够很好的应对因网络故障致使节点失去联系的状况。
官网中,关于Eureka
的高可用部分是这么描述的:
因此能够获悉,Eureka Server
能够运行多个实例来构建集群,解决单点问题,Eureka Server
采用的是Peer to Peer对等通讯。这是一种去中心化的架构,无master/slave区分,每个Peer都是对等的。在这种架构中,节点经过彼此互相注册来提升可用性,每一个节点须要添加一个或多个有效的serviceUrl指向其余节点。每一个节点均可被视为其余节点的副本。
若是某台Eureka Server
宕机,Eureka Client
的请求会自动切换到新的Eureka Server
节点,当宕机的服务器从新恢复后,Eureka
会再次将其归入到服务器集群管理之中。当节点开始接受客户端请求时,全部的操做都会进行replicateToPeer(节点间复制)
操做,将请求复制到其余Eureka Server
当前所知的全部节点中。
因此,简单来讲,Eureka Server
的高可用,实际上就是将本身也做为服务向其余服务注册中心进行注册,这样就能够造成一组相互注册的服务注册中心,以实现服务清单的互相同步,达到高可用的效果。
另外,从官网文档中有提到Zones
、Regions
,Region
和Zone
(或者Availability Zone)均是AWS的概念。在非AWS环境下,咱们能够先简单地将region理解为Eureka集群,zone理解成机房。下图就能够理解为一个Eureka集群被部署在了zone1机房和zone2机房中。
对这些概念的其余相关知识,也深刻了解,你们感兴趣,可自行搜索下吧。
示例前,先看看集群模式下,Eureka的架构图。
具体的原理分析,能够看看这篇比较早的文章:https://nobodyiam.com/2016/06/25/dive-into-eureka/,虽然是比较早的文章,但写的比较详细,能够看看。
接下来,以官网文档demo,示例下。
经过点对点配置,注册中心经过相互注册来实现高可用配置。如下构建一个双节点的集群模式。
修改spring-cloud-eureka-server
项目。
0.建立一个application-ha.properties
配置文件,同时修改application.properties
文件。
application-ha.properties
spring.application.name=eureka-service-ha # 修改端口 server.port=1001 # 实例的主机名称 eureka.instance.hostname=myPeer2 ## 不要向注册中心注册本身 #eureka.client.register-with-eureka=false ## 表示不去检索其余的服务,由于服务注册中心自己的职责就是维护服务实例,它也不须要去检索其余服务 #eureka.client.fetch-registry=false # 指定服务注册中心地址 eureka.client.service-url.defaultZone=http://myPeer1:1000/eureka
application.properties
spring.application.name=eureka-service-ha # 修改端口 server.port=1000 # 实例的主机名称 eureka.instance.hostname=myPeer1 ## 不要向注册中心注册本身 #eureka.client.register-with-eureka=false ## 表示不去检索其余的服务,由于服务注册中心自己的职责就是维护服务实例,它也不须要去检索其余服务 #eureka.client.fetch-registry=false # 指定服务注册中心地址 eureka.client.service-url.defaultZone=http://myPeer2:1000/eureka #spring.profiles.active=ha
1.因为是在同一台进行模拟,首先修改hosts
文件,当浏览器请求一个地址时,首先会今后文件选择对应对应的IP地址,找不到时才请求CDS域名解析服务器进行解析
C:\Windows\System32\drivers\etc\hosts
文件:
127.0.0.1 myPeer1 127.0.0.1 myPeer2
友情提示:若提示无修改权限,可根据如下网址进行相应修改:编辑hosts文件没法保存怎么办
2.启动应用,咱们这里直接启动两个,可经过修改spring.profiles.active
值来启动不一样环境的应用,或者使用-jar xx.jar --spring.profiles.active=xx
来启动。
题外话:第一个启动的应用,后台会报错Connection refused: connect
,等第二个应用启动后,就正常了~
客户端只须要经过修改配置文件的
eureka.client.service-url.defaultZone
值便可。
修改spring-cloud-eureka-client
项目
0.修改配置文件application.properties
spring.application.name=eureka-client server.port=2000 # 注册中心地址 eureka.client.service-url.defaultZone=http://myPeer1:1000/eureka,http://myPeer2:1001/eureka # 启用ip配置 这样在注册中心列表中看见的是以ip+端口呈现的 eureka.instance.prefer-ip-address=true # 实例名称 最后呈现地址:ip:2000 eureka.instance.instance-id=${spring.cloud.client.ip-address}:${server.port}
固然,也可只注册到某个节点上,其余的节点也会有此服务列表的,通常建议以集群方式进行配置,即多注册中心配置。避免单点故障,Eureka在搜索注册中心时,根据defaultZone列表,找到一个可用的,以后就不会继续去下一个注册中心地址拉取服务列表了,此时若其中一个注册中心挂了,这个时候客户端会继续去第二个注册中心拉取服务列表的。
启动后,能够看见eureka-client
注册上去了。
如今咱们中止一个应用,能够看出不可用的服务列表中已经有相关信息了
为了验证高可用性是否成功,建立一个
spring-cloud-eureka-server-ha-test
项目,做为服务消费者使用RestTemplate+ribbon
进行调用spring-cloud-eureka-client
的服务。
建立spring-cloud-eureka-server-ha-test
项目 0.引入pom依赖
<!-- 客户端依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!-- 引入web,提供一个简单的api接口 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
1.配置文件,配置注册中心地址
spring.application.name=eureka-ha-test server.port=8888 #指定注册中心地址 eureka.client.serviceUrl.defaultZone=http://myPeer1:1000/eureka/,http://myPeer2:1001/eureka/ # 启用ip配置 这样在注册中心列表中看见的是以ip+端口呈现的 eureka.instance.prefer-ip-address=true # 实例名称 最后呈现地址:ip:2000 eureka.instance.instance-id=${spring.cloud.client.ip-address}:${server.port}
2.启动类,配置RestTemplate
Bean类,同时加入@LoadBalanced注解实现服务调用。
@SpringCloudApplication @EnableDiscoveryClient @Slf4j public class EurekaServiceHaApplication { public static void main(String[] args) throws Exception { SpringApplication.run(EurekaServiceHaApplication.class, args); log.info("spring-cloud-eureka-server-ha-test启动!"); } //加入负载均衡能力 //同时可根据applicationName 来访问服务 //如http://EUREKA-CLIENT/add @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } }
3.编写一个控制类,简单调用EUREKA-CLIENT
服务方法。
/** * 调用简单示例 * @author oKong * */ @RestController public class DemoController { @Autowired private RestTemplate testTemplate; @GetMapping("/") public String index() { //访问服务提供者 return testTemplate.getForObject("http://EUREKA-CLIENT/", String.class); } }
4.启动应用类,访问注册中心和http://127.0.0.1:8888 。
可看见输出spring-cloud-eureka-client!
代表调用成功。在控制台也能够看见,从注册中心拉取了EUREKA-CLIENT
的服务地址:
这个时候,能够试着中止其中一个甚至所有的Eureka Server
,再继续访问,能够看见服务仍是能够调用的,但要知道此时调用方是多是根据本地的缓存列表中直接获取地址的,而不是从注册服务中心,等待下次心跳机制时间到时,才会去进行拉取最新的服务列表的。
关于RestTemplate
和Ribbon
相关知识点,会在下一章节进行阐述的,这里就不敞开了。
第一次看官网文档进行demo时,还在想经过设置端口号不就能够了吗,为什么还须要指定eureka.instance.hostname
呢,多麻烦?尝试下使用ip或者不配置试试看。
eureka.instance.hostname
为localhost
或者127.0.0.1
时,能够看见,在DS Replicas
中是空的,说明没有可复制的服务,registered-replicas
和available-replicas
都是空的。
客户端配置eureka.client.service-url.defaultZone
。
eureka.client.service-url.defaultZone=http://127.0.0.1:1001/eureka,http://127.0.0.1:1000/eureka
会发现,在1001
服务上有注册上去,1000
服务没有服务信息。(客户端注册是按顺序进行优先注册和获取服务列表的)
1001服务:
1000服务:
这说明集群模式是没有生效的,注册中心之间没有相互复制服务列表。
127.0.0.1
,另外一个修改为localhost
或者实际内网ip地址说明下: 1001对应hostname
为:127.0.0.1 1000对应hostname
为:192.168.81.1
1001服务:
1000服务:
会发现,集群模式成功了。 接着咱们启动客户端。
1000服务:
1001服务:
能够看见,和上面设置myPeer1
和myPeer2
效果是同样的,都有被复制了。
从上面能够大体获悉,Eureka
互相注册要求各个Eureka server
实例的eureka.instance.hostname
不一样,若是相同,则会被Eureka
标记为unavailable-replicas
(像本地设置为127.0.0.1,干脆就不显示了,具体不明。。⊙﹏⊙‖∣),以前的同步就失效了。而对于客户端的defaultZone
配置而言,是优先从第一个开始注册和拉取服务的,成功联通后就不会再继续找下一个注册服务了。
综上所述:
Eureka server
服务时,设置每一个服务 的hostname
不一致,同时要设置eureka.instance.prefer-ip-address
为false
,不使用IP地址进行注册。hostname
为本机的ip地址,可以使用spring.cloud.client.ip-address
变量进行赋值。同时设置eureka.instance.prefer-ip-address
为true
。这样的话,客户端使用ip进行链接就方便了,否则还要去配置host有点坑了。defaultZone
建议仍是配置多个注册中心地址。就算自己集群模式无效,好歹其中一个不可用了,还能连其余的注册中心呀。在最后收尾时,搜索到一篇文章,也是大体的意思,你们能够点击看一看:构建高可用Eureka注册中心
其源码地址:https://github.com/wangfei0904306/eureka-HA
以上可能理解有误差,还但愿知道的同窗能不吝赐教下,谢谢了!
默认状况下,访问注册中心页面是匿名访问的,不须要一些认证。在生产中,为了安全性,可加入身份认证功能。
添加认证很简单,官网文档也有说明:
0.加入pom依赖:
<!-- 开启认证 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
1.修改配置文件,设置用户名和密码及集群状态下,注册至其余服务中心时,加入用户名和密码:
# 设置账号密码 # 若不设置 默认账号是user,密码随机,启动时会打印在控制台上 spring.security.user.name=oKong spring.security.user.password=123456 # 加入用户名和密码 eureka.client.service-url.defaultZone=http://oKong:123456@127.0.0.1:1001/eureka
友情提示:不设置name和password时,默认用户是user,密码是随机的,启动时会打印在控制台上的:
2.修改启动类,根据官网的提示,关闭/eureka/**
的CSRF的令牌。
/** * Eureka服务端 * @author oKong * */ @SpringBootApplication @EnableEurekaServer @Slf4j public class EureakServiceApplication { public static void main(String[] args) throws Exception { SpringApplication.run(EureakServiceApplication.class, args); log.info("spring-cloud-eureka-service启动!"); } /** * 忽略此路径下的CSRF令牌 * @author oKong * */ @EnableWebSecurity static class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().ignoringAntMatchers("/eureka/**"); super.configure(http); } } }
3.启动应用。再次访问:http:127.0.0.1:1000 时,就须要输入用户名和密码了。
4.客户端也是相似的,修改defaultZone
加入用户名和密码便可:
eureka.client.service-url.defaultZone=http://oKong:123456@127.0.0.1:1001/eureka,http://oKong:123456@127.0.0.1:1000/eureka
本章节主要是
Eureka
服务的高可用进行了简单的介绍了下。对于集群模式下的一些最佳实践仍是有待商讨的,还但愿你们能说说自个的方案!至于一些其余的特性,如元数据
配置等,这里就不阐述了,用的很少。同时,本章节利用RestTemplate+ribbon
进行了简单的服务调用,没有敞开说,下一章节就是开始讲解服务消费者
相关知识点,应该也会分红两章节来描述,由于涉及到了Ribbon
和Feign
相关知识点。我一直以为使用都是很简单的,只有理解里面的原理之类的,这样才是一通百通吧,加深印象!使用起来也能比较顺利。
目前互联网上大佬都有分享
SpringCloud
系列教程,内容可能会相似,望多多包涵了。原创不易,码字不易,还但愿你们多多支持。若文中有错误之处,还望提出,谢谢。
499452441
lqdevOps
我的博客:http://blog.lqdev.cn
源码示例:https://github.com/xie19900123/spring-cloud-learning
原文地址:http://blog.lqdev.cn/2018/09/09/SpringCloud/chapter-three/