springBoot学习
2019-12-04 09:42:02 举报
AI智能生成
登录查看完整内容
相似推荐
查看更多
微服务技术架构(SpringBoot)
学习状态_考试状态
学习资源
SpringBoot源码解析
新手爬虫学习
Java学习路线
学习进度
适配小组学习计划
学习状态_课程进度
学习类目
《Spring Cloud 微服务实战》
作者其他创作
大纲/内容
springBoot学习《Spring Cloud微服务实战》
基本信息
作者
翟永超
微服务构建:Spring Boot
创建
访问 http://start.spring.io/ ,以Maven 或 Gradle构建
编写测试用例
范例
@RunWith
引入Junit4
@SpringApplicationConfiguration
指定spring boot 的启动类
@WebAppConfiguration
开启Web应用的配置,用于模拟ServletContext
MockMvc对象
模拟调用Controller接口发起请求
perform函数执行一次请求调用,accept用于执行接收的数据类型,andExpect用于判断接口返回的期望值
配置
默认配置文件为 application.properties文件,同时支持YAML文件
YAML文件无法通过@PropertySource注解来加载配置
YAML将属性加载到内存中保存是有序的
自定义参数
在配置文件中声明后,可以在应用中通过@Value进行加载
参数引用
各参数之间可以通过PlaceHolder的方式来进行引用
使用随机数
在属性配置文件中,可以通过使用${random}配置来产生随机的int值,long值,string字符串
说明
命令行参数
启动spring boot应用,需要使用命令:java -jar
在Linux 系统启动时,需要以nohup开头, &结尾,否则会意外结束进程
在命令行方式启动spring boot应用时,连续的两个减号 -- 就是对application.properties中的属性值进行赋值的标识
可以在启动应用时对配置属性重新赋值
多环境配置
多环境配置的文件名,需要满足:application-{profile}.properties的格式
具体加载哪个配置文件,由application.properties文件中的spring.profiles.active属性来配置
启动顺序
监控和管理
集成actuator
客户端负载均衡: Spring Cloud Ribbon
基于HTTP和TCP的客户端负载均衡工具,基于Netflix Ribbon实现
微服务中负载均衡调用的实现
服务提供者只需要启动多个服务实例并注册到一个注册中心或是多个相关联的服务注册中心
服务消费者直接通过调用被@LoadBalanced注册修饰过的RestTemplate来实现面向服务的接口调用
RestTemlate
GET请求
getForEntity
getForObject
可以理解为对getForEntity进一步封装
通过HttpMessageConverterExtractor对HTTP的请求响应体body内容进行转换,实现请求直接返回包装好的对象
POST请求
postForEntity(与GET请求中的类似)
postForObject(与GET请求中的类似)
postForLocation
实现以POST请求提交资源,并返回新资源的URI
PUT请求
DELETE请求
源码分析
LoadBalanceClient
RestTemplate 通过@LoadBalance标记,使用LoadBalanceClient来配置
choose(String serviceId)
根据传入的服务名serviceId,从负载均衡器中挑选一个对应服务的实例
使用从负载均衡器中挑选出的服务实例来执行请求内容
通过ServiceInstance 中的host和port,拼接出具体host:port形式的请求地址
在分布式系统中,使用逻辑上的服务名称作为host来构建URI进行请求
LoadBalancerAutoConfigration
实现客户端负载均衡器的自动化配置类
Ribbon实现负载均衡自动化配置
@ConditionOnClass(RestTemplate.class)
RestTemplate 类必须在于当前工程的环境中
@ConditionalOnBean(LoadBalancerClient.class)
在spring的Bean工程中必须有LoadBalancerClient的实现类
主要操作
创建一个LoadBalancerInterceptor的Bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡
创建一个RestTemplateCustomizer的Bean,用于给RestTemplate增加LoadbBalancerInterceptor拦截器
维护一个被@LoadBalanced注解修饰的RestTemplate对象列表
并在这里进行初始化,通过调用RestTemplateCustomizer的实例来给需要客户端负载均衡的RestTemplate增加LoadBalancerInterceptor拦截器
LoadBalancerInterceptor拦截器如何将一个普通RestTemplate变成客户端负载均衡的
当一个被LoadBalanced注解修饰的RestTemplate对象向外发起HTTP请求时,会被LoadBalanceInterceptor类的intercept函数拦截
RestTemplate中使用服务名作为host,调用execute函数根据服务名来选择实例并发起实际请求
RibbonLoadBalanceClient
负载均衡器实现类
getServer(ILoadBalancer loadBalancer)
获取具体服务器实例时,没有使用LoadBalancerClient接口中的choose函数
ILoadBalancer
addservers
向负载均衡器中维护的实例列表增加服务实例
chooseServer
通过某种策略,从负载均衡器中挑选一个具体的服务实例
markServerDown
用来通知和标识负载均衡器中某个具体实例已经停止服务,不然负载均衡器在下一次获取服务实例清单前都会认为服务实例均是正常的
getReachableServers
获取当前正常服务的实例列表
getAllServers
获取所有已知的服务实例列表,包括正常和停止的
ILoadBalancer的实现
BaseLoadBalancer
基础负载
DynamicServerListLoadBalancer
ZoneawareLoadBalancer
SpringClientFactory
用来创建客户端负载均衡器的工厂类
会为每一个不同名的Ribbon客户端生成不同的Spring上下文
RibbonLoadBalancerContext
LoadBalancerContext的子类
存储一些被不在均衡器使用的上下文内容和API操作
Ribbon通过RibbonStatsRecorder对象对服务的请求进行了跟踪记录
负载均衡器
Spring Cloud中定义了LoadBalancerClient作为负载均衡器的通用接口
AbstractLoadBalancer
ILoadBalancer类的抽象实现
ServerGroup
服务实例的分组枚举
ALL:所有服务实例
STATUS_UP:正常服务的实例
STATUS_NOT_UP:停止服务的实例
getLoadBalancerStats()
获取LoadBalancerStats对象
LoadBalancerStats
用来储存负载均衡器中各个服务实例当前的属性和统计信息
这些信息可以反应负载均衡的运行情况
也是制定负载均衡策略的重要依据
Ribbon负载均衡器的基础实现类
定义了两个存储服务实例Server对象的列表,一个是所有的实例,一个是正常服务的
定义了LoadBalancerStats对象
定义了检查服务实例是否正常服务的IPing对象
默认为null,需要在构造时注入具体实现
定义了检查服务实例操作的执行策略对象IPingStrategy,并有内部类实现
采用线性遍历ping服务实例的方式实现检查
定义了负载均衡的处理规则IRule对象
负载均衡器实际将服务实例选择任务委托给了IRule实例中的choose函数来实现
默认初始化了RoundRobinRule为IRule的实现对象
RoundRobinRule实现了最基本且常用的线性负载均衡规则
启动ping任务,默认构造函数中,会直接启动给一个用于定时检查Server是否健康的任务,默认执行间隔为10秒
继承于BaseLoadBalancer,是对基础负载均衡器的扩展
实现了服务实例清单在运行期的动态更新能力
对服务实例清单的过滤功能
ServerList
服务列表操作对象
ServicerListUpdater
触发向Eureka Server 去获取服务实例清单以及如何在获取到服务实例清单后更新本地的服务实例清单
PollingServerListUpdater
默认策略,通过定时任务的方式进行服务列表的更新
EurekaNotificationBalancer
需要利用Eureka的事件监听器来驱动服务列表的更新操作
ServerListFilter
通过传入的服务实例清单,根据一些规则返回过滤后的服务实例清单
ZoneAwareLoadBalancer
依旧是以线性轮询的方式来选择调用的服务实例
该算法实现简单并没有区域(Zone)的概念,所以会把所有实例视为一个Zone下的节点来看待
同时,避免了多区域部署的性能问题
setServerListForZones
创建一个ConcurrentHashMap类型的balancers对象
用来存储每个Zone区域对应的负载均衡器
具体负载均衡器的创建则通过getLoadBalancer函数完成
同时创建负载均衡器时候会创建它的规则
如果当前实现没有IRule的实例,就创建一个AvaliabilityFilteringRule规则
如果有具体实例,就克隆一个
创建完负载均衡器后,调用setServersList函数为其设置对应Zone区域的实例清单
检查Zone区域中实例清单,如果Zone区域下已经没有实例了,就把balancers中对应Zone区域的实例列表清空
防止过时的Zone区域统计信息干扰具体实例的选择算法
挑选实例
只有当负载均衡器中维护的实例所属的Zone区域个数大于1的时候才会执行选择策略,否则还将使用父类实现
Photo 1
负载均衡策略
IRule接口的各个实现
AbstractLoadBalancerRule
负载均衡策略的抽象类,定义了负载均衡器ILoadBalancer对象
在具体实现选择服务策略时,获取一些负载均衡器中维护的信息作为分配依据
RandomRule
从服务实例清单中随机选择一个服务实例
RoundRobinRule
按照线性轮询的方式依次选择每个服务实例
和RandomRule类似,除循环条件不同,增加了一个count计数变量,在每次循环后累加,如果一直选择不到server超过10次,就会结束,并打印警告信息
RetryRule
具备重试机制的实例选择
在其内部定义了一个IRule对象,默认使用RoundRobinRule实例
在choose方法中则实现了对内部定义的策略进行反复尝试的策略
WeightedResponseTimeRule
对RoundRobinRule的扩展,增加了根据实例运行情况计算权重
并根据权重挑选实例,以达到更优分配的效果
实现的3个核心内容
定时任务
在策略初始化的时候,会启动一个定时任务,为每个服务实例计算权重,默认30秒一次
权重计算
存储权重的对象:List accumulatedWeights
该List中每个权重值所处的位置对应了负载均衡器维护的服务实例清单中所有实例在清单中的位置
实现步骤
根据LoadBalancerStats中记录的每个实例的统计信息,累加所有实例的平均响应时间,得到总平均响应时间totalResponseTime
为实例清单逐个计算权重
实例选择
ClientConfigEnabledRoundRobinRule
高级策略均基于此类扩展
BestAvailableRule
遍历所有服务实例,过滤掉故障的,找到并发请求数最小的
依据统计对象loadBalancerStats,为空时候,策略无法执行,采用父类的线性轮询策略
PredicateBasedRule
基于Predicate实现的策略
Predicate是Google Guava Collection工具对集合进行过滤的条件接口
AvailabilityFilteringRule
继承自PredicateBasedRule,先过滤清单,再轮询选择
过滤条件使用AvailabilityPredicate
判断过滤条件
是否故障,即断路器是否生效已断开
实例并发请求数大于阈值,默认2的32次方减1
选择一个实例,接着用过滤条件判断该实例是否满足要求,若不满足继续循环,重复10次还找不到符合条件的,就采用父类的实现方案
ZoneAvoidanceRule
继承自PredicateBaseRule的具体实现类
配置详解
自动化配置
参数配置
与Eureka的结合
重试机制
Spring Cloud Eureka实现的服务治理机制强调了CAP原理中的AP,可用性和可靠性
与Zoookeeper这类强调CP(一致性,可靠性)的最大区别是为了实现服务的可用性,牺牲了一定的一致性
Eureka的自动保护机制,即使有部分故障节点,也可以保障大多数的服务正常消费
Spring Cloud整合了Spring Retry来增强RestTemplate的重试能力
开启重试配置
声明式服务调用: Spring Cloud Feign
分布式配置中心:Spring Cloud Config
用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持
服务端
也称为分布式配置中心,用来连接配置仓库并未客户端提供获取配置信息、加密、解密信息等访问接口
客户端
客户端是微服务架构中的各个微服务应用或基础设施,它们通过制定的配置中心来管理应用资源与业务相关的配置内容
Spring Cloud Config 实现了对服务端和客户端中环境变量和属性配置的抽象映射
默认使用git来存储信息
服务端详解
基础架构
远程Git仓库
用来存储配置文件的地方
Config Server
构建的分布式配置中心,在工程中指定了所要连接的Git仓库位置以及账户、密码等连接信息
本地Git仓库
在Config Server的文件系统中,每次客户端请求获取配置信息时,Config Server从Git仓库中获取最新配置到本地,然后在本地Git仓库中读取并返回
当远程仓库无法获取时,直接将本地内容返回
具体的微服务应用
指定了Config Server地址,从而实现从外部化获取应用自己要用的配置信息这些应用在启动的时候,会向Config Server请求获取配置信息来进行加载
客户端获取配置信息的执行流程
1、应用启动时,根据bootstrap.properties中配置的应用名{application}、环境名{profile}、分支名{label},向Config Server请求获取配置信息
2、Config Server 根据维护的Git仓库信息和客户端传递过来的配置定位信息去查找配置信息
3、通过 git clone 命令将找到的配置信息下载到Config Server的文件系统中
4、Config Server创建Spring的ApplicationContext实例,并从Git本地仓库中加载配置文件,最后将这些配置内容读取出来返回给客户端应用
5、客户端应用在获得外部配置文件后加载到客户端的ApplicationContext实例,该配置内容的优先级高于客户端Jar包内部的配置内容,所以在Jar包中重复的内容不会再被加载
通过git clone将配置信息存于本地,起到了缓存的作用
Git配置仓库
配置仓库默认实现采用Git
设置Git请求地址
spring.cloud.config.server.git.uri
默认http请求uri
file:// 设置一个文件地址,以本地仓库的方式运行
file://${user.home}/config-repo
${user.home} 代表当前用户的所属目录
对本地开发调试时使用非常方便
账号
spring.cloud.config.server.git.username
spring.cloud.config.server.git.password
占位符配置URI
这些占位符除了用于标识配置文件的规则外,还可以用于对Git仓库地址的URI配置
{application}
代表了应用名,Config Server会根据客户端的spring.application.name信息来填充
{profile}
环境,dev/test等
{label}
Git的分支和标签名包含\"/\
通过在URI中使用占位符可以实现通用的仓库配置
代码库
使用服务名作为Git仓库名称
配置库
使用服务名加上-config后缀作为Git仓库名称
使用{application}-config配置,来同时匹配多个不同服务的配置仓库
配置多个仓库
通过带有通配符的表达式来匹配,当有多个匹配规则的时候,还可以用逗号来分割多个{application}/{profile}配置规则
当未匹配到合适的仓库时,就将在该默认仓库下获取配置信息
当配置多个仓库时,Config Server在启动时会直接克隆第一个仓库的配置库,其他的配置库只有在请求时才会克隆到本地
子目录存储
spring.cloud.server.git.searchPaths
指定仓库的子目录下实现配置的存储
支持使用{application}、{profile}和{label}占位符
访问权限
HTTP的认证方式,username和password配置账户
SSH的方式,通过生成Key并在Git仓库中进行配置匹配实现访问
SVN仓库
引入SVN依赖:svnkit groupId: org.tmatesoft.svnkit
svn配置url: svn://localhost:443/didispace/config-repo
高可用配置
传统模式
所有的Config Server都指向同一个Git仓库,客户端指定Config Server只需要配置Config Server上层的负载均衡设备地址
服务模式
Config Server 作为一个普通的微服务应用,纳入 Eureka的服务治理体系中
加密解密
在微服务架构中,通常采用DevOps的组织方式来降低因团队间沟通早场的巨大成本
DevOps(英文Development和Operations的组合)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。它的出现是由于软件行业日益清晰地认识到:为了按时交付软件产品和服务,开发和运营工作必须紧密合作。可以把DevOps看作开发(软件工程)、技术运营和质量保障(QA)三者的交集。
子主题
使用前提
在配置中心的运行环境中安装不限长度的JCE版本(Unlimited Strength Java Cryptography Extension)
下载压缩包,把里面的jar包复制到$JAVA_HOME/jre/lib/security目录下,覆盖原来的默认内容
相关端点
配置密钥
通过encrypt.key属性在配置文件中直接指定密钥信息(对称性密钥)
encrypt.key=didispace
非对称加密
生成公钥/私钥
使用%JAVA_HOME%\\bin\\keytool.exe
一次生成密钥库
默认生成的密钥只有90天有效期,通过 -validity参数来实现
通过命令生成方式,最终会在命令当前执行目录下生成一个config-server.keystore文件,在配置中心加入相关的配置信息
配置文件直接配置,如果密钥文件放在src/main/resource目录下,可以这样配置:encrypt.key-store.location=config-server.keystore
通过环境变量配置,可以获得更好的安全性,将敏感 的口令信息存储在配置中心的环境变量中
安全保护
引入 spring-boot-starter-security依赖,无需配置
默认情况下,会获取一个名为user的用户,在配置中心启动的时候,日志中会打印出该用户的随机密码
Using generated security password: 16f3cae7-4cab-4b8d-acd4-b71cb49f172f
指定用户名和密码
security.user.name=user
sercurity.user.password=123
在服务端配置了安全保护,那么客户端也要加入安全信息来通过校验
spring.cloud.config.username=userspring.cloud.config.password=123
属性覆盖
通过 spring.cloud.config.server.overrids 属性来设置键值对的参数,这些参数以Map的方式加载到客户端的配置中
通过该属性配置的参数,不会被Spring Cloud的客户端修改
Spring Cloud客户端从 Config Server中获取配置信息时,都会获取这些配置信息。可以配置一些共同属性或默认值
客户端可以改变更高优先级的配置,来选择是否使用Config Server提供的默认值
健康监测
在Server Config 中有spring-boot-actuator模块,/actuator/health有对应的健康检测器:ConfigServerHealthIndicator
单独配置check仓库
spring.cloud.config.server.health.repositories.check.name=check
应用名
spring.cloud.config.server.health.repositories.check.label=master
分支名
spring.cloud.config.server.health.repositories.check.profiles=default
环境名
关闭健康检测器
spring.cloud.config.server.health.enabled=false
本地仓库
在使用了Git或SVN仓库后,文件会在Config Server的本地文件系统中存储一份,默认存储于以 config-repo为前缀的临时目录中,如: /tmp/config-repo-<随机数>
配置:spring.cloud.config.server.<svn/git>.basedir
本地文件系统
不使用远程仓库,而是使用本地文件系统的存储方式来保存配置信息
spring.profiles.active=native
默认从应用的src/main/resource目录下搜索配置文件
spring.cloud.config.server.native.searchLocations 指定具体的配置文件的位置
客户端详解
URI指定配置中心
Spring Cloud Config 的客户端在启动的时候,默认从工程的classpath中加载配置信息并启动应用
如果不指定spring.cloud.config.uri, 会默认使用 http://localhost:8888
服务化配置中心
失败快速响应与重试
实现客户端优先判断 Config Server获取是否正常,并快速响应失败内容
在bootstrap.properties中配置spring.cloud.config.failFast=true
实现重试
pom配置
<dependency>\t\t\t<groupId>org.springframework.retry</groupId>\t\t\t<artifactId>spring-retry</artifactId>\t\t</dependency>\t\t<dependency>\t\t\t<groupId>org.springframework.boot</groupId>\t\t\t<artifactId>spring-boot-starter-aop</artifactId>\t\t</dependency>
获取远程配置
动态刷新配置
增加
消息驱动的微服务: Spring Cloud Stream
分布式服务跟踪:Spring Cloud Sleuth
消息总线:Spring Cloud Bus
API网关服务:Spring Cloud Zuul
基于Netflix Zuul实现的API网关组件
通过和Spring Cloud Eureka进行整合,将自身注册为Eureka服务治理下的应用,同时从Eureka中获得了所有其它微服务的实例信息
对于路由规则的维护,Zuul默认会将通过以服务名作为ContextPath的方式来创新路由映射
API网关的作用
解决微服务架构中,对于微服务接口访问时各前置校验的冗余问题
定义类似于面向对象设计模式中的Facade模式,就像系统的门面一样,所有的外部客户端访问都需要经过它来进行调度和过滤
路由详解
传统路由配置
单例配置
zuul.routes..path和zuul.routes..url参数对应的方式
例子
zuul.routes.user-service.path=/user-service/**
zuul.routes.user-service.url=http://localhost:8080/
多实例配置
zuul.routes..path与zuul.routes..serviceId参数对的方式进行配置
zuul.routes.user-service.serviceId=user-service
ribbon.eureka.enabled=false
服务路由配置
zuul.routes.user-service=/user-service/**
服务路由的默认规则
Zuul引入Eureka之后,会为Eureka中每个服务创建一个默认路由规则
这些默认规则的path会使用serviceId配置的服务名作为请求前缀
设置不生成默认路由规则的服务
zuul.ignored-services
自定义路由映射规则
为一组互相配合的微服务定义一个版本标识来管理
类似于这样的命名:userservice-v1,orderservice-v1
通常的做法是为这些不同版本的微服务生成以版本号前缀的URL路径
分支主题
PatternServiceRouteMapper对象可以让开发者通过正则表达式来自定义服务和路由映射的生成关系
如果没有 匹配上的服务则还是会使用默认的路由映射规则,即采用完整服务名作为前缀的路径表达式
路径匹配
Zuul中,路由匹配的路径表达式采用了Ant分隔定义
?匹配任意单个字符
* 匹配任意数量的字符
** 匹配任意数量的字符,支持多级目录
路由规则匹配请求路径的时候通过线性遍历的方式,在请求路径获取到第一个匹配的路由规则之后就返回并结束匹配过程
路由规则是通过LinkedHashMap保存的,也就是说,路由规则的保存是有序的
由于properties的配置内容无法保证有序
为了保证路由的优先顺序,需要使用YAML文件来配置
忽略表达式
zuul.ignored-patterns
设置不希望被API网关进行路由的URL表达式
zuul.ignored-patterns=/**/hello/**
路由前缀
zuul.prefix=/api
设置网关上的路由规则都增加/api的前缀
关闭代理前缀
移除代理前缀的动作
zuul.stripPrefix=false
对指定路由关闭移除代理前缀的动作
zuul.routes..strip-prefix=true
本地跳转
支持forward形式的服务端跳转
只需要通过使用path与url的配置方式
通过url中使用forward来指定需要跳转的服务器资源路径
zuul.routes.api-b.path=/api-b/**
zuul.routes.api-b.url=forward:/local
所以该请求会被API网关转发到网关的/local/hello请求上进行本地处理
Cookie与头信息
默认情况下,Spring Cloud Zuul在请求路由时,会过滤掉HTTP请求头信息中的一些敏感信息,防止传递到下游的外部服务器
默认的敏感头信息通过zuul.sensitiveHeaders参数定义,包括Cookie、Set-Cookie、Authorization
Web项目时常用的Cookie在Zuul网关中默认不会传递
解决Cookie传递的问题
通过设置全局参数为空来覆盖默认值(不推荐)
zuul.sensitiveHeaders
指定路由的参数来配置(推荐)
对指定路由开启自定义敏感头
zuul.routes..customSensitiveHeaders=true
将指定路由的敏感头设置为空
zuul.routes..sensitiveHeaders=
重定向问题
通过网关
zuul.addHostHeader=true
重定向返回:return new ModelAndView(\"redirect:http://localhost:5555/hellos/hello?accessToken=token\");
Hystrix 和 Ribbon支持
Zuul自身包含了对Hystrix和Ribbon的依赖,所以Zuul天生就拥有线程隔离和断路器的自我保护功能,以及对服务调用的客户端负载均衡功能
通过path和url的映射关系来配置路由规则的时候,路由转发请求不会采用HystrixCommand来包装
推荐 使用path和serviceId的组合来进行配置,这样保证了API网关的健壮和稳定,也能用到Ribbon的客户端负载均衡功能
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
设置API网关中路由转发请求HystrixCommand执行超时时间,单位:毫秒
路由转发请求的命令执行时间超过该配置值后,Hystrix会将该执行命令标记为TIMEOUT并抛出异常
Zuul会对该异常进行处理并返回JSON信息给外部调用方
ribbon.ConnectTimeout
设置路由转发请求时,创建请求连接的超时时间
当ribbon.ConnectTimeout的配置值小于hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds配置值得时候,若出现路由请求连接超时,就会自动重试
依然失败的话,就向外部抛出JSON信息
ribbon.ReadTimeout
设置路由转发请求的超时时间,它的超时是对请求连接建立之后的处理时间
过滤器详解
过滤器
每个进入Zuul的HTTP请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端
路由功能在真正运行时,路由映射通过pre类型的过滤器完成,它将请求路径和配置的路由规则进行匹配;而请求穿发则由route类型的过滤器来完成
四个基本特征
过滤类型
执行顺序
执行条件
具体操作
ZuulFilter接口
filterType()
返回一个字符串来代表过滤器类型,这个类型就是HTTP请求过程中定义的各个阶段
pre
可以在请求被路由之前调用
routing
在路由请求时被调用
post
在routing和error过滤器之后被调用
error
处理请求时发生错误时被调用
filterOrder()
通过 int 值来定义过滤器的执行顺序,数值越小优先级越高
shouldFilter()
返回一个boolean值判断过滤器是否执行
run()
过滤器的具体逻辑
请求生命周期
不同类型过滤器在HTTP请求中执行的位置
核心过滤器
在Zuul中,HTTP请求生命周期的各个阶段默认实现一批核心过滤器,它们在API网关服务启动的时候被自动加载和启用
pre过滤器
ServletDetectionFilter
执行顺序为 -3,是最先被执行的过滤器
主要检测当前请求是通过Spring的DispathcherServlet处理运行,还是通过ZuulServlet;来运行的
一般情况下,发送到API网关的外部请求都会被Spring的DispathcerServlet处理,除了/zuul/*路径访问的请求会绕过被ZuulServlet
Servlet30WrapperFilter
执行顺序:-2
主要为了把HttpServletRequest包装成Servlet30RequestWrapper对象
FormBodyWrapperFilter
执行顺序: -1
对两类请求生效
Content-Type=application/x-www-form-urlencoded
主要目的是将符合要求的请求体包装成FormBodyRequestWrapper对象
DebugFilter
执行顺序:1
根据配zuul.debug.request 和请求中的 debug 参数来决定是否执行过滤器
具体操作内容是将请求上下文中的 debugRouting 和 debugRequest 参数设置为true,用于通过请求的方式激活哪些根据这两个值定义的debug信息
请求参数中debug参数,可以通过zuul.debug.parameter来自定义
PreDecorationFilter
执行顺序:5
判断请求上下文中是否存在forward.to 和 serviceId 参数
如果有一个存在的话,说明当前请求已经被处理过了,因为这两个信息就是根据当前请求的路由信息加载进来的
具体操作内容是为当前请求做一些预处理
路由规则的匹配
在请求上下文中设置该请求的基本信息及路由匹配结果等一些设置信息
这些信息是后续过滤器进行处理的重要依据
可以通过 RequestContext.getCurrentContext( )来访问这些信息
对HTTP头请求进行处理
Zuul在请求跳转时默认会为请求增加X-Forwarded-*头域
route过滤器
RibbonRoutingFilter
执行顺序:10
只对请求上下文中存在serviceId参数的请求进行处理,即只对通过serviceId配置路由规则的请求生效
通过使用Ribbon和Hystrix来向服务实例发起请求,并将服务实例的请求结果返回
SimpleHostRoutingFilter
执行顺序:100
对请求上下文中存在routeHost参数的请求进行处理,即只对通过url配置路由规则的请求生效
该过滤器的执行逻辑就是直接向routeHost参数的物理地址发起请求,httpclient包实现的,没有线程隔离和断路器的保护
SendForwardFilter
执行顺序:500
只对请求上下文中存在forward.to参数的请求进行处理,即处理路由规则中的forward本地跳转配置
post过滤器
SendErrorFilter
执行顺序:0
在请求上下文中包含error.status_code参数(由之前执行的过滤器设置的错误编码)并且还没有被该过滤器处理过的时候执行
利用请求上下文中的错误信息组成一个forward到API网关/error错误端点的请求来产生错误响应
SendResponseFilter
执行顺序:1000
该过滤器会检查请求上下文中是否包含请求响应相关的头信息、响应数据流或是响应体,只有在包含它们其中一个的时候执行处理逻辑
异常处理
try-catch 处理
整个发起请求的逻辑都采用了try-catch块处理
在catch异常的处理逻辑中并没有做任何输出操作,而是向请求上下文中添加一些error相关的参数
error.status_code
错误编码
error.exception
Exception 异常对象
error.message
错误信息
ErrorFilter处理
让错误信息能够顺利地流转到后续的SendErrorFilter过滤器来组织和输出
统一的处理
由于在请求生命周期的pre,route,post三个阶段中有异常抛出的时候都会进入error阶段处理,可以用到error类型的过滤器
Throwable throwable = ctx.getThrowable();
不足和优化
自定义过滤器中处理异常的基本解决方法
1、每个阶段的过滤器中增加try-catch块,实现过滤器内部的异常处理
开发人员基本要求
2、利用error类型过滤器的生命周期特性,集中处理pre、route、post阶段抛出的异常信息
对第一种处理方式的补充,防止意外情况的发生
各阶段过滤器的调度实现
com.netflix.zuul.http.ZuulServlet
service()
存在的问题
那两种处理方法都在异常处理时向请求上下文添加了一系列的error.*参数,而这些参数真正起作用的地方是在post阶段的SendErrorFilter
而对于post阶段抛出的异常的情况下,由error过滤器处理之后并不会再调用post阶段的请求,自然这些error.*参数也不会被SendErrorFilter消费输出
解决办法
创建一个继承自SendErrorFilter的过滤器,复用它的run方法,然后重写它的类型、顺序以及执行条件,实现对原有逻辑的复用
怎么判断引起异常的过滤器来自什么阶段
RequestContext对象中并没有存储异常来源的内容,所以需要扩展原来的过滤器处理逻辑
当有异常抛出的时候,记录下抛出异常的过滤器,就可以在shouldFilter方法中获取并以此判断异常是否来自post阶段的过滤器
Zuul过滤器的核心处理器:com.netfilx.zuul.FilterProcessor
getInstance():该方法用来获取当前处理器的实例setProcessor(FilterProcessor processor):该方法用来设置处理器实例,可以使用此方法来设置自定义的处理器processZuulFilter(ZuulFilter filter):该方法定义了用来执行filter的具体逻辑,包括对请求上下文的设置,判断是否应该执行,执行时一些异常处理等getFiltersByType(String filterType):该方法用来根据传入的filterType获取API网关中对应类型的过滤器,并根据这些过滤器的filterOrder从小到大排序,组织成一个列表返回runFilters(String sType):该方法会根据传入的filterType来调用getFiltersByType(String filterType)获取排序后的过滤器列表,然后轮询这些过滤器,并调用processZuulFilter(ZuulFilter filter)来依次执行它们preRoute():调用runFilters(\"pre\")来执行所有pre类型的过滤器route():调用runFilters(\"route\")来执行所有route类型的过滤器postRoute():调用runFilters(\"post\")来执行所有post类型的过滤器error():调用runFilters(\"error\")来执行所有error类型的过滤器
通过扩展processZuulFilter(ZuulFilter filter)方法,当过滤器执行抛出异常的时候,我们捕获它,并往请求上下中记录一些信息
public class DidiFilterProcessor extends FilterProcessor { @Override public Object processZuulFilter(ZuulFilter filter) throws ZuulException { try { return super.processZuulFilter(filter); } catch (ZuulException e) { RequestContext ctx = RequestContext.getCurrentContext(); ctx.set(\"failed.filter\
完善之前ErrorExtFilter中的shouldFilter()方法,通过从请求上下文中获取该信息作出正确的判断
@Componentpublic class ErrorExtFilter extends SendErrorFilter { @Override public String filterType() { return \"error\"; } @Override public int filterOrder() { return 30;\t// 大于ErrorFilter的值 } @Override public boolean shouldFilter() { // 判断:仅处理来自post过滤器引起的异常 RequestContext ctx = RequestContext.getCurrentContext(); ZuulFilter failedFilter = (ZuulFilter) ctx.get(\"failed.filter\"); if(failedFilter != null && failedFilter.filterType().equals(\"post\")) { return true; } return false; }}
最后,我们需要在应用主类中,通过调用FilterProcessor.setProcessor(new DidiFilterProcessor());方法来启用自定义的核心处理器以完成我们的优化目标。
自定义异常信息
往往默认的错误信息并不符合系统设计的响应格式,需要对返回的异常信息进行定制
对/error端点的实现
org.springframework.boot.autoconfigure.web.BasicErrorController
通过调用getErrorAttributes方法会将具体组织逻辑委托给org.springframework.boot.autoconfigure.web.ErrorAttributes接口提供的getErrorAttributes来实现
在Spring Boot的自动化配置机制中,默认会采用org.springframework.boot.autoconfigure.web.DefaultErrorAttributes作为接口的实现
在定义Error处理的自动化配置中采用了@ConditionOnMissingBean修饰
说明DefaultErrorAttributes对象实例仅在没有ErrorAttributes接口的实例时才会被创建来使用
自定义一个ErrorAttributes接口实现类,并创建它的实例能替代这个默认的实现,从而达到自定义错误信息的效果
自定义范例
在应用主类加入配置,替换默认的实现
禁用过滤器
禁用过滤器可以通过重写shouldFilter逻辑,返回false,这样过滤器对于任何请求都不会被执行
通过一个参数来禁用指定的过滤器
zuul.<SimpleClassname>.<filterType>.disable=true
<SimpleClassName>
过滤器的类名,例如:AccessFilter
<filterType>
过滤器类型,如:AccessFilter的过滤器类型是pre
zuul.AccessFilter.pre.disable=true
也可以禁用Spring Cloud Zuul中默认定义的核心过滤器
动态加载
动态路由和动态过滤器的能力
动态路由
服务容错保护:Spring Cloud Hystrix
在微服务架构中,存在着许多的服务单元,若一个单元出现故障,很容易因为依赖关系而引发故障蔓延,最终导致整个系统的瘫痪,为了解决这样的问题,产生了断路器等一系列的服务保护机制
当某个服务单元发生故障,通过断路器的故障监控,向调用方返回一个错误响应,而不是长时间等待
Spring Cloud Hystrix 实现了断路器,线程隔离等一系列服务保护功能,也是基于Netflix的开源框架Hystrix实现的
该框架的目标在于通过控制那些访问远程系统,服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力
Hystrix具备服务降级,服务熔断,线程和信号隔离,请求缓存,请求合并以及服务监控等功能
原理分析
创建HystrixCommand或HystrixObservableCommand对象
表示对依赖服务的操作请求,同时传递所有需要的参数
通过\"命令模式\"来实现对服务调用操作的封装
将来自客户端的请求封装成一个对象,从而可以使用不同的请求对客户端进行参数化
实现了\"行为请求者\"和\"行为实现者\"的解耦,以便使两者可以适应变化
Invoker和Receiver的关系类似于\"请求-响应\",比较适合实现记录日志,撤销操作,队列请求等
使用命令模式作为回调-CallBack(先将一个函数登记上,然后在以后调用此函数)
需要在不同的时间指定请求,将请求排队
命令对象和原先的请求发出者可以有不同的生命周期
命令接收者可以在本地,也可以在网络上
命令对象可以在序列化后传输到另外一台机器上
系统需要支持命令的撤销
命令对象可以吧状态存储起来,等客户端需要撤销命令所产生的效果时,调用undo()方法
命令对象还提供了redo()方法,重新实施命令
通过日志进行数据恢复
如果要将系统中所有的数据更新到日志中,以便在系统崩溃时,可以根据日志读回所有的数据更新命令,重新调用Execute()方法一条一条执行
HysrixCommand
用于依赖的服务返回单个操作结果的时候
HystrixObservableCommand
用于服务返回多个操作结果的时候
代码分析
Receiver
接收者,知道如何处理具体的业务逻辑
Command
抽象命令,定义了一个命令对象应具备的一系列命令操作
execute
undo
redo
当命令操作被调用的时候,就会触发接收者去做具体命令对应的业务逻辑
ConcreteCommand
具体的命令实现,在这里它绑定了命令操作和接收者之间的关系
execute()命令的实现委托给了Receiver的action()函数
Invoker
调用者,持有一个命令对象,并且可以在需要的时候通过命令对象完成具体的业务逻辑
命令执行
HystrixCommand
execute()
同步执行,从依赖的服务返回一个单一的结果对象,或是在发送错误的时候抛出异常
queue()
异步执行,直接返回一个Future对象,其中包含了服务执行结束时要返回的单一结果对象
toObservable()
同样返回Observable对象,也代表了操作的多个结果,但返回的是一个Cold Observable
observe()
返回Observable对象,代表了操作的多个结果,是一个HotObservable
Hystrix的底层实现中大量使用了Rxjava
RxJava
Observable(事件源 或 被观察者)
一个Observable可以发出多个事件,直到结束或是发生异常
Observable对象每发出一个事件,就会调用对应观察者Subscriber对象的onNext()方法
每一个Observable的执行,最后一定会通过调用Subscriber.onCompleted()或者Subscriber.onError()来结束该事件的操作流
Subscriber( 订阅者 或 观察者)
Observable用来向订阅者Subscriber对象发布事件,Subscriber对象则在接收到事件后对其进行处理,而在这里所指的事件就是对依赖服务的调用
Hot Observable
无论 事件源 是否有 订阅者,都会在创建后对事件进行发布
Cold Observable
没有 订阅者 的时候并不会发布事件,而是进行等待,直到有\"订阅者\"之后才发布事件
HystrixCommand中也是使用RxJava实现的
通过queue()返回的异步对象Future的get()方法来实现同步执行
通过toObservable( )来获取一个 Cold Observable
并通过toBlocking()将该Observable转换成BlockingObservable,可以把数据以阻塞的方式发射出来
Future要求Observable只发射一个数据
通过toFuture方法则是把BlockingObservable转换为一个Future,该方法只是创建一个Future返回,并不会阻塞,消费者可以自己决定如何处理异步操作
结果是否被缓存
若当前命令的请求缓存功能是被启用的,并且该命令缓存命令中,那么缓存的结果会立即以Observable对象的形式返回
断路器是否打开
在命令结果没有缓存命中的时候,Hystrix在执行命令前检查断路器状态
断路器是打开的,那么Hystrix不会执行命令,而是转接到fallback处理逻辑
如果断路器是关闭的,那么Hystrix检查是否有可用资源来执行命令
线程池/请求队列/信号量是否占满
如果线程池/请求队列/信号量(不使用线程池的时候)已经被占满了,那么Hystrix也不会执行命令,而是转接到fallback处理逻辑
线程池
Hystrix所判断的线程池并非容器的线程池,而是每个依赖服务的专有线程池
舱壁模式(Bulkhead Pattern)
隔离每个依赖的服务,保证不会因为某个依赖服务的问题影响到其他依赖服务
请求依赖服务的具体方式
HystrixCommand.run()
返回一个单一的结果,或者抛出异常
HystrixObservableCommand.construct()
返回一个Observable对象来发射多个结果,或通过onError发送错误通知
这两个方法执行超过命令设置的超时阈值,当前处理线程会抛出一个TimeoutException,直接转到fallback处理逻辑
如果该命令不在其自身线程中执行,则会通过单独的计时线程来抛出
如果命令没有抛出异常并返回了结果,那么Hystrix在记录一些日志并采集监控报告之后将该结果返回
计算断路器的健康度
Hystrix会将 成功,失败,拒绝,超时等信息报告给断路器,断路器会维护一组计数器来统计这些数据
通过这些数据断路器会判断是否要打开,来对某个依赖服务的请求进行\"熔断/短路\",直到恢复期结束
恢复期结束后,如果统计数据还未达到健康指标,就再次\"熔断/短路\"
fallback处理
命令执行失败时,会进入fallback尝试回退处理,也称之为\"服务降级\"
服务降级逻辑中,实现一个通用的响应结果,并且该结果的处理逻辑应当从缓存或是根据一些静态逻辑来获取,而不是依赖网络请求
也可以是包含网络请求的,该请求也应包装在Hystrix命令注解中,从而形成降级策略
但最终的降级策略一定是一个不依赖网络请求,并能够稳定返回结果的处理逻辑
通过实现HystrixCommand.getFallback()来实现服务降级逻辑
返回一个Observable对象,该对象会发射getFallback()的处理结果
通过实现resumeWithFallback()来实现服务降级逻辑
将Observable对象直接返回
如果没有为命令实习那降级逻辑或者降级逻辑中抛出了异常
Hystrix依然会返回一个Observable对象,但不会发射任何结果数据,而是通过onError方法通知命令立即中断请求,并将引起命令是失败的异常发送给调用者
返回成功的响应
命令执行成功后,会将处理结果返回或是以Observable形式返回
Observable可以转换成需要的方式
断路器原理
断路器定义
HystrixCircuitBreaker
allowRequet()
每个Hystrix命令的请求都通过它来判断
isOpen()
当前断路器是否打开
markSuccess()
用来闭合断路器
key 通过HystrixCommandKey定义
维护了Hystrix命令和HystrixcircuitBreaker的关系
NoOpCircuitBreaker
一个什么都不做的断路器,状态始终闭合,允许所有请求
HystrixCircuitBreakerImpl
实现类
断路器的4个核心对象
HystrixCommandProperties properties
断路器对应HystrixCommand实例的属性对象
HystrixCommandMetrics metrics
用来让HystrixCommand记录各类度量指标的对象
AtomicBoolean circuitOpen
断路器是否打开的标志,默认false
AtomicLong circuitOpenedOrLastTestedTime
断路器打开火上一次测试的时间戳
isOpen()实现
打开标识为true,直接返回ture
否则,从度量指标对象metrics中获取HealthCounts统计对象做进一步判断
HealthCounts
记录了一个滚动时间窗内的请求信息快照,默认时间窗为10秒
如果请求总数(QPS)在预设的阈值范围内,默认20,就返回false,未打开
如果错误百分比在阈值范围内就返回false,默认50
如果上面两个条件都不满足,就将断路器设置为打开状态,如果是从关闭到打开状态,并记录当前时间
allowRequest()实现
判断请求是否被允许
先根据配置对象properties中的断路器判断强制打开或关闭属性是否被设置
默认情况下,不会进入强制打开或关闭分支,而是通过 !isOpen()II allSingleTest()来判断是否允许请求访问
allowSingleTest()
获取断路器从闭合到打开时候所记录的时间戳
这里会判断断开时的时间戳 + 配置中的 断路器休眠时间 是否小于当前时间,是的话,就将当前时间更新到记录断路器打开的时间对象,并允许此次请求
断路器打开后的休眠时间,默认5秒,circuitBreakerSleepWindowInMilliseconds
半开 状态
在一个断路器打开之后的休眠时间,在该休眠时间到达之后,将再次允许请求尝试访问
此时断路器处于\"半开\"状态,若此时请求继续失败,断路器又进入打开状态,并继续等待下一个休眠窗口过去之后再次尝试
若请求成功,则将断路器重新置于关闭状态
markSuccess()
在\"半开\"状态使用
若Hystrix命令调用成功,通过调用此函数将打开的断路器关闭,并重置度量指标对象
依赖隔离
舱壁模式
Docker
通过\"舱壁模式\"实现进程的隔离,使得容器与容器之间不会互相影响
Hystrix则使用该模式实现线程池的隔离
为每一个依赖服务创建一个独立的线程池
这样就算某个依赖服务出现延迟过高的情况,也不会拖慢其他的依赖服务
优点
应用自身得到完全保护,不会受不可控的依赖服务影响
可以有效降低接入心服务的风险
当依赖的服务从失效恢复正常后,线程池会被清理,马上回复健康服务,容器级的清理要慢很多
当依赖对的服务出现配置错误的时候,线程池会快速反应此问题(通过失败次数,延迟,超时,拒绝等指标的增加情况),同时可以在不影响应用功能的情况下,通过实时的动态属性刷新来处理
每个专有线程池都提供了内置的并发实现,可以利用它为同步的依赖服务构建异步访问
使用信号量来控制单个依赖服务的并发度
信号量的开销远比线程池的开销小,但不能设置超时和实现异步访问
execution.isolation.strategy设置SEMAPHORE,Hystrix会使用信号量代替线程池来依赖服务的并发
降级逻辑
当Hystrix尝试降级逻辑时,它会在调用线程中使用信号量
使用详解
创建请求命令
通过继承方式实现
通过注解方式实现
HystrixCommand注解控制
使用observe()执行
observableExecutionMode = ObservableExecutionMode.EAGER
使用toObservable()执行
observableExecutionMode = ObservableExecutionMode.LAZY
定义服务降级
代码实现
HystrixCommand是通过重载getFallback()方法实现
HystrixObservableCommand通过重载resumeWithFallback()方法实现
注解实现
@HystrixCommand 中通过fallbackMethod参数指定具体的服务降级实现
可以不去降级的情况
执行写操作的命令
执行批处理或离线计算的命令
不论Hystrix命令是否实现了服务降级,命令状态和断路器状态都会更新,而且可以由此了解到命令执行的失败情况
异常传播
在HystrixCommand实现的run()方法中抛出异常时,除了HystrixBadRequestException之外,其他异常都会被认为是命令执行失败并触发服务降级处理逻辑
当需要在命令执行中抛出不触发服务降级的异常时,来使用它
@HystrixCommand注解的ignoreExceptions属性可以指定忽略的异常类型
异常获取
在继承方式实现Hystrix命令中,可以用getFallback()方法通过Throwable getExecutionException()方法来获取具体的异常
注解中,只需要在fallback实现方法的参数中,增加Throwable e 对象的定义就可以了
命令名称,分组以及线程池划分
以继承方式实现的Hystrix命令使用类名为默认的命令名称
可以在构造函数中通过Setter静态类来设置
命令组
通过设置命令组,Hystrix会根据组来组织和统计命令的告警,仪表盘等信息
默认情况下,Hystrix会让相同组名的命令使用同一个线程池
Hystrix还提供了HystrixThreadPoolKey来对线程池进行设置,可以实现更细粒度的线程池的划分
注解中和编程中的实现
请求缓存
开启请求缓存功能
可以通过重载getCacheKey()方法来开启请求缓存
通过在getCacheKey方法中返回的请求缓存key值,就能让该请求命令具备缓存功能
当不同的外部请求处理逻辑调用了同一个依赖服务时,Hystrix 会根据getCacheKey方法返回的值来区分是否是重复请求
如果cacheKey相同,那么该依赖服务只会在第一个请求到达时被真实调用,另一个请求则直接从缓存中返回结果
好处
减少请求数,降低依赖服务的并发度
在同一用户请求的上下文中,相同依赖服务的返回数据始终保持一致
请求缓存在run()和construct()执行之前生效,可以减少不必要的线程开销
清理失效缓存功能
在写操作时进行清理,防止读操作请求命令获取到了失效的数据
可以通过HystrixequestCache.clear()来清理缓存
Photo 2
从默认的Hystrix并发策略中根据GETTER_KEY获取HystrixRequestCache的实例
工作原理
getCacheKey()方法的默认实现是返回null,所以只有重载这个方法才能实现请求缓存
同时请求命令的缓存开启属性也需要设置为true才能开启
从命令异步执行的核心方法toObservable()
尝试获取请求缓存
通过isRequestCachingEnabled()方法判断当前命令是否启用了请求缓存
如果开启了缓存,就通过getCacheKey()获取一个非null的缓存Key值
通过这个Key值调用HystrixRequestCache中的get(String cacheKey)来获取缓存的HystrixCachedObservable对象
将请求结果加入缓存
判断当前命令是否开启了请求缓存功能,如果开启了就调用getCacheKey()获取缓存key
将命令执行结果hystrixObservable对象包装成请求缓存结果HystrixCacheObservable的实例对象,放入当前命令的缓存对象中
使用注解实现请求缓存
设置请求缓存
通过注解为请求命令开启缓存功能
Hystrix会将返回的结果置入请求缓存中,它的缓存Key值会使用所有的参数
定义缓存Key
当使用注解定义请求缓存时,若要为请求命令指定具体的缓存Key生成规则
可以使用@CacheResult和@CacheRemove注解的cacheKeyMethod方法来指定具体的生成函数
也可以通过使用@CacheKey注解在方法中指定用于组装缓存Key的元素
比cacheKeyMethod的优先级低,使用了cacheKeyMethod那么@CacheKey注解就不生效了
还允许访问参数对象的内部属性作为缓存Key,如: @CacheKey(\"id\")User user
缓存清理
通过@CacheRemove注解来实现失效缓存的清理
注解中commandKey属性必须要指定,用来指明需要使用请求缓存的请求命令,只有通过该属性的配置,才能找到正确的请求命令缓存位置
请求合并
因为依赖服务的线程池资源有限,将出现排队等待和响应延迟的情况
Hystrix提供了HystrixCollapser来实现请求的合并
HystrixCollapser实现了在HystrixCommand之前放置一个合并处理器,将处于一个很短的时间窗(默认10毫秒)内对同一依赖服务的多个请求进行合并并以批量方式发送请求的功能
三个类型
BathReturnType
合并后批量请求的返回类型
ResponseType
单个请求返回的类型
RequestArgumentType
请求参数类型
三个抽象方法
getRequestArgument()
定义获取请求参数的方法
合并请求产生批量命令的具体实现方法
批量命令结果返回后的处理,这里需要实现将批量结果拆分并传递给合并前的各个原子请求命令的逻辑
代码使用
为请求合并的实现准备一个批量请求命令实现
通过继承HystrixCollapser实现请求合并器
通过注解实现请求合并器
通过collapserProperties属性为合并请求器设置了相关属性
设置了合并时间窗为100毫秒
batchMethod属性制订了批量请求的实现方法
请求合并的额外开销(衡量延迟时间窗的时间损耗)
请求命令本身的延迟
如果依赖服务的请求命令本身是一个高延迟的命令,可以使用请求合并器
延迟时间窗的并发量
如果一个时间窗内具有很高的并发量
属性详解(优先级从低到高)
全局默认值
全局配置属性
通过配置文件中定义全局属性值
可以通过其他组件实现动态刷新配置
实例默认值
通过代码为实例定义的默认值
实例配置属性
通过配置文件来指定的实例进行属性配置
通过其他组件可以实现对具体实例配置动态调整
其他配置
服务治理:Spring Cloud Eureka
服务治理
服务注册
在服务治理框架中,通常构建一个注册中心,每个服务单元向注册中心登记自己提供的服务
将主机及端口号,版本号,通讯协议等一些附加信息告知注册中心
注册中心按照服务名分类组织服务清单
服务发现
调用方需要向服务注册中心咨询服务,并获取所有服务的实例清单,以实现对具体实例的访问
在服务治理框架下,服务间调用通过向服务名发起请求调用实现
Netflix Eureka
Eureka服务端,服务注册中心,支持高可用配置
Eureka客户端,主要处理服务的注册与发现
实例
搭建注册中心
注册中心
注册服务提供者
高可用注册中心
两个注册中心互相注册
服务治理机制
服务提供者
服务提供者在启动的时候会发送REST请求将自己注册到Eureka Server,同时带上自身服务的一些元数据
Eureka Server接收到REST请求之后,将元数据存储在一个双层结构Map中,第一层的key是服务名,第二层的key是具体服务的实例名
eureka.client.register-with-eureka=true 启动注册操作
服务同步
服务注册中心互相注册为服务,当服务通过者发送注册请求到一个服务注册中心时,它会将该请求转发给集群中相连的其他注册中心,从而实现注册中心之间的服务同步
服务续约
与注册中心维持的心跳
服务消费者
获取服务
服务消费者在启动时,会发送一个REST请求给服务注册中心,来获取注册的服务清单
Eureka Server会维护一份只读的服务清单给客户端,缓存清单30秒更新一次
服务调用
服务清单中有提供服务的实例名和该实例的元数据信息
客户端可以根据自己的需要决定调用,在Ribbon中默认采用轮询,从而实现客户端的负载均衡
Region 和 Zone
一个Region中可以包含多个zone
每个服务客户端需要被注册到一个Zone中,所以每个客户端对应一个Region和一个Zone
在服务调用时,优先访问同处一个Zone中的服务提供方,若访问不到,就访问其他的Zone
服务下线
当服务实例进行正常的关闭操作时,会触发一个服务下线的REST请求给Eureka Srever
注册中心收到请求后,会将服务状态置为下线(DOWN),并把该下线事件传播出去
服务注册中心
失效剔除
Eureka server 在启动后,会创建一个定时任务
默认每隔一段时间将当前清单中超时没有续约的服务剔除
自我保护
当注册中心统计心跳失败的比例在15分钟之内是否低于85%
单机调试容易满足,线上通常是网络不稳定
注册中心会将当前实例注册信息保护起来,让这些实例不会过期,尽可能保护这些注册信息
但,如果在这段时间内实例出现问题,那么客户端容易拿到已经不存在的实例
出现调用失败的情况
所以客户端必须要有容错机制,比如请求重试,断路器等
关闭自我保护配置,eureka.server.enable-self-preservation=false
DiscoveryClient
该类用于帮助与Eureka Server互相协作
负责任务
向 Eureka Server注册服务实例
向Eureka Server服务租约
当服务关闭期间,向Eureka Server取消租约
查询Eureka Server中的服务实例列表
需要配置一个Eureka Server的URL列表
通过 eureka.client.serviceUrl.defaultZone配置
Region,Zone
Region
一个微服务应用只可以属于一个Region,默认是default
可以通过Eureka.client.region属性来定义
客户端的加载顺序 第一个是Region,第二个是zone
Zone
没有特别为Region配置Zone的时候,默认采用defaultZone
配置eureka.client.serviceUrl.defaultZone的由来
指定zone,可以通过eureka.client.availability-zones的属性来设置
zone可以配置多个,以逗号分隔
Region和Zone是一对多的关系
serviceUrls
在获取Region和Zone的信息,才开始真正加载Eureka Server的具体地址
根据传入的参数按一定算法确定加载位于哪一个zone配置的serviceUrls
微服务应用中使用Ribbon来实现服务调用时,可以通过Zone属性的定义,配合实际部署的物理结构,有效设计出对区域性故障的容错集群
服务注册,获取,续约
都是通过定时任务来实现的
服务注册中心处理
服务注册相关的配置信息,包括服务注册中心的地址,服务获取的间隔时间,可用区域等
都是以eureka.client为前缀
服务实例相关的配置信息,包括服务实例的名称,IP地址,端口号,健康检查路径等
都是以eureka.instance为前缀
元数据
com.netflix.appinfo.InstanceInfo类中对元数据的定义
标准化元数据
eureka.instance.=
就是EurekaInstanceConfigBean对象中的成员变量名
自定义元数据
eureka.instance.metadataMap.=
常用元数据
实例名配置
区分同一服务中不同实例的唯一标识,默认使用主机名
端点配置
健康检测
跨平台支持
使用HTTP的REST接口实现
有多语言对其的支持
通信协议
Eureka 使用Jersey和XStream配合JSON作为server和client之间的通讯协议,可替换
基础知识
微服务架构
将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间通过基于HTTP的RESTful API进行通信协作
服务组件化
按业务组织团队
做\"产品\"的态度
智能端点和哑管道
去中心化治理
去中心化管理数据
分布式事物实现难度非常大,强调在服务之间进行\"无事务\"调用
数据最终一致性
通过补偿机制处理错误数据,达到最终一致性
基础设施自动化
自动化测试
自动化部署
容错设计
防止故障的蔓延
快速检测出故障源,尽可能自动恢复服务
演进式设计
各类分布式框架
spring cloud简介
介绍
一个基于Spring boot实现的微服务架构开发工具
为微服务架构汇总涉及的配置管理,服务治理,断路器,智能路由,微代理,控制总线,全局锁,决策竞选,分布式会话和集群状态管理等操作提供了一种简单的开发方式
组成子项目
Spring Cloud Config
配置管理工具
Spring Cloud Netflix
核心组件,对多个Netflix OSS开源套件进行整合
Eureka
服务治理组件,包含服务注册中心,服务注册和发现机制的实现
Hystrix
容错管理组件,实现 断路器模式,帮助服务依赖中出现的延迟和为故障提供强大的容错能力
Ribbon
客户端负载均衡的服务调用组件
Feign
基于Ribbon和Hystrix的声明式服务调用组件
Zuul
网关组件
Archaius
外部化配置组件
组件列举
收藏
收藏
0 条评论
回复 删除
下一页