写在前面
Spring Cloud是一系列框架的有序集合, 它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用SpringBoot的开发风格做到一键启动和部署。
Spring Cloud微服务工具包为开发者提供了分布式系统中的配置管理、服务发现、断路器、智能路由、微代理、控制总线等开发工具包。它的各个项目基于Spring Boot,将Netlix的多个框架进行封装,并且通过自动配置的方式将这些框架绑定到Spring的环境中,从而简化了这些框架的使用。
各组件的运行流程如下:
所有请求都统一通过API网关(Zuul)来访问内部服务。
网关接收到请求后,从注册中心(Eureka) 获取可用服务。
由Ribbon进行均衡负载后,分发到后端的具体实例。
微服务之间通过Feign进行通信处理业务。
Hystrix负责处理服务超时熔断。
Turbine监控服务间的调用和熔断相关指标。
上面只是Spring Cloud体系的一部分, Spring Cloud共集成了19 个子项目,里面都包含一个或者多个第三方的组件或框架。
Spring Cloud工具框架如下。
Spring Cloud Config:配置中心,利用Git集中管理程序的配置。
Spring Cloud Netflix:集成众多Netlix的开源软件。
Spring Cloud Bus:消息总线,利用分布式消息将服务和服务实例连接在一起,用于在一个集群中传播状态的变化。
Spring Cloud for Cloud Foundry:利用Pivotal Cloudfoundry集成应用程序。
Spring Cloud Foundry Service Broker:为建立管理云托管服务的服务代理提供了一个起点。
Spring Cloud Cluster:基于ZooKeeper、Redis、 Hazelcast、 Consul实现的领导选举和平民状态模式的抽象和实现。
Spring Cloud Consul:基于Hashicorp Consul实现的服务发现和配置管理。
SpringCloudSecurity:在Zuul代理中为OAuth2REST客户端和认证头转发提供负载均衡。
Spring Cloud Sleuth:应用的分布式追踪系统和Zipkin、HTrace、 ELK兼容。
Spring Cloud Data Flow:一个云本地程序和操作模型,在一个结构化的平台上组成数据微服务。
Spring Cloud Stream:基于Redis、RabbitMQ、 Kafka 实现的消息微服务,简单声明模型用于在Spring Cloud应用中收发消息。
Spring Cloud Stream App Starters:基于Spring Boot为外部系统提供Spring的集成。
Spring Cloud Task:短生命周期的微服务,为Spring Boot应用简单声明添加功能和非功能特性。
Spring Cloud Task:任务调度框架。
Spring Cloud ZooKeeper:服务发现和配置管理基于Apache ZooKeeper。
Spring Cloud for Amazon Web Services:快速和亚马逊网络服务集成。
Spring Cloud Connectors:便于PaaS应用在各种平台上连接到后端数据库和消息经纪服务。
Spring Cloud Starters:项目已经终止并且在Angel.SR2后的版本和其他项目合并。
Spring Cloud CLI:基于Spring Cloud CLI,可以以命令行方式快速建立云组件。
注册中心
CAP理论指出,一个分布式系统不可能同时满足C (一致性)、A (可用性)和P (分区容错性)。由于分区容错性在分布式系统中是必须要保证的,因此我们只能在A和C之间进行权衡。
那么如何选择一款适合 自己的注册中心呢?就让我们来看一下常用的注册中心。注册中心就像是书的目录,而章节的内容就是具体的实现,当服务之间互相调用时,相当于先通过注册中心找到对应的目录,然后去调用相应的实现完成功能。
常用的注册中心包括ZooKeeper、Eureka、 etcd 和Consul。
ZooKeeper
ZooKeeper是一-种为分布式应用所设计的高可用、高性能且一致的开源协调服务,它提供了一项基本服务:分布式锁服务。由于ZooKeeper的开源特性,后来开发者在分布式锁的基础上,摸索出了其他的使用方法:配置维护、组服务、分布式消息队列、分布式通知/协调等。ZooKeeper性能上的特点决定了它能够用在大型的、分布式的系统当中。从可靠性方面来说,它并不会因为一个节点的错误而崩溃。除此之外,它严格的序列访问控制意味着复杂的控制原语可以应用在客户端上。
很多场景下ZooKeeper也作为Service发现服务解决方案。ZooKeeper 保证的是CP,即任何时刻对ZooKeeper的访问请求能得到一致的数据结果,同时系统对网络分割具备容错性,但是它不能保证每次服务请求的可用性。
Eureka
Eureka是Ntlix 开发的服务发现框架,Spring Cloud 将它集成在自己的子项目Spring-cloud-netflix中,实现Spring Cloud的服务发现功能。Eureka Server会提供服务注册功能,各个服务节点启动后,会在Eureka Server中进行注册,这样Eureka Server中就有了所有服务节点的信息,并且Eureka有监控页面,可以在页面中直观地看到所有注册的服务的情况。同时Eureka有心跳机制,当某个节点服务在规定时间内没有发送心跳信号时,Eureka 会从服务注册表中把这个服务节点移除。
Eureka还提供了客户端缓存的机制,即使所有的EurekaServer都挂掉,客户端仍然可以利用缓存中的信息调用服务节点的服务。Eureka一般配合Ribbon进行使用,Ribbon提供了客户端负载均衡的功能,Ribbon 利用从Eureka中读取到的服务信息,在调用服务节点提供的服务时,会合理地进行负载。Eureka 遵守的就是AP原则。
etcd
etcd是一个高可用的键值存储系统,主要用于共享配置和服务发现。etcd是由CoreOS开发并维护的,灵感来自ZooKeeper和Doozer, 它使用Go语言编写,并通过Raft一致性算法处理日志复制以保证强一致性。Raft 是一个新的一致性算法,适用于分布式系统的日志复制,Raft通过选举的方式来实现一致性。 Google 的容器集群管理系统Kubernetes、开源PaaS平台Cloud Foundry和CoreOS的Fleet都广泛使用了etcd。 在分布式系统中,如何管理节点间的状态一直是一个难题,etcd 像是专门为集群环境的服务发现和注册而设计的,它提供了数据TTL失效、数据改变监视、多值、目录监听、分布式锁原子操作等功能,可以方便地跟踪并管理集群节点的状态。
Consul
Consul 是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置共享。对比其他分布式服务注册与发现的方案,Consul 的方案更“一站式”,内置了服务注册与发现框架、分布--致性协议实现(Raft算法)、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(比如ZooKeeper等)。Consul用Golang 实现,因此具有天然可移植性(支持Linux、Windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与Docker等轻量级容器可无缝配合。
在注册中心的选择方面,可以针对自身业务的特点,选择一款合适的注册中心,如果团队原有框架基于Dubbo,那么ZooKeeper会更合适一些;如果团队整体使用Spring Cloud, 那么Eureka的优势就会更大一些,如果团队对于容器化的要求比较高,那么etcd和Consul都是不错的选择。由于我们的整体构建所需,我们选择的是Spring Cloud中的Eureka。
Eureka 介绍
Eureka的一些概念如下。
Register: 服务注册
当Eureka客户端向Eureka Server 注册时,它提供自身的元数据,比如IP地址、端口、运行状况指示符URL、主页等。
Renew: 服务续约
Eureka客户会每隔30秒发送一次心跳来续约。 通过续约来告知Eureka Server该Eureka客户仍然存在,没有出现问题。正常情况下,如果Eureka Server在90秒后没有收到Eureka客户的续约,则它会将实例从其注册表中删除。建议不要更改续约间隔。
Fetch Registries:获取注册列表信息
Eureka客户端从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每30秒)更新一次。每次返回的注册列表信息可能与Eureka客户端的缓存信息不同,Eureka客户端会自动处理。如果由于某种原因导致注册列表信息不能及时匹配,则Eureka客户端会重新获取整个注册表信息。Eureka服务器缓存注册列表信息,整个注册表及每个应用程序的信息都进行了压缩,压缩内容和没有压缩的内容完全相同。Eureka客户端和Eureka服务器可以使用JSON/XML格式进行通信。在默认的情况下,Eureka客户端使用压缩JSON格式来获取注册列表的信息。
Cancel: 服务下线
Eureka客户端在程序关闭时向Eureka服务器发送取消请求。发送请求后,该客户端实例信息将从服务器的实例注册表中删除。该下线请求不会自动完成,它需要调用以下内容:
DiscoveryManager.getInstance() .shutdownComponent() ;
Eviction:服务剔除
在默认的情况下,当Eureka客户端连续90秒没有向Eureka服务器发送服务续约(即心跳)时,Eureka服务器会将该服务实例从服务注册列表删除,即服务剔除。
Eureka由多个instance ( 服务实例)组成,这些服务实例可以分为两种: Eureka Server 和Eureka Client。
Eureka Client再分为Service Provider和Service Consumer。
Eureka Server:服务的注册中心,负责维护注册的服务列表。
Service Provider:服务提供方,作为一个Eureka Client,向Eureka Server 进行服务注册、续约和下线等操作,注册的主要数据包括服务名、机器IP、端口号、域名等。
Service Consumer:服务消费方,作为一个Eureka Client,向Eureka Server获取Service Provider的注册信息,并通过远程调用与Service Provider进行通信。
Service Provider 和Service Consumer 不是严格的概念,Service Consumer 也可以随时向Eureka Server注册,来让自己变成一个Service Provider。
Eureka程序构成如下。
纯正的Servlet应用,需构建成war包部署。
使用了Jersey 框架实现自身的RESTful HTTP接口。
peer 之间的同步与服务的注册全部通过HTTP协议实现。
定时任务(发送心跳、定时清理过期服务、节点同步等)通过JDK自带的Timer实现。
内存缓存使用Google的guava包实现。
服务发现
在现在的软件开发中,如果对性能要求不是非常高,则一般使用REST API来开放服务的接口。
服务发现有如下两种模式。
客户端服务发现(Client-Side Discovery)如下图所示。
服务实例启动时会向服务注册中心进行注册,服务的注册中心能够看到所有注册的实例。客户端需要调用服务时,先到注册中心摘取可用的服务列表的地址,然后根据负载均衡算法, 去获取一个可用的实例的地址来响应这次请求。
一个服务实例被启动,它的网络地址会被写到注册中心,当服务实例终止,会从注册表中删除。这个服务实例的注册表通过心跳机制动态刷新。
服务端服务发现(Server-Side Discovery)如下图所示。
客户端通过负载均衡器向某个服务提出请求,负载均衡器向注册中心发出请求,将每个请求转发至可用的服务实例。和客户端发现一样,服务实例启动时在注册中心注册,当服务实例销毁时,会从服务注册表中进行删除。
使用服务器端服务现,客户端无须关注发现的细节,只需要简单地向负载均衡器发送请求即可,实际上减少了编程语言框架需要完成的服务发现逻辑。缺点是除非部署环境能够提供负载均衡,否则负载均衡器是另外一个需要配置管理的高可用系统功能。目前比较流行的方式是使用Nginx来进行服务器端的负载均衡。
负载均衡
负载均衡是云计算的基础组件,是网络流量的入口,其重要性不言而喻。
什么是负载均衡呢?用户输入的流量通过负载均衡器按照某种负载均衡算法把流量均匀地分散到后端的多个服务器上,接收到请求的服务器可以独立地响应请求,达到负载分担的目的。从应用场景上来说,常见的负载均衡模型有全局负载均衡和集群内负载均衡。
服务端负载均衡
负载均衡是处理高并发、缓解网络压力和进行服务端扩容的重要手段之-一,但是一般情况下我们所说的负载均衡通常都是指服务端负载均衡,服务端负载均衡又分为两种,一种是硬件负载均衡,另一种是软件负载均衡。
硬件负载均衡主要通过在服务器节点之间安装专门用于负载均衡的设备实现,常见的设备如F5。
软件负载均衡则主要通过在服务器上安装一些 具有负载均衡功能的软件来完成请求分发进而实现负载均衡,常见的就是Nginx。
无论是硬件负载均衡还是软件负载均衡,它的工作原理均如下图所示。
无论是硬件负载均衡还是软件负载均衡,都会维护一个可用的服务端清单,然后通过心跳机制来删除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。当客户端的请求到达负载均衡服务器时,负载均衡服务器按照某种配置好的规则从可用服务端清单中选出一台服务器去处理客户端的请求,这就是服务端负载均衡。
负载均衡策略:
简单轮询负载均衡;
加权响应时间负载均衡;
区域感知轮询负载均衡;
随机负载均衡。
Feign
在Spring Cloud Netlix中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端。我们可以使用JDK原生的URLConnection、Apache的Http Client、Netty 的异步HTTP Client,以及Spring的RestTemplate。但是,用起来最方便、最优雅的还是Feign。
Feign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用Feign,我们可以做到使用HTTP请求远程服务时能与调用本地方法一样的编码体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。
微服务容错
在大中型分布式系统中,通常系统有很多依赖。在并发量很小的时候,通常不会造成很严重的后果,但是当并发量激增,这些依赖的稳定性就有可能造成整个系统的瘫痪,这也就是我们经常说的雪崩。
雪崩的形成
服务雪崩效应是一种因服务提供者的不可用而导致服务调用者的不可用的现象,并将不可用逐渐放大的过程。举例来说,我们使用链式设计模式构建的微服务,当其他的服务出现问题时,就会出现连锁支应,导致整个服务链条不可用。
造成服务不可用的原因包括:
硬件故障;
网络连接缓慢;
程序Bug;
缓存击穿,一般发生在缓存应用重启,所有缓存被清空时,以及短时间内大量缓存失效时。大量的缓存不命中,使请求直击后端,造成服务提供者超负荷运行,引起服务不可用;
用户大量请求。
那么出现雪崩应该如何应对呢?
应对雪崩一般有以下几种办法。
流量控制
一般是使用Nginx进行流量控制,对流量大的应用采用分流和限制,这个功能也可以使用网关来完成。
服务自动扩容
取决于硬件的限制,也可以使用第三方的云服务以达到扩容的效果。如果微服务构建得比较成熟,则可以通过容器的动态扩容来完成服务的扩容。
降级和资源隔离
资源隔离主要是对调用服务的线程池进行隔离,监控一般要细致到线程级别,当发现某个线程占用资源过高时,进行有效的处理来解决性能瓶颈。
我们根据具体业务将依赖服务分为强依赖和弱依赖。强依赖服务不可用会导致当前业务中止,而弱依赖服务的不可用不会导致当前业务的中止。
不可用服务的调用快速失败一般通过超时机制、熔断器和熔断后的降级方法来实现。
降级
在网络访问中,为了优化用户体验,遇到超时的情况,可以直接放弃本次请求,不等待结果的返回,直接返回用户默认数据,也可以降级为从另一个服务或者使用缓存中设置的默认数据。
熔断
熔断是指错误达到某个设定的阈值,或者请求量超过阈值后,系统自动(或手动)阻止代码或服务的执行调用,从而达到系统整体保护的效果。当检测到系统可用时,需要恢复访问。
熔断器模式
熔断器模式定义了熔断器开关相互转换的逻辑。
正常运行 (Closed)
当一个系统运行平稳时,成功状态计数器用于测量弹性系统的稳定性,而故障表用于跟踪任何故障。该设计确保当达到故障的阆值时,断路器断开电路,以防止进一步的资源请求。
失败状态 (Open)
在这个时刻,每-一个依赖调用是短路的,并抛出HystrixRuntimeException 异常,伴随SHORTCIRCUIT失败类型,给出异常明确的原因。一旦等待时间过后,Hystrix 断路器移到半开放状态。
半开放状态
在这种状态下,由Hystrix负责发送第一一个请求, 检查系统的可用性,让其他的请求快速失败,直到得到依赖的响应。如果调用是成功的,则断路器被重置为Closed状态;如果发生故障,则系统返回Open状态,并且整个过程继续循环。
断路器是Hystrix库默认提供的一个功能。断路器的功能可以概括如下:
熔断器对所有调用状态进行验证;
如果是closed状态,则允许请求通过;
如果是open状态,则失败所有的请求;
如果是half-open状态,则允许一个请求通过,并在成功或者失败时,转换成closed或open状态。
Hystrix
Hystrix:通过服务熔断、降级、限流、异步RPC等手段控制依赖服务的延迟与失败。通过命令模式封装调用来实现弹性保护,继承HytrixCommand并且实现run方法,就完成了最简单的封装。可以为分布式服务提供弹性保护。
Hystrix的设计原则包括:资源隔离、熔断器、命令模式。
断路器机制
断路器很好理解,当Hystrix Command请求后端服务失败数量超过一定 比例,默认为50%,断路器会切换到开路状态(open), 这时所有请求会直接失败而不会发送到后端服务。断路器保持在开路状态一段时间后, 默认为5秒,自动切换到半开路状态(half-open), 这时会判断下一次请求的返回情况。如果请求成功,则断路器切回闭路状态(closed),否则重新切换到开路状态(open)。Hystrix 的断路器就像我们家庭电路中的保险丝,一旦后端服务不可用,断路器会直接切断请求链,避免发送大量无效请求影响系统吞吐量,并且断路器有自我检测并恢复的能力。
fallback
fallback相当于降级操作。对于查询操作,我们可以实现一个fallback 方法,当请求后端服务出现异常时,可以使用fallbak方法返回的值。fllback 方法的返回值一般是设置的默认值或者来自缓存。
资源隔离
在Hystrix中,主要通过线程池来实现资源隔离。通常在使用时我们会根据调用的远程服务划分出多个线程池。例如,调用产品服务的Command放入A线程池,调用账户服务的Command放入B线程池。这样做的主要优点是运行环境被隔离开了,就算调用服务的代码存在Bug或者由于其他原因导致自己所在线程池被耗尽时,不会对系统的其他服务造成影响。但代价就是维护多个线程池会对系统带来额外的性能开销。如果对性能有严格要求而且确信自己调用服务的客户端代码不会出问题,则可以使用Hystrix 的信号模式(Semaphores)来隔离资源。
Hystrix服务调用的内部逻辑如下图所示
构建的Command对象,调用执行方法。
Hystrix检查当前服务的熔断器开关是否开启,若开启,则执行降级服务getFallback 方法。
若熔断器开关关闭,则Hystrix检查当前服务的线程池是否能接收新的请求,若超过线程池已满,则执行降级服务getFallback 方法。
若线程池接收请求,则Hystrix开始执行服务调用具体逻辑run方法。
若服务执行失败,则执行降级服务getFallback 方法,并将执行结果上报Metrics更新服务健康状况。
若服务执行超时,则执行降级服务getFallback方法,并将执行结果上报Metrics更新服务健康状况。
若服务执行成功,则返回正常结果。
若服务降级方法getFallback执行成功,则返回降级结果。
若服务降级方法getFallback执行失败,则抛出异常。
以上就是小编整理的微服务Spring Cloud框架解析,只是小编的个人见解,有哪里不准确的地方,还请大佬们多多指出,咱们共同学习进步~~~
喜欢文章或小编的朋友,请多多点赞评论转发,你们的支持就是小编最大的动力!!!
有疑问加站长微信联系(非本文作者)