SpringCloud微服务实战读书笔记

第二章 Springboot
在配置文件中可以使用 属性的方式 , 引用之前定义的属性值配置文件中使用 {属性}的方式, 引用之前定义的属性值 配置文件中使用 属性的方式,引用之前定义的属性值配置文件中使用{random}来生产随机数, 如${random.int}
使用命令行参数改变配置
第三章 Eureka
服务端引入eureka-server的starter, 并在启动类上加上@EnableEurekaServer注解,
默认设置下该注册中心也会像自己注册, 所以可以通过配置关闭该操作

客户端需要加入eureka的starter, 在主类加上@EnableDiscoveryClent注解, 激活自动化配置,

在服务和客户端都需要指明serverUrl来标注server端提供了注册中, client端注册到这个中心

高可用注册中心
实际上就是将自己最为服务像其他服务注册中心注册, 从而形成一组相互注册的中心,
构建两个注册中心, 相互注册

Client端需要对这两个注册中心都要注册, 在指定serviceUrl时候, 使用逗号分隔多个注册中心,

默认使用主机名的方式来定义注册中心地址, 如果想使用IP则需要配置

Eureka基础架构
三个角色: 服务注册中心, 服务提供者, 消费者

服务续约
防止被剔除出去, 两个重要的配置, 需要的时间间隔, 服务失效的时间

服务调用
Eureka有region和zone的概念, region包含多个zone, 每个服务都注册到zone中, 优先访问同处于一个zone的服务, 地区亲和性

自我保护机制
统计心跳失败的十五分钟内成功率低于百分之八十五的就会开启, 从而保证这些实例不会被剔除, 同时客户端需要有重试等机制,
在本地调试时候可以关闭这个自我保护

Region和zone
一个微服务只能属于一个region, 不配置则为default, 可以通过属性设置
可以通过属性设置zone, 设置多个用逗号隔开
配合着物理结构, 实现地域亲和性设置

服务获取与续约
通过两个定时任务完成

客户端配置
主要有两个方面:
服务注册相关: 服务注册中心地址, 服务获取时间间隔, 可用区域
服务实例相关: 服务实例名称, IP地址, port, 健康检查路径等

Eureka更多时候类似一个产品, 不需要改他的配置

Server端的配置均以eureka.server作为前缀
Client端以eureka.client作为前缀

为了安全考虑, 很多时候会给server加上校验, 所以在配置serviceUrl路径时候需要加上用户名密码

网络好文
https://blog.csdn.net/qwe86314/article/details/94552801
第四章 ribbon
基本使用
加入起步依赖
配置RESTTemplate, 加上@LoadBalanced
配置文件中users.ribbon.listOfServers

使用ribbon实现客户端负载均衡需要两步
服务提供者启动多个实例并注册到注册中心
消费者通过@LoadBalanced修饰的RestTemplate来实现服务接口调用

通过源码分析得出
通过@LoadBalanced修饰的RestTemplate之后
使用LoadBalancedInterceptor拦截器对它的请求进行拦截
并利用LoadBalancedClient将逻辑服务名转为host的uri
自动化配置会采用ZoneAwareLoadBalancer的实例来实现客户端负载

负载均衡的顶级接口
ILoadBalance 负载均衡器
IRule 路由

负载均衡器 [4个]
AbstractloadBalancer
AbstractLoadBalancer是ILoadBalancer接口的抽象实现
1:在该抽象类中定义 了一个关于服务实例的分组枚举类 ServerGroup, 它包含以下三种不同类型:

ALL: 所有服务实例 STATUS_UP: 正常服务的实例 STATUS_NOT_UP: 停止服务的实例

2:实现了 一 个 chooseServer () 函数, 该 函数通过调用接口中的 chooseServer (Object key)实现, 其中参数key为 null, 表示在选择具体服务实例 时忽略key的条件判断。

3:定义了两个抽象函数:
getServerList(ServerGroup serverGroup): 定义了根据分组类型来获取 不同的服务实例的列表
getLoadBalancerStats(): 定义了获取 LoadBalancerStats 对象的方法, LoadBalancerStats 对象被用来存储负载均衡器中各个服务实例当前的属性和 统计信息。 这些信息非常有用, 我们可以利用这些信息来观察负载均衡器的运行情 况, 同时这些信息也是用来制定负载均衡策略的重要依据。

BaseloadBalancer
BaseLoadBalancer 类是和Ribbon 负载均衡器的基础实现类,在该类中定义了很多关 于负载均衡器相关的基础内容。
1:定义并维护了两个存储服务实例 Server 对象的列表。 一个用于存储所有服务实例 的清单,一个用于存储正常服务的实例清单

2:定义了之前我们提到的用来存储负载均衡器各服务实例属性和统计信息的 LoadBalancerStats 对象

3:定义了检查服务实例是否正常服务的IPing对象,在 BaseLoadBalancer 中默 认为 null, 需要在构造时注入它的具体实现

4:定义了检查服务实例操作的执行策略对象IpingStrategy,在BaseLoadBalancer 中默认使用了该类中定义的静态内部类 SerialPingStrategy 实现。 根据源码, 我们可以看到该策略采用线性遍历 ping 服务实例的方式实现检查。 该策略在当 IPing 的实现速度不理想, 或是 Server 列表过大时, 可能会影响系统性能, 这时 候需要通过实现 IPingStrategy 接口并重写 pingServers(IPing ping, Server[] servers) 函数去扩展 ping 的执行策略。

5:定义了负载均衡的处理规 则 IRule 对 象,从 BaseLoadBalancer 中 chooseServer (Object key) 的实现源码,我们可以知道,负载均衡器实际将服 务实例选择任务委托给了 IRule 实例中的 choose 函数来实现. 而在这里, 默认 初始化了 RoundRob江Rule 为工Rule 的实现对象。RoundRobinRule 实现了最 基本且常用的线性负载均衡规则。

6:启动 ping 任务:在 BaseLoadBalancer 的默认构造函数中,会直接启动一个用于 定时检查 Server 是否健康的任务。该任务默认的执行间隔为 10 秒。实现了 ILoadBalancer 接口定义的负载均衡器应具备以下一系列基本操作

7:addServers(List newServers): 向负载均衡器中增加新的服务实例列表, 该实现将原本已经维护着的所有服务实例清单 al1ServerList 和新传入的服 务实例清单 newServers 都加入到 newList 中, 然后通 过调用 setServersList 函数对 new口st 进行处理,在 BaseLoadBalancer 中实 现的时候会使用新的列表覆盖旧的列表

8:chooseServer (Object key): 挑选一个具体的服务实例

9:markServerDown (Server server): 标记某个服务实例暂停服务。

10:getReachableServers(): 获取可用的服务实例列表。由于 BaseLoadBalancer 中单独维护了一个正常服务的实例清单, 所以直接返回即可。

11:getAllServers (): 获取所有的服务实例列表。由于 BaseLoadBalancer 中单 独维护了一个所有服务的实例清单, 所以也直接返回它即可。

DynamicServerlistloadBalancer
DynamicServerListLoadBalancer 类继承于 BaseLoadBalancer 类, 它是对 基础负载均衡器的扩展。 在该负载均衡器中, 实现了服务实例清单在运行期的动态更新能 力;同时, 它还具备了对服务实例清单的过滤功能
1:ServerList (ServerList的实现类,DomainExtractingServerList)
它定义了两个抽象方法: getInitialListOfServers用于获取初始化的服务实例清单,而getUpdatedListOfServers用于获取更新的服务实例清单

2:ServerListUpdater:服务更新器, 实现类:
PollingServerListUpdater: 动态服务列表更新的默认策略, 也就是说, DynamicServer巨stLoadBalancer 负载均衡器中的默认实现就是它, 它通过 定时任务的方式进行服务列表的更新。 · EurekaNotificationServerListUpdater: 该更新器也可服务于 DynamicServerListLoadBalancer 负载均衡器,但是它的触发机制与 PollingServer巨stUpdater 不同,它需要利用 Eureka 的事件监听器来驱动服务列表的更新操作。

3:ServerListFilter
主要用于实现对服务实例列表的过滤, 通过传入的 服务实例清单, 根据一些规则返回过滤后的服务实例清单。实现类:

     1:AbstractServerListFilter: 这是一个抽象过滤器,在这里定义了过滤时需要 的一个重要依据对象 LoadBalancerStats

     2:ZoneAffinityServerListFilter: 该过滤器基于 “ 区域感知 (Zone Affinity)" 的方式实现服务实例的过滤, 也就是说,它会根据提供服务的实例所处的区域 (Zone) 与消费者自身的所处区域 (Zone) 进行比较, 过滤掉那些不是同处一个区 域的实例。对于服务实例列表的过滤是通过Iterables.filter(servers,this.zoneAffinityPredicate.getServerOnlyPredicate()) 来实现的, 其中判断依据由 ZoneAffinityPredicate 实现服务实例与消费者的 Zone比较。 而在过滤之后, 这里并不会马上返回过滤的结果, 而是通过 shouldEnableZoneAffnity 函数来判断是否要启用“区域感知” 的功能。 它使用了 LoadBalancerStats的 getZoneSnapshot 方法 来获取这些过滤后的同区域实例的基础指标(包含实例数量、断路器断开数、 活动请求数、 实例平均负载等),根据一系列的算法求出下面的几个评价值并与设置的阙值进行对比(下 面的为默认值), 若有一个条件符合, 就不启用 “ 区域感知 ” 过滤的服务实例清单。 这一算 法实现为集群出现区域故障时, 依然可以依靠其他区域的实例进行正常服务提供了完善的 高可用保障。 

                  blackOutServerPercentage: 故障实例百分比(断路器断开数/实例数量)>=0.8

                  activeReqeustsPerServer: 实例平均负载 >=0.6

                  availableServers: 可用实例数(实例数量 - 断路器断开数)<2

      3:DefaultNIWSServerListFilter: 该过滤器完全继承自ZoneAffnityServerListF辽ter, 是默认的NIWS (Netflix Internal Web Service)过滤器。

       4: ServerListSubsetSusetFilter: 该 过 滤器也继承自 ZoneAffinityServerListFilter, 它非常适用于拥有大规模服务器集群(上百或更多)的系统。 因为 它可以产生一个 “ 区域感知 ” 结果的子集列表, 同时它还能够通过比较服务实例的 通信失败数量和并发连接数来判定该服务是否健康来选择性地从服务实例列表中剔 除那些相对不够健康的实例。

该过滤器的实现主要分为以下三步:

                     1:获取 “ 区域感知 ” 的过滤结果, 作为候选的服务实例清单

                     2:从当前消费者维护的服务实例子集中剔除那些相对不够健康的实例(同时也将这 些实例从候选清单中剔除, 防止第三步的时候又被选入)不够健康的标准如下 所示。

                          a. 服务实例的并发连接数超过客户端配置的值, 默 认为0' 配置参数为 ..ServerListSubsetFilter.eliminat ionConnectionThresold。

                           b. 服务实 例的失败 数超过客户端配置的值 , 默 认为0' 配置参数为 ..ServerListSubsetFilter.eliminat ionFailureThresold。

c如果按符合上面任一规则的服务实例剔除后,剔除比例小于客户端默认配置的 百分比, 默认为o .1 C 10%), 配置参数为… ServerListSubsetFilter.forceEliminatePercent, 那么就先对剩 下的实例列表进行健康排序,再从最不健康的实例进行剔除,直到达到配置的 剔除百分比。

3:在完成剔除后, 清单已经少了至少10% C默认值)的服务实例, 最后通过随机 的方式从候选清单中选出 一批实例加入到清单中, 以保持服务实例子集与原来 的数量一致, 而默认的实例子集数量为20

ZoneAwareloadBalancer
ZoneAwareLoadBalancer 负载均衡器是对 DynamicServerListLoadBalancer 的扩展。在 DynamicServerListLoadBalancer 中, 我们可以看到它并没有重写选择 具体服务实例的 chooseServer 函数, 所以它依然会采用在 BaseLoadBalancer 中实 现的算法。 使用RoundRobinRule 规则, 以线性轮询的方式来选择调用的服务实例, 该 算法实现简单并没有区域 (Zone) 的概念, 所以它会把所有实例视为一个 Zone下的节点来 看待, 这样就会周期性地产生跨区域 (Zone) 访问的情况, 由于跨区域会产生更高的延迟, 这些实例主要以防止区域性故障实现高可用为目的而不能作为常规访问的实例, 所以在多 区域部署的清况下会有一定的性能问题, 而该负载均衡器则可以避免这样的问题, 他是怎么实现呢

负载均衡策略
AbstractloadBalancerRule
负载均衡策略的抽象类,在该抽象类中定义了负载均衡器ILoadBalancer对象 ,该 对象能够在具体实现选择服务策略时,获取到一些负载均衡器中维护的信息来作为分配依 据,并以此设计一些符法来实现针对特定场景的高效策略
Random Rule

      该策略实现了从服务实例清单中随机选择 一个服务实例的功能。 它的具体实现如下, 可以看到IRule 接口的 choose(Object key)函数实现, 委托给了该类中的 choose (ILoadBalancer lb, Object key), 该方法增加了 一个负载均衡器对象的参 数。 从具体的实现上看,它会使用传入的负载均衡器来获得可用实例列表upList和所有 实例列表all归st, 并通过rand.nextint(serverCount)函数来获取 一个 随机数,并将该随机数作为 upList 的索引值来返回具体实例。 同时, 具体的选择逻辑在一个 while (server == null) 循环之内, 而根据选择逻辑的实现, 正常情况下每次选择都应该选出 一个服务实例, 如果出现死循环获取不到服务实例时, 则很有可能存在并发的 Bug。

RoundRobinRule

      该策略实现了按照线性轮询的方式依次选择每个服务实例的功能。 它的具体实现如下,其 详细结构与 RandomRule 非常类似。 除了循环条件不同外, 就是从可用列表中获取所谓的逻 辑不同。 从循环条件中, 我们可以看到增加了一个 count计数变量, 该变量会在每次循环之 后累加, 也就是说, 如果一直选择不到 server 超过 10 次, 那么就会结束尝试, 并打印一个警 告信息 No available alive servers after 10 tries from load balancer:而线性轮询的实现则是通过Atomicinteger nextServerCyclicCounter对象实现, 每次进行实例选择时通过调用 incrementAndGetModulo 函数实现递增。

RetryRule

      该策略实现了一个具备重试机制的实例选择功能。 从下面的实现中我们可以看到, 在 其内部还定义了 一个 IRule 对象,默认使用了 RoundRobinRule 实例。 而在 choose 方 法中则实现了对内部定义的策略进行反复尝试的策略, 若期间能够选择到具体的服务实例 就返回,若选择不到就根据设置的尝试结束时间为阙值 (maxRetryMillis 参数定义的值 + choose 方法开始执行的时间戳), 当超过该阀值后就返回 null。

WeightedResponseTimeRule

     该策略是对 RoundRobinRule 的扩展, 增加了根据实例的运行情况来计算权重, 并 根据权重来挑选实例, 以达到更优的分配效果, 它的实现主要有三个核心内容:

1:定时任务

  WeightedResponseTimeRule 策略在初始化的时候会通过 serverWeightTimer. schedule (new   DynarnicServerWeightTask (), 0, serverWeightTaskTimerinterval) 启动一个定时任务, 用来为每个服务实例计算权重, 该任务默认30秒执行一次。

2:权重计算

  在源码中我们可以轻松找到用千存储权重的对象 List<Double> accumulatedWeights = new ArrayList () ,该List 中每个权重值所处的位置对应了负载均衡器维护的服务实例清单中所有实例在清单中的位置。维护实例权重的计算过程通过 maintainWeights函数

该函数的实现主要分为两个步骤:

根据LoadBalancerStats中记录的每个实例的统计信息, 累加所有 实例的平均 响应时间, 得到总平均响应时间totalResponseTime, 该值会用于后续的计算。 ·

为负载均衡器中维护的实例清单逐个计算权重(从第 一个开始),计算规则为 weightSoFar+totalResponseTime 实例的平均响应时间,其中weightSoFar 初始化为零, 并且每计算好一个权重需要累加到 weightSoFar上供下一次计算使用

3:实例选择

   生成一个[ 0, 最大权重值)区间内的随机数。 

遍历权重列表, 比较权重值与随机数的大小, 如果权重值大于等千随机数, 就拿当 前权重列表的索引值去服务实例列表中获取具体的实例。 这就是在上一节中提到的 服务实例会根据权重区间挑选的原理, 而权重区间边界的开闭原则根据绊法, 正常 每个区间为(x, y)的形式, 但是第一个实例和最后一个实例为什么不同呢?由于 随机数的最小取值可以为O, 所以第一个实例的下限是闭区间, 同时随机数的最大 值取不到最大权重值, 所以最后一个实例的上限是开区间

ZoneAvoidanceRule

    ZoneAvoidanceRule 在实现的时候并没有像 AvailabilityFilteringRule 那 样重写 choose 函数来优化,所以它完全遵循了父类的过滤主逻辑:“先过滤清单,再轮询 选择” 。 其中过滤清单的条件就是我们上面提到的以 ZoneAvoidancePredicate 为主过滤条件、 AvailabilityPredicate 为次过滤条件的组合过滤条 件 CompositePredicate, 从CompositePredicate 的源码片段中,我们可以看到它定义了 一 个主过 滤条件 AbstractServerPredicate delegate 以及 一组次过沽条件列表List fallbacks, 所以它的次过滤列表是可以拥有多个的, 并且由于它采用了 List 存储所以次 过滤条件是按顺序执行的在获取过滤结果的实现函数 getEligibleServers 中, 它的处理逻辑如下所示:

1:使用主过滤条件对所有实例过滤并返回过滤后的实例清单

2:依次使用次过滤条件列表中的过滤条件对主过滤条件的结果进行过滤

3:每次过滤之后(包括主过滤条件和次过滤条件),都需要判断下面两个条件,只要有 一个符合就不再进行过滤, 将当前结果返回供线性轮询算法选择:

  1:过滤后的实例总数>=最小过滤实例数(minimalFilteredServers, 默认为 1)

  2:过滤后的实例  比例>最小过滤百分比 (minimalFilteredPercentage, 默认为 0)

配置详解
自动化配置
包括:
IClientConfig ribbon客户端配置
IRule 负载均衡策略
IPing ribbon的实例检查策略
ServerListFilter 服务实例清单过滤机制
ILoadBalancer 负载均衡器

覆盖自动化配置: 只需在创建对应的实现实例就能覆盖这些默认配置

Camden版本对RibbonClent配置进行了优化, 可以在主配置文件中通过kv形式配置接口的实现

参数配置:
两种: 全局配置以及指定客户端配置

全局配置: 只需使用ribbon.= 格式进行配置即可

全局配置可以做默认值, 当指定客户端配置了相应的key时, 将覆盖全局配置

指定客户端: 采用.ribbon.=的形式

与eureka结合
Ribbon的很多实现类都将由eureka的实现类代替,
由于ribbon默认实现了区域亲和, 所以可以配置eureka实例的元数据实现区域化亲和
eureka.instance.metadataMap.zone=shanghai

也可用通过配置来禁用eureka对于ribbon实例的维护

重试机制
Eureka实现的服务治理强调的CAP中的ap, 即可用性和可靠性,
与zk这类强调cp的一致性可靠性的区别就是eureka为了更高的可用性牺牲了一定的一致性, 从自我保护机制可见, 所以可能会获取到失效的实例, 所以cloud整合了原来的springRetry来增强了restTemplate的重试能力, 只要简单的配置即可开启重试

第五章 Hystrix
快速入门
在consumer服务中加入starter, 并在主类加上注解
同时@SpringCloudApplication注解包含boot,eureka,hystrix三个注解, 也就说一个标准的cloud应用应该包含这三个
在使用restTemplate调用远程服务的方法上通过@HystrixCommand注解的fallbackMethod属性指定降级方法, 降级方法应该返回静态内容, 防止降级方法出现异常

原理分析
当一个请求调用了相关依赖之后hystrix是怎么工作的

  1. 创建HystrixCommand或HystrixObservableCommand对象, 前者用在依赖服务返回单个操作结果时候, 后者为返回多个操作结果
  2. 命令执行, 两个对象都提供了同步和异步的执行方法, 并采用了响应式编程, 观察者模式, 其实同步方法是调用了Future中的get方法后阻塞的
  3. 结果是否被缓存, 如果当前请求的缓存开启了, 并且命中, 那么会立即返回
  4. 断路器是否打开, 如果打开了hystrix是不会执行请求的, 直接到fallback的处理中
  5. 线程池请求队列信号量是否占满, 如果已经满了, 则直接进入fallback, 注意是每个服务的专有的线程池, hystrix采用舱壁模式隔离每个服务, 类似沙箱
  6. 执行1中的两个对象的执行方法,
  7. 计算断路器的健康度, hystrix会将成功失败等信息报告给断路器, 断路器使用这些数据来决定要不要打开断路器和关闭
  8. Fallback处理, 1中的不同对象的降级处理不同, 调用的方法也不同,
  9. 返回成功的响应

断路器原理
通过一些属性实现

依赖隔离
Hystrix使用舱壁模式为每个服务创建了一个独立的线程池, 这样就算依赖服务出现延迟过高的情况, 也不会拖慢其他的服务,

在hystrix中除了使用线程池之外还可以使用信号量来控制单个依赖服务的并发,

使用详解
创建请求命令, 继承HystrixCommand, 他用来封装具体的依赖服务调用逻辑, 重写run方法, 通过继承之后我们即可以同步执行execute, 又可以异步执行queue,
也可以通过@HystrixCommand注解来优雅的定义Hystrix, 普通的注解到方法上的形式只能支持同步调用, 异步调用需要改造一下,
还能将HystrixCommand通过Observable来实现响应式执行的方式,
HystrixObservableCommand的方式和HystrixCommand差不多, 也是通过继承重写方法等

定义服务降级
在HystrixCommand中重载getFallback方法即可,
注解中使用参数fallbackMethod执行降级方法名, 必须在同一个类下

异常处理
在run方法中抛出异常, 除了HystrixBadRequestException之外,其他异常均会被认为执行失败, 并触发服务降级, 所以在命令执行不希望抛出异常时来使用@HystrixCommand的ignoreExceptions参数执行异常, 则此异常不会出现降级

异常获取
在继承方式中通过Throwable getExecutionException()方法获取,
在注解方式中, 只需要在降级方法中加入Throwable的入参即可

命令名称,分组,线程池划分

请求缓存
开启请求缓存, 在使用HystrixCommand或HystrixObservableCommand时通过重载getCacheKey()方法开启请求缓存, 方法返回缓存的key, 请求时候, 会根据此方法返回的key来判断是否使用缓存,

清理失效的缓存, 通过clear方法清理,

工作原理

使用注解实现缓存
@CacheResult, 标记请求结果被缓存, 与@HystrixCommand一起用,
@CacheRemove, 让缓存失效, 根据key决定, 标记在update方法上, key指定让那个方法刷新缓存
@CacheKey, 用来请求参数上标记用来做key

请求合并
提供了HystrixCollapser来实现请求的合并, 以减少通信和线程的占用

使用注解实现请求合并

请求合并的额外开销,

属性详解
所有属性存在四个不同的优先级, 全局默认值, 全局配置属性, 实例默认值, 实例配置属性,

Command属性
Collapser属性, 该属性除了在配置文件和set中配置之外还可以使用注解配置,
ThreadPool属性, 用来设置线程池

Hystrix仪表盘
Dashboard, 主要用来实时监控hystrix的各项指标信息, 可以快速的发现系统中存在的问题,

快速入门
创建一个Hystrix Dashboard很容易, 加入starter, 主类加上@EnableHystrixDashboard,
然后访问, ip:port/hystrix页面即可

共支持三种不同的监控方式,
默认的集群监控
指定的集群监控
单体应用监控

前两个都是集群监控需要整合Turbine才能实现

Turbine集群监控
需要引入Turbine和actuator的starter, 在主类加@EnableTurbine开启, 配置文件
还可以与消息代理整合, 实现异步收集
第六章 feign
声明式web客户端调用方式, 整合了ribbon和hystrix

创建一个接口并用注解的方式配置它, 接可以完成对服务提供方的接口绑定

扩展了springmvc的注解, 想使用mvc去使用

使用
加入starter, 主类加上@EnableFeignClient, 开启feign支持
定义service接口, 通过@FeignClient注解指定服务名来绑定服务,
然后在使用springmvc注解绑定具体的该服务提供的rest接口

注意传参时候@RequestParam等注解的value属性不能少

继承特性
服务提供方和调用方通过feign之后, 发现都使用相同的DTO, 相同的接口定义, 所以可以抽出来
在抽取的项目中, 定义DTO, 并定义共同的请求接口, 然后服务提供方的controller实现这个接口后重写方法, 服务调用方的接口只需要继承此接口即可,

Ribbon配置
全局配置, 直接使用ribbon.=的方式
指定服务配置, .ribbon.=

重试机制
默认实现了重试机制, 同第四章的重试机制
需要注意hystrix的超时比ribbon的要长

Hystrix配置
全局配置, 通过hystrix.command.defualt设置即可, 同hystrix, 需要注意feign没有关闭对hystrix的支持,

禁用hystrix
可以通过feign.hystrix.enabled=false来全局关闭,
如果只想某个客户端关闭, 则可以构建一个关闭hystrix的配置类, 使用@Scope(“prototype”)注解, 在@FeignClient中通过configuration引入该配置
指定命令配置, 同hystrix

服务降级配置
hystrixCommand定义被封装了, 服务降级只需要一个该服务接口的实现类, 重写其中方法, 通过@FeignClient的fallback属性指定一下.

其他配置
请求压缩, 支持对请求响应的GZIP压缩, 需要开启, 默认2m以上才压缩

日志配置

第七章 zuul
主要包括两个功能, 一个请求路由, 一个是请求过滤

从eureka上获取所有其他微服务实例信息,
对于签名, 登录等校验 zuul提供了一套过滤器机制,

快速入门
加入starter, 在zuul的starter中包含了hystrix, ribbon, actuator的starter,
在主类加上注解, 开启

请求路径
传统路由方式很简单, 在配置文件中配置
Zuul.routes.api-a-url.path=/api-a-url/**
Zuul.routes.api-a-url.url=http://localhost:8080/
该配置定义了所有发往网关请求中, 符合/api-a-url/**规则的访问都被路由
到http://localhost:8080/地址上

面向服务的路由
整合eureka后, 使用服务名, 就不用维护IP了
Zuul.routes.api-b.path=/api-a/**
Zuul.routes.api-b.serviceId=hello-service

请求过滤
只需要继承ZuulFilter抽象类并实现4个抽象方法即可
四个方法分别是:
FilterType: 过滤器类型, 决定过滤器在请求的那个生命周期执行,
FilterOrder: 过滤器顺序, 当请求在一个阶段有多个过滤器需要根据该方法返回值依次执行
ShouldFilter: 判断是否要执行, true为执行
Run: 执行具体逻辑

继承之后, 还有在配置类中定义该Bean才会生效, 否则不会得到执行

路由详解

传统路由配置
单实例配置: zuul.routes..path与zuul.routes..url的kv形式配置

多实例配置:
zuul.routes..path与zuul.routes..serviceId
ribbon.eureka.enabled=false, 关闭服务发现, 否则会按serviceId查找服务
.ribbon.listOfServers=http://,http:// 该处与serviceId对应

服务路由配置
除了快速入门中的配置方式, 还可以采用
Zuul.routes.= 的方式

服务路由的默认规则
实际使用中, 发现路由的url往往和服务名都是一样的, 所有在引入eureka之后, zuul为每个服务都配置了默认的路由,
例如:
Zuul.routes.hello-service.path=/hello-service/**
Zuul.routes.hello-service.serviceId=hello-service

默认请求全部服务都会创建路由, 但是有些我们不希望被外界访问到,
使用: zuul.ignored-services参数设置不创建路由的服务, zuul会判断如果符合这里定义就不创建

比如定义zuul.ignored-services=*, 之后再手动定义那些需要被路由的服务

自定义路由规则
根据我们的需要, 创建一个符合我们要求的规则, 配置一个Bean, 并用正则表达式即可, bean为PatternServiceRouteMapper

路径匹配
配置路由时的path参数可以使用ant的风格配置
包含三种通配符:
? 匹配任意单个

  • 匹配任意数量的字符
    ** 匹配任意多的字符, 支持多级目录

使用通配符的时候经常会遇见一个表达式匹配多个
这时候就需要配置有顺序, 当匹配一个就行了, 就需要yaml配置文件, 才能保证顺序

忽略表达式
Zuul.ignored-patterns属性

路由前缀
全局的添加前缀, zuul.prefix参数

本地跳转
支持forward形式的服务端跳转配置
Zuul.routes.hello-service.path=/api-a/**
Zuul.routes.hello-service.url=forward:/hello-service
这样配置之后比如 api-a/hello的请求将会被转发网关的hello-service/hello请求上进行本地处理, 注意要在网关上实现这个hello-service/hello接口, 否则会404

Cookie与头信息
在请求路由时会过滤一些http的敏感信息, 防止被传到下游的外部服务器, 默认的敏感头信息包括cookie, set-cookie, authorization三个

由于上述三个头信息由zuul.sensitiveHeaders参数定义, 所以可以设置这个参数为空, 来覆盖默认设置, 另外方式可以对指定路由开启敏感头, 对于指定路由设置敏感头为空, 推荐第二种方式

重定向问题
登录成功后, 跳转的url往往是具体的web应用实例的地址, 而不是网关的路由地址,
通过配置文件可以解决此问题

Hystrix和ribbon支持
有了上述两个支持, 所以zuul天生就有线程隔离和断路器的自我保护功能, 以及服务的客户端负载均衡功能, 可以通过上述两个的配置参数来调整zuul的路由, 需要注意在没有使用eureka的时候, zuul没有这个两个组件的功能, 注意hystrix的超时时间大于ribbon的超时时间

过滤器详解
Zuul实现的过滤器必须包括4个基本特征, 过滤器类型, 执行顺序, 执行条件, 具体操作
实际上就是ZuulFilter中定义的四个抽象方法

FilterType方法: 返回一个字符串代表过滤器类型, 这个类型就是在http请求定义的各个阶段, 默认定义了四种不同声明周期的过滤器类型
Pre 在请求被路由前调用
Routing 路由请求时调用
Post 在routing和error过滤器之后调用
Error 处理请求时发生错误调用

FilterOrder: 通过int值定义过滤器执行的顺序, 数越小优先级越高
ShouldFilter: 返回一个boolean值判断是否要执行该过滤器,
Run: 具体的操作逻辑, 可以自定义过滤逻辑, 来确定是否要拦截当前的请求, 不对后续调用, 或是对请求结果做些处理

请求生命周期
当请求到达api网关时候, 先进入第一个阶段pre, 被pre类型的过滤器处理, 该过滤器目的在路由之前做一些前置工作, 比如请求校验, 之后进入routing, 请求转发阶段, 请求将会被routing类型过滤器处理, 当服务实例请求结果都返回之后, routing阶段完成, 进入第三阶段post, 请求被post类型过滤器处理, 这些过滤器不仅能获取请求信息, 还能获取返回信息, 特殊阶段error, 该阶段只有上述三个阶段发生异常才会触发, 但是最后还是流向post, 因为需要post返回给请求客户端

核心过滤器
在zuul中每个阶段都定义了一些关键的过滤器以完成默认的功能,
请求参见260页

异常处理
核心过滤器中并没有error类型的过滤器,
在抛出异常时需要在上下文中加入一些指定的key值, 来使post中有个SendErrorFilter能检测到异常, 如error.status_code参数, 所以需要在方法中使用trycatch的方式在发生异常时候进行设置context.set…

ErrorFilter处理
用error类型的过滤器解决未知的一些异常的情况, 并在该过滤器中沿用trycatch捕获的异常这样SendErrorFilter就能组织返回给客户端了

不足与优化
对自定义过滤器中处理异常的两种基本解决方法: 一种是利用在各个阶段trycatch块, 实现过滤器内部异常处理, 二种是利用error类型过滤器集中处理其他三个阶段抛出的异常信息,
但是当post类型的过滤器抛出的异常时候, 由error处理完之后,就没有交给post类型, 这样就没办法利用SendErrorFilter返回, 所以可以继承该过滤器复用执行逻辑, 重新定义执行顺序, 类型等, 详细处理参见272页

自定义异常信息

禁用过滤器
通过配置文件实现, 可以禁用默认的zuul的过滤器, 从而实现一套自己的

动态加载
通过和config整合实现

动态过滤器

第八章 config
默认采用git存储配置信息

快速入门
Server端引入server starter, 主类加注解开启, 配置文件中配置git仓库地址

配置规则详解
注意配置url和配置文件名的映射关系, 包括application, profile, label几个参数值
配置服务器在git获取配置之后, 会缓存在本地一份 git clone, 防止git出问题

客户端配置映射
加入config的starter, 创建bootstrap.properties配置, 来指定配置文件的位置, bootstrap.properties配置文件使应用可以获取外部配置信息,

服务端详解
基础架构
包含几个要素: 远程git仓库, config server, 本地git仓库, 服务应用

获取流程
应用启动时, 根据bootstrap配置文件中的应用名, 环境名, 分支名想server获取配置信息
Server根据自己维护的git信息, 查找配置
Git clone到server本地
从本地读取返回客户端
客户端获取外部配置加载到客户端的ApplicationContext实例中, 该配置优由内部配置

Git配置仓库
占位符配置url, 从而可以根据客户端的信息进行动态的获取, 具体git目录等等

配置多个仓库
支持通配符, 或者使用逗号分隔

子目录存储

访问权限
使用用户名密码方式, 或者使用SSH方式,

还能使用svn,

不管使用什么版本的仓库都会在本地仓库缓存一份

支持本地文件系统配置

健康检查

属性覆盖
在config server端配置一些参数, 该属性参数不会被客户端修改, 并且获取配置时都会获取到该配置, 客户端可以通过更高优先级的配置覆盖该配置

安全保护
结合security

加密解密
提供对属性的加密功能, 从而保证配置文件的信息安全, 防止开发人员看到生产数据库的密码等等这类敏感信息, 详情参见302页
对称加密与非对称加密都可使用

高可用配置
服务化配置中心
与eureka结合

通过url获取远程配置

动态刷新配置
通过actuator监控模块提供的/refresh端点刷新配置信息

第九章 bus
在微服务系统中, 通常会使用轻量级的消息代理来构建一个共用的消息主题, 让系统中所有微服务实例都连接上来, 由于该主题产生的消息会被所有的实例监听和消费, 所以成为消息总线,在消息总线上各实例都可以广播一些需要让其他连接在主题上的实例都知道的消息, 例如配置信息的变更, 配合config实现

快速入门
使用rabbitmq实现的消息总线, 引入starter, 配置文件配置mq地址

原理分析
所有实例都连接到了bus上, 在git仓库中修改某个服务的属性, 发送/refresh请求, 然后刷新配置,

指定刷新范围
Bus/refresh接口提供了一个destination参数, 指定服务名, 各实例根据此参数决定要不要刷新配置,

架构优化
将config server也引入到消息总线中, Bus/refresh请求发送到config server, 并通过destination指定需要更新的实例,

Rabbitmq配置
详情参见330

Kafka实现

原理分析
Bus采用了事件驱动模型, 详情参见原书

Bus实际上是基于stream提供的一个消息总线, 是stream模块的上层模块, 实现了消息广播这种功能, 想要细粒度的控制消息, 需要使用stream模块

第十章 stream
为一些消息供应商提供了个性化的自动化配置实现, 引入了发布订阅和消费组和分区的概念

快速入门
引入starter, 创建消费者, 使用@EnableBinging注解

核心概念
通过绑定器来隔离每个消息实现的差异性, 通过向应用程序暴露统一的channel通道, 更换消息中间件产品时更换绑定器即可,

发布订阅模式
通过共享的Topic进行广播,

消费组
如果在统一主题上的应用启动了多个实例的时候, 可以通过配置属性指定一个组名, 这样这个应用的多个实例在接受消息时候, 只会有一个接受,
默认情况下, 没有为应用指定消费组,stream为其分配一个独立的匿名消费组,

组内竞争组间共享, 类似kafka

消息分区
为了让某个实例能一直消费某一规则的消息, 比如统计,

使用详解
开启绑定功能: @EnableBingding注解启动,
绑定消息通道, 通过@Input, @Output注解定义消息通道,
注入绑定接口
注入消息通道

消息生产与消费

核心理解
就是使微服务各个实例依赖消息中间件进行通信, 而非直接通信, 这样就可以避免强耦合, 同时可以实现监听模式, 通过监听的topic实现,
提供了source, sink, channel,bingding几个概念, 用于生产消费者之间的通信,
通过使用注解从而优雅的使用消息,

第十一章 sleuth
全链路跟踪

为每个需要跟踪的服务加上starter

跟踪原理
在全部系统流转都保持一个唯一标识, TraceID, 这样就能将请求关联起来
为了统计每个服务的时间延迟, 需要一个SpanID, 有开始和结束的时间戳, 统计这个值从而确定延迟, 同时还会包含一些其他的元数据

当加上starter之后会自动为应用构建起各通信通道的跟踪机制, 比如: mq, 通过zuul代理传递的请求, 通过restTemplate发起的请求,

在第一个服务像下一个服务发送请求时候, sleuth会在请求的Header中增加实现跟踪的重要信息,
主要包括:
X-B3-TraceId, 一条连上的唯一标识
X-B3-SpanId, 一个单元的标识
X-B3-ParentSpanId, 上一个所属的工作单元, 第一个为空
X-B3-Sampled, 是否被抽样输出, 1输出, 0不输出
X-Span-Name, 工作单元的名称

这些信息可以通过获取头信息获取

将springmvc的日志改为debug之后, 可以得到更多的跟踪信息

抽样收集
默认收集10%的日志, 开发总可以设为1表示全部收集,
也可以实现自己的收集策略, 只需要实现Sampler接口

与logstash整合
在工程中引入logstash’的依赖在做一些配置即可, 通过logback输出

与Zipkin整合
虽然ELK很便利, 但是缺少了对请求链路中各阶段时间的关注,
Zipkin提供了rest接口, 和UI界面, 所以可以开发自己想要的展示界面

主要由四个组件组成
Collector, 收集器
Storage, 存储组件, 默认内存, 可以配置后存到数据库中
RestAPI, api组件, 提供外部访问
WebUI, 基于api展示

HTTP收集
配置个springboot应用作为zipkin server, 并引入相关依赖, 在主类上加入@EnableZipkinServer注解开启

为其他应用引入zipkin, 引入starter, 并在配置文件中配置zipkin server的地址, 这样才能通过http请求发送到server

消息中间件收集
通过结合stream, 我们可以非常方便的异步收集输出到中间件上, 服务端也异步消费
各客户端需要引入sleuth对stream的支持的starter, 并在配置文件中增加消息中间件的配置, 服务端也更改starter, 和配置文件

收集原理
Zipkin中的核心概念
Span, 一个工作单元, 还存储了一些其他信息
Trace, 由一系列相同的traceId的span串联起来

Annotation, 记录一个事件的存在,通过计算每个事件的时间间隔得出延迟
主要由四个事件,
Cs(client send), 记录客户端发起一个请求, 也是标识一个http的开始
Sr(server received), 记录服务端收到了请求,
Ss(server send), 服务端处理完请求, 准备发送响应,
Cr(client received), 标识客户端收到了服务端的回复, 标识结束这个http请求

Binary Annotation, 用来对跟踪信息做一些额外的补充说明

收集机制
根据425页图片, 查看http请求响应在zipkin角度的机制,
通过相同的spanId不同的Annotation事件得出的不同时间, 最后相应的展示在页面

在页面上查询结果显示的spans数和total span不是同一事, 所以会不一样, 详细参考426页

数据存储
默认在内存中, 可以配置到mysql中, 同时还支持Cassandra和ElasticSearch数据库

API接口

zipkin与dubbo

  1. 项目添加上zipkin所需要的maven配置包
  2. 然后配置好zipkins.address服务地址
  3. 关于dubbo的配置,只需要如下
    #add tracing filter
    dubbo.consumer.filter = tracing
    #add tracing filter
    dubbo.provider.filter = tracing
  4. 搭建一个zipkin服务端
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值