上一篇 : 从零开始学习微服务 -微服务基本概述、微服务案例 1.1 Eureka服务注册中心
常用的服务注册中心:Eureka、Nacos、Zookeeper、Consul 1.1.1 关于注册中心
注意:服务注册中心本质上是为了解耦服务提供者和服务消费者。服务消费者 --> 服务提供者服务消费者 --> 服务注册中心 --> 服务提供者对于任何一个微服务,原则上都应存在或者支持多个提供者(比如商品微服务部署多个实例),这
是由微服务的分布式属性决定的更进一步,为了支持弹性扩、缩容特性,一个微服务的提供者的数量和分布往往是动态变化的,也
是无法预先确定的。因此,原本在单体应用阶段常用的静态LB机制就不再适用了,需要引入额外的组件
来管理微服务提供者的注册与发现,而这个组件就是服务注册中心。 1.1.2 注册中心实现原理
分布式微服务架构中,服务注册中心用于存储服务提供者地址信息、服务发布相关的属性信息,消
费者通过主动查询和被动通知的方式获取服务提供者的地址信息,而不再需要通过硬编码方式得到提供
者的地址信息。消费者只需要知道当前系统发布了那些服务,而不需要知道服务具体存在于什么位置,
这就是透明化路由。
(1) 服务提供者启动 :
(2) 服务提供者将相关服务信息主动注册到注册中心
(3) 服务消费者获取服务注册信息:
pull模式:服务消费者可以主动拉取可用的服务提供者清单push模式:服务消费者订阅服务(当服务提供者有变化时,注册中心也会主动推送更新后的服务清单给消费者
(4) 服务消费者直接调用服务提供者另外,注册中心也需要完成服务提供者的健康监控,当发现服务提供者失效时需要及时剔除;
另外,注册中心也需要完成服务提供者的健康监控,当发现服务提供者失效时需要及时剔除
1.2 主流服务中心 1.2.1 Zookeeper Dubbo + ZookeeperZookeeper它是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决
分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布
式应用配置项的管理等。简单来说zookeeper本质 = 存储 + 监听通知。Zookeeper 用来做服务注册中心,主要是因为它具有节点变更通知功能,只要客户端监听相
关服务节点,服务节点的所有变更,都能及时的通知到监听客户端,这样作为调用方只要使用
Zookeeper 的客户端就能实现服务节点的订阅和变更通知功能了,非常方便。另外,Zookeeper可用性也可以,因为只要半数以上的选举节点存活,整个集群就是可用的,最少节点数为3。 1.2.2 Eureka
由Netflix开源,并被Pivatal集成到SpringCloud体系中,它是基于 RestfulAPI 风格开发的服务
注册与发现组件。 1.2.3 Consul
Consul是由HashiCorp基于Go语言开发的支持多数据中心分布式高可用的服务发布和注册服
务软件, 采用Raft算法保证服务的一致性,且支持健康检查 1.2.4 Nacos
Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。简单来说
Nacos 就是 注册中心 + 配置中心的组合,帮助我们解决微服务开发必会涉及到的服务注册 与发
现,服务配置,服务管理等问题。
Nacos 是 Spring Cloud Alibaba 核心组件之一,负责服务注册与发现,还有配置
CAP定理又称CAP原则,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用
性)、Partition tolerance(分区容错性),最多只能同时三个特性中的两个,三者不可兼得。
P:分区容错性:分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性或可用
性的服务(一定的要满足的)C:数据一致性:all nodes see the same data at the same timeA:高可用:Reads and writes always succeedCAP不可能同时满足三个,要么是AP,要么是CP 1.2.5 服务注册中心组件
服务注册中心的一般原理、对比了主流的服务注册中心方案,目光聚焦Eureka。
Eureka 基础架构
Eureka 交互流程及原理
Eureka 包含两个组件:Eureka Server 和 Eureka Client,Eureka Client是一个Java客户端,用于简化与Eureka Server的交互;Eureka Server提供服务发现的能力,各个微服务启动时,会通过Eureka Client向Eureka Server 进行注册自己的信息(例如网络信息),Eureka Server会存储该服务的信息;
(1) 图中us-east-1c、us-east-1d,us-east-1e代表不同的区也就是不同的机房
(2) 图中每一个Eureka Server都是一个集群
(3) 图中Application Service作为服务提供者向Eureka Server中注册服务,Eureka Server接受到注册事件会在集群和分区中进行数据同步,Application Client作为消费端(服务消费者)可以从EurekaServer中获取到服务注册信息,进行服务调用。
(4) 微服务启动后,会周期性地向Eureka Server发送心跳(默认周期为30秒,默认Eureka Server
90S会将还没有续约的给剔除)以续约自己的信息
(5) Eureka Server在一定时间内没有接收到某个微服务节点的心跳,Eureka Server将会注销该微
服务节点(默认90秒)
(6) 每个Eureka Server同时也是Eureka Client,多个Eureka Server之间通过复制的方式完成服务
注册列表的同步
(7) Eureka Client会缓存Eureka Server中的信息。即使所有的Eureka Server节点都宕掉,服务消
费者依然可以使用缓存中的信息找到服务提供者
Eureka通过心跳检测、健康检查和客户端缓存等机制,提高系统的灵活性、可伸缩性和高可用性。
1.3 Eureka服务注册中心搭建 1.3.1 基本步骤单实例Eureka Server—>访问管理界面 服务提供者(商品微服务注册到集群)服务消费者(页面静态化微服务注册到Eureka/从Eureka Server获取服务信息)完成调用 1.3.2 搭建Eureka Server
创建一个项目名为 lg-cloud-eureka 的 maven 项目 1.3.3 添加配置
maven配置
<?xml version="1.0" encoding="UTF-8"?>
application.yml
# Eureka server服务端口server: port: 9200spring: application: # 应用名称,会在Eureka中作为服务的id标识 name: lg-cloud-eureka-servereureka: instance: hostname: 127.0.0.1 client: # 客户端与EurekaServer交互的地址,如果是集群,也需要写其它Server的地址 service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 自己就是服务不需要注册自己 register-with-eureka: false # 自己就是服务不需要从Eureka Server获取服务信息,默认为true,置 为false fetch-registry: false
hostname 需要设置为 127.0.0.1, 若是 localhost 可能会报错
参考 : unable-to-send-heartbeat
1.3.4 关于jaxb
注意:在父工程的pom文件中手动引入jaxb的jar,因为Jdk9之后默认没有加载该模块,Eureka
Server使用到,所以需要手动导入,否则EurekaServer服务无法启动
所以需要在 lg-parent 添加 jaxb 配置
如果版本不兼容可能会出现无法启动的问题, 报一些过滤器无法创建之类的错误,
所以一定要确定 SpringBoot和Eureka的版本
No qualifying bean of type 'javax.servlet.Filter' available: expected at least
1.3.6 创建启动类
EurekaApplication, @EnableEurekaServer 开启 Eureka
package cn.knightzz;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@SpringBootApplication@EnableEurekaServerpublic class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class,args); }}
访问http://127.0.0.1:9200,如果看到如下页面(Eureka注册中心后台),则表明EurekaServer发布成功
1.4 Eureka微服务注册商品微服务和页面静态化微服务注册到Eureka 1.4.1 添加配置
分别在 lg-service-page 和 lg-service 的 pom文件中添加Eureka Client依赖
这里需要注意的是, 可能会出现找不到依赖的问题, 这是因为没有指定版本
具体的版本需要点击 Greenwich.RELEASE 即可查看对应的spring-cloud 版本
添加 Eureka Client客户端
添加 eureka 配置
eureka: client: # eureka server的路径 service-url: defaultZone: http://127.0.0.1:9200/eureka/ instance: #使用ip注册,否则会使用主机名注册了(此处考虑到对老版本的兼容,新版本经过实验都是ip) prefer-ip-address: true #自定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress instance-id: ${spring.cloud.client.ip- address}:${spring.application.name}:${server.port}:@project.version@
需要注意的是, 请求路径不能是 localhost , 需要是 127.0.0.1 , 否则会出现
was-unable-to-send-heartbeat
参考 :
Spring Cloud 客户端报错:- was unable to send heartbeat!unable-to-send-heartbeat 1.4.2 添加注解
在启动类上添加服务发现注解 @EnableDiscoveryClient
@SpringBootApplication@EnableDiscoveryClientpublic class Application {}
启动后 http://localhost:9200/
1.5 Eureka细节详解 1.5.1 Eureka元数据详解
Eureka的元数据有两种:标准元数据和自定义元数据。
标准元数据:主机名、IP地址、端口号等信息,这些信息都会被发布在服务注册表中,用于服务之
间的调用
自定义元数据:可以使用eureka.instance.metadata-map配置,符合KEY/VALUE的存储格式。这
些元数据可以在远程客户端中访问
在 lg-cloud-eureka 中添加
eureka: instance: hostname: 127.0.0.1 metadata-map: ip: 192.168.200.128 port: 10000 user: admin pwd: 123456
1.5.2 客户端读取元数据
我们可以在程序中可以使用DiscoveryClient 获取指定微服务的所有元数据信息
在 lg-service-page 里添加Controller获取元数据信息
package cn.knightzz.page.controller;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;import java.util.List;import java.util.Map;import java.util.Set;@RestController@RequestMapping("/metadata")public class metadataController { @Resource private DiscoveryClient discoveryClient; @RequestMapping("show") public String showmetaData() { StringBuilder result = new StringBuilder(); // 获取实例, 因为是集群方式, 所以一个微服务可能会有多个服务 List
debug查看元数据
1.5.3 Eureka客户端详解
服务提供者(也是Eureka客户端)要向EurekaServer注册服务,并完成服务续约等工作
服务注册详解(服务提供者)
当我们导入了eureka-client依赖坐标,配置Eureka服务注册中心地址服务在启动时会向注册中心发起注册请求,携带服务元数据信息Eureka注册中心会把服务的信息保存在Map中。
服务续约详解(服务提供者)
服务每隔30秒会向注册中心续约(心跳)一次(也称为报活),如果没有续约,租约在90秒后到期,
然后服务会被失效。每隔30秒的续约操作我们称之为心跳检测
Eureka Client :30S续约一次,在Eureka Server更新自己的状态 (Client端进行配置)
Eureka Server:90S还没有进行续约,将该微服务实例从服务注册表(Map)剔除 (Client端进行
配置)
Eureka Client: 30S拉取服务最新的注册表并缓存到本地 (Client端进行配置)
往往不需要我们调整这两个配置, 默认就可以, 如果需要(在 lg-cloud-eureka配置)
eureka: instance: # 租约续约间隔时间,默认30秒 lease-renewal-interval-in-seconds: 30 # 租约到期,服务时效时间,默认值90秒,服务超过90秒没有发生心跳,EurekaServer会将服务从列 表移除 lease-expiration-duration-in-seconds: 90
获取服务列表(服务注册表)详解(服务消费者)
每隔30秒服务会从注册中心中拉取一份服务列表,这个时间可以通过配置修改。往往不需要我们调
整
#向Eureka服务中心集群注册服务 eureka: client: # 每隔多久拉取一次服务列表 registry-fetch-interval-seconds: 30
服务消费者启动时,从 EurekaServer服务列表获取只读备份,缓存到本地
每隔30秒,会重新获取并更新数据
每隔30秒的时间可以通过配置eureka.client.registry-fetch-interval-seconds修改
1.5.4 Eureka服务端详解服务下线:
当服务正常关闭操作时,会发送服务下线的REST请求给EurekaServer。服务中心接受到请求后,将该服务置为下线状态 失效剔除:
Eureka Server会定时(间隔值是eureka.server.eviction-interval-timer-in-ms,默认60s)进行检查,如果发现实例在在一定时间(此值由客户端设置的eureka.instance.lease-expiration-duration-inseconds定义,默认值为90s)内没有收到心跳,则会注销此实例。 1.5.5 自我保护机制
自我保护机制:
自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的
健壮、稳定的运行。自我保护机制的工作机制是:如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么
Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前
节点依然可用。当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
因此Eureka Server可以很好的应对因网络故障导致部分节点失联的情况,而不会像ZK那样如果有一半
不可用的情况会导致整个集群不可用而变成瘫痪
为什么会有自我保护机制?
默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,
Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。
服务中心页面会显示如下提示信息
我们在单机测试的时候很容易满足心跳失败比例在 15 分钟之内低于 85%,这个时候就会触发 Eureka
的保护机制,一旦开启了保护机制(默认开启),则服务注册中心维护的服务实例就不是那么准确了,
此时我们通过修改Eureka Server的配置文件来关闭保护机制,这样可以确保注册中心中不可用的实例被
及时的剔除
经验:建议生产环境打开自我保护机制
1.6 关于无法获取心跳机制的问题was unable to send heartbeat!
有一定原因是需要等待几分钟, 在服务中心注册完成后, 才可以, 如果刚启动立马请求获取元数据存在失败的可能 Cannot execute request on any known server