Skip to content

easycodebox/spring-cloud-demo

Repository files navigation

Spring Cloud

Eureka - 注册中心

  • 显示已注册的服务:http://localhost:7000/
  • 监控信息:http://localhost:7000/actuator
  • Eureka REST operations - 官网 - 请求Eureka Server URL地址时需去掉/v2/
    • 返回所有Apps的信息 - http://localhost:7000/eureka/apps
    • 返回指定应用的所有实例信息 - http://localhost:7000/eureka/apps/{svcId}
spring:
  application:
    name: eureka-server
server:
  port: 7000
eureka:
  client:
    fetch-registry: false
    register-with-eureka: false
  • 例1

    spring:
      application:
        name: eureka-server
    management:
      endpoints:
        web:
          exposure:
            # 测试使用 - 开放所有端口
            include: "*"
    eureka:
      client:
        serviceUrl:
          defaultZone: http://peer1:7000/eureka/,http://peer2:7001/eureka/,http://peer3:7002/eureka/
    
    ---
    spring:
      profiles: peer1
    server:
      port: 7000
    eureka:
      instance:
        hostname: peer1
    
    ---
    spring:
      profiles: peer2
    server:
      port: 7001
    eureka:
      instance:
        hostname: peer2
    
    ---
    spring:
      profiles: peer3
    server:
      port: 7002
    eureka:
      instance:
        hostname: peer3
    
    127.0.0.1 peer1
    127.0.0.1 peer2
    127.0.0.1 peer3
    
  • 例2

    spring:
      application:
        name: eureka-server
    server:
      port: 7000
    management:
      endpoints:
        web:
          exposure:
            # 测试使用 - 开放所有端口
            include: "*"
    eureka:
      client:
        serviceUrl:
          defaultZone: http://192.168.100.101:7000/eureka/,http://192.168.100.102:7000/eureka/,http://192.168.100.103:7000/eureka/
      instance:
        # 使用IP
        prefer-ip-address: true

注:Peer Awareness模式不一定非得3个实例,2个或大于3个都可以。

Hystrix - 熔断机制

  • Feign启用Hystrix时,HystrixCommandKey默认通过以下规则生成:feign.Feign.configKey(java.lang.Class, java.lang.reflect.Method)

    hystrix:
      command:
        # 通过上述Feign.configKey()方法生成
        FooHttpApi#list(String,List):
          execution.isolation.thread.timeoutInMilliseconds: 10000
    • HystrixPropertiesStrategy - 属性获取规则
    • HystrixCommandProperties - HystrixCommand配置及默认值
    • HystrixThreadPoolProperties - HystrixThreadPool配置及默认值
    • 默认HystrixThreadPoolKey值为HystrixCommandGroupKeyHystrixCommandGroupKeyFeign Client Name
  • hystrix-javanica -【java注解风格 - 重要】

  • How it Works

  • How To Use

  • 监控详解及配置调优(timeouts/threads/semaphores)

  • Configuration

  • Metrics and Monitoring

  • Plugins

  • Propagating the Security Context or Using Spring Scopes

  • SEMAPHORETHREAD区别

    • THREAD超时后可立即放弃线程执行,SEMAPHORE超时后也需要等待Command执行完毕后才能执行后续逻辑(即使配了fallback), 因为SEMAPHORE使用caller线程执行的,并没有另起线程。所以SEMAPHORE通常使用于执行时间不长的场景中。 if a dependency is isolated with a semaphore and then becomes latent, the parent threads will remain blocked until the underlying network calls timeout.
  • Making the Netflix API More Resilient - Hystrix概念及监控介绍

  • Fault Tolerance in a High Volume, Distributed System - 深入讲解Fault Tolerance、原理图、最佳实践

  • Performance and Fault Tolerance for the Netflix API -【非常重要】 - Fault Tolerance详解、性能调优、提供各种指标依据(包含SEMAPHORE和THREAD压测对比数据)、指标详解

    • Tryable semaphores for “trusted” clients and fallbacks. Semaphore isolation on the other hand is used for dependencies which are very high-volume in-memory lookups that never result in a synchronous network request.
    • Separate threads for “untrusted” clients
    • Thread Queue
      • Requests in queue block user threads thus must be considered part of the resources being allocated to a dependency.
      • Setting a queue to 100 is equivalent to saying 100 incoming requests can block while waiting for this dependency. There is typically not a good reason for having a queue size higher than 5-10.
      • Bursting should be handled through batching and throughput should be accommodated by a large enough thread pool. It is better to increase the thread-pool size rather than the queue as commands executing in the thread-pool receive forward progress whereas items in the queue do not.

Hystrix Dashboard

访问地址:http://{localhost}:{port}/hystrix

Turbine

Turbine is a tool for aggregating streams of Server-Sent Event (SSE) JSON data into a single stream. The targeted use case is metrics streams from instances in an SOA being aggregated for dashboards.

  • spring-cloud-netflix-hystrix - 官网

    • 显示当前可用的clusters - http://localhost:7300/clusters
  • Getting Started (1.x)

  • Configuration (1.x)

  • 配置1 - 手动配置cluster-config、app-config(一对一)

    turbine:
      aggregator:
        cluster-config: EASYCODE-DEMO-ORG
      app-config: easycode-demo-org

    TurbineClustersProvider默认使用ConfigurationBasedTurbineClustersProvider,此类只会去获取cluster-config 配置项。如果你没有配置cluster-config配置项,则获取不到cluster,也就没有监控的应用可显示。

    InstanceDiscovery默认使用EurekaInstanceDiscovery,此类会根据app-config配置项,去eureka注册中心获取应用对应的所有实例。 实例取出来后,默认使用com.netflix.appinfo.InstanceInfoappName属性作为cluster值。当监控页面需要查看特定cluster 信息时,首先会去TurbineClustersProvider查看有没有此值,有则再过滤出InstanceDiscovery实例中cluster值相等的应用实例。

  • 配置2 - 自动配置

    /**
     * 通过{@link EurekaClient}自动获取当前所有的应用,并把应用名(大写)作为Cluster值
     *
     * @param eurekaClient EurekaClient
     * @return {@link TurbineClustersProvider}
     */
    @Bean
    public TurbineClustersProvider clustersProvider(EurekaClient eurekaClient) {
        return new EurekaBasedTurbineClustersProvider(eurekaClient);
    }
    
    @Bean
    public InstanceDiscovery instanceDiscovery(TurbineProperties turbineProperties, EurekaClient eurekaClient,
        TurbineClustersProvider clustersProvider) {
        return new EurekaInstanceDiscovery(turbineProperties, eurekaClient) {
            /**
             * 重写获取应用名的来源,例如从Eureka获取
             */
            @Override
            protected List<String> getApplications() {
                return clustersProvider.getClusterNames();
            }
        };
    }

    注:上述只是例子,需要自行调整(例:通过Eureka返回的数据信息,来判断此应用能否适用于监控)。 通过上述的例子,就不需要手动配置cluster-config/app-config

  • 配置3 - 混合配置cluster-config、app-config

    turbine:
      aggregator:
        cluster-config: SYSTEM,USER
      app-config: easycode-demo-org,easycode-demo-shop,easycode-demo-user
      clusterNameExpression: metadata['cluster']

    即从Eureka服务中或取easycode-demo-org/easycode-demo-shop/easycode-demo-user三个应用的信息,获取 metadata.cluster(配置于eureka.instance.metadata-map)属性值,与cluster-config配置中的值进行匹配,匹配成功则表明该应用属于对应的cluster。

    注:参考官方文档

Archaius - 动态更新配置

Ribbon - 客户端负载均衡,适用于HTTP、TCP

  • Native
  • Spring Cloud
    • 官网 - 【重要】

    • IClientConfig默认实现为DefaultClientConfigImpl(参考RibbonClientConfiguration类),此类里面提供了Ribbon Client默认配置

    • Using the Ribbon API Directly

      public class MyClass {
          @Autowired
          private LoadBalancerClient loadBalancer;
      
          public void doStuff() {
              ServiceInstance instance = loadBalancer.choose("stores");
              URI storesUri = URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
              // ... do something with the URI
          }
      }
      @Configuration
      public class MyConfiguration {
      
          @Bean
          @LoadBalanced
          RestTemplate restTemplate() {
              return new RestTemplate();
          }
      }
      
      public class MyClass {
          @Autowired
          private RestTemplate restTemplate;
      
          public String doOtherStuff() {
              return restTemplate.getForObject("http://stores/stores", String.class);
          }
      }
    • Ribbon是否会重试依赖于AbstractLoadBalancerAwareClient.getRequestSpecificRetryHandler()实现及是否使用了Spring-retry。 OpenFeign配置的RetryerFeignClientsConfiguration)和Ribbon的重试没有任何关联,两个是完全独立的。

    • Retrying Failed Requests - 【重要】

注:当获取到的服务使用了域名时,当http连接超时设置为1s时,不是指整个操作连接超时为1s,而是域名解析出的所有IP 连接超时都是1s。所以会出现一种现象:当一个域名解析到4个IP时,且请求的connectTimeout、readTimeout都配置为1s, 当这4个IP都不通时,整个请求耗时会是4s。因为尝试了4个IP的连接,且4个IP都连接超时。

OpenFeign - 简化java http client编程

注:非常重要的组件,此组件的关注程度高于Ribbon和Hystrix。

Zuul - Router and Filter

Zuul是由Netflix提供的基于JVM路由及服务端负载均衡的组件。Zuul是一个网关服务,提供了dynamic routing、monitoring、 resiliency、security等功能。

总结

  • Spring Cloud Zuul启用从注册中心获取服务时,获取到的服务在创建ZuulRoute对象时,不会设置sensitiveHeaders属性, 此时PreDecorationFilter会获取默认的zuul.sensitive-headers(默认值为:"Cookie", "Set-Cookie", "Authorization")。 所以此时如果你集成了Spring OAuth2,并不会把OAuth2 Access Token传递给下游服务,因为Authorization被忽略了。

    解决方案如下,去除Authorization配置项:

    zuul:
      # 默认值为:"Cookie", "Set-Cookie", "Authorization"
      sensitive-headers: "Cookie, Set-Cookie"
    proxy:
      auth:
        routes:
          server1: oauth2
          server2: passthru
          server3: none

    注:此时可以通过proxy.auth.routes来配置具体哪个服务需要使用Authorization header,哪些直接剔除掉。

    • oauth2 - authorization header值重置为当前OAuth2 Token,传递给下游服务
    • passthru - 当前请求有authorization header则传递给下游服务,没有也不会去创建
    • none - 忽略authorization header,不会传递给下游服务
  • 自定义特定服务的 route sensitiveHeaders,优先级高于sensitiveHeaders配置

    zuul:
      routes:
        users:
          path: /myusers/**
          # 全局sensitiveHeaders配置项无效
          sensitiveHeaders: Cookie,Set-Cookie,Authorization
          url: https://downstream
    zuul:
      routes:
        users:
          path: /myusers/**
          # 全局sensitiveHeaders配置项无效
          sensitiveHeaders:
          url: https://downstream
  • ignore-security-headers: true - 忽略SpringSecurity安全相关的header,去除downstream response中SpringSecurity安全相关的header。 但你可能会发现浏览器中response还有SpringSecurity安全相关的header,这是由于网关服务自己SpringSecurity后面添加的。

Spring Cloud Config

Spring Cloud Gateway

  • Retrieving route filters

    • GET /actuator/gateway/globalfilters - 返回全局filter
    • GET /actuator/gateway/routefilters - 返回GatewayFilter factories
    • POST /actuator/gateway/refresh - To clear the routes cache
    • GET /actuator/gateway/routes - 返回定义的GatewayFilter
    • GET /actuator/gateway/routes/{id} - 返回指定route的详细信息
    • POST /gateway/routes/{id_route_to_create} - 创建route,请求体为GET /actuator/gateway/routes/{id}JSON格式
    • DELETE /gateway/routes/{id_route_to_delete} - 删除route
  • 输出Reactor Netty Access Logs - 【重要】

    开启Reactor Netty Access Logs的配置项:-Dreactor.netty.http.server.accessLogEnabled=true,必须使用Java System Property 而不是Spring Boot property。开启后可以把日志分离出单个日志文件(可选),例:

    <appender name="accessLog" class="ch.qos.logback.core.FileAppender">
        <file>access_log.log</file>
        <encoder>
            <pattern>%msg%n</pattern>
        </encoder>
    </appender>
    <appender name="async" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="accessLog" />
    </appender>
    
    <logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
        <appender-ref ref="async"/>
    </logger>
  • 参考文档

注:当前Spring Cloud Gateway版本(Greenwich.RELEASE)不支持Ribbon的LoadBalancerRules、retriesNextServer、 隔离无效node等功能,只用到了Ribbon的choose server功能,如果想使用上述功能请切换至Spring Cloud Zuul

About

spring-cloud-demo

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published