springCloud笔记详细

Eureka

1. 服务提供者

1. 服务注册

服务提供者在启动的时候会通过发送REST请求的方式将自己注册到EurekaServer上, 同时带上了自身服务的 一 些元数据信息。Eureka Server接收到这个REST请求之后,将元数据信息存储在 一 个双层结构Map中, 其中第 一 层的key是服务名, 第二层的key是具体服务的实例名。

2. 服务同步

当服务提供者发送注册请求到 个服务注册中心时, 它会将该请求转发给集群中相连的其他注册中心,从而实现注册中心之间的服务同步。

3. 服务续约

在注册完服务之后,服务提供者会维护 个心跳用来持续告诉EurekaSe1-ver: "我还活着”,以防止Eureka Server的 “剔除任务”将该服务实例从服务列表中排除出去,我们称该操作为服务续约(Renew)

关千服务续约有两个重要属性,我们可以关注并根据需要来进行调整:

eureka.instance.lease-renewal-interval-in-seconds=30

eureka.instance.lease-expiration-duration-in-seconds=90

2. 服务消费者

1. 获取服务

启动服务消费者的时候,它会发送 REST请求给服务注册中心,来获取上面注册的服务清单。为了性能考虑,EurekaServer会维护 一 份只读的服务清单来返回给客户端,同时该缓存清单会每隔30秒更新一次。

2. 服务调用

通过服务名可以获得具体提供服务的实例名和该实例的元数据信息。 因为有这些服务实例的详细信息, 所以客户端可以根据自己的需要决定具体调用哪个实例,在沁bbon中会默认采用轮询的方式进行调用,从而实现客户端的负载均衡。

3. 服务下线

当服务实例进行正常的关闭操作时, 它会触发 个服务下线的REST请求给Eureka Server, 告诉服务注册中心:“我要下线了” 。 服务端在接收到请求之后, 将该服务状态置为下线(DOWN), 并把该下线事件传播出去。

3. 服务注册中心

1. 失效剔除

EurekaServer在启动的时候会创建 一 个定时任务,默认每隔 一 段时间(默认为60秒) 将当前清单中超时(默认为90秒)没有续约的服务剔除出去。

2. 自我保护

EurekaServer在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%, 如果出现低于的情况(在单机调试的时候很容易满足, 实际在生产环境上通常是由于网络不稳定导致), EurekaServer会将当前的实例注册信息保护起来, 让这些实例不会过期,尽可能保护这些注册信息。

Ribbon

1. 客户端负载均衡

客户端负载均衡和服务端负载均衡最大的不同点在千上面所提到的服务清单所存储的位置。 在客户端负载均衡中, 所有客户端节点都维护着自己要访问的服务端清单, 而这些服务端的清单来自于服务注册中心,比如上 章我们介绍的Eureka服务端。同服务端负载均衡的架构类似, 在客户端负载均衡中也需要心跳去维护服务端清单的健康性, 只是这个步骤需要与服务注册中心配合完成。

2. 参数配置

对于Ribbon的参数 配置通常有两种方式: 全局配置以及指定客户端配置。

1. 全局配置

使用 ribbon. < key>= < value>格式进行配置即可。其中, <key>代表了Ribbon 客户端配置的参数名,< value>则代表了对应参数的值。 比如, 我们可以像下面这样全局配置Ribbon创建连接的超时时间:ribbon.ConnectTimeout = 250

全局配置可以作为默认值进行设置, 当指定客户端配置 了相应key的值时,将覆盖全局配置的内容。

2. 指定客户端配置

指定客户端的配置方式 采用<clinet> .ribbon. < key>= < value>的格式进行配置。<client>代表了客户端的名称

Hystrix

Hystrix具备服务降级、 服务熔断、 线程和信号隔离、 请求缓存、 请求合并以及服务监控等强大功能。

1. 断路器原理

断路器 HystrixCircuitBreaker 的定义,主要定义了三个断路器的抽象方法。

(1)  allowRequest ():

每个Hystrix命令的请求都通过它判断是否被执行。

如果断路器打开标识为true, 则直接返回true, 表示断路器处于打开状态。否则,就从度量指标对象 metrics 中获取 HealthCounts 统计对象做进 一 步判断(该对象记录了一个滚动时间窗内的请求信息快照,默认时间窗为10秒)。

