Spring Cloud 微服务实战

Eureka 服务治理

Maven dependency

  • 与spring boot的版本的对应
    1. Finchley兼容Spring Boot 2.0.x,不兼容Spring Boot 1.5.x
    2. Dalston和Edgware兼容Spring Boot 1.5.x,不兼容Spring Boot 2.0.x
  • Spring Boot 1.5.x
    1. org.springframework.boot:spring-cloud-starter-eureka-server
    2. org.springframework.boot:spring-cloud-starter-eureka
  • Spring Boot 2.0.x
    1. org.springframework.boot:spring-cloud-starter-netflix-eureka-server
    2. org.springframework.boot:spring-cloud-starter-netflix-eureka-client
  • parent
    1. org.springframework.boot:spring-boot-starter-parent
    2. org.springframework.cloud:spring-cloud-dependencies

服务注册

  • 搭建注册中心
    1. server.contextPath无法指定
    2. @EnableEurekaServer :注册为Eureka服务端应用
    3. eureka.client.register-with-eureka=true: 注册中心不需要注册自己,但是搭建集群需要
    4. eureka.client.fetch-registry=true: 注册中心也不需要发现服务,但是搭建集群需要
    5. eureka.instance.hostname :
      1. 注册中心实例名字,单注册中心设置为localhost,从而取消默认的registered-replicas
      2. 如果搭建集群,hostname需要和eureka.client.serviceUrl.defaultZone的host相同,否则会出现在unavaiable里
  • 搭建Client,注册服务
    1. eureka.client.register-with-eureka : 注册服务,默认true
    2. eureka.instance.instanceId : 实例名
      1. 默认 ${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}
      2. 随即port使用${spring.cloud.client.hostname}:${spring.application.name}:${random.int[1000,99999]}
    3. eureka.instance.preferIpAddress : 是否优先使用IP作为主机名的标示
    4. eureka.instance.appname: 注册服务名字,并不是service-id,默认取spring.application.name
    5. eureka.instance.hostname: 主机名,默认取操作系统主机名
    6. eureka.client.serviceUrl.defaultZone=http://localhost:8090/eureka : 服务注册中心地址
    7. @EnableDiscoveryClient : 注册为Eureka客户端应用,spring boot app可以省略
    8. 客户端除了java,还有很多其他语言的客户端,也可以开发自己语言的客户端
      1. eureka-js-client
      2. python-eureka

服务维护

  • 服务下线
    1. 当用spring boot actuator shutdown功能正确关闭spring boot 应用时,会向注册中心发送下线(Down)请求,剔除服务
    2. 杀死进程等非正确关闭不会触发
  • 服务剔除
    1. 注册中心默认每隔1min触发一次服务剔除任务,剔除失效的服务
    2. eureka.instance.lease-expiration-duration-in-seconds=90 : 服务失效时间,默认90s
  • 服务续约
    1. eureka.instance.lease-renewal-interval-in-seconds=30 : 服务续约心跳频率,默认30s
    2. 服务在被剔除后还是能通过续约再次注册
  • 自我保护
    1. 注册中心红色字体警告表示已经启用自我i保护
    2. 15分钟内心跳成功比例高于85%触发自我保护,使得服务实例不会过期
      1. eureka.server.renewal-percent-threshold配置比例
    3. Renews threshold : Eureka Server 期望每分钟收到客户端心跳的总数。
      1. Renews threshold = Client实例数(60/心跳频率,默认30)心跳成功率(默认85%)
    4. Renews (last min) : Eureka Server 最后 1 分钟收到客户端实例续约的总数。
    5. 如果Renews (last min) < Renews threshold,5分钟后看到注册中心红色字体警告,表示有实例心跳异常,并且已经启用自我保护
    6. 如果网络恢复,一段时间后红色字体警告消失
    7. eureka.server.enable-self-preservation=false : 本地调试建议关闭服务中心自我保护功能
    8. 在生产环境中自我保护功能十分必要

服务消费

  • 发现服务与调用
    1. spring-cloud-starter-eureka
    2. eureka.client.fetch-registry :获取服务注册列表,默认true
    3. eureka.clent.registry-fetch-interval-seconds=30 : 获取注册列表的时间间隔,默认30s
    4. @EnableDiscoveryClient : 注册为Eureka客户端应用
    5. RestTemplate.getForEntity("http://MD-SERVICE/MarkdownNote/menu/get", String.class)

Eureka集群 - High Available : 多个Eureka组成的集群组成了高可用,更健壮的注册服务

  • Eureka集群需满足以下条件
    1. 当两个注册中心相互设置对方url到eureka.client.serviceUrl.defaultZone,超过两个的,在每个下配置所有其他服务地址,以","隔开
    2. 每个注册中心的eureka.instance.hostname必须不同
    3. eureka.client.register-with-eureka和eureka.client.fetch-registry可以设置为false
    4. 每个注册中心的spring.application.name可以相同也可不同
    5. 当满足上面条件的时候,registered-replicas中会出现对方,这时候往这个注册中心注册服务的时候,请求会被转发到registered-replicas中所有的注册中心,同而实现服务同步
    6. 转发只限一次,无法循环转发
  • Client使用Eureka集群
    1. Eureka服务器不可用不会影响Client自身的启动
    2. Client推荐设置所有注册中心,以","隔开,Client按顺序连接到第一个可用的Eureka注册中心
    3. Client会默认每30s从当前连接的注册中心获取注册列表,并且同时维护心跳并转发,转发失败不会报错
    4. 一旦Client获取到某个服务,即使所有的注册中心都不可用,Client也能使用此服务
    5. 当当前连接的Eureka服务器不可用,获取列表和维护心跳都会尝试3次retry,这3次会尝试连接别的Eureka服务器,三次后还失败,则等待下一个30s
  • Eureka集群的压力只与Client的数量有关,与Client之间的请求交互量无关,一般无须做Eureka集群的load balance
    1. prefer-same-zone-eureka=false的时候执行以上规则
    2. prefer-same-zone-eureka=true的时候先通过region取availability-zones内的第一个zone,然后通过这个zone取service-url下的list执行以上规则

Ribbon 负载均衡

RestTemplate

  • 配合Spring MVC使用
    1. @RequestMapping( value= "/subject/{alias}/put", method=RequestMethod.PUT )
    2. @PathVariable String alias
    3. @RequestParam String param
    4. @RequestBody List subjects
  • 满足两个条件的RestTemplate会使用Ribbon自动装载
    1. 是一个spring bean
    2. @LoadBalanced注释

负载均衡器

BaseLoadBalancer

  • 默认使用RoundRobinRule
  • 启动间隔为10秒的定时任务,检查server是否健康

DynamicServerListLoadBalancer extends BaseLoadBalancer

ServerList : 从Eureka获取实例清单和实例信息

  • DomainExtractingServerList : 默认配置
    1. spring cloud 的实现
    2. 构造函数传入DiscoveryEnabledNIWSServerList

ServerListUpdater : 服务更新器

  • PollingServerListUpdater 默认配置
    1. ServerListUpdater的默认实现,通过间隔为30s的定时任务更新服务
  • EurekaNotificationServerListUpdater
    1. 利用Eureka的事件监听器更新服务

ServerListFilter :更新服务的时候,对服务进行过滤

  • ZoneAffinityServerListFilter : 区域亲和,ServerListFilter的抽象实现类
    1. 通过服务提供方和消费方的区域来筛选实例, 剔出不同区域
    2. 当前区域符合以下任何条件,则不执行区域亲和,从而保证跨区域的高可用性
      1. 当前区域实例故障百分比 >= 80% : 如果只有1个可用,总量在5个或以上的区域不亲和
      2. 实例平均负载 >=60%
      3. 可用实例数(总数-故障) < 2 : 只有1个可用不亲和
    3. getFilteredListOfServers方法
  • DefaultNIWSServerListFilter Netflix默认配置
    1. 完全继承ZoneAffinityServerListFilter,默认的NIWS(Netflix Internal Web Service)过滤器
  • ServerListSubsetFilter : 大规模集群
    1. 继承ZoneAffinityServerListFilter,适合大规模服务器集群(百个)
    2. 第一,通过区域亲和获取服务清单作为候选清单
    3. 第二,同时从当前消费方的服务清单和候选清单剔除不健康的服务
      1. 实例并发连接数超过客户端配置的值,默认0
      2. 实例失败数超过配置,默认0
      3. 如果按照以上规则剔除后,剔除比例不满10%(默认),则按健康算法排序后继续剔除末尾的服务,直到满足比例
      4. 改变默认设置
        1. {clientName}.{nameSpace}.ServerListSubsetFilter.eliminationConnectionThresold
        2. {clientName}.{nameSpace}.ServerListSubsetFilter.eliminationFailureThresold
        3. {clientName}.{nameSpace}.ServerListSubsetFilter.eliminationFailureThresold
    4. 第三, 从剔除后的候选清单随即选出一批实例子集,默认20个
      1. {clientName}.{nameSpace}.ServerListSubsetFilter.size
  • ZonePreferenceServerListFilter: SpringCloud默认配置
    1. 除了这个是spring cloud新增的过滤器,其他都是Netflix Ribbon源生,继承ZoneAffinityServerListFilter
    2. 第一,通过区域亲和获取服务清单作为候选清单
    3. 第二,通过Client配置预设的Zone属性进行过滤,选出务提供方和消费方Zone设置相同的清单,如果为空,则使用原候选清单
    4. 与ZoneAffinityServerListFilter 区别在于永远执行区域亲和,不关心3个条件,不保证跨区域的高可用性,只有当当前区域没有服务实例的时候才会使用ZoneAffinityServerListFilter 筛选的服务
    5. getFilteredListOfServers方法

ZoneAwareLoadBalancer extends DynamicServerListLoadBalancer

  • SpringCloud默认配置
  • ZoneAwareLoadBalancer会通过ZoneAvoidanceRule选择合适的区域,合适的服务器
  • ZoneAwareLoadBalancer重写了chooseServer方法,在服务方的Zone只有一个的时候(也就是DynamicServerListLoadBalancer 的区域亲和生效的时候),使用BaseLoadBalancer默认的RoundRobinRule,否则使用ZoneAvoidanceRule
  • DynamicServerListLoadBalancer已经是区域亲和,为什么还需要ZoneAwareLoadBalancer
    1. DynamicServerListLoadBalancer 采用BaseLoadBalancer默认的RoundRobinRule
    2. 如果消费方的Zone和服务方所有的Zone都不同,就会得到所有Zone的服务器,区域亲和实效

负载均衡策略

RandomRule

  • 随机选择

RoundRobinRule

  • 按顺序选择
  • 10次后退出循环

RetryRule

  • 对RoundRobinRule进行重试一定时间

WeightedResponseTimeRule

  • 30s的定时任务对服务实例进行权重计算
  • 权重计算方式
    1. 计算出所有实例的总的平均响应时间,totalResponseTime
    2. 权重区间=totalResponseTime - 实例自己的avgResponseTime
    3. 权重区间宽度越大,被选中概率越大
    4. 比如A,B,C的avgResponseTime为10,20,40,那么 权重区间为(0-60],(60-110],(110-140)
  • 选择实例
    1. 从0-140产生一个随机数,比如70,那么就选择70所在权重区间的B

BestAvailableRule

  • 过滤掉故障实例,选出当前并发数最小的实例,最空闲的那个

AvailabilityFilteringRule

  • 先按RoundRobinRule选择
  • 然后按以下条件过滤
    1. 是否故障
    2. 并发请求大于阈值,默认232-1
  • 如果满足任意一个,就重复选择
  • 重复10此后,还是不满足过滤条件,直接使用RoundRobinRule

ZoneAvoidanceRule

  • SpringCloud默认配置
  • ZoneAwareLoadBalancer只有Zone的个数>1,才使用ZoneAvoidanceRule ,否则使用RoundRobinRule
  • 先按以下规则剔除符合的Zone区域
    1. Zone的实例为0
    2. Zone实例平均负载<0
    3. 实例故障率大于阈值,默认0.99999,实例故障率=段滤器断开次数/实例数
    4. 然后剔除平均负载最高的Zone
  • 如果上面规则没有剔除任何Zone
    1. 如果实例最大平均负载小于阈值,默认20%,则返回所有Zone为可用Zone
      1.如果实例最大平均负载大于阈值,随机剔除一个高负载的区域
  • 如果0<可用Zone<总Zone,则随机选择一个Zone,否则使用所有Zone
  • 从返回的Zone过滤实例
    1. 通过主过滤条件和次过滤条件进行实例过滤
    2. 任何过滤之后,满足2条件中的任一,停止过滤
  • 过滤后的实例进行RoundRobinRule

Region / Zone

Eureka Server的Zone

  • 通过eureka.client.prefer-same-zone-eureka=true开启client对Eureka Server的区域亲和功能
  • client的availability-zones的第一个zone写成client自己的zone(eureka.instance.metadata-map.zone的值)
  • 这样client就会找到availability-zones的第一个zone,找到service-url相应的zone的Eureka Server的服务器list,去注册服务并且维护心跳

Client的Zone

  • 同一个Zone可以属于不同的Region,但是这是没有意义的,spring cloud之根据Zone进行服务器分组,所有原则上一个Zone只属于一个Region
  • eureka.client.region : 设置region
  • eureka.client.availability-zones.{ragion}=sh,bj,sz : 设置当前region所有的zone
  • eureka.instance.metadataMap.zone=sh : 设置client自己的zone

参数配置

  • 全局参数 : ribbon.ConnectTimeOut=250
  • 客户端参数 : hello-service.ribbon.ConnectTimeOut=250
    1. 你当前实例调用的hello-service的配置,而不是你当前实例的配置
    2. 客户端参数覆盖全局参数
  • 如果没有Eureka,可以使用以下客户端参数 配置实现Ribbon负载均衡
    1. hello-service.ribbon.listOfServers=localhost:8080,localhost:8081,localhost:8082
  • 更多参数查看
    1. com.netflix.client.config.CommonClientConfigKey

重试机制

  • spring.cloud.loadbalancer.retry.enabled=true
    1. 开启重试机制
  • ribbon.OkToRetryOnAllOperations
    1. 对所有操作进行重试
  • ribbon.ConnectTimeout
    1. 请求连接的超时
  • ribbon.ReadTimeout
    1. 请求处理超时时间
  • ribbon.MaxAutoRetries
    1. 对当前实例的重试次数
  • ribbon.MaxAutoRetriesNextServer
    1. 切换实例的重试次数
  • hystrix.command.defalut.execution.isolation.thread.timeoutInMilliseconds > ribbon.ConnectTimeOut
    1. 断路器的超时要大于ribbon的超时,否则不触发重试

Hystrix 容错保护

Getting Start

  • Maven dependency
    • springboot 1.5.x
      1. org.springframework.cloud:spring-cloud-starter-hystrix
    • springboot 2.0.x
      1. org.springframework.cloud:spring-cloud-starter-netflix-hystrix
  • @EnableCircuitBreaker : 开启断路器功能
  • @SpringCloudApplication : 包含了断路器功能
    1. @SpringBootApplication
    2. @EnableDiscoveryClient
    3. @EnableCircuitBreaker
  • @HystixCommand(fallbackMethod="helloFallBack") : 需要容错保护的方法
  • 定义回调方法helloFallBack
    1. helloFallBack所执行的操作被称为服务降级
    2. 与@HystixCommand修饰的方法具有相同返回类型和参数
    3. 在参数里添加Throwable e来获取原操作抛出的异常
  • 会引起服务降级的情况
    1. 断路器打开
    2. 当前命令的线程池,请求队列或者信号量占满
    3. 被保护的操作抛出异常,比如网络错误
    4. 网络超时,Hystrix默认超时时间为2s
  • 降级操作可以再次使用HystixCommand,形成级联的降级策略
  • 非annonation的方式支持请求缓存功能

Hystrix的功能与作用

  • 控制被依赖服务的延时和失败

断路器原理

  • isOpen() : 断路器是否打开
    1. 断路器标识circuitOpen=true,返回true
    2. 判断度量指标metrics的HealthCounts统计对象
      1. 这个统计对象由一个默认间隔为10s的定时任务维护
      2. 如果满足以下全部的两个条件,将circuitOpen设置为true,并返回true
        1. 请求总数QPS超出阈值,默认20,
        2. 错误百分比超出阈值,默认50%
    3. 如果断路器从关闭切换至打开,将当前时间记录到circuitOpenedOrLastTestedTime中
  • allowRequest(): 判断请求是否被允许
    1. 判断是否强制打开或者强制关闭,如果强制关闭,所有请求都允许,但是isOpen还是会执行,用于模拟断路器打开/关闭
    2. 通过isOpen判断断路器状态,断路器关闭,则返回true
    3. 如果断路器打开,则调用allowSingleTest(),通过circuitOpenedOrLastTestedTime判断断路器是否在休眠期内,默认5s
      1. 如果是在休眠期,则返回false
      2. 如果不在休眠期,则返回true,并设置当前时间到circuitOpenedOrLastTestedTime,设置断路器状态为"半开"
  • 当断路器"半开",请求会被允许
    1. 请求成功,调用markSuccess(),关闭断路器
    2. 请求失败,重新打开断路器
  • 通过以上原理可以看出,断路器并不是用于标识网络错误,请求抛错,网络超时等信息的,这些信息会直接引起服务降级,断路器是用于表示网络压力和网络异常比例的
  • 断路器的开闭策略保证了不会因为某个服务的网络异常影响整个服务链,不会因为某个服务的网络问题造成服务积压,影响其他服务性能

依赖隔离

  • 传统的服务对线程池线程个数的限制是作用于整个服务进程实例的,当线程池占满之后,进程下的所有服务都需要等待
  • Hystrix对每个当前进程所依赖的服务创建独立的线程池,好处如下
    1. 某个依赖服务出现延迟也不会影响其他服务
    2. 当依赖服务出现配置问题,能快速的反映出问题,同时可以通过spring cloud config/bus 动态解决问题
  • 每个专有线程池提供内置的并发实现,从而创建异步访问(就是请求合并?)
  • 这种设计会给系统带来更多的负载和开销,但是Netflix通过实验证明这种开销是很小的
  • 对于对延迟有极高要求的系统,Hystrix设计了另外的解决方案:信号量
    1. 信号量比线程池的开销小,但是不能实现异步,不能设置超时,所以要求依赖服务足够可靠
    2. execution.isolation.strategy=SEMAPHORE,使用信号量代替线程池
    3. 信号量默认值为10,估算方法与线程池并发度的估算类似,标准如下
      1. 访问内存的请求一般耗时1ms以内,性能可以达到5000rps的,可以设置为1或2?

使用详解

  • 非Annonation方式
  • @HystixCommand
    1. fallbackMethod="helloFallBack" : 需要容错保护的方法
    2. ignoreExceptions=HystrixBadRequestException.class : 使得服务降级忽略某个exception,HystrixBadRequestException默认忽略
    3. threadPoolKey=''pool-a' : 线程池分组名称,默认等于groupKey,建议不用默认
    4. groupKey="group-a" : 命令组名,按组统计命令告警,仪表盘等信息,默认等于类名
    5. commandKey="hello" : 命令名称,默认方法名
    6. commandProperties={@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="500")} : 属性配置
  • 请求缓存
    1. @CacheResult(cacheKeyMethod="helloKey")
      1. 必须与@HystixCommand合用
    2. @CacheKey(value="参数同名"):
      1. 定义在参数上,如果没有使用所有参数作为key
      2. cacheKeyMethod比他优先级高
      3. 支持使用参数的属性作为key,只要将value=属性名字
    3. @CacheRemove
      1. 定义在一些写操作的请求上面,使得缓存能被更新
      2. commandKey:需要被缓存的请求的命令名称,必须指定
    4. Hystix的缓存局限性很强,只适合一些简单的缓存需求,复杂需求建议使用缓存框架
  • 请求合并
    1. 将一段时间窗内(默认10ms)的请求合并,进行批处理,服务提供方需要提供批处理接口
    2. @HystrixCollapser
      1. 注解在需要进行合并的操作上面,这里是findUser,返回User对象
      2. batchMethod="findAllUser" : 调用批处理接口的方法,返回一个User的List,需要用@HystixCommand修饰
      3. collapserProperties={@HystrixProperty(name="timerDelayInMilliseconds",value="100")} : 设置批处理的配置属性,这里设置批处理时间窗为100毫秒
    3. 请求合并能够减少通讯次数,减少线程池的使用量,但是也会带来相应的额外开销
      1. 请求合并会增加请求的延迟,最坏的情况延迟=原请求延迟+时间窗
      2. 原本就高延迟的请求适合使用请求合并
      3. 高并发系统适合使用请求合并,如果一个时间窗里只有寥寥几个请求,不适合使用
    4. 请求合并功能目前不提供开启/关闭属性来控制,这样使得代码不够灵活

属性配置

  • 配置优先级由低到高
    1. 全局默认值 : 默认值,无需配置
    2. 全局配置属性: hystrix.command.default,spring cloud config和spring cloud bus实现
    3. 实例默认值 : 默认值,无需配置
    4. 实例配置属性: hystrix.command.HystrixCommandKey,spring cloud config和spring cloud bus实现
  • 可以使用配置文件,也可以使用注解的commandProperties和collapserProperties等

hystrix.command.default/HystrixCommandKey

execution: 执行相关 控制HystrixCommand.run() 执行

  • 执行的隔离策略: execution.isolation.strategy
    1. 线程池隔离:THREAD(默认) / 信号量隔离:SEMAPHORE
    2. 信号量适用于接口并发量高的情况,如每秒数百次调用的情况,导致的线程开销过高, 适用于非网络调用,执行速度快,可靠
  • 超时时间: execution.isolation.thread.timeoutInMilliseconds
    1. 全局默认1000,实例默认2000
  • 是否启用超时设置: execution.timeout.enabled
    1. 默认true
  • 超时是否中断正在执行的run : execution.isolation.thread.interruptOnTimeout
    1. 默认true
  • 执行取消动作时,是否中断正在执行的run: execution.isolation.thread.interruptOnCancel
    1. 默认true
  • 设置最大的信号量: execution.isolation.semaphore.maxConcurrentRequests
    1. 只对于使用信号量[SEMAPHORE]策略的生效, 默认10
    2. 且该值必需小于容器的线程池大小,否则并不起保护作用,因为其实容器线程池的一小部分而已

fallback: 控制HystrixCommand.getFallback() 执行 对于线程池或者信号量执行策略都生效

  • 最大的并发调用getFallback() : fallback.isolation.semaphore.maxConcurrentRequests
    1. 默认10, 如果超出该数,则后续的会被拒绝,并抛出异常
  • 是否启用降级服务: fallback.enabled
    1. 默认true

circuitBreaker: 断路器 控制HystrixCircuitBreaker

  • 是否开启断路器用于健康监控和短路请求 : circuitBreaker.enabled
    1. 默认true
  • 请求总数QPS的最小值:circuitBreaker.requestVolumeThreshold
    1. 默认20,超出20才有可能开启断路器,开启的条件之一
  • 错误百分比最大值:
    1. 默认50%,超出有可能开启断路器,开启的条件之一
  • 断路器休眠时间:circuitBreaker.sleepWindowInMilliseconds
    1. 默认5000毫秒,如果不在休眠期,则设置断路器状态为"半开",允许此次请求
  • 是否强制打开断路器:circuitBreaker.forceOpen
    1. 默认false,如果打开则会拒绝左右的请求
    2. 优先级比circuitBreaker.forceClosed高
  • 是否强制关闭断路器: circuitBreaker.forceClosed
    1. 默认false,true则允许所有的请求

requestContext: 请求上下文,控制HystrixRequestContext 被HystrixCommand使用

  • 是否开启请求缓存: requestCache.enabled
    1. 默认true
  • 是否记录HystrixCommand执行或者事件的日志到HystrixRequestLog : requestLog.enabled
    1. 默认true

metrics[一般不需要设置] : 度量HystrixCommand 和 HystrixObservableCommand 的执行情况

  • 设置滚动时间窗的长度 :metrics.rollingStats.timeInMilliseconds
    1. 默认10000毫秒,将这10000毫秒分成10个桶,统计每个桶采集健康度等信息,供断路器使用
    2. 该项不可以动态修改,以防止统计的不正确
  • 设置滚动的统计窗口被分成的桶的数量:metrics.rollingStats.numBuckets
    1. 默认10
    2. metrics.rollingStats.timeInMilliseconds % metrics.rollingStats.numBuckets == 0 这个必须成立(整除),否则会抛异常
  • 是否开启百分数统计: metrics.rollingPercentile.enabled
    1. 默认tru, 如果为false,则所有概要统计值为-1
  • 设置百分数统计滚动时间窗的长度 :metrics.rollingPercentile.timeInMilliseconds
    1. 默认60000
  • 设置百分数统计的桶的数量:metrics.rollingPercentile.numBuckets
    1. 默认6
  • 设置百分数统计的桶的最大数量,metrics.rollingPercentile.bucketSize
    1. 默认100
  • 断路器健康快照的采集时间窗的长度:metrics.healthSnapshot.intervalInMilliseconds
    1. 默认500ms

hystrix.collapser.default/HystrixCollapserKey

  • 设置批处理合并在多少时间内的请求 : timerDelayInMilliseconds
    1. 默认10毫秒
  • 一次最多合并的请求数: maxRequestsInBatch
    1. 默认Integer.MAX_VALUE
  • 批处理是否开启请求缓存 :requestCache.enabled
    1. 默认true

hystrix.threadpool.default/HystrixThreadPollKey

  • 设置依赖服务的命令执行的线程数 : coreSize
    1. 默认10
  • 线程池的最大队列大小 : maxQueueSize
    1. 默认-1,这时候使用SynchronousQueue,其他则为
    2. LinkedBlockingQueue达到限制时,请求会被拒绝(服务降级?),无法动态改变
  • 设置拒绝请求的队列最大值
    1. 默认5,如果maxQueueSize=-1时无效
    2. 通过设置此值,即使LinkedBlockingQueue没有达到限制,也能拒绝请求,解决LinkedBlockingQueue无法动态改变的问题
  • 设置滚动时间窗的长度:metrics.rollingStats.timeInMilliseconds
    1. 统计线程池的度量指标,默认10000毫秒
  • 设置桶数: metrics.rollingStats.numBuckets
    1. 默认10

Hystrix Dashboard

Getting Start

  • Maven dependency
    • springboot 1.5.x
      1. org.springframework.cloud : spring-cloud-starter-hystrix-dashboard
    • springboot 2.0.x
      1. org.springframework.cloud : spring-cloud-starter-netflix-hystrix-dashboard
      2. com.netflix.hystrix : hystrix-metrics-event-stream
  • @EnableHystrixDashboard
  • ServletRegistrationBean

      @Bean
      public ServletRegistrationBean registration(){
          HystrixMetricsStreamServlet servlet = new HystrixMetricsStreamServlet();
          ServletRegistrationBean registrationBean = new ServletRegistrationBean();
          registrationBean.setServlet(servlet);
          registrationBean.setEnabled(true);//是否启用该registrationBean
          registrationBean.addUrlMappings("/hystrix.stream");
          return registrationBean;
      }
  • 调用带有@HystrixCommand的服务

仪表盘详解

  • 实行圆
    1. 颜色代表健康状况,从高到低: 绿色,黄色,橙色,红色
    2. 大小表示流量
  • 曲线
    1. 两分钟内流量的变化
  • 其他
    1. https://blog.csdn.net/zzhou1990/article/details/79098814
    2. https://ahus1.github.io/hystrix-examples/manual.html
  • 服务名字默认等于方法名

Turbine集群监控

  • Maven dependency
    • springboot 1.5.x
      1. org.springframework.cloud : spring-cloud-starter-turbine
    • springboot 2.0.x
      1. org.springframework.cloud : spring-cloud-starter-netflix-turbine
  • @EnableTurbine
  • 不再需要ServletRegistrationBean
  • turbine.app-config
    1. 需要监控的服务名,以逗号隔开,不支持*
  • turbine.cluster-name-expression="default"
    1. 可以自定义当前的集群名字cluster-a,dashboard使用http://host:port/turbine.stream?cluster=cluster-a来监控
    2. 当为default时,dashboard使用http://host:port/turbine.stream来监控
    3. 当app太多的时候,可以启动多个turbine,但是dashboard只需要一个
  • turbine.aggregator.clusterConfig=cluster-a
    1. 当turbine.cluster-name-expression="cluster-a"时,需要设置此值为cluster-a
    2. 默认default,所以当turbine.cluster-name-expression="default"的时候不需要设置
  • turbine.combine-host-port=true
    1. 默认true,如果为false,会只用host来区分服务,这样会导致turbine只监控一个服务器上turbine.app-config配置的第一个服务,因为它认为其他服务都是同一个
  • commandKey
    1. 不同服务实例,相同的commandKey会被认为是同一个服务,合并监控,默认等于方法名
    2. 相同服务的不同实例由于commandKey相同,会被合并监控
  • threadPoolKey
    1. 不同服务实例相同的threadPoolKey会被认为是相同thread pool,默认等于类名

与消息代理结合

  • [?]

Feign 服务客户端

Getting Start

  • Maven Dependency
    1. springboot 1.5.x
      1. org.springframework.cloud : spring-cloud-starter-feign
    2. springboot 2.0.x
      1. org.springframework.cloud : spring-cloud-starter-openfeign
  • @EnableFeignClients
    1. basePackages="com.citi.cv.rates" : 需要扫描的包路径,如果@FeignClient在其他依赖包里面,需要手动设置
  • 用@FeignClient("hello-service")修饰自定义的hello service 的interface作为client
    1. 可以使用配置来设置service-id: @FeignClient("${rates.ds.appname}")
  • 用@RequestMapping("/hello")修饰interface的方法
    1. interface的方法如果有参数,需要与hello-service的Controller的参数保持一致,包括@PathVariable,@RequestBody等注解
    2. 这里的注解的value值与Controller的默认方式不同,必须要显示的声明@PathVariable("name")

Feign 特性

  • Feign 继承特性
    1. Service提供方可以通过引入FeignClient interface包来让Controller继承
      1. 这样Controller便不再需要重复RequestMapping的操作
      2. 但是参数还是需要@PathVariable等配置,这里可以显示的声明@PathVariable("name"),也可以不显示声明
      3. Service端的FeignClient interface的@FeignClient的名字并没有实际意义
    2. 服务消费方也可以通过引入FeignClient interface包来直接调用Serivce提供的服务
    3. FeignClient应该由服务提供方提供,服务提供方在构建和维护FeignClient的时候,需要注意开闭原则和方法等的注释,以提供友好的服务
  • Feign整合了Ribbon
    1. 一切Ribbon的配置和直接使用Ribbon没有区别
  • Feign整合Hystrix
    1. Feign默认给所有FeignClient方法封装Hystrix
    2. 一切Hystrix的配置和直接使用Hystrix没有区别
    3. 禁用Hystrix
      1. 全部禁用 : feign.hystrix.enable=false
      2. 单个禁用 : FeignClient(name="hello-service",configuration=DisableHystrixConfiguration.class)

         @Configuration
         public class DisableHystrix {
             @Bean
             @Scope("prototype")
             public Feign.Builder feignBuilder() {
                 return Feign.Builder();
             }
         }
    4. 服务降级与Hystrix存在差异
      1. 需要实现FeignClient interface,然后使用FeignClient(name="hello-service",fallback=HelloFallBack.class)
      2. 也可以使Feign禁用Hystrix,转而在本地的服务上面自己使用Hystrix进行服务降级
    5. 使用请求合并
      1. 在本地的服务上面自己使用Hystrix进行请求合并
  • 请求压缩 : 使用GZIP进行请求和响应的压缩
    1. feign.compression.request.enable=true
    2. feign.compression.response.enable=true
    3. feign.compression.request.mime-types
      1. 指定压缩的请求类型
      2. 默认值:text/xml,application/xml,application/json
    4. feign.compression.request.min-request-size
      1. 指定开启压缩功能的请求数据大小的下限
      2. 默认2048
  • 日志配置
    1. 默认不会输出任何日志,因为Feign默认的日志级别是feign.Logger.Level.NONE
    2. 设置全局日志级别

       @Bean
       Logger.Level feignLoggerLevel() {
           return Logger.Level.FULL
       }
    3. 单独设置日志级别 : FeignClient(name="hello-service",configuration=FullLogConfiguration.class)

       @Configuration
       public class FullLogConfiguration{
           @Bean
           Logger.Level feignLoggerLevel() {
               return Logger.Level.FULL
           }
       }
    4. 日志级别
      1. NONE : 不记录任何信息
      2. BASIC : 记录请求方法,URL,响应状态码,执行时间
      3. HEADERS : BASIC, 请求和响应头信息
      4. FULL : 所有请求响应的明细: 头信息,请求体,元数据等
    5. feign的日志是DEBUG级别的,通过配置开启DEBUG
      1. logging.level.com.service.hello.HelloService=DEBUG

Gateway

Gatting Start

  • 官网 : https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.0.RELEASE/single/spring-cloud-gateway.html#gateway-starter
  • 依赖
    1. org.springframework.boot:spring-boot-starter-webflux
    2. org.springframework.cloud:spring-cloud-starter-gateway
    3. 不能使用spring boot web,会冲突

Predicate

  • Before / After / Between : 时间断言
  • Cookie / Header : 匹配name和一定规则的value
  • Host / Method/Path
  • Query : param name和value
  • RemoteAddr : 限定ip

Filters

  • AddRequestHeader / AddResponseHeader / SecureHeaders :添加头信息
  • SetResponseHeader / RewriteResponseHeader:修改头信息
  • RemoveRequestHeader / RemoveResponseHeader /RemoveNonProxyHeaders / PreserveHostHeader : 删除头信息
  • Hystrix / FallbackHeaders
    1. 需要引入spring-cloud-starter-netflix-hystrix依赖,开启断路器功能,实现服务降级
    2. fallbackUri : 支持本地app的path uri。是否直接支持远程uri?
    3. FallbackHeaders用于添加异常详细信息
  • RequestRateLimiter : TO-DO
  • SaveSession : TO-DO
  • RewritePath / SetPath / PrefixPath / StripPrefix / RedirectTo : 修改路径
  • SetStatus
  • RequestSize : 限制request大小
  • Retry :目前不支持带body的retry

修改RequestBody

@Bean
fun routes(builder: RouteLocatorBuilder): RouteLocator {
    return builder.routes().route(routeId) { predicateSpec ->
        predicateSpec.path(predicatePath).filters { gatewayFilterSpec ->
            gatewayFilterSpec.modifyRequestBody(String::class.java, String::class.java, MediaType.APPLICATION_JSON_VALUE) { _, body ->
                Mono.just( body.toUpCase() )
            }
        }.uri(routeUri)
    }.build()
}

Global Filter

  • To-Do

Actuator API

  • TO-DO

其他

  • Reactor Netty Access Logs?
  • CORS Configuration?

Zuul 网关服务

路由详解

  • Maven dependency
    1. org.springframework.cloud : spring-cloud-starter-netflix-zuul
  • @EnableZuulProxy
  • 单实例配置
    1. zuul.routes.route-name.path=/user-service/**
    2. zuul.routes.route-name.url=http://host:port/
  • 多实例配置
    1. zuul.routes.route-name.path=/user-service/**
    2. zuul.routes.route-name.serviceId=user-service
    3. ribbon.eureka.enabled=false
      1. Spring cloud Zuul默认与eureka集成实现serviceId,这里没有eureka,所以关闭此集成功能
    4. user-service.ribbon.listOfServers=http://host1:port1/,http://host2:port2/
  • 集成eureka
    1. 路由配置
      1. zuul.routes.route-name.path=/user-service/???
      2. zuul.routes.route-name.serviceId=user-service
    2. 简易写法
      1. zuul.routes.user-service=/user-service/???
    3. 默认规则
      1. zuul.routes.route-name.path=/user-service/???这种写法,默认会将user-service作为真实url的前缀
      2. 所以使用zuul的spring could application建议不要使用content path,从而省略不必要的前缀,否则前缀过多
      3. spring cloud zuul默认将所有eureka的服务配置了路由,前缀默认等于eureka的服务名字
      4. zuul.ignored-services : 忽略某些service的路由,*代表全部忽略,然后用简易写法开启某些
  • 自定义路由规则
    1. PatternServiceRouteMapper : 通过正则表达式自定义服务名与路由的规则
    2. 只需要用@Bean new一个PatternServiceRouteMapper 的实例
  • 通配符
    1. 符号? 匹配任意单个字符
    2. 符号* 匹配任意多个字符
    3. 符号** 支持多级目录结构
  • 路由规则冲突
    1. 先配置路由规则的优先
    2. properties配置文件是不保证顺序的,但是yaml是保证的
  • 忽略表达式
    1. zuul.ignored-patterns=/xx/hello/xx
    2. 忽略所以路由的hello,注意是所有,建议配置忽略表达式的时候带上service name,以免将其他服务的错误忽略了
  • forward 本地跳转
    1. 不知道有什么作用?
  • Cookie与头信息
    1. Zuul默认忽略请求中的敏感信息
    2. 通过zuul.sensitiveHeaders配置,默认包括Cookie,Set-Cookie,Authorization三个属性
    3. 这样做的原因
      1. 微服务价格,无状态的RESTful API占大多数
      2. 网关本事就可以处理安全方面的过滤
      3. 有状态的可以放在网关之外
    4. 关闭忽略的方法
      1. zuul.sensitiveHeaders=Cookie : 用覆盖的方式关闭除了Cookie之外的其他头信息的忽略,设为空关闭全部忽略,不推荐
      2. zuul.routes.[route].customSensitiveHeaders=true : 对指定路由开启自定义敏感头
      3. zuul.routes.[route].sensitiveHeaders= :设置指定路由的敏感头
    5. 重定向问题
      1. 请求后的重定向跳转,会直接跳转到路由后的具体实例的host:port
      2. zuul.addHostHeader=true : 将当前网关服务的host信息加到转发的host斗信息里,解决重定向问题
  • Ribbon和Hystrix
    1. 当用serviceId进行配置时,会启用Ribbon和Hystrix,配置参数与单独使用Ribbon和Hystrix时一样
    2. zuul实现了服务降级,返回特定的JSON信息
    3. 如果Ribbon timeout小于Hystrix timeout,zuul默认配置了重试功能
      1. zuul.retryalbe=true
      2. zuul.routes.[route].retryalbe=true
      3. 添加Maven dependency
        1. org.springframework.retry : spring-retry
  • 动态路由
    1. 利用spring cloud config 动态刷新机制实现动态路由功能
    2. @RefreshScope修饰zuul配置类

       @RefreshScope
       @ConfigurationProperties("zuul")
       public ZuulProperties zuulProperties() {
           return new ZuulProperties();
       }

过滤器详解

  • ZuulFilter
    1. 继承ZuulFilter并且注册为spring bean的filter默认会被启用
  • pre : 请求被路由之前调用
    1. [-3] ServletDetectionFilter :
      1. 检查请求是否通过Spring Dispatcher。通过isDispatcherServletRequest设置
      2. 通过RequestUtils.isDispatcherServletRequest()获取
    2. [-2] Servlet30WrapperFilter : 包装HttpServletRequest
    3. [-1] FormBodyWrapperFilter : 解析表单数据,并为请求重新编码
    4. [1] DebugFilter :
      1. 当这个过滤器被开启的时候,会设置RequestContext.setDebugRouting() ,RequestContext.setDebugRequest()为true
      2. 所以filter都能拿到这两个值,可以判断该值做一些debug操作,比如debug日志,更多返回信息等
      3. 开启方式
        1. zuul.debug.request=true
        2. 在请求上加参数debug=true
    5. [5] PreDecorationFilter : 对请求进行路由前的预处理
  • routing : 路由请求时被调用
    1. [10] RibbonRoutingFilter : 对serviceId配置生效,通过Ribbon和Hystrix进行路由
    2. [100] SimpleHostRoutingFilter : 对url配置生效,不使用Ribbon和Hystrix,只用http client进行路由
    3. [500] SendForwardFilter : 处理forward本地跳转
  • post : routing后被调用, pre,routing之后的error之后也会再调用post
    1. [0] SendErrorFilter
      1. 在有error.status_code参数时生效
      2. 处理有错误的请求
    2. [1000] SendResponseFilter : 处理响应信息
  • error: 异常处理,在pre,routing, post 抛出异常之后被调用
    1. pre,routing阶段的filter的异常被error处理之后,会再调用post。post阶段的filter的异常被error处理之后不会再调用post
    2. 没有默认的error阶段的过滤器
    3. 自定义的Filter[pre,routing]抛出异常的时候,如果不设置error.status_code参数,不会被post的SendErrorFilter处理
    4. 可以通过RequestContext.set设置error.status_code,error.message,error.exception。但是这种方式需要在所有filter处理
    5. 自定义error filter设置error.status_code,error.message,error.exception,这样就能被SendErrorFilter处理了
    6. 但是post阶段的filter的异常被error filter处理之后不会再调用post,所以还是不能被SendErrorFilter处理
    7. 自定义另一个error filter,继承SendErrorFilter,优先级必须大于第一个自定义的error filter,并且只处理post之后的异常
      1. 只处理post之后的异常可以通过自定义的post阶段的filter设置自定义属性值请求上下文RequestContext里
      2. 也可以通过继承FilterProcessor方式
    8. 自定义error处理
      1. 通过自定义post过滤器,并且禁用SendErrorFilter
      2. 通过修改spring boot的/error端点
  • 禁用过滤器
    1. zuul.SendErrorFilter.post.disable=true
    2. 由于使用的是SimpleClassName,所以需要保证过滤器名字不能重复

Config 配置中心

Getting Start

  • 搭建配置中心
    1. Maven dependency
      1. org.springframework.cloud : spring-cloud-config-server
    2. @EnableConfigServer
    3. spring.cloud.config.server.git.uri=ssh://[email protected]:7999/cvp/rates-cloud.git
    4. spring.cloud.config.server.git.searchPaths=rates-config/src/main/resources
    5. spring.cloud.config.server.git.username=username
    6. spring.cloud.config.server.git.password=password
  • 配置规则
    1. 通过host:port/配置规则 访问配置中心的配置文件:applicationname-profile.properties/yml
      1. /applicationname/profile/lable
      2. /lable/applicationname-profile.properties/yml
      3. applicationname-profile.properties/yml
    2. 说明
      1. lable代表git branch
      2. lable不写默认master branch
      3. 只支持properties/yml 文件
      4. 支持将properties/yml文件互相转换
      5. 以下配置文件会被映射
        1. applicationname-profile.yml
        2. applicationname-profile.properties
        3. applicationname.yml 里的[--- profiles]
        4. applicationname.properties
        5. application.yml 里的[--- profiles]
        6. application.properties
        7. application-profile.yml 里的[--- profiles2]
        8. application-profile.properties
      6. 如果用/lable/applicationname-profile.properties/yml,会合并属性,后加载的覆盖先加载的
      7. 支持通配符,比如*,但是如果以通配符开始的,要用双引号引起来
    3. 使用技巧
      1. application.yml: 存放所有项目的公用属性
      2. lable: ${spring.profiles.active}:
        1. 通过不同的branch分不同的环境
        2. 但是每个环境也可以保持所有的环境profiles,这样可以在不同环境的branch进行merge
        3. 如果想保持每个环境的属性的整洁性,则无法进行merge,需要手动提交所有的branch,这样难免会有遗漏,可以注释所有不同环境的属性,方便对比branch,避免遗漏
        4. local可以使用本地文件系统
      3. profile: module-name: application-module-name.yml:存放同一个module下的不同服务的mudule公用属性
      4. app name: appname.yml : 存放一个project或者instance的独有属性
      5. 一个project如果使用不同的appname部署了多个instance,如何将它们的公用属性集合存放
        1. 使用第二个profile: project,以逗号隔开。application-project.yml
        2. 多个profile,排在后面的后加载,也就是后面的拥有更高的优先等级,会覆盖前面的,applicationname.yml优先级更高
        3. application-profile.yml里写第二个profile,第一个profile要排在第二个profile前面,否则加载不到
  • 搭建client
    1. Maven dependency
      1. org.springframework.cloud : spring-cloud-starter-config
    2. bootstrap.yml
      1. spring.application.name=test
      2. spring.cloud.config.profile=local
      3. spring.cloud.config.label=master
      4. spring.cloud.config.uri=http://localhost:9601

配置中心详解

  • 使用Git作为默认仓库的好处
    1. Git的版本控制功能,可以满足一些根据不同版本启动不同配置的服务的特殊要求
    2. Git的Hook功能可以有效的监控配置内容的修改
  • Git 配置多个仓库
    1. uri占位符
      1. {application},{profile},{label}除了用于配置规则之外,还用于git uri和searchPaths的占位符
      2. ssh://[email protected]:7999/cvp/{application}.git可以实现根据application name动态配置多个git仓库
    2. 配置repos
      1. 以spring.cloud.config.server.git.uri/searchPaths方式配置一个主仓库,主仓库会在启动的时候clone到本地,其他的只在请求的时候clone
      2. spring.cloud.config.server.git.repos.applicationname.uri/searchPaths/pattern
      3. pattern=applicationname/profile/label,支持通配符,比如applicationname/prod*,多个pattern以逗号隔开
    3. 利用配置repos和searchPaths占位符{application}的配合使用,可以实现一个git仓库拥有多个子项目的情况下,获取各自git仓库的配置文件
  • Git 仓库访问权限
    1. http方式需要使用username/password
    2. ssh方式需要生成ssh key,配置在git仓库
      1. spring.cloud.config.server.git.ignoreLocalSshSettings=true: 关闭本地ssh默认设置,使用配置文件设置
      2. spring.cloud.config.server.git.privateKey=| + key,注意格式
      3. spring.cloud.config.server.git.passphrase : 私钥密码
      4. hostKeyAlgorithm和hostKey不设置
      5. 多个仓库的所有repos都要设置ignoreLocalSshSettings和privateKey,这时候可以使用配置引用
      6. 生成ssh key,可以利用eclipse的ssh功能privateKey是id_rsa,publicKey使用Load Existing Key,选择id_rsa文件
  • Git使用本地文件
    1. spring.cloud.config.server.git.uri=file://path/config-repo
    2. windows使用file:///
    3. 本地仓库多用于开发调试
  • Git/SVN 本地仓库
    1. 默认配置在/tmp/config-repo-随机数里
    2. spring.cloud.config.server.git/svn.basedir : 指定默认路径
  • 不使用Git,使用项目本地配置
    1. spring.profiles.active=native
  • 属性覆盖
    1. spring.cloud.config.server.overrides.xxx
  • 安全保护
    1. 利用spring security实现安全保护
  • 加密解密
    1. [?]

客户端详解

  • 通过指定的文件名加载配置文件
    1. spring.cloud.config.name=core,default
  • 整合Eureka服务化的配置中心
    1. spring.cloud.config.discovery.enabled=true : 开启通过服务访问配置中心
    2. spring.cloud.config.discovery.serviceId=rates-config
    3. bootstrap.yml
      1. 可以使用---spring: profiles:的方式设置不同环境的eureka url
      2. 也可以使用bootstrap-{profile}.yml的方式设置不同环境的eureka url
      3. spring.cloud.config.profile可以等于${spring.profiles.active},从而减少一个shell配置
  • 客户端启动健壮性
    1. 如何由于config service网络问题造成拿不到配置文件,有可能会导致客户端启动失败的情况,这对release是一种灾难
    2. 方案1:失败快速响应 + 重试
      1. spring.cloud.config.failFast=true : 一旦config service宕机,会快速失败,停止启动应用
      2. 如果只是网络延迟等问题造成的,那么可以加上重试功能,只需要加上Maven dependency
        1. org.springframework.retry : spring-retry
        2. org.springframework.boot : org-boot-starter-aop
      3. 默认重试6次,配置参数:
        1. spring.cloud.config.retry.multiplier: 第一次重试的间隔
        2. spring.cloud.config.retry.initial-interval: 下一次重试的间隔的倍数,默认1.1
        3. spring.cloud.config.retry.max-interval: 最大的重试间隔
        4. spring.cloud.config.retry.max-attempts: 最大重试次数,默认6次
    3. 方案2:不使用失败快速响应,当 config service出问题的时候,使用本地配置文件进行启动
      1. spring.cloud.config.failFast=false
      2. 本地需要有一份配置,做好的做法是将本项目Git仓库作为配置中心的Git仓库
      3. 启动后,当config service恢复正常,动态刷新机制还能使用
      4. 方案2完全隔离了config service对客户端的影响,但是相比于方案1,无法实现不同项目共用相同配置文件的目的
  • 动态刷新
    1. 对config server的每次请求,config server会去git pull最新版本
    2. @RefreshScope :
      1. 修饰@Value相应的类,加在启动类没用
      2. 配合@ConfigurationProperties使用
    3. 通过actuator的refresh功能刷新客户端
    4. 可以与git Web Hook配合实现提交自动刷新
    5. 这种方式需要refresh所以相应服务,很麻烦,可以用Spring Cloud Bus解决

Bus 消息总线

  • Kafka整合spring cloud bus
    1. Maven dependency
      1. org.springframework.cloud : spring-cloud-starter-bus-kafka
    2. kafka broker host配置
      1. spring.cloud.stream.kafka.binder.brokers=localhost
    3. 启动应用,POST访问/actuator/bus-refresh
      1. bus自动创建名为springCloudBus的Topic
      2. refresh会刷新所以接入此Kafka总线的应用的配置
    4. refresh提供参数来支持刷新指定目的地的应用,并且支持通配符
      1. /actuator/bus-refresh?destination=serviceId:port
    5. spring bus与kafka的交互实际上是由spring cloud stream完成的,bus只是stream的一种应用
  • 配置bus
    1. spring.cloud.bus.trace.enable=true
      1. bus整合了kafka的消费者控制台功能:kafka-console-consumer
      2. 先设置为true开启,然后访问 /trace
      3. 除了trace默认关闭,Env,Refresh,Ack默认开启,Env用于刷新环境变量
    2. spring.cloud.bus.enable
      1. 设置设否启动消息总线,默认true
    3. spring.cloud.bus.destination
      1. 定义Topic名字,默认springCloudBus

Stream 消息驱动

Maven dependency

  • org.springframework.cloud : spring-cloud-stream-binder-kafka

核心概念

  • 发布-订阅模式
    1. 消息驱动的模式,使用了发布-订阅设计模式
  • 绑定器 Binder
    1. 绑定器负责实现应用于消息中间件的交互
    2. 通过绑定器的概念实现应用于消息中间件的隔离
    3. 切换不同的消息中间件之需要切换绑定器的实现
  • 消费组
    1. 实现一个消息只被同组的一个消费者独占消费
    2. 解决分布式架构相同服务不需要重复处理事件的问题
    3. spring.cloud.stream.bindings.[channelName].group : 指定消费者所属的组
  • 消息分区
    1. 拥有相同特征的消息会被同一个消费者消费
    2. spring cloud 不关心消息中间件是否实现了消息分区,都会在其上层实现分区

使用详解

  • 绑定消息通道
    1. @EnableBinding
      1. 绑定指定消息通道,发送和接收消息前都要先绑定
      2. value是消息通道接口的class,会自动实现接口,并创建相应的spring bean,可以直接注入
    2. @Input("ChannelName")
      1. value是消息通道的名称,默认对应Kafka的Topic,可以设置对应的Topic
      2. 修饰消息接收通道接口的方法,返回SubscribableChannel
    3. @Output("ChannelName")
      1. value是消息通道的名称,默认对应Kafka的Topic,可以设置对应的Topic
      2. 修饰消息发送通道接口的方法,返回MessageChannel
    4. spring.cloud.stream.bindings.[channelName].destination=xxx
      1. 指定消息通道的目的地,比如Kafka的Topic
      2. 如果是个@Input通道,可以指定多个目的地,用逗号隔开
      3. 如果没有设置,默认等于消息通道的Value
      4. 在一个应用里避免使用同一个Topic的@Input通道和@Output,因为你没理由监听自己发送的消息
      5. 通过这个属性,可以实现修改配置文件来启动不同的实例监听不同的Topic,Nice!!!
  • 发送消息
    1. 注入消息通道MidCurveChannel
      1. MidCurveChannel.getChannel().send(MessageBuilder.withPayload("message").build());
    2. 注入MessageChannel
      1. MessageChannel.send(MessageBuilder.withPayload("message").build());
      2. 如果有多个MessageChannel的bean,可以用@Qualifier("ChannelName"),或者属性名字定义为ChannelName,这时候要注意ChannelName的大小写
    3. 使用Spring Integration的原生支持
      1. @InboundChannelAdapter(value=RatesOutputChannel.TEST,poller=@Poller(fixedDelay="3000"))
      2. 支持返回自定义对象作为消息发送的类型
      3. 不支持传入参数
    4. 大部分情况下, 消息以JSON等形式传输
  • 接收消息
    1. @StreamListener("ChannelName")
      1. StreamListener支持对JSON的自定转换
    2. 如果发送的是Message[T] message,那么可以用Message[T]进行接收,也可以用T进行接收
  • 消息反馈
    1. @SendTo("ChannelName")
    2. 修饰在接收消息的@StreamListener的方法上
    3. 将方法的返回值发送给ChannelName通道,返回值可以不是Message,可以是任何类型
    4. @StreamListener的方法有返回值必须用@SendTo修饰
    5. 消息反馈要发送到与监听通道不同的Topic里,不要反馈到原来的Topic,所以如果需要反馈消息,需要创建一个对应的反馈Topic

响应式编程

  • [?]

消费组与消息分区

  • 消费组
    1. spring.cloud.stream.bindings.[channelName].group : 指定消费者所属的组
    2. 指定消费组后,消费者会根据组名来获取当前的offset,从当前offset开始获取kafka中的消息,如果不指定,offset=HW
    3. 同一个应用只能为一个Topic启动一个消费者,也就是说一个消费组里的两个消费者肯定是两个进程
  • 生产者创建分区
    1. spring.cloud.stream.kafka.binder.brokers
      1. kafka集群,只写其中一个,也能使用集群
      2. 如果写了多个,但是不属于一个集群,那么后面的回覆盖前面的
    2. spring.cloud.stream.kafka.binder.autoAddPartitions=true
      1. 开启自动添加分区
    3. spring.cloud.stream.kafka.binder.replicationFactor=2
      1. 设置自动每个分区的副本数量,默认1即只有Leader副本
      2. spring cloud会自动均分副本broker,尽量保证leader与副本不在同一个broker
    4. spring.cloud.stream.bindings.[channelName].producer.partitionCount=2
      1. 为Topic开启多个分区
      2. pring cloud会自动均分partition,尽量保证partition不在同一个broker
    5. spring.cloud.stream.bindings.[channelName].producer.partitionKeyExpression=payload.name
      1. 为分区设置key,相同key的消息会分到一个分区
      2. payload表示消息本身
      3. payload.name表示以消息对象的name属性作为key
      4. 当相同key的消息必须在同一个消费者处理的时候使用此特性
  • Kafka开启消费者分区
    1. spring.cloud.stream.bindings.[channelName].consumer.partitioned=true
    2. spring.cloud.stream.instanceCount :
      1. 准备启动多少个实例数量,默认情况下Kafka分区会平均分配给这些实例
    3. spring.cloud.stream.bindings.[channelName].consumer.concurrency=1
      1. 为当前实例的输入通道启动多少个消费者进行并发处理, 当前实例分配到的分区会平均分配给这些消费者
    4. spring.cloud.stream.instanceIndex : 实例索引,使用Kafka需要设置,从0开始

绑定器

  • 自动配置选择classpath绑定器,在pom中引入绑定器
  • 设置默认绑定器
    1. spring.cloud.stream.defaultBinder=rabbit
  • 为消息通道单独设置绑定器
    1. spring.cloud.stream.bindings.[channelName].binder=kafka1
    2. spring.cloud.stream.binders.kafka1.type=kafka
    3. spring.cloud.stream.binders.kafka1.environment : 设置绑定器属性

配置详解

  • Kafka绑定器配置 : spring.cloud.stream.kafka.binder
    1. brokers : Kafka的host:port,以逗号隔开,也可以不设置port
    2. defaultBrokerPort: 默认的Kafka的port
    3. zkNodes : Zookeeper的host:port,以逗号隔开,也可以不设置port,2.0之后版本不再需要
    4. defaultBrokerPort: 默认的Zookeeper的port
    5. header : 设置自定义头信息
    6. socketBufferSize: kafka的socket,默认2097152,2m
    7. autoCreateTopics
      1. 自定创建新主题,默认true,false如果主题不存在,启动会失败
    8. autoAddPartitions
      1. 自动创建新的分区,默认false,当已有topic的分区数量少于需求数量时,会启动失败
      2. 虽然默认false,但是在第一次创建Topic的时候,会根据生产者的partitionCount创建分区
    9. minPartitionCount
      1. 只有当autoCreateTopics,autoAddPartitions 为true时生效,表示最少需要的分区数量
      2. 如果这个值小于partitionCount或者instanceCount * concurrency,则会被他们覆盖,也就是会根据实际需要设置
    10. replicationFactor : 自动创建主题的副本数量
  • Kafka消费者配置 : spring.cloud.stream.kafka.bindings.[channelName].consumer
    1. maxAttempts : 重试次数,默认3
    2. backOffInitialInterval : 重试起始间隔时间,默认1000
    3. backOffMaxInterval : 重试最大间隔时间,默认10000
    4. backOffMultiplier :: 重试间隔时间乘数,默认2
    5. autoCommitOffset
      1. 是否自动提交offset,默认true,false会加入ask头信息实现延迟确认
    6. autoCommitOnError
      1. autoCommitOffset为true才生效
      2. 发生错误时是否自动提交offset,默认等于enableDlq,enableDlq默认false
    7. enableDlq
      1. 是否启动Dlq行为
      2. Dlq会将引起错误的消息发送到topic为error.destination.group的topic,destination是原topic的名字
    8. recoveryInterval
      1. 尝试恢复连接的时间间隔,默认5000
    9. resetOffsets
      1. 是否用startOffset的值来重置消费者的offset,默认false
    10. startOffset
      1. 见上一条
  • kafka生产者配置 :spring.cloud.stream.kafka.bindings.[channelName].producer
    1. partitionCount=2
      1. 为Topic开启多个分区
    2. partitionKeyExpression=payload.name
      1. 为分区设置key,相同key的消息会分到一个分区
      2. payload表示消息本身
      3. payload.name表示以消息对象的name属性作为key
      4. 当相同key的消息必须在同一个消费者处理的时候使用此特性
    3. sync
      1. 是否同步发送,默认false,即允许批量发送
    4. batchTimeout
      1. 批量发送等待时间,默认0,0不代表不批量发送,而是说只批量发送前一次发送时产生的消息
      2. 如果消息生产快速,可以用这个值来以延迟的代价增加系统吞吐量
    5. bufferSize
      1. Kafka批量发送的缓存数据上限,默认16384,16kb

Sleuth 服务跟踪

Getting Start

  • Maven dependency
    1. org.springframework.cloud : spring-cloud-starter-sleuth
  • 添加任意日志,Sleuth会为以下通讯方式添加跟踪信息
    1. 浏览器发起的请求
    2. 通过RestTemplate发起的请求
    3. 通过Zuul路由的请求
    4. 通过Stream绑定的消息中间件传递的请求
  • 跟踪信息
    1. 服务的ServiceId
    2. TraceId : 表示一次请求链路的Id,等于第一个工作单元的SpanId
    3. SpanId : 表示一个工作单元的Id,比如一次Http请求代表一个工作单元
      1. 通过记录第一次SpanId和最后一次SpanId可以计算工作单元的时间消耗
    4. 是否输出到Zipkin等服务,默认false
    5. Http头信息里包含这些跟踪信息
      1. X-B3-TraceId
      2. X-B3-SpanId
      3. X-B3-ParentSpanId : 上一个工作单元的SpanId
      4. X-B3-Sampled : 是否被抽样输出
      5. X-Span-Name : 工作单元名称
    6. spring mvc请求分发日志设置为DEBUG,可以获取更多跟踪信息
      1. logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG
  • 抽样收集
    1. 通过实现Sampler接口实现抽样策略
    2. 默认使用PercentageBasedSampler实现的抽样策略,百分比策略
      1. spring.sleuth.sampler.percentage=0.1
    3. 在不对系统造成性能印象的前提下,在一定时间窗内充分利用存储空间来实现抽样

整合ELK

  • [?]

整合Zipkin

转载于:https://www.cnblogs.com/judesheng/p/10622189.html

你可能感兴趣的:(大数据,git,运维)