SpringCloud是分布式微服务架构的一站式解决方案,是一个全家桶式的技术栈,包含了很多组件。接下来咱们就开始学习其最核心的几个组件入手,也就是Eureka、Ribbon、Feign、Hystrix、Zuul这几个组件,并且会剖析一下每个组件其底层的工作原理。
在学习Eureka之前,咱们先来思考一个业务场景,假设咱们现在开发一个电商网站,要实现支付订单的功能,流程如下:
(1)创建一个订单后,如果用户立刻支付了这个订单,我们需要将订单状态 更新为“已支付” (2) 扣减相应的商品库存 (3) 通知仓储中心,进行发货 (4) 给用户的这次购物增加相应的积分
针对上述流程,我们需要有订单服务、库存服务、仓储服务、积分服务。整个流程的大体思路如下:
(1) 用户针对一个订单完成支付之后,就会去找订单服务,更新订单状态
(2) 订单服务调用库存服务,完成相应功能
(3) 订单服务调用仓储服务,完成相应功能
(4) 订单服务调用积分服务,完成相应功能
至此,整个支付订单的业务流程结束:
咱们来考虑第一个问题:订单服务想要调用库存服务、仓储服务,或者是积分服务,怎么调用?
**订单服务压根儿就不知道人家库存服务在哪台机器上啊!他就算想要发起一个请求,都不知道发送给谁,有心无力!**这时候,就轮到Spring Cloud Eureka出场了。Eureka是微服务架构中的注册中心,专门负责服务的注册与发现。
如上图所示,库存服务、仓储服务、积分服务中都有一个Eureka Client组件,这个组件专门负责将这个服务的信息注册到Eureka Server中。说白了,就是告诉Eureka Server,自己在哪台机器上,监听着哪个端口。而Eureka Server是一个注册中心,里面有一个注册表,保存了各服务所在的机器和端口号。
订单服务里也有一个Eureka Client组件,这个Eureka Client组件会找Eureka Server问一下:库存服务在哪台机器啊?监听着哪个端口啊?仓储服务呢?积分服务呢?然后就可以把这些相关信息从Eureka Server的注册表中拉取到自己本地缓存起来。
这时如果订单服务想要调用库存服务,不就可以找自己本地的Eureka Client问一下库存服务在哪台机器?监听哪个端口吗?收到响应后,紧接着就可以发送一个请求过去,调用库存服务扣减库存的那个接口!同理,如果订单服务要调用仓储服务、积分服务,也是如法炮制。
Eureka Client:负责将这个服务的信息注册到Eureka Server中或者是从Eureka Server中读取服务的注册表信息。 Eureka Server:注册中心,里面有一个注册表,保存了各个服务所在的机器和端口号
Eureka(优瑞卡)
Eureka在微服务架构中充当着注册中心的角色,用于实现服务的注册和发现。
举一个生活中的例子,就比如我们平时租房子找中介的事情。 在没有中介的时候我们需要一个一个去寻找是否有房屋要出租的房东,这显然会非常的费力,仅凭你一个人的能力是找不到很多房源供你选择,再者你也懒得这么找下去(找了这么久,没有合适的只能将就)。这里的我们就相当于微服务中的Consumer(消费者) ,而那些房东就相当于微服务中的Provider(提供者) 。消费者Consumer需要调用提供者 Provider提供的一些服务,就像我们现在需要租他们的房子一样。 但是如果只是租客和房东之间进行寻找的话,他们的效率是很低的,房东找不到租客赚不到钱,租客找不到房东住不了房。所以,后来房东肯定就想到了广播自己的房源信息(比如在街边贴贴小广告),这样对于房东来说已经完成他的任务(将房源公布出去),但是有两个问题就出现了。第一、其他不是租客的都能收到这种租房消息,这在现实世界没什么,但是在计算机的世界中就会出现资源消耗的问题了。第二、租客这样还是很难找到你,试想一下我需要租房,我还需要东一个西一个地去找街边小广告,麻不麻烦? 那怎么办呢?我们当然不会那么傻乎乎的,第一时间就是去找 中介 呀,它为我们提供了统一房源的地方,我们消费者只需要跑到它那里去找就行了。而对于房东来说,他们也只需要把房源在中介那里发布就行了。
但是,这个时候还会出现一些问题。 **房东注册之后如果不想卖房子了怎么办?**我们是不是需要让房东定期续约 ?如果房东不进行续约是不是要将他们从中介那里的注册列表中移除 。 租客是不是也要进行注册呢?不然合同乙方怎么来呢? 中介可不可以做连锁店呢?如果这一个店因为某些不可抗力因素而无法使用,那么我们是否可以换一个连锁店呢?
好了,举完这个例子我们就可以来看关于 Eureka 的一些基础概念了,你会发现这东西理解起来怎么这么简单。
Eureka Server提供服务注册功能,各个节点启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。Eureka Server提供了一个图像化界面,访问这个页面就可以看到所有注册的服务的信息。
Eureka Client是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器(Ribbon)。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)
Eureka服务端是不是就是相当于中介呀,Eureka客户端是不是就是我们和房东呀,我们是不是通过中介找到的房东啊?
Consumer:消费者 Provider:提供者
Consumer:消费者 Provider:提供者
提供服务注册和发现。同时,Eureka Server也为我们提供了可视化的监控页面,可以直观地看到各个Eureka Server当前的运行状态和所有已注册服务的情况。再说白点Eureka Server其实就是服务提供者和服务消费者之间的“桥梁”,服务提供者可以把自己注册到服务中介那里,而服务消费者如需要消费一些服务(使用一些功能)就可以在服务中介中寻找注册在服务中介的服务提供者。
服务提供者。将自身服务注册到Eureka Server,从而使服务消费者能够找到。
服务消费者。从Eureka Server获取注册服务列表,从而能够消费服务。
(1) 在pom.xml中添加该组件对应的依赖
(2) 修改配置文件,做一些和该组件相对应的配置
(3) 修改启动类,在启动类上加组件相对应的注解:@EnableXXXX
https://www.jianshu.com/p/1f840b7f7de4
7.1 搭建Eureka Server注册中心
l 新建一个Maven父工程并添加SpringCloud和SpringBoot的依赖
<properties>
<spring.cloud-version>Hoxton.SR12</spring.cloud-version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
l 在父工程下新建一个子模块
l 在子模块的pom.xml中添加Eureka Server的依赖
<!--引入EurekaServer的依赖-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
l 修改子模块的配置文件
server:
port: 22222
#进行Eureka Server的配置
eureka:
instance:
hostname: localhost #Eureka Server实例的主机名
client:
#这块设成false表名不向Eureka注册中心注册自己
register-with-eureka: false
#这块设成false是表名就是Eureka服务器
fetch-registry: false
#Eureka客户端想要向EurekaServer查询服务和注册服务都需要依赖于这个地址
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
l 修改子模块的启动类
7.2 注册微服务
l 在父工程下新建一个部门服务提供者模块用于测试
l 为部门服务提供者添加Eureka Client依赖
<!--引入EurekaClient的依赖-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
l 修改部门服务提供者配置文件
spring:
application:
name: //名字
eureka:
client
service-url:
defaultZone: //EurekaServer提供的交互地址
l 启动类上添加@EnableEurekaClient注解
(1) 配置微服务实例ID
instance:
instance-id:
(2) 访问信息有IP信息提示
prefer-ip-address: true #访问路径可以显示IP地址
(3) 配置微服务详细描述信息
父工程的pom.xml加入以下配置
<build>
<finalName>microservicecloud</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<delimiters>
<delimiter>$</delimiter>
</delimiters>
</configuration>
</plugin>
</plugins>
</build>
在服务提供者模块的pom.xml中加入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
服务启动向Eureka注册中心注册服务,注册中心将注册信息同步给其他注册中心,当服务消费者需要调用服务提供者,则向服务注册中心获取服务提供者地址,然后会将获取的地址缓存在本地,下次调用的则直接从缓存中取,当服务注册中心检测到服务提供者宕机、网络不通等服务不可用时,则在注册中心将服务置为down状态,并向订阅者发布当前服务提供者状态,订阅过的服务消费者更新本地缓存。
服务提供者启动后周期性(30秒)向服务注册中心发送心跳,以证明当前服务可用,如果注册中心在一定时间(默认90秒)未收到心跳,则认为该服务宕机,注销该实例。
默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。
自我保护机制的工作机制是:如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:
l Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。 l Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。 l 当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
Renews Threshold和Renews(last min)
Renews Threshold(期望每分钟收到的心跳总数): 如果Eureka Server向自身注册:(int) 2 * n * 0.85 n指实例的个数 如果Eureka Server不向自身注册:(int) 2 * (n+1) * 0.85 Renews(last min):最后一分钟实际收到的心跳总数: 如果实例个数为299,请问期望每分钟收到的心跳总数是多少?
在Spring Cloud中,可以使用eureka.server.enable-self-preservation = false 禁用自我保护模式。
Eureka作为Spring Cloud的服务发现与注册中心,在整个的微服务体系中,处于核心位置。单一的Eureka服务,显然不能满足高可用的实际生产环境,这就要求我们配置一个能够应对各种突发情况,具有较强容灾能力的Eureka集群服务。
(1) 新建两个eureka-server项目
(2) 修改hosts文件,添加主机和IP地址映射
C:\Windows\System32\drivers\etc
127.0.0.1 eureka-server22222
127.0.0.1 eureka-server22223
127.0.0.1 eureka-server22224
(3) 把之前eureka-sever项目中pom配置和配置文件复制粘贴到新建的两个项目中
(4) 然后分别修改每个eureka-server项目的配置文件
eureka: server: hostname: eureka-serverxxxxxx client: service-url: defaultZone: 集群中其它两台Eureka Server的交互地址
(5) 分别修改每个微服务项目的配置文件
eureka: client: service-url: defaultZone: 集群中所有EurekaServer的交互地址,地址之间使用逗号隔开
官方解释:当 Eureka Client向 Eureka Server 注册时,它提供自身的元数据 ,比如IP地址、端口,运行状况指示符URL,主页等。
结合中介理解:房东 (服务提供者)在中介 (服务器 Eureka Server) 那里登记房屋的信息,比如面积,价格,地段等等(元数据 metaData)。
官方解释:Eureka Client会每隔30秒(默认情况下)发送一次心跳来续约 。 通过续约来告知 Eureka Server 该 Eureka Client仍然存在,没有出现问题。正常情况下,如果 Eureka Server 在90秒没有收到 Eureka Client的续约,它会将实例从其注册表中删除。
结合中介理解:房东 (服务提供者 ) 定期告诉中介 (服务器 Eureka Server) 我的房子还租(续约) ,中介 (服务器Eureka Server) 收到之后继续保留房屋的信息。
官方解释:Eureka Client从Eureka Server获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每30秒钟)更新一次。每次返回注册列表信息可能与 Eureka Client的缓存信息不同, Eureka Client自动处理。如果由于某种原因导致注册列表信息不能及时匹配,Eureka Client则会重新获取整个注册表信息。Eureka Server缓存注册列表信息,整个注册表以及每个应用程序的信息进行了压缩,压缩内容和没有压缩的内容完全相同。Eureka Client和 Eureka Server可以使用JSON / XML格式进行通讯。在默认的情况下 Eureka Client使用压缩 JSON 格式来获取注册列表的信息。
结合中介理解:租客(消费者 Eureka Client Consumer) 去中介 (服务器 Eureka Server) 那里获取所有的房屋信息列表 (客户端列表 Eureka Client List) ,而且租客为了获取最新的信息会定期向中介 (服务器 Eureka Server) 那里获取并更新本地列表。
官方解释:Eureka Client在程序关闭时向Eureka Server发送取消请求。 发送请求后,该客户端实例信息将从服务器的实例注册表中删除。该下线请求不会自动完成,它需要调用以下内容:DiscoveryManager.getInstance().shutdownComponent();
结合中介理解:房东 (提供者 Eureka Client Provider) 告诉中介 (服务器 Eureka Server) 我的房子不租了,中介之后就将注册的房屋信息从列表中剔除。
官方解释:在默认的情况下,当Eureka客户端连续90秒(3个续约周期)没有向Eureka服务器发送服务续约,即心跳,Eureka服务器会将该服务实例从服务注册列表删除 ,即服务剔除。
结合中介理解:房东(提供者 Eureka Client Provider) 会定期联系中介 (服务器 Eureka Server) 告诉他我的房子还租(续约),如果中介 (服务器 Eureka Server) 长时间没收到提供者的信息,那么中介会将他的房屋信息给下架(服务剔除)。