如果它的请求总数(QPS)在预设的阙值范围内就返回 false , 表示断路器处于未打开状态。该阙值的配置参数为 circuitBreakerRequestVolumeThreshold,默认值为20

如果错误百分比在阑值范围内就返回 false, 表示断路器处于未打开状态。该阙值的配置参数为 circuitBreakerErrorThresholdPercentage, 默认值为50

如果上面的两个条件都不满足,则将断路器设置为打开状态 (熔断/短路)。 同时,如果是从关闭状态切换到打开状态的话,就将当前时间记录到上面提到的circuitOpenedOrLastTestedTirne 对象中。

(2) isOpen():  

返回当前断路器是否打开。

先根据配置 对象properties中的断路器判断强制打开或关闭属性是否被设置。 如果强制打开,就直接返回false, 拒绝请求。如果强制关闭,它会允许所有请求,但是同时也会调用isOpen ()来执行断路器的计算逻辑,用来模拟断路器打开/关闭的行为。

在默认情况下,断路器并不会进入这两个强制打开或关闭的分支中去,而是通过!isOpen ()I I allowSingleTest ()来判断是否允许请求访问。!isOpen()之前已经介绍过, 用来判断和计算当前断路器是否打开,如果是断开状态就允许请求。

allowSingleTest()的实现中我们可以看到,这里使用了在isOpen()函数中当断路器从闭合到打开时候所记录的时间戳。当断路器在打开状态的时候,这里会判断断开时的时间戳+配置中的circuitBreakerSleepwindowinMillliseconds时间是否小于当前时间,是的话,就将当前时间更新到记录断路器打开的时间对象circuitOpenedOrLastTestedTime 中,并且允许此次请求。

简单地说,通过circuitBreakerSleepWindowinMillseconds 属性设置了一个断路器打开之后的休眠时间(默认为5秒),在该休眠时间到达之后,将再次允许请求尝试访问,此时断路器处于“半开”状态,若此时请求继续失败,断路器又进入打开状态, 并继续等待下 一 个休眠窗口过去之后再次尝试;若请求成功, 则将断路器重新置于关闭状态。所以通过 allowSingleTest()isOpen ()方法的配合,实现了断路器打开和关闭状态的切换。

(3) markSuccess():  

用来闭合断路器。

该函数用来在“半开路”状态时使用。若 Hystrix 命令调用成功,通过调用它将打开的断路器关闭,并重置度量指标对象。

2. 依赖隔离

Hystrix 则使用该模式实现线程池的隔离,它会为每一个依赖服务创建一个独立的线程池, 这样就算某个依赖服务出现延迟过高的情况, 也只是对该依赖服务的调用产生影响, 而不会拖慢其他的依赖服务。

通过实现对依赖服务的线程池隔离, 可以带来如下优势:

(1) 应用自身得到完全保护,不会受不可控的依赖服务影响。即便给依赖服务分配的线程池被填满,也不会影响应用自身的其余部分。

(2) 可以有效降低接入新服务的风险。 如果新服务接入后运行不稳定或存在问题, 完全不会影响应用其他的请求。

(3) 当依赖的服务从失效恢复正常后,它的线程池会被清理并且能够马上恢复健康的服务,相比之下,容器级别的清理恢复速度要慢得多。

(4) 当依赖的服务出现配置错误的时候, 线程池会快速反映出此问题(通过失败次数、延迟、超时、拒绝等指标的增加情况)。 同时, 我们可以在不影响应用功能的情况下通过实时的动态属性刷新(后续会通过Spring Cloud ConfigSpring Cloud Bus的联合使用来介绍) 来处理它。

(5) 当依赖的服务因实现机制调整等原因造成其性能出现很大变化的时候, 线程池的监控指标信息会反映出这样的变化。 同时, 我们也可以通过实时动态刷新自身应用对依赖服务的阙值进行调整以适应依赖方的改变。

(6) 除了上面通过线程池隔离服务发挥的优点之外, 每个专有线程池都提供了内置的并发实现,可以利用它为同步的依赖服务构建异步访问。

总之, 通过对依赖服务实现线程池隔离, 可让我们的应用更加健壮, 不会因为个别依赖服务出现问题而引起非相关服务的异常。 同时, 也使得我们的应用变得更加灵活, 可以在不停止服务的情况下, 配合动态配置刷新实现性能配置上的调整。

Feign

1. 参数绑定

2. 继承特性

1. 优点

