springCloud第三课(Hystrix监控平台、DashBoard、Turbine监控、熔断器的状态、微服务网关Zuul))
上文回顾
上文我们实现了hystrix的熔断降级,我们知道,当请求失败,被拒绝,超时的时候,都会进入到降级方法中。但进入降级方法并不意味着断路器已经被打开。那么如何才能了解断路器中的状态呢? 上文链接 项目源码gitee:gitee地址
一、Hystrix的监控平台
除了实现容错功能,Hystrix还提供了近乎实时的监控,HystrixCommand和HystrixObservableCommand在执行时,会生成执行结果和运行指标。比如每秒的请求数量,成功数量等。这些状态会暴露在Actuator提供的/health端点中。 只需为项目添加 spring-boot-actuator 依赖,重启项目,访问http://localhost:9013/actuator/hystrix.stream ,即可看到实时的监控数据。
搭建Hystrix监控平台
引入jar包
这个不出意外的话,在之前健康检查时就已经引入了。
重启服务
访问测试一下 发现根本访问不到,这里其实有个坑,要在yml文件中配置一下如下
配置yml文件
management:
endpoints:
web:
exposure:
include: '*'
再次重启服务
再次请求
发现它一直ping,然后请求一个服务
搭建Hystrix DashBoard监控
刚刚讨论了Hystrix的监控,但访问/hystrix.stream接口获取的都是以文字形式展示的信息。很难通过文字直观的展示系统的运行状态,所以Hystrix官方还提供了基于图形化的DashBoard(仪表板)监控平台。Hystrix仪表板可以显示每个断路器(被@HystrixCommand 注解的方法)的状态。
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
添加注解
在启动类使用@EnableHystrixDashboard 注解激活仪表盘项目
重启服务
重启服务,访问测试
将之前的访问的连接输入到监控连接里面,如下图
然后点击按钮进入
然后访问服务一个测试
注意:Hystrix仪表板可以显示每个断路器(被@HystrixCommand 注解的方法)的状态,未被添加注解的方法无法监控。
二、断路器聚合监控Turbine
在微服务架构体系中,每个服务都需要配置Hystrix DashBoard监控。如果每次只能查看单个实例的监控数据,就需要不断切换监控地址,这显然很不方便。要想看这个系统的Hystrix Dashboard数据就需要用到Hystrix Turbine。Turbine是一个聚合Hystrix 监控数据的工具,他可以将所有相关微服务的 Hystrix监控数据聚合到一起,方便使用。引入Turbine后,整个监控系统架构如下:
搭建ebuy-turbine
新建一个springboot项目
过程略
引入jar包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--turbine监控平台-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
<!--hystrix熔断 (因为监控平台只监控hystrix监控的方法)-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--监控 文本数据显示-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
配置多个微服务的hystrix监控
在application.yml的配置文件中开启turbine并进行相关配置
server:
port: 8031 #端口
spring:
application:
name: ebuy-turbine #服务名称
logging:
level:
cn.ebuy: DEBUG
eureka:
client:
service-url:
# 集群配置多个,单机配置一个
defaultZone: http://127.0.0.1:9880/eureka/,http://127.0.0.1:9890/eureka/
instance:
prefer-ip-address: true #使用ip地址注册
lease-expiration-duration-in-seconds: 10 #eureka client 发送心跳给server端,续约到期时间(默认90秒)
lease-renewal-interval-in-seconds: 5 #发送心跳续约时间间隔
turbine:
cluster-name-expression: "'default'"
app-config: ebuy-order
注:# 要监控的微服务列表,多个用,分隔
- eureka相关配置 : 指定注册中心地址
- turbine相关配置:指定需要监控的微服务列表
turbine会自动的从注册中心中获取需要监控的微服务,并聚合所有微服务中的 /hystrix.stream 数据
配置启动类
- 作为一个独立的监控项目,需要配置启动类,开启HystrixDashboard监控平台,并激活Turbine
测试
浏览器访问 http://localhost:8031/hystrix 展示HystrixDashboard。 并在url位置输入 http://localhost:8031/turbine.stream(文本数据),http://localhost:8031/hystrix(监控平台),动态根据turbine.stream数据展示多个微服务的监控数据
输入需要监控的微服务地址
这样turbine监控微服务平台便是搭建完成了。
三、熔断器的状态
回顾一下
回顾一下上面的情况,当product停止服务时,请求order服务,order由于请求不到product而熔断,如下图
正常未停止product微服务时: 停止product微服务: 请求order服务,发生熔断
上图可以看出监控平台显示服务状态是failure失败,而非Short-Circuited熔断,那这是为什么呢???
熔断器状态
熔断器有三个状态 CLOSED 、 OPEN 、 HALF_OPEN 熔断器默认关闭状态,当触发熔断后状态变更为OPEN ,在等待到指定的时间,Hystrix会放请求检测服务是否开启,这期间熔断器会变为 HALF_OPEN 半开启状态,熔断探测服务可用则继续变更为 CLOSED 关闭熔断器。
Closed:关闭状态(断路器关闭),所有请求都正常访问。代理类维护了最近调用失败的次数,如果某次调用失败,则使失败次数加1。如果最近失败次数超过了在给定时间内允许失败的阈值,则代理类切换到断开(Open)状态。此时代理开启了一个超时时钟,当该时钟超过了该时间,则切换到半断开(Half-Open)状态。该超时时间的设定是给了系统一次机会来修正导致调用失败的错误。
Open:打开状态(断路器打开),所有请求都会被降级(熔断兜底)。Hystix会对请求情况计数,当一定时间 内失败请求百分比达到阈值,则触发熔断,断路器会完全关闭。默认失败比例的阈值是50%,请求次数最少不低于20次。
Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放1次请求通过,若这个请求是健康的,则会关闭断路器,否则继续保持打开,再次进行5秒休眠计时。
测试之前的准备
为了能够精确控制请求的成功或失败,我们在一下代码做调整 注:这样如果参数是id为816753,一定成功,其它情况都失败。
我们准备两个请求窗口:
- 成功请求:http://localhost:9013/order/hystrix/816753
- 不成功请求:http://localhost:9013/order/hystrix/1
熔断器的默认触发阈值是20次请求,不好触发。休眠时间时5秒,时间太短,不易观察,为了测试方便,我们可以通过配置修改熔断策略:
circuitBreaker.requestVolumeThreshold=5
circuitBreaker.sleepWindowInMilliseconds=10000
circuitBreaker.errorThresholdPercentage=50
- requestVolumeThreshold:触发熔断的最小请求次数,默认20
- errorThresholdPercentage:触发熔断的失败请求最小占比,默认50%
- sleepWindowInMilliseconds:熔断多少秒后去尝试请求
开始测试
熔断器处于关闭状态
- 请求错误的服务,但是请求次数小于5次
- 服务仍是关闭状态
- 请求错误的服务,次数大于5次 - 此时熔断器的状态变为开启状态,进入休眠时间(5秒)
熔断器处于打开状态
-
在熔断器进入开启状态之后不到5s内请求正确的服务 -
发现正确的服务也被熔断处理
- 在熔断器进入开启状态之后不到5s内请求错误的服务
总结:熔断器处于开启状态下,所有服务都会被降级处理。
熔断器处于半开状态
- 熔断器进入开启状态之后5s,进入半开状态
- 此时请求正确的服务 - 服务正常返回,并且熔断器状态变为关闭
- 熔断器进入开启状态之后5s,进入半开状态
- 此时请求错误的服务
- 熔断器状态变为开启状态,进入5秒休眠期
注意:为了方便理解,可以根据上述的熔断器状态流程图来分析
四、微服务网关
在学习完前面的知识后,微服务架构已经初具雏形。但还有一些问题:不同的微服务一般会有不同的网络地址,客户端在访问这些微服务时必须记住几十甚至几百个地址,这对于客户端方来说太复杂也难以维护。如下图:
如果让客户端直接与各个微服务通讯,可能会有很多问题:
- 客户端会请求多个不同的服务,需要维护不同的请求地址,增加开发难度
- 在某些场景下存在跨域请求的问题
- 加大身份认证的难度,每个微服务需要独立认证
因此,我们需要一个微服务网关,介于客户端与服务器之间的中间层,所有的外部请求都会先经过微服务网关。客户端只需要与网关交互,只知道一个网关地址即可,这样简化了开发还有以下优点:
- 易于监控
- 易于认证
- 减少了客户端与各个微服务之间的交互次数
服务网关的概念
什么是微服务网关
API网关是一个服务器,是系统对外的唯一入口。API网关封装了系统内部架构,为每个客户端提供一个定制的API。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和 管理服务。
作用和应用情景
网关具有的职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。当然,最主要的职责还是与“外界联系”。
常见的API网关实现方式
基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。问题:只支持Http协议;二次开发,自由扩展困难;提供管理API,缺乏更易用的管控、配置方式。
Netflix开源,功能丰富,使用JAVA开发,易于二次开发;需要运行在web容器中,如Tomcat。问题:缺乏管控,无法动态配置;依赖组件较多;处理Http请求依赖的是Web容器,性能不如Nginx;
Go语言开发;轻量易用;提供大多数的功能:服务路由,负载均衡等等;提供WebUI 问题:二进制文件部署,二次开发难度大;UI更多的是监控,缺乏配置、管理能力;
SpringCloud提供的网关服务
使用Nginx的反向代理和负载均衡可实现对api服务器的负载均衡及高可用 问题:自注册的问题和网关本身的扩展性
基于Nginx的网关实现
Nginx介绍
正向/反向代理
正向代理
正向代理,“它代理的是客户端,代客户端发出请求”,是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端必须要进行一些特别的设置才能使用正向代理。
反向代理
多个客户端给服务器发送的请求,Nginx服务器接收到之后,按照一定的规则分发给了后端的业务处理服务器进行处理了。此时~请求的来源也就是客户端是明确的,但是请求具体由哪台服务器处理的并不明确了,Nginx扮演的就是一个反向代理角色。客户端是无感知代理的存在的,反向代理对外都是透明的,访问者并不知道自己访问的是一个代理。因为客户端不需要任何配置就可以访问。反向代理,“它代理的是服务端,代服务端接收请求”,主要用于服务器集群分布式部署的情况下,反向代理隐 藏了服务器的信息,如果只是单纯的需要一个最基础的具备转发功能的网关,那么使用Ngnix是一个不错的选择。
微服务网关Zuul
Zuul简介
ZUUL是Netflix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用,Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:
- 动态路由:动态将请求路由到不同后端集群
- 压力测试:逐渐增加指向集群的流量,以了解性能
- 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
- 静态响应处理:边缘位置进行响应,避免转发到内部集群
- 身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求。Spring Cloud对Zuul进行
了整合和增强。
搭建Zuul网关服务器
创建工程导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--zuul网关的jar-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
编写启动类
- @EnableZuulProxy : 通过 @EnableZuulProxy 注解开启Zuul网管功能
编写配置
- 创建配置文件 application.yml ,并添加相应配置
server:
port: 9090 #端口
spring:
application:
name: ebuy-zuul #服务名称
logging:
level:
cn.ebuy: DEBUG
Zuul中的路由转发
最直观的理解:“路由”是指根据请求URL,将请求分配到对应的处理程序。在微服务体系中,Zuul负责接收所有的请求。根据不同的URL匹配规则,将不同的请求转发到不同的微服务处理。
对应的配置
zuul:
routes:
ebuy-order: #只是个在这个配置文件中的节点名字这里是路由id,随意写
path: /ebuy-order/** # 映射路径
url: http://127.0.0.1:9013/ # 映射路径对应的实际url地址
sensitive-Headers: #默认zuul会屏蔽cookie,cookie不会传到下游服务,这里设置为空则取
消默认的黑名单,如果设置了具体的头信息则不会传到下游服务
ebuy-product:
path: /ebuy-product/**
url: http://127.0.0.1:9015/
sensitive-Headers:
例子: path: /ebuy-order001/** # 映射路径 地址栏访问 :http://localhost:9090/ebuy-order001/order/hystrix/816753
启动测试
-
启动服务 -
访问测试
面向服务的路由
微服务一般是由几十、上百个服务组成,对于一个URL请求,最终会确认一个服务实例进行处理。如果对每个服务实例手动指定一个唯一访问地址,然后根据URL去手动实现请求匹配,这样做显然就不合理。
Zuul支持与Eureka整合开发,根据ServiceID自动的从注册中心中获取服务地址并转发请求,这样做的好处不仅可以通过单个端点来访问应用的所有服务,而且在添加或移除服务实例的时候不用修改Zuul的路由配置。
添加Eureka客户端依赖
改造上述的项目 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
添加Eureka客户端发现功能
添加Eureka配置,获取服务信息
# Eureka配置
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:9890/eureka/,http://127.0.0.1:9880/eureka
lease-expiration-duration-in-seconds: 10 #eureka client发送心跳给server端后,续约到期时间(默认90秒)
lease-renewal-interval-in-seconds: 5 #发送心跳续约时间间隔
registry-fetch-interval-seconds: 5 # 获取服务列表的周期:5s
instance:
prefer-ip-address: true # 使用ip地址註冊
因为已经有了Eureka客户端,我们可以从Eureka获取服务的地址信息,因此映射时无需指定IP地址,而是通过服务名称来访问,而且Zuul已经集成了Ribbon的负载均衡功能。
zuul:
routes:
ebuy-order:
path: /ebuy-order/**
serviceId: ebuy-order #配置转发的微服务名称
ebuy-product:
path: /ebuy-product/**
serviceId: ebuy-product #配置转发的微服务名称
注:serviceId: 指定需要转发的微服务实例名称
访问测试
- 重启服务,开始测试
- 可以看出zuul已经实现了负载均衡
简化的路由配置
在刚才的配置中,我们的规则是这样的:
zuul.routes.<route>.path=/xxx/** : 来指定映射路径。 <route> 是自定义的路由名。zuul.routes.<route>.serviceId=/product-service :来指定服务名。 而大多数情况下,我们的 <route> 路由名称往往和服务名会写成一样的。因此Zuul就提供了一种简化的配置语法: zuul.routes.<serviceId>=<path> 上面的配置可以简化为一条:
zuul:
routes:
ebuy-product: /ebuy-product/** #第一个ebuy-product是指服务名,第二个是地址栏要输入的映射路径
ebuy-order: /ebuy-order/**
重启服务测试
不再截图展示
发现问题
- 模拟高并发情况下的问题
- 重启,然后再次使用zuul的路径访问服务
- 发现提示
com.netflix.zuul.exception.ZuulException: Hystrix Readed time out
回顾之前我们配置,可以给zuul配置如下(注意是ebuy-zuul项目)
ebuy-product:
ribbon:
#NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
ConnectTimeout: 2500 # Ribbon的连接超时时间(创建连接时间:毫秒)
ReadTimeout: 5000 # Ribbon的数据读取超时时间 (得到数据的时间)
OkToRetryOnAllOperations: true # 是否对所有操作都进行重试
MaxAutoRetriesNextServer: 1 # 切换实例的重试次数
MaxAutoRetries: 1 # 对当前实例的重试次数 (1表示不重试自己)
ebuy-order:
ribbon:
#NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
ConnectTimeout: 2500 # Ribbon的连接超时时间(创建连接时间:毫秒)
ReadTimeout: 5000 # Ribbon的数据读取超时时间 (得到数据的时间)
OkToRetryOnAllOperations: true # 是否对所有操作都进行重试
MaxAutoRetriesNextServer: 1 # 切换实例的重试次数
MaxAutoRetries: 1 # 对当前实例的重试次数 (1表示不重试自己)
- 然后重启配置
- 重新访问测试
- 问题解决(可以把ebuy-product的模拟所写的线程休眠注释掉了)
默认的路由规则
在使用Zuul的过程中,上面讲述的规则已经大大的简化了配置项。但是当服务较多时,配置也是比较繁琐的。因此Zuul就指定了默认的路由规则:
- 默认情况下,一切服务的映射路径就是服务名本身。
- 例如服务名为: ebuy-product ,则默认的映射路径就是: /ebuy-product/**
那么到底是什么意思呢? 意思就是说上述的路由配置是完全不需要的(当然前提是你的访问路径想要和应用名同名)
- 注释掉路由相关的配置
- 访问服务测试
- 访问完全没问题
|