使用 Spring Cloud Feign 继承特性的优点很明显, 可以将接口的定义从 Controller 中剥离,同时配合Maven私有仓库就可以轻易地实现接口定义的共享, 实现在构建期的接口绑定,从而有效减少服务客户端的绑定配置。

2. 缺点

由于接口在构建期间就建立起了依赖,那么接口变动就会对项目构建造成影响, 可能服务提供方修改了 个接口定义,那么会直接导致客户端工程的构建失败。 所以, 如果开发团队通过此方法来实现接口共享的话, 建议在开发评审期间严格遵守面向对象的开闭原则,尽可能地做好前后版本的兼容,防止牵 发而动全身的后果,增加团队不必要的维护工作量。

3. 配置

1. Ribbon配置

(1) 全局配置:

全局配置的方法非常简单, 我们可以直接使用ribbon.<key> = <value>的方式来设置ribbon的各项默认参数。 比如, 修改默认的客户端调用超时时间:

ribbon.ConnectTimeout=500

ribbon.ReadTimeout=5000

(2) 指定服务配置

采用<client>. rbbon.key=value 的格式进行设置

client指的是@FiegnClientname或者value属性对应的值

HELLO-SERVICE.ribbon.ConnectTimeout=500

4. 重试机制

当接口超时时,feign会自动重试,第一次访问的重试次数由

HELLO­SERVICE.ribbon.MaxAutoRetries=1确定,失败后更换实例访问,重试次数由HELLO-SERVICE.ribbon.MaxAutoRetriesNextServer=2确定,所以会更换两次实例进行重试。

这里需要注意一 点,Ribbon的超时 与Hystrix的超时是两个概念。 为了让上述实现有效,我们需要 让Hystrix的超时时间大于Ribbon的超时时间, 否则Hystrix命令超时后, 该命令直接熔断, 重试机制就 没有任何意义了。

5. Hystrix配置

默认情况下,SpringCloud Feign会为将所有Feign客户端的方法都封装到Hystrix命令中进行服务保护

1. 全局配置

先设置feign.hystrix.enabled=true

hystrix.command.default.execution.isolation.thread.timeoutinMilliseconds = 5000

可以通过feign.hystrix.enabled=false来关闭Hystrix功能

如果不想全局地关闭Hystrix支持,而只想针对某个 服务客户端关闭Hystrix支待时,需要通过使用@Scope ("prototype")注解为指定的客户端配置Feign.Builder实例

 

2. 指定命令配置

3. 服务降级配置

使用@FeignClientfallback或者FallBackFactroy属性

6. 日志配置

可以在 application.properties 文件中使用 logging.level.

<FeignClient> 的参数配置格式来开启指定 Feign 客户端的 DEBUG 日志, 其中

<FeignClent> Feign 客户端定义接口的完整路径, 比如针对本章中我们实现的

HelloService 可以按如下配置开启:logging.level.com.didispace.web.HelloService = DEBUG

此外还需要配置Feign的日志级别,

 


 

Zuul

zuul中核心的两个模块是路由和过滤

1. 路由

1. 面向服务的路由

Zuuleureka无缝结合,可以让路由的path不是映射具体的url, 而是让它映射到某个具体的服务,而具体的url则交给Eure ka的服务发现机制去自动维护, 我们称这类路由为面向服务的路由。然后在配置文件中配置:

zuul.routes.api-b.path=/api-b/**

zuul.routes.api-b.serviceid=feign-consumer

eureka.client.serviceUrl.defaultZone=http://localhost:8080/eureka/

2. 传统路由配置

(1) 单实例配置

通过zuul.routes. < route>.pathzuul.routes. < route>.url参数对的方式进行配置,比如:

zuul. routes. user-service.path = /user - service/**

zuul. routes. user-service.url = http: / / localhost: 8080/

(2) 多实例配置

通过zuul.routes. < route>.pathzuul.routes. < route>.service Id参数对的方式进行配置,比如:

zuul. routes. user - service. path = /user - service/**

zuul. routes. user - service. serviceid=user - service

ribbon.eureka.enabled = false

user-service.ribbon.listOfServers = http://localhost:8080/,http://localhost:8081/

3. 服务路由的默认配置

 这个时候, 我们可以使用zuul.ignored-services参数来设置 一 个服务名匹配表达式来定义不自动创建路由的规则。比如设置为zuul.ignored-services=*的时候,Zuul将对所有的服务都不自动创建路由规则。

4. 自定义路由映射规则

5. 路由匹配

6. 路由前缀

7. 本地跳转

Zuul 实现的 API 网关路由功能中, 还支持 forward 形式的服务端跳转配置。 实现方式非常简单,只需通过使用 path url 的配置方式就能完成,通过 url 中使用 forward来指定需要跳转的服务器资源路径。

zuul.routes.api-b.path=/api-b/**

zuul.routes.api-b.url=forward:/local

所以相应的我们也需要为本地跳转实现对应的请求接口。 按照上面的例子, API 网关上还需要增加 一 个/ local/hello的接口实现才能让 api-b 路由规则生效

2. Cookie与头信息

默认情况下,SpringCloud Zuul在请求路由时,会过滤掉HTTP请求头信息中的

些敏感信息,防止它们被传递到下游的外部服务器。

  通过设置全局参数为空来覆盖默认值, 具体如下:

zuul.sensitiveHeaders =

这种方法并不推荐,虽然可以实现Cookie的传递, 但是破坏了默认设置的用意。在微服务架构的API网关之内, 对于无状态的RESTful API请求肯定是要远多于这些Web类应用请求的, 甚至还有 一 些架构 设计会 将Web类应用和App客户端一 样都归为API 网关之外的客户端应用。

通过指定路由的参数来配置, 方法有下面两种。

#方法一:对指定路由开启自定义敏感头

zuul.routes.<router>.customSensitiveHeaders = true

#方法二:将指定路由的敏感头设置为空

zuul.routes.<router>.sensitiveHeaders =

3. Hystrix Ribbon 支持

Zuul天生就拥有线程隔离和断路器的自我保护功能,以及对服务调用的客户端负载均衡功能。但是需要注意,当使用pathurl的映射关系来配置路由规则的时候,对于路由转发的请求不会采用HystrixCommand来包装,所以这类路由请求没有线程隔离和断路器的保护,并且也不会有负载均衡的能力。

有很多设置可以自己看。

4. 过滤器

路由功能在真正运行时,它的路由映射和请求转发都 是由几个不同的过滤器完成的。 其中,路由映射主要通过pre类型的过滤器完成,它将请求路径与配置的路由规则进行匹配,以找到需要转发的目标地址;而请求转发的部分则是由route类型的过滤器来完成,对pre类型过滤器获得的路由地址进行转发。

Spring Cloud Zuul中实现的过滤器必须包含4个基本特征: 过滤类型、 执行顺序、

执行条件、 具体操作。 这些元素看起来非常熟悉,实际上它就是ZuulFier接口中定义的4个抽象方法。

1. 请求声明周期

 

(1)  个阶段pre, 在这里它会被pre类型的过滤器进行处理,该类型过滤器的主要目的是在进行请求路由之前做 一 些前置加工, 比如请求的校验等。

(2) 第二个阶段roung, 也就是之前说的路由请求转发阶段, 请求将会被routing类型过滤器处理。这里的具体处理 内容就是将外部请求转发到具体服务实例上去的过程,当服务实例将请求结果都返回之后,routing 阶段完成

(3) 请求将会被post类型的过滤器处理,这些过滤器在处理的时候不仅可以获取到请求信息,还能获取到服务实例的返回信息所以在post类型的过滤器中,我们可以对处理结果进行一些加工或转换等内容。

(4) 还有一个特殊的阶段error, 该阶段只有在上述三个阶段中发生异常的时候才会触发,但是它的最后流向还是 po江类型的过滤器,因为它需要通过post过滤器将最终结果返回给请求客户端

2. 核心过滤器

(1) Pre过滤器

(2) Route过滤器

① RibbonRoutingFilter:只对通过serviced配置路由规则的请求生效

② SimpleHostRoutingFilter:只对通过 url 配置路由规则的请求生效

③ SendForwardFilter:该过滤器只对请求上下文中存在forward.to参数的请求进行处理,即用来处理路由规则中的 forward本地跳转配置。

(3) post过滤器

① SendErrorFilter: 该过滤器仅在请求上下文中包含 error.status_code参数(由之前执行的过滤器设置的错误编码)并且还没有被该过滤器处理过的时候执行

② SendPesponseFilter

(4) 

3. 异常处理

(1) 过滤器处理流程

 

如果不是post出现的异常,那么经error处理之后,会进入post的一个SendErrorFilter进行错误信息整合,然后直接返回给用户。

如果是post出现的异常,那么经过error过滤器处理之后,就没有过滤器再接手了,直接会返回给用户。

SendErrorFilter是否会处理异常,一个重要的标志就是ctx.containsKey("error.status_code"),而如果产生这个参数,这就需要在各个过滤器中捕获异常,并且放入参数,主要有三个:

 

所以如果是自己的过滤器,需要捕获异常并且放入这些参数,才能进入SendErrorFilter。如果需要进行统一的异常处理,可以自己实现一个error过滤器,然后在这里面获取到异常,并且放入异常信息即可。

自定义的过滤器如果没有设置error.status_code,也没有自定义error过滤器实现统一异常处理的话,发生异常时,不会返回任何错误信息给用户。

但是由于上面描述的,只能处理非post阶段出现的异常,如果是post阶段出现的异常,需要另外优化处理,如果我们想继续沿用SendErrorFilter,那就需要再考虑把post异常的情况也做下处理,并且实现两种情况下,返回给用户的信息是一致的。

springCloud微服务实战中,使用的方案是这样的:

实现一个error过滤器,继承SendErrorFilter

扩展processZuulFliter(ZuulFliter filter),当过滤器执行抛出异常的时候,我们捕获它,并向请求上下中记录一些信息。比如过滤器类型等。

在第一步实现的error过滤器中,根据第二步记录的信息判断是否是post过滤器,如果是则执行。

4. 禁止过滤器

zuul.<SimpleClassName>.<filterType>.disable = true

如:zuul.AccessFilter.pre.disable = true

5. 动态路由

Zuul的动态路由是由配置中心来实现的,具体步骤:

创建配置中心

创建zuul配置类

 

发送/refresh请求,刷新配置

Config

1. 服务端配置规则详解

1. 配置文件名称

didispace-dev.properties

didispace :服务名称

div : 开发环境

访问如下连接:

 

上面的 url 会映射 {application}-{profile} .properties 对应的配置文件,其中 {label} 对应Git上不同的分支,默认为 master

配置服务器使用git clone命令从git上读取一份配置,并放在本地文件系统中,然后读取这些配置返回给客户端。这样可以防止git出现故障而引起无法加载配置的情况。断开网络,访问配置,配置服务器依然可以返回配置信息,它读取就是本地暂存的。

2. 客户端配置

客户端的配置必须在bootstrap.properties中,才能被正确加载。

3. 安全保护

可以使用 Spring Security来保证安全保护

服务端:

<dependency>

<groupid>org.springframework.boot</groupid>

<artifactid>spring-boot-starter-security</artifacId>

</dependency>

配置密码

security.user.name=user

security.user.password=37cc5635-559b-4e6f-b633-7e932b813f73

客户端也需要加上用户密码才能获取到配置:

spring.cloud.config.username=user

spring.cloud.config.password=37cc5635-559b-4e6f-b633-7e932b813f73

4. 高可用配置

1. 传统模式

2. 服务模式

可以将 Config Server 作为 一个普通的微服务应用,纳入 Eureka 的服务治理体系中。 这样我们的微服务应用就可以通过配置中心的服务名来获取配置信息。

5. 快速失败与重试

1. 快速失败

在客户端bootstrap.properties中配置spring.cloud.config.failFast= true。实现客户端优先判断ConfigServe r获取是否正常, 并快速响应失败内容。

2. 重试

若是网络波动以及其他原因倒是网络超时或者访问失败的情况下,如果直接失败显然是不合适的,所以config客户段还提供了自动重试功能,在开启重试功能前,先确保已经配置了快速失败。

在客户端添加spring-retry spring-boot-strarter-aop的依赖

<dependency>

<groupid>org.springframework.retry</groupid>

<artifactid>spring-retry</artifactid>

</dependency>

<dependency>

<groupid>org.springframework.boot</groupid>

<artifactid>spring-boot-starter-aop</artifactid>

</dependency>

这时候启动客户端,可以看到连接服务器失败的情况,会重试6次,才返回错误信息。

重试次数和重试间隔可以自定义配置。

6. 动态刷新配置

添加actuator依赖

如果修改了配置,请求/refresh接口,配置就会自动刷新

需要注意的是,只有加了@RefreshScope注解的配置,才会自动刷新

猜你喜欢

转载自blog.csdn.net/wangzhanzheng/article/details/80597430