SpringCloud组件整合使用。(Eureka Ribbon Hystrix OpenFeign Gateway)

 

SpringCloudNetflix概述

Eureka

Eureka服务端配置
eureka.client.service-url.defaultZone值的格式
Eureak集群
Eureka客户端配置
定时续约和拉取注册表
服务端的自我保护机制
完整配置项
手动清理已经关闭的服务
远程关闭服务
服务平滑上下线

Ribbon

自定义负载均衡策略
DefaultRibbonConfig被@SpringBootApplication组合注解扫描到
配置文件方式自定义负载均衡策略
@RibbonClients自定义全局的负载均衡策略
内置负载均衡策略
不依赖Eureka使用Ribbon

OpenFeign

openFeign的使用
@SpringQueryMap表单提交需要带这个
超时设置

Hystrix(还需要深入的学习,难点)

方法级别的降级处理
服务级别的降级处理的两种实现
hystrix中的超时设置
隔离策略
Fallback也有限制
熔断器
Dashboard监控中心
Turbine集群监控
Turbine分组集群监控

Gateway

扩展写法
同时匹配到多个route
11个Route

route之 - Before
route之 - After
route之 - Between
route之 - Cookie
route之 - Header
route之 - Host(get不到用法)
route之 - Method
route之 - Path
route之 - Query
route之 - RemoteAddr
route之 - Weight

31个Filter

filter之 - AddRequestHeader
filter之 - AddRequestParameter
filter之 - AddResponseHeader
filter之 - DedupeResponseHeader
filter之 - Hystrix
filter之 - CircuitBreaker
filter之 - FallbackHeaders
filter之 - MapRequestHeader
filter之 - PrefixPath
filter之 - PreserveHostHeader
filter之 - RequestRateLimiter
filter之 - RedirectTo
filter之 - RemoveRequestHeader
filter之 - RemoveResponseHeader
filter之 - RemoveRequestParameter
filter之 - RewritePath
filter之 - RewriteLocationResponseHeader
filter之 - RewriteResponseHeader
filter之 - SaveSession
filter之 - SecureHeaders
filter之 - SetPath
filter之 - SetRequestHeader
filter之 - SetResponseHeader
filter之 - SetStatus
filter之 - StripPrefix
filter之 - Retry
filter之 - RequestSize
filter之 - SetRequestHost
filter之 - ModifyRequestBody
filter之 - ModifyResponseBody
filter之 default-filters

以上yml配置都可以用JAVA编程方式实现
全局过滤器

SpringCloudNetflix概述

官方文档地址: https://cloud.spring.io/spring-cloud-netflix/2.2.x/reference/html

SpringCloudNetflix可以通过自动配置将.properties或.yml的属性绑定到SpringEnvironment,也可以

使用Spring编程方式(注解)构建。Netflix组件提供的服务有Eureka(服务发现)Hystrix(断路器)Ribbon(负载均衡)

Zuul(路由本博客不做研究,官方已经不推荐使用了,后边会补充官方推荐的Gateway)另外Netflix和spring cloud不知道

啥原因,Netflix的组件正在被spring cloud慢慢替换。Gateway就是第一个(PS:真心学不动)。

Eureka

注册中心是分布式项目中不可缺少的一环,不同的业务模块拆分为独立的微服务(springboot项目)服务于服务之间需要互相调用,功能

相同的服务又要做集群负载均衡。若有A,B,C业务服务组成一个系统,A,B,C每个负载均衡1分,则该系统共有6个微服务。服务之间互相调用

一般为内部调用,其实也可以理解为A服务Http访问B服务,但B服务有两个此时IP+Port的方式就不灵了。所以我们需要将每个微服务都注册

到注册中心,并且周期的向注册中心发送心跳,证明自己活着。当服务自身的状态发生了更改也要告诉注册中心,例如A服务中的其中一台主机

需要下架服务(即暂不提供服务)。服务之间需要互相调用,则需要到注册中心获取注册表,例如A调用B则需要获取B服务的实例IP+Port,当然

具体的选择哪一个B服务取决于负载均衡策略。同时注册中心也要负责管理每个服务的当前状态,若发现有不可以用的服务,负责将其清理出注册中心。

若B服务挂掉了一台,则注册中心维护的注册表中不会有该服务,若有其他服务要访问B服务则只会获取到仅存活的一个B服务的IP+Port。

主流的注册中心有Zookeeper和Eureka,需要注意的是Zookeeper是CP,Eureka是AP。CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、

可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。

Eureka的AP特性决定了多个Eureka组成的集群中,每个注册中心的注册表可能会不同,这一点特别重要。

Eureka服务端配置

EurekaServer的基本配置以下三步,1 导入依赖 spring-cloud-starter-netflix-eureka-server 2 yml配置 3 Springboot启动类增加注解@EnableEurekaServer

其实eureka的配置有很多,官方文档只提供了最基础的配置,按照以下配置可以快速构建eureka服务端。对于yml配置:eureka.instance.hostname 此处填写的为

当前服务器的真实IP。eureka.client.register-with-eureka 若为true则表示将该服务注册到注册中心,对于eureka服务端可以配置为false,以下配置为true只是

为了在页面可以看到。eureka.client.fetch-registry 如果为false则表示不从注册中心集群中拉去注册表,身为服务端也是不需要拉去的,只有客户端需要拉去注册表。

eureka.client.service-url.defaultZone 此处要填写当前此处要填写eureka服务端集群的中所有eureka的地址。若为单机eureka则只需填自己的即可。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.dfsn.cloud</groupId>
    <artifactId>erueka-7001</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
    </properties>


    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>



    </dependencies>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>
View Code
server:
  port: 7001

spring:
  application:
    name: eureka

eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: false
    service-url:
      defaultZone: http://localhost:7001/eureka
View Code
package com.dfsn.cloud.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaServer_7001 {


    public static void main(String[] args) {
        SpringApplication.run(EurekaServer_7001.class, args);
    }
}
View Code

启动项目后可以访问eureka控制查看是否配置成功。

eureka.client.service-url.defaultZone值的格式

请注意 eureka.client.service-url.defaultZone的值固定格式为 http://ip:port/上下文路径/eureka

以上代码值为 http://localhost:7001/eureka 是因为当前的项目并没有配置server.servlet.context-path上下文名称。

server:
  port: 7001
  servlet:
    context-path: /e1

spring:
  application:
    name: eureka

eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: false
    service-url:
      defaultZone: http://localhost:7001/e1/eureka
View Code

Eureak集群

Eureka多个节点集群没有主从之分,每个node之间都是平等的。

在配置上,只需要在属性 eureka.client.service-url.defaultZone 中添加其他eureka节点的地址即可。有几个节点,就增加几个。

server:
  port: 7001

spring:
  application:
    name: eureka

eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: false
    service-url:
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka
View Code
server:
  port: 7002

spring:
  application:
    name: eureka

eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: false
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
View Code

Eureka客户端配置

客户端只需要导入对应依赖配置yml即可。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.dfsn.cloud</groupId>
    <artifactId>consumer-8001</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
    </properties>


    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

    </dependencies>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!-- 没有该配置,devtools 不生效 -->
                    <fork>true</fork>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
View Code
package com.dfsn.cloud.consumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;



@SpringBootApplication
public class Consumer_8001 {

    public static void main(String[] args) {
        SpringApplication.run(Consumer_8001.class, args);
    }
}
View Code

需要注意的是做负载均衡的代码属性 spring.application.name 需要相同,eureka.client.register-with-eureka 需要为 true 表示需要将此服务注册到注册中心(我们需要的不就是这个吗?)eureka.client.fetch-registry 需要为 true

表示从注册中心拉去注册表(获取其他服务的ip+port)

下面附录一份较为详细的配置,属性名是 .properties的配置方式,若你的配置文件是 .yml 把 . 替换成 : 换行缩进 :后的值留一个空格就可以了。

定时续约和拉取注册表

对于Eureka客户端来说,心跳续约和拉取注册表是两个必须有而且需要我们关注的配置。

eureka.instance.lease-renewal-interval-in-seconds 定时续约单位是秒默认值是 30秒

eureka.instance.lease-expiration-duration-in-seconds 告诉服务端,如果我多少秒没有发送心跳续约则证明我宕机默认值是 90秒。

eureka.client.registry-fetch-interval-seconds 定时获取注册表单位是秒默认值是 30秒

服务端的自我保护机制

自我保护机制是Eureka服务端的一种机制,在15分钟内如果服务端得到的续约数不满足一定比例默认85%

则服务端认为是自己出了问题,则不会将没有收到心跳的客户端从注册表中删除。

eureka.server.enable-self-preservation 自我保护机制开关 默认是 true

eureka.server.renewal-percent-threshold 失去连接比例 默认是 0.85

在生产环境应该避免关闭掉自我保护机制,这样服务宕机后才能最快速度从注册表中删除。

除此之外,如果需要最快的清理没有续约的服务,还需要设置

eureka.server.eviction-interval-timer-in-ms 多少毫秒清除过期服务。默认 60000毫秒

完整配置项

Eureka Client 配置项(eureka.client.*)

org.springframework.cloud.netflix.eureka.EurekaClientConfigBean

参数名称    说明    默认值
eureka.client.enabled    用于指示Eureka客户端已启用的标志    true
eureka.client.registry-fetch-interval-seconds    指示从eureka服务器获取注册表信息的频率(s)    30
eureka.client.instance-info-replication-interval-seconds    更新实例信息的变化到Eureka服务端的间隔时间,(s)    30
eureka.client.initial-instance-info-replication-interval-seconds    初始化实例信息到Eureka服务端的间隔时间,(s)    40
eureka.client.eureka-service-url-poll-interval-seconds    询问Eureka Server信息变化的时间间隔(s),默认为300秒    300
eureka.client.eureka-server-read-timeout-seconds    读取Eureka Server 超时时间(s),默认8秒    8
eureka.client.eureka-server-connect-timeout-seconds    连接Eureka Server 超时时间(s),默认5秒    5
eureka.client.eureka-server-total-connections    获取从eureka客户端到所有eureka服务器的连接总数,默认200个    200
eureka.client.eureka-server-total-connections-per-host    获取从eureka客户端到eureka服务器主机允许的连接总数,默认50个    50
eureka.client.eureka-connection-idle-timeout-seconds    连接到 Eureka Server 空闲连接的超时时间(s),默认30    30
eureka.client.registry-refresh-single-vip-address    指示客户端是否仅对单个VIP的注册表信息感兴趣,默认为null    null
eureka.client.heartbeat-executor-thread-pool-size    心跳保持线程池初始化线程数,默认2个    2
eureka.client.heartbeat-executor-exponential-back-off-bound    心跳超时重试延迟时间的最大乘数值,默认10    10
eureka.client.serviceUrl.defaultZone    可用区域映射到与eureka服务器通信的完全限定URL列表。每个值可以是单个URL或逗号分隔的备用位置列表。     
eureka.client.use-dns-for-fetching-service-urls    指示eureka客户端是否应使用DNS机制来获取要与之通信的eureka服务器列表。当DNS名称更新为具有其他服务器时,eureka客户端轮询eurekaServiceUrlPollIntervalSeconds中指定的信息后立即使用该信息。    false
eureka.client.register-with-eureka    指示此实例是否应将其信息注册到eureka服务器以供其他服务发现,默认为false    true
eureka.client.prefer-same-zone-eureka    实例是否使用同一zone里的eureka服务器,默认为true,理想状态下,eureka客户端与服务端是在同一zone下    true
eureka.client.log-delta-diff    是否记录eureka服务器和客户端之间在注册表的信息方面的差异,默认为false    false
eureka.client.disable-delta    指示eureka客户端是否禁用增量提取    false
eureka.client.fetch-remote-regions-registry    逗号分隔的区域列表,提取eureka注册表信息     
eureka.client.on-demand-update-status-change    客户端的状态更新到远程服务器上,默认为true    true
eureka.client.allow-redirects    指示服务器是否可以将客户端请求重定向到备份服务器/集群。如果设置为false,则服务器将直接处理请求。如果设置为true,则可以将HTTP重定向发送到具有新服务器位置的客户端。    false 
eureka.client.availability-zones.*    获取此实例所在区域的可用区域列表(在AWS数据中心中使用)。更改在运行时在registryFetchIntervalSeconds指定的下一个注册表获取周期生效。     
eureka.client.backup-registry-impl    获取实现BackupRegistry的实现的名称,该实现仅在eureka客户端启动时第一次作为后备选项获取注册表信息。 对于需要额外的注册表信息弹性的应用程序,可能需要这样做,否则它将无法运行。     
eureka.client.cache-refresh-executor-exponential-back-off-bound    在发生一系列超时的情况下,它是重试延迟的最大乘数值。    10
eureka.client.cache-refresh-executor-thread-pool-size    缓存刷新线程池初始化线程数量    2
eureka.client.client-data-accept    客户端数据接收的名称    full
eureka.client.decoder-name    解码器名称     
eureka.client.dollar-replacement    eureka服务器序列化/反序列化的信息中获取“$”符号的替换字符串。默认为“_-”     
eureka.client.encoder-name    编码器名称     
eureka.client.escape-char-replacement    eureka服务器序列化/反序列化的信息中获取“_”符号的的替换字符串。默认为“__“     
eureka.client.eureka-server-d-n-s-name    获取要查询的DNS名称来获得eureka服务器,此配置只有在eureka服务器ip地址列表是在DNS中才会用到。默认为null    null
eureka.client.eureka-server-port    获取eureka服务器的端口,此配置只有在eureka服务器ip地址列表是在DNS中才会用到。默认为null  null
eureka.client.eureka-server-u-r-l-context    表示eureka注册中心的路径,如果配置为eureka,则为http://ip:port/eureka/,在eureka的配置文件中加入此配置表示eureka作为客户端向注册中心注册,从而构成eureka集群。此配置只有在eureka服务器ip地址列表是在DNS中才会用到,默认为null null
eureka.client.fetch-registry    客户端是否获取eureka服务器注册表上的注册信息,默认为true    true
eureka.client.filter-only-up-instances    是否过滤掉非up实例,默认为true    true
eureka.client.g-zip-content    当服务端支持压缩的情况下,是否支持从服务端获取的信息进行压缩。默认为true     
eureka.client.property-resolver    属性解析器     
eureka.client.proxy-host    获取eureka server 的代理主机名    null
eureka.client.proxy-password    获取eureka server 的代理主机密码    null
eureka.client.proxy-port    获取eureka server 的代理主机端口    null
eureka.client.proxy-user-name    获取eureka server 的代理用户名    null
eureka.client.region    获取此实例所在的区域(在AWS数据中心中使用)。    us-east-1
eureka.client.should-enforce-registration-at-init    client 在初始化阶段是否强行注册到注册中心    false
eureka.client.should-unregister-on-shutdown    client在shutdown情况下,是否显示从注册中心注销    true
View Code

服务实例配置项(eureka.instance.*)

org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean

参数名称    说明    默认值
eureka.instance.appname 注册到注册中心的应用名称 unknown
eureka.instance.a-s-g-name 注册到注册中心的应用所属分组名称(AWS服务器)    null 
eureka.instance.app-group-name 注册到注册中心的应用所属分组名称 null
eureka.instance.data-center-info 指定服务实例所属数据中心    
eureka.instance.instance-enabled-onit 指示是否应在eureka注册后立即启用实例以获取流量 false
eureka.instance.non-secure-port http通信端口    80
eureka.instance.non-secure-port-enabled 是否启用HTTP通信端口    ture
eureka.instance.secure-port HTTPS通信端口    443
eureka.instance.secure-port-enabled 是否启用HTTPS通信端口    false
eureka.instance.secure-virtual-host-name 服务实例安全主机名称(HTTPS)    unknown
eureka.instance.virtual-host-name 该服务实例非安全注解名称(HTTP)    unknown
eureka.instance.secure-health-check-url 该服务实例安全健康检查地址(URL),绝对地址    
eureka.instance.lease-renewal-interval-in-seconds  该服务实例向注册中心发送心跳间隔(s)    30
eureka.instance.lease-expiration-duration-in-seconds 指示eureka服务器在删除此实例之前收到最后一次心跳之后等待的时间(s) 90
eureka.instance.metadata-map.*
eureka.instance.ip-address 该服务实例的IP地址    null
eureka.instance.prefer-ip-address  是否优先使用服务实例的IP地址,相较于hostname    false
eureka.instance.status-page-url 该服务实例的状态检查地址(url),绝对地址    null
eureka.instance.status-page-url-path 该服务实例的状态检查地址,相对地址     /actuator/info
eureka.instance.home-page-url 该服务实例的主页地址(url),绝对地址    
eureka.instance.home-page-url-path  该服务实例的主页地址,相对地址    /
eureka.instance.health-check-url  该服务实例的健康检查地址(url),绝对地址    null
eureka.instance.health-check-url-path  该服务实例的健康检查地址,相对地址     /actuator/health
eureka.instance.instance-id 该服务实例在注册中心的唯一实例ID    
eureka.instance.hostname 该服务实例所在主机名    
eureka.instance.namespace  获取用于查找属性的命名空间。 在Spring Cloud中被忽略。eureka
eureka.instance.environment  该服务实例环境配置    
eureka.instance.default-address-resolution-order  默认地址解析顺序    
eureka.instance.initial-status  该服务实例注册到Eureka Server 的初始状态    up
eureka.instance.registry.default-open-for-traffic-count 【Eureka Server 端属性】默认开启通信的数量    1
eureka.instance.registry.expected-number-of-renews-per-min 【Eureka Server 端属性】每分钟续约次数    1
View Code

Eureka Server 配置项(eureka.server.*)

org.springframework.cloud.netflix.eureka.server.EurekaServerConfigBean 

参数名称    说明    默认值
eureka.server.enable-self-preservation 启用自我保护机制,默认为true    true
eureka.server.eviction-interval-timer-in-ms 清除无效服务实例的时间间隔(ms),默认1分钟    60000
eureka.server.delta-retention-timer-interval-in-ms  清理无效增量信息的时间间隔(ms),默认30秒    30000
eureka.server.disable-delta  禁用增量获取服务实例信息    false
eureka.server.log-identity-headers  是否记录登录日志    true
eureka.server.rate-limiter-burst-size  限流大小    10
eureka.server.rate-limiter-enabled  是否启用限流    false
eureka.server.rate-limiter-full-fetch-average-rate平均请求速率    100
eureka.server.rate-limiter-throttle-standard-clients  是否对标准客户端进行限流    false
eureka.server.rate-limiter-registry-fetch-average-rate  服务注册与拉取的平均速率    500
eureka.server.rate-limiter-privileged-clients 信任的客户端列表    
eureka.server.renewal-percent-threshold  15分钟内续约服务的比例小于0.85,则开启自我保护机制,再此期间不会清除已注册的任何服务(即便是无效服务) 0.85  
eureka.server.renewal-threshold-update-interval-ms  更新续约阈值的间隔(分钟),默认15分钟    15
eureka.server.response-cache-auto-expiration-in-seconds  注册信息缓存有效时长(s),默认180秒    180
eureka.server.response-cache-update-interval-ms  注册信息缓存更新间隔(s),默认30秒    30
eureka.server.retention-time-in-m-s-in-delta-queue  保留增量信息时长(分钟),默认3分钟    3
eureka.server.sync-when-timestamp-differs  当时间戳不一致时,是否进行同步    true
eureka.server.use-read-only-response-cache   是否使用只读缓存策略    true
View Code

Eureka集群配置

参数名称    说明    默认值
eureka.server.enable-replicated-request-compression 复制数据请求时,数据是否压缩    false
eureka.server.batch-replication 节点之间数据复制是否采用批处理    false
eureka.server.max-elements-in-peer-replication-pool  备份池最大备份事件数量,默认1000    1000
eureka.server.max-elements-in-status-replication-pool  状态备份池最大备份事件数量,默认1000     1000
eureka.server.max-idle-thread-age-in-minutes-for-peer-replication  节点之间信息同步线程最大空闲时间(分钟)    15
eureka.server.max-idle-thread-in-minutes-age-for-status-replication  节点之间状态同步线程最大空闲时间(分钟)    10
eureka.server.max-threads-for-peer-replication  节点之间信息同步最大线程数量    20
eureka.server.max-threads-for-status-replication  节点之间状态同步最大线程数量    1
eureka.server.max-time-for-replication  节点之间信息复制最大通信时长(ms)    30000
eureka.server.min-available-instances-for-peer-replication  集群中服务实例最小数量,-1 表示单节点    -1
eureka.server.min-threads-for-peer-replication  节点之间信息复制最小线程数量 5
eureka.server.min-threads-for-status-replication  节点之间信息状态同步最小线程数量    1
eureka.server.number-of-replication-retries  节点之间数据复制时,可重试次数     5
eureka.server.peer-eureka-nodes-update-interval-ms  节点更新数据间隔时长(分钟)    10
eureka.server.peer-eureka-status-refresh-time-interval-ms  节点之间状态刷新间隔时长(ms)    30000
eureka.server.peer-node-connect-timeout-ms 节点之间连接超时时长(ms)    200
eureka.server.peer-node-connection-idle-timeout-seconds  节点之间连接后,空闲时长(s)    30
eureka.server.peer-node-read-timeout-ms  几点之间数据读取超时时间(ms)    200 
eureka.server.peer-node-total-connections 集群中节点连接总数    1000
eureka.server.peer-node-total-connections-per-host  节点之间连接,单机最大连接数量    500
eureka.server.registry-sync-retries  节点启动时,尝试获取注册信息的次数     500
eureka.server.registry-sync-retry-wait-ms  节点启动时,尝试获取注册信息的间隔时长(ms)    30000
eureka.server.wait-time-in-ms-when-sync-empty  在Eureka服务器获取不到集群里对等服务器上的实例时,需要等待的时间(分钟) 5
View Code

手动清理已经关闭的服务

发送Delete请求 http://ip:port/eureka/apps/应用的服务名和端口。例如:

http://127.0.0.1:7001/eureka/apps/CONSUMER/FTS-SZ-9073186.szwb.fsc.cntaiping.com:consumer:8001

可以在手动关掉服务进程后发送这个请求,立即手动的清理注册表。

远程关闭服务

远程关闭其实是SpringBoot的功能,在这里提到只是为了引入下面的服务平滑上下线。

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    shutdown:
      enabled: true
View Code
   <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
View Code

导入依赖后,配置yml配置。Post访问 http://localhost:8001/actuator/shutdown 即可停止服务,但是这种方式再次开启服务

需要手动到服务器启动。

服务平滑上下线

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    shutdown:
      enabled: true
View Code
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
View Code

配置如上,POST访问 http://localhost:9001/actuator/service-registry {"status":"DOWN"}下线{"status":"UP"}上线

这种方式下线后,Eureka会标记该服务暂时不可用,其他服务拉去注册表时,就不会在拉到这个服务。

Ribbon

Ribbon是一个客户端负载均衡器,SpringCloud生态中,zuul依赖Ribbon,使用RestTemplate通过服务名调用也要用到Ribbon,包括OpenFeign内部调用也使用的有Ribbon。

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
View Code

一般来说,我们不需要单独引入Ribbon依赖,eureka中引入的有,zuul中也有。

package com.dfsn.cloud.producer.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ProducerController {

    @GetMapping(value = "producerHandler")
    public String handler() {
        return "producer-9001";
    }
}
View Code
package com.dfsn.cloud.producer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
public class Producer_9001 {


    public static void main(String[] args) {
        SpringApplication.run(Producer_9001.class, args);
    }
}
View Code
server:
  port: 9001


spring:
  application:
    name: producer
  


eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka
View Code
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.dfsn.cloud</groupId>
    <artifactId>producer-9001</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
    </properties>


    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        
        

    </dependencies>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>
View Code

将以上代码复制两份,创建两个producer项目,构成一个服务提供者集群,同时注册到Eureka集群中。

package com.dfsn.cloud.consumer.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class ConsumerController {
    @GetMapping(value = "consumerHandler")
    public String handler() {
        return "consumer-8001";
    }

    @Autowired
    private RestTemplate restTemplate;


    @GetMapping(value = "to")
    public String toProducer(){
        String forObject = restTemplate.getForObject("http://localhost:9001/producerHandler", String.class);
        return forObject;
    }
}
View Code
package com.dfsn.cloud.consumer.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class CreateBean {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
View Code

在consumer中通过RestTemplateAPI访问producer

那问题来了producer服务是一个集群,要搞负载均衡的,在代码中写死了ip:port是不行的。现在我们可以借助Ribbon的注解

@LoadBalanced 将这个注解放到 RestTemplate的bean上。使用RestTemplate的方法传递url参数时IP和端口写消费者集群的名称

也就是 producer服务中配置的 spring.application.name 的值,两个producer的值相同,这样就可以做到负载均衡了。

package com.dfsn.cloud.consumer.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class CreateBean {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
View Code
package com.dfsn.cloud.consumer.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class ConsumerController {
    @GetMapping(value = "consumerHandler")
    public String handler() {
        return "consumer-8001";
    }

    @Autowired
    private RestTemplate restTemplate;


    @GetMapping(value = "to")
    public String toProducer(){
        String forObject = restTemplate.getForObject("http://producer/producerHandler", String.class);
        return forObject;
    }
}
View Code

自定义负载均衡策略

当我们使用@LoadBalanced注解注释RestTemplate后,通过服务名调用其他微服务,Ribbon则使用它自己的负载均衡策略为我们

找到一个服务使用。下面我们来做一个自定义的负载均衡策略。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.dfsn.cloud</groupId>
    <artifactId>order-9100</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
    </properties>


    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        
        

    </dependencies>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>
View Code
server:
  port: 9100


spring:
  application:
    name: order
  


eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka
View Code
package com.dfsn.cloud.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Order_9100 {
    public static void main(String[] args) {
        SpringApplication.run(Order_9100.class, args);
    }
}
View Code

首先我们在创建两个order服务,作为order集群。现在我们有producer两台服务,order两台服务。

1 在consumer中使用RestTemplate的方式调用两个服务。

package com.dfsn.cloud.consumer.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.ThreadLocalRandom;

@RestController
public class ConsumerController {
    @GetMapping(value = "consumerHandler")
    public String handler() {
        return "consumer-8001";
    }

    @Autowired
    private RestTemplate restTemplate;


    @GetMapping(value = "toproducer")
    public String toProducer(){
        String forObject = restTemplate.getForObject("http://producer/producerHandler", String.class);
        return forObject;
    }


    @GetMapping(value = "toorder")
    public String toOrder(){
        String forObject = restTemplate.getForObject("http://order/orderHandler", String.class);
        return forObject;
    }
}
View Code

2 创建一个针对producer的配置类。该类使用@RibbonClient注解注释 name为producer的服务名,configuration为一个自定义的负载均衡策略。

package com.dfsn.cloud.consumer.config;

import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Configuration;

@Configuration
@RibbonClient(name = "producer",configuration = com.dfsn.cloud.DefaultRibbonConfig.class)
public class MyRibbonClient {
}
View Code

3 创建自定义的负载均衡策略。该负载均衡策略需要一个IRule的实例Bean,我这里使用的是RandomRule的子类,RandomRule是Ribbon提供的实现类。

MyRult类只覆盖chooseRandomInt(int serverCount)方法,该方法返回一个整数,根据返回值从已有的服务中取出一个服务。我这里写死的原因是为了

测试它到底能不能生效,如果剩下则producer服务每次访问都是一个不会负载均衡。请注意!DefaultRibbonConfig类不能被@SpringBootApplication

组合注解扫描到,否则将被认为是全局的负载均衡策略,也就是说,order也会使用该策略。下图是我的文件目录。

package com.dfsn.cloud;

import com.dfsn.cloud.consumer.config.MyRule;
import com.netflix.loadbalancer.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DefaultRibbonConfig {

    @Bean
    public IRule ribbonRule() {
        return new MyRule();
    }
}
View Code
package com.dfsn.cloud.consumer.config;

import com.netflix.loadbalancer.RandomRule;

public class MyRule extends RandomRule {
    @Override
    protected int chooseRandomInt(int serverCount) {
        return 0;
    }
}
View Code

 

从结果看,order还是走的Ribbon的负载均衡策略而producer则每次都是同一个,表示自定义的负载均衡策略生效。

其次除了IRule可以重写,官方文档中还提到了其他的几个类也可以重新。IPing是一个定时任务

用于定时去查询集群服务的状态,当然默认的这些都是先从Eureka中获取的数据。

https://cloud.spring.io/spring-cloud-netflix/2.2.x/reference/html/#spring-cloud-ribbon

DefaultRibbonConfig被@SpringBootApplication组合注解扫描到

将目录调整为DefaultRibbonConfig可以被@SpringBootApplication扫描到,会发现无论是producer还是order都会使用自定义的负载均衡策略。也就是说当前自定义的负载均衡策略变成了全局的。

 

配置文件方式自定义负载均衡策略

对于单个服务使用注解的方式自定义策略还是有些麻烦的。1.2之后的版本支持在yml或者properties中配置负载均衡器

server:
  port: 8001


spring:
  application:
    name: consumer

eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka


producer:
   ribbon:
     NFLoadBalancerRuleClassName: com.dfsn.cloud.consumer.config.MyRule2
View Code
package com.dfsn.cloud.consumer.config;

import com.netflix.loadbalancer.RandomRule;

public class MyRule2 extends RandomRule {
    @Override
    protected int chooseRandomInt(int serverCount) {
        System.out.println("MyRule2");
        return 1;
    }
}
View Code

配置文件的格式为 服务名.ribbon.NFLoadBalancerRuleClassName=负载均衡类 并不需要在写其他的配置类,也无需关心是否被SpringBootApplication扫描到因为不需要这个配置类了。

只需要直接指定负载均衡的策略类即可。经测试,使用配置文件的优先级低于使用注解的方式。注意配置文件的方式,只能重写5个类。

@RibbonClients自定义全局的负载均衡策略

如果你想为所有服务都使用自定义的负载均衡策略使用该注解即可。它的defaultConfiguration参数接收一个负载均衡策略配置类,也就是那个不能配@SpringBootApplication扫描到的类。

package com.dfsn.cloud.consumer.config;

import com.dfsn.cloud.DefaultRibbonConfig;
import org.springframework.cloud.netflix.ribbon.RibbonClients;

@RibbonClients(defaultConfiguration ={DefaultRibbonConfig.class} )
public class AllRibbonClient {

}
View Code

内置负载均衡策略

RoundRobinRule:轮询策略。Ribbon 默认采用的策略。若经过一轮轮询没有找到可用的 provider,其最多轮询 10 轮。若最终还没有找到,则返回 null。
RandomRule:随机策略,从所有可用的 provider 中随机选择一个。
RetryRule:重试策略。先按照 RoundRobinRule 策略获取 provider,若获取失败,则在指定的时限内重试。默认的时限为 500 毫秒。
BestAvailableRule:最可用策略。选择并发量最小的 provider,即连接的消费者数量最少的 provider。
AvailabilityFilteringRule:可用过滤算法。该算法规则是:过滤掉处于熔断状态的 provider 与已经超过连接极限的provider,对剩余 provider 采用轮询策略。
ZoneAvoidanceRule:zone(配置eureka客户端时可以指定客户端所在地区,用于机房间就近选择) 回避策略。根据 provider 所在 zone 及 provider 的可用性,对 provider 进行选择。
WeightedResponseTimeRule:“权重响应时间”策略。根据每个 provider 的平均响应时间计算其权重,响应时间越快权重越大,被选中的机率就越高。在刚启动时采用轮询策略。后面就会根据权重进行选择了。

不依赖Eureka使用Ribbon

上面我们自定义负载均衡策略,其实也是建立在Eureka服务发现的基础上,思考一下,如果没有Eureka,我们只是指定了负载均衡策略,请求能转发到对应的服务器吗?如果可以那IP:Port是怎么确定的?

server:
  port: 8001


spring:
  application:
    name: consumer

eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka


producer:
   ribbon:
     NFLoadBalancerRuleClassName: com.dfsn.cloud.consumer.config.MyRule2

order:
  ribbon:
    NFLoadBalancerRuleClassName: com.dfsn.cloud.consumer.config.MyRule2
ribbon:
  eureka:
    enabled: false
View Code

ribbon.eureka.enabled=false进制Ribbon使用Eureka,通过浏览器访问会失败。

server:
  port: 8001


spring:
  application:
    name: consumer

eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka


producer:
   ribbon:
     NFLoadBalancerRuleClassName: com.dfsn.cloud.consumer.config.MyRule2
     listOfServers: 127.0.0.1:9001,127.0.0.1:9002

order:
  ribbon:
    NFLoadBalancerRuleClassName: com.dfsn.cloud.consumer.config.MyRule2
    listOfServers: 127.0.0.1:9100,127.0.0.1:9101
ribbon:
  eureka:
    enabled: false
View Code

给每个服务配置 listOfServers属性,该属性的值为微服务集群的物理IP:Port。以上配置可以完全不依赖Eureka。

OpenFeign

openFeign不在SpringCloudNetflix中,而是单独的一个项目 https://cloud.spring.io/spring-cloud-openfeign/2.2.x/reference/html

Feign:假装,伪装。OpenFeign可以将提供者提供的Restful服务伪装为接口进行消费消费者只需要使用“feign”接口+“注解”的方式即
可直接调用提供者提供的Restful服务,而无需在使用RestTemplate。需要注意的是:伪装的feign接口是消费者调用的与提供者没有任何
关系。Feign仅仅是一个伪客户端,其不会对请求做任何处理,Feign是通过注解的方式实现Restful请求的。OpenFeign会使用Ribbon的负载均衡,
根据Ribbon的默认的策略或者我们设置的策略选择不同的IP:Port

openFeign的使用

  <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
View Code
@SpringBootApplication
@EnableFeignClients
public class Consumer_8001 {

    public static void main(String[] args) {
        SpringApplication.run(Consumer_8001.class, args);
    }
}
View Code
@FeignClient("order")
public interface OrderService {
    @GetMapping(value = "orderHandler")
    public String handler();
}
View Code
package com.dfsn.cloud.consumer.feigns;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient("producer")
public interface ProducerService {
    @GetMapping(value = "producerHandler")
    public String handler();
}
View Code
@Autowired
    private OrderService orderService;

    @Autowired
    private ProducerService producerService;

    @GetMapping(value = "toproducer2")
    public String toProducer2() {
        String forObject = producerService.handler();
        return forObject;
    }


    @GetMapping(value = "toorder2")
    public String toOrder2() {
        String forObject = orderService.handler();
        return forObject;
    }
View Code

导入依赖后在启动类上加上注解@EnableFeignClients注解。创建两个接口,接口上使用@FeignClient注解,value属性是服务提供者的名字,就是注册到Eureka

的名字。接口中的方法是服务提供者的方法,除了没有方法体,剩余的需要和服务提供者的方法一模一样。在Controller中注入接口,然后调用接口的方法。

这样就像是调用本地方法一样。

@SpringQueryMap表单提交需要带这个

请注意!以上的代码片段服务提供者接收JSON格式参数,调用者的feign也需要带@RequestBody注解。

如果是表单提交按道理是不需要带任何的注解,但是这是不行的。你必须在feign客户端给方法使用

@SpringQueryMap注解。否则参数绑定失败。

    @PostMapping(value = "oder")
    public OrderVo order(@RequestBody OrderVo orderVo) {
        System.out.println("postjson");
        return orderVo;
    }


    @PostMapping(value = "oder2")
    public OrderVo order2(OrderVo orderVo) {
        System.out.println("post表单");
        return orderVo;
    }

    @GetMapping(value = "oder3")
    public OrderVo order3(OrderVo orderVo) {
        System.out.println("get表单");
        return orderVo;
    }

    @DeleteMapping(value = "oder4")
    public OrderVo order4(OrderVo orderVo) {
        System.out.println("delete表单");
        return orderVo;
    }

    @PutMapping(value = "oder5")
    public OrderVo order5(OrderVo orderVo) {
        System.out.println("put表单");
        return orderVo;
    }
View Code
    @PostMapping(value = "oder")
    public OrderVo order(@RequestBody OrderVo orderVo);

    @PostMapping(value = "oder2")
    public OrderVo order2(@SpringQueryMap OrderVo orderVo);

    @GetMapping(value = "oder3")
    public OrderVo order3(@SpringQueryMap OrderVo orderVo);

    @DeleteMapping(value = "oder4")
    public OrderVo order4(@SpringQueryMap OrderVo orderVo);

    @PutMapping(value = "oder5")
    public OrderVo order5(@SpringQueryMap OrderVo orderVo);
View Code
    @GetMapping(value = "addorder")
    public OrderVo addorder() {
        OrderVo orderVo = new OrderVo();
        orderVo.setId(1);
        orderVo.setName("方便面");
        return orderService.order(orderVo);
    }


    @GetMapping(value = "addorder2")
    public OrderVo addorder2() {
        OrderVo orderVo = new OrderVo();
        orderVo.setId(1);
        orderVo.setName("方便面");
        return orderService.order2(orderVo);
    }

    @GetMapping(value = "addorder3")
    public OrderVo addorder3() {
        OrderVo orderVo = new OrderVo();
        orderVo.setId(1);
        orderVo.setName("方便面");
        return orderService.order3(orderVo);
    }


    @GetMapping(value = "addorder4")
    public OrderVo addorder4() {
        OrderVo orderVo = new OrderVo();
        orderVo.setId(1);
        orderVo.setName("方便面");
        return orderService.order4(orderVo);
    }

    @GetMapping(value = "addorder5")
    public OrderVo addorder5() {
        OrderVo orderVo = new OrderVo();
        orderVo.setId(1);
        orderVo.setName("方便面");
        return orderService.order5(orderVo);
    }
View Code

超时设置

feign:
  client:
    config:
      order:
        connectTimeout: 2000
        readTimeout: 5000
View Code
    @GetMapping(value = "orderHandler")
    public String handler() throws Exception {
        Thread.sleep(3000);
        return "order-9100";
    }
View Code
    @GetMapping(value = "producerHandler")
    public String handler()throws Exception {
        Thread.sleep(3000);
        return "producer-9001";
    }
View Code

以上代码设置order服务的连接超时为2000毫秒相应超时为5000毫秒。在order和producer中线程休眠3000毫秒。

最终order不会发生超时而producer则会发生超时。如果要设置全局的超时时间则将order(具体的服务)替换成default。

请注意这个超时时间仅仅是远程调用的时间,并不包含调用者本身代码的消耗时间。

Hystrix(还需要深入的学习,难点)

熔断与降级。这里需要搞清楚熔断和降级的概念。

降级:当服务发生异常时做出的补偿机制就是降级。说白了,我们程序中写的try{}catch(){}也是一种降级手段

只是为了在出现错误时反馈给用户一个更好的体验。

熔断:当触发某个条件时,熔断该请求,例如请求时间过长会影响整体微服务的响应时间。当某个服务频繁出错,

这些情况都可以预先设置熔断。熔断以后请求链路就断了,最终和有异常,而熔断后的补偿就是降级。

方法级别的降级处理

1 导入依赖spring-cloud-starter-netflix-hystrix

2 在启动类增加@EnableCircuitBreaker或者@EnableHystrix两个注解任意一个就行

3 在需要降级处理的方法上加@HystrixCommand注解属性 fallbackMethod 指定本类的一个方法,该方法是降级方法。

     <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
View Code
package com.dfsn.cloud.consumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableFeignClients
@SpringBootApplication
@EnableHystrix
@EnableCircuitBreaker
public class Consumer_8001 {

    public static void main(String[] args)  {
        SpringApplication.run(Consumer_8001.class, args);
    }
}
View Code
    @GetMapping(value = "toorder2")
    @HystrixCommand(fallbackMethod = "toOrder2Fallback")
    public String toOrder2() throws Exception {
        int i=1/0;
        String forObject = orderService.handler();
        return forObject;
    }


    public String toOrder2Fallback() throws Exception {
        return "我是降级处理哦";
    }
View Code

请注意!方法级别的降级处理无论是在远程调用中异常或者是方法本身异常,都会发生降级处理,这一点很重要。

服务级别的降级处理的两种实现

服务级别的降级处理需要在feign中开启使用hystrix

feign.hystrix.enable=true

1 fallbackFactory

feign:
  hystrix:
    enabled: true
View Code
package com.dfsn.cloud.consumer.feigns;

import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

@Component
public class OrderServiceFallbackFactory implements FallbackFactory<OrderService> {
    @Override
    public OrderService create(Throwable throwable) {
        return new OrderService() {
            @Override
            public String handler() {
                throwable.printStackTrace();
                return "fallbackFacotry降级";
            }
        };
    }
}
View Code
package com.dfsn.cloud.consumer.feigns;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(value = "order", fallbackFactory = OrderServiceFallbackFactory.class)
public interface OrderService {
    @GetMapping(value = "orderHandler")
    public String handler();
}
View Code

2 fallback

feign:
  hystrix:
    enabled: true
View Code
package com.dfsn.cloud.consumer.feigns;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(value = "order", fallback = OrderServiceFallback.class)
public interface OrderService {
    @GetMapping(value = "orderHandler")
    public String handler();
}
View Code
package com.dfsn.cloud.consumer.feigns;

import org.springframework.stereotype.Component;
@Component
public class OrderServiceFallback implements OrderService{

    @Override
    public String handler() {
        return "fallback降级";
    }
}
View Code

以上两种方法都可以实现降级处理,区别是fallbackFactory可以获取具体的异常信息。这里需要注意,无论是以上哪一种都只会对远程

调用过程中出错的错误进行降级。而方法级别是方法内任意一行异常都会触发降级。以下截图中有fallbackfactory降级处理。

toorder2有单独的method降级。toorder2和toorder3都会在第一行抛出一个算术异常。而toorder2最终走了方法级别的降级,toorder3

没有走降级。toorder3使用的是服务级别的降级,但是这个异常并不是调用服务产生的所以不会被降级。

 

 

hystrix中的超时设置

hystrix.command.default.execution.timeout.enabled 属性默认为true有超时设置。

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 设置超时时间默认是1000毫秒

同时需要注意在openFeign中也有超时时间 feign.client.config.default.connectTimeout和readTimeout两个时间

如果在配置文件中同时设置了hystrix和openfeign则哪个先超时就是超时。(谁的时间短,谁为主。)

隔离策略

信号量隔离:对依赖的调用所使用的线程仍为请求线程,即不会为依赖请求再新创建新 的线程。但系统会为每种依赖分配一定数量的信号量,而每个依赖请求分配一个信号号。

当对该依赖的调用请求数量达到上限后再有请求,则该请求阻塞。所以对某依赖的并发 量取决于为该依赖所分配的信号数量。 

hystrix.command.default.execution.isolation.strategy=SEMAPHORE

hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=10

使用JMeter测试从第四个请求开始返回的就是降级结果了。

server:
  port: 8001


spring:
  application:
    name: consumer

eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka

feign:
  hystrix:
    enabled: true
  client:
    config:
      default:
        connectTimeout: 2000
        readTimeout: 3000

hystrix:
  command:
    default:
      execution:
        timeout:
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 3000
          strategy: SEMAPHORE
          semaphore:
            maxConcurrentRequests: 3
View Code

线程隔离 thread:Hystrix 的默认隔离策略。系统会创建一个依赖线程池,为每个依赖请 求分配一个独立的线程,而每个依赖所拥有的线程数量是有上限的。

当对该依赖的调用 请求数量达到上限后再有请求,则该请求阻塞。所以对某依赖的并发量取决于为该依赖 所分配的线程数量。 

hystrix:
  threadpool:
    default:
      coreSize: 5
      maxQueueSize: -1
  command:
    default:
      execution:
        timeout:
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 3000
          strategy: THREAD
View Code

hystrix.command.default.execution.isolation.strategy=THREAD

hystrix.threadpool.default.coreSize=可用线程数默认为10

hystrix.threadpool.default.maxQueueSize=等待队列个数默认为-1

hystrix.threadpool.default.queueSizeRejectionThreshold=可热修改的队列默认为5如果maxQueueSize为-1则不生效。

Fallback也有限制

以上测试无论是超时还是信号量不够用或者是异常都会最走到降级方法。但降级方法也有默认的并发数量最多10

hystrix.command.default.fallback.enabled 设置是否开启fallback默认是开启的。

hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests 设置回退方法的并发量

package com.dfsn.cloud.order.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @GetMapping(value = "orderHandler")
    public String handler() throws Exception{
        int i=1/0;
        return "order-9100";
    }
}
View Code
package com.dfsn.cloud.consumer.feigns;

import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

@Component
public class OrderServiceFallbackFactory implements FallbackFactory<OrderService> {
    @Override
    public OrderService create(Throwable throwable) {
        return new OrderService() {
            @Override
            public String handler() {
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    System.out.println(e);
                }

                throwable.printStackTrace();
                return "fallbackFacotry降级";
            }
        };
    }
}
View Code
server:
  port: 8001


spring:
  application:
    name: consumer

eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka

feign:
  hystrix:
    enabled: true
  client:
    config:
      default:
        connectTimeout: 2000
        readTimeout: 3000

hystrix:
  command:
    default:
      fallback:
        enabled: true
        isolation:
          semaphore:
            maxConcurrentRequests: 2
      execution:
        timeout:
          enabled: false
View Code

以上代码片段在远程调用时必然的失败,而回退方法中休眠2秒钟。fallback设置成2表示最多两个回退方法。

熔断器

当一个服务频繁出错被降级,则表示该接口是不正常的。hystrix允许我们预先设置,达到什么情况下主动熔断该接口。则以后再访问该接口都会直接降级。

hystrix.command.default.circuitBreaker.enabled 默认true开启熔断器

hystrix.command.default.circuitBreaker.requestVolumeThreshold 默认20时间窗(10秒)内最低多少请求会触发熔断 

hystrix.command.default.circuitBreaker.errorThresholdPercentage 默认50 百分之50和上一个值组合使用:10秒内最低20个请求并且有50%的错误率则开启熔断

hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds 默认5000毫秒 开启熔断后,多少秒恢复正常

hystrix.command.default.circuitBreaker.forceClosed 默认false 如果开启则通过所有请求,即熔断不生效感觉和enabled=false效果一样。

hystrix.command.default.circuitBreaker.forceOpen 拒绝所有请求,强行熔断还没有时间限制。

hystrix.command.default.metrics.rollingStats.timeInMilliseconds 设置时间窗的大小默认10000毫秒

hystrix.command.default.metrics.rollingStats.numBuckets 设置滚动窗口划分的桶数,例如,滚动窗口持续时间为10秒,默认配置10个桶,那么每秒钟一个桶用于存放统计数据。配置值必须符合以下条件 metrics.rollingStats.timeInMilliseconds % metrics.rollingStats.numBuckets == 0,否则会抛出异常。

hystrix:
  command:
    default:
      circuitBreaker:
        enabled: true
        requestVolumeThreshold: 20
        sleepWindowInMilliseconds: 60000
        errorThresholdPercentage: 50
      fallback:
        enabled: true
        isolation:
          semaphore:
            maxConcurrentRequests: 200
      execution:
        timeout:
          enabled: false
        isolation:
          strategy: SEMAPHORE
          semaphore:
            maxConcurrentRequests: 200
View Code
public class Bean {
    private int val;

    public int getVal() {
        return val;
    }

    public void setVal(int val) {
        this.val = val;
    }
}
View Code
    @GetMapping(value = "toorder2")
    public String toOrder2(Integer val) throws Exception {
        Bean bean = new Bean();
        bean.setVal(val);
        String forObject = orderService.handler(bean);
        return forObject;
    }
View Code
    @GetMapping(value = "orderHandler")
    public String handler(Bean bean) throws Exception{
        System.out.println(bean.getVal());
        if (bean.getVal() == 1) {
            int i = 1 / 0;
        }
        return "order-9101";
    }
View Code

order服务接收参数如果为1则抛出异常。再consumer设置20个请求里如果有50%发生错误,则熔断该接口,往后的一分钟内访问该接口都会直接降级。

但是不会影响order服务的其他接口。

Dashboard监控中心

hystrix允许我们通过GUI的形式查看当前服务的允许状态,主要查看被hystrix监控的接口信息。

我这里使用的是SpringBoot2.2.2 SpringCloudHoxton.SR1 特意说版本是因为各个版本的配置方式不同。

1 导入依赖

   <!-- hystrix-dashboard 依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
        <!--actuator 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
View Code

2 添加配置

management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream
View Code

3 启动类添加注解 @EnableHystrixDashboard

Turbine集群监控

以上是单节点的配置,你只能查看一个节点的状态。现在我们来搭建集群监控,order集群两台,consumer两台,producer两台。consumer中远程调用order,order中远程调用producer。

以下为order,consumer集群的配置和pom。如果不需要单机查看则可以去掉依赖和@EnableHystrixDashboard。producer无需配置。

server:
  port: 6300


spring:
  application:
    name: turbine

eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka



turbine:
  app-config: CONSUMER,ORDER
  aggregator:
    cluster-config: default
  cluster-name-expression: "'default'"
View Code
server:
  port: 8002


spring:
  application:
    name: consumer

eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka


feign:
  hystrix:
    enabled: true

management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream
View Code
server:
  port: 9100


spring:
  application:
    name: order
  
eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka


feign:
  hystrix:
    enabled: true


management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream
View Code
server:
  port: 9101


spring:
  application:
    name: order
  
eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



feign:
  hystrix:
    enabled: true




management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream
View Code
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
View Code

新建一个项目,作为Turbine的启动项目。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
View Code
server:
  port: 6300


spring:
  application:
    name: turbine

eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka



turbine:
  app-config: CONSUMER,ORDER
  aggregator:
    cluster-config: default
  cluster-name-expression: "'default'"
View Code
package com.dfsn.cloud.turbine;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.turbine.EnableTurbine;

@SpringBootApplication
@EnableCircuitBreaker
@EnableHystrixDashboard
@EnableTurbine
public class Turbine_6300 {

    public static void main(String[] args)  {
        SpringApplication.run(Turbine_6300.class, args);
    }
}
View Code

Turbine分组集群监控

上边的集群监控order和consumer两个集群打到了一个控制台上,不方便查看。Turbine给我们提供了可以分开查看的方法。

给每个服务实例增加元数据,就是注册到Eureka中的数据,两个分为一组。最后再Turbine项目中设置根据组分类。

server:
  port: 8001


spring:
  application:
    name: consumer

eureka:
  instance:
    hostname: 127.0.0.1
    metadata-map:
      dsahboardcluster: consumerdsahboard
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka

feign:
  hystrix:
    enabled: true


management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream
View Code
server:
  port: 8002


spring:
  application:
    name: consumer

eureka:
  instance:
    hostname: 127.0.0.1
    metadata-map:
      dsahboardcluster: consumerdsahboard
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka


feign:
  hystrix:
    enabled: true

management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream
View Code
server:
  port: 9100


spring:
  application:
    name: order
  
eureka:
  instance:
    hostname: 127.0.0.1
    metadata-map:
      dsahboardcluster: orderdsahboard
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka


feign:
  hystrix:
    enabled: true


management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream
View Code
server:
  port: 9101


spring:
  application:
    name: order
  
eureka:
  instance:
    hostname: 127.0.0.1
    metadata-map:
      dsahboardcluster: orderdsahboard
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



feign:
  hystrix:
    enabled: true




management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream
View Code
server:
  port: 6300


spring:
  application:
    name: turbine

eureka:
  instance:
    hostname: 127.0.0.1
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka



turbine:
  app-config: CONSUMER,ORDER
  aggregator:
    cluster-config: consumerdsahboard,orderdsahboard
  cluster-name-expression: metadata['dsahboardcluster']
View Code

Gateway

网关,思考一个问题,再服务内部使用OpenFeign按照服务名可以负载均衡(Ribbon)的访问集群服务。现在我们面对客户端该如何提供IP呢?

例如当前order服务集群中有三台服务器,用户需要访问order服务,我们不能把三个服务的IP+Port给用户,所以我们需要对外统一提供一

个入口,就是网关。网关的类型有很多比如netflix的zuul和spring cloud自己开发的Gateway。中心思想就是将网关从注册中心拉去服务注册表

用户访问网关时通过head,url等方式将自己要真正方法的服务名和controller交给网关,网关内部通过规则解析,然后匹配注册表中的服务,

最终转发。在这里负载均衡依然使用到了Ribbon,我们也可以把网关和hystrix整合使用,做服务熔断降级。

  <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
View Code
server:
  port: 6001

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka




spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: consumerPath
          uri: lb://consumer
          predicates:
            - Path=/gateway/c/**
            - Header=X-Request-Id, d+
          filters:
            - StripPrefix=2
            
        - id: consumerPath
          uri: lb://order
          predicates:
            - Path=/gateway/o/**
          filters:
            - StripPrefix=2
View Code

以上代码片段引入eureka客户端依赖和gateway依赖,yml中除了配置eureka之外,还有gateway自身的配置。

首先gateway服务不需要任何的注解,只要引入依赖就可以使用,所以如果有依赖包但是想关闭网关。设置

spring.cloud.gateway.enabled=false 表示不开启网关

spring.cloud.gateway.routes 这里的routes,routes一组route的集合,route是网关的构造块,它包含ID,

目标URL,predicates包含一组predicate是匹配器集合(官方给出了11种,但是可以互相配合使用),必须要匹配集合内的所有条件才算通过(与的关系)。

filters对过滤器再匹配规则需要转发到具体的服务,filter对转发前对请求做操作或者得到响应后对结果做操作所有的

filter都会做处理。  

如上代码:id为consumerPath,其转发到lb://consumer,lb://xxx  是内部服务的格式,其实也可以直接

写IP,例如需要转发到www.baidu.com predicates里有两组匹配规则,Path规定url中需要有指定的路径,

Header规定必须有X-Request-Id的请求头,请求头的值是数字。最后filters有一个StripPrefix它修改了路径

predicates已经满足,准备转发到consumer服务,但/gateway/c/consumer1这个路径是多出来/gateway/c/这

一部分的,2就是去掉两级路径。

扩展写法

以上写法是简化写法,大部分也这样用,但是有些route或者filter参数比较多,gateway提供了完整的配置格式。

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - name: Cookie
          args:
            name: mycookie
            regexp: mycookievalue
View Code

以上代码片段是一个 -Cookie route,再下边的代码示例中有的用的就是这种完整配置。

同时匹配到多个route

若同一个路径能匹配到多个route则第一个生效。

11个Route

route之 - Before

Before匹配规则,规定只有在某个时间之前的请求才会接收,否则404。以下代码片段规定了只有在 2020-08-01T23:20:00.000+08:00[Asia/Shanghai]

这个写法是固定的,年-月-日T时:分:秒.毫秒+8:00(东八区)[时区标记]。要根据服务器的时区规定。满足上边时间要求的会转发到博客园的官网,否则

404。看我下图两个结果,20之前的可以转发,20的就不行了。

server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: consumerPath
            uri: https://www.cnblogs.com
            predicates:
            - Before=2020-08-01T23:20:00.000+08:00[Asia/Shanghai]
View Code

route之 - After

这个规则和上边那个刚好相反,它规定必须在某个时间之后才可以访问,否则404。

server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: consumerPath
            uri: https://www.cnblogs.com
            predicates:
            - After=2020-08-01T23:30:00.000+08:00[Asia/Shanghai]
View Code

route之 - Between

这个规则接收两个时间,只有在两个时间之间的才会被转发。

server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: consumerPath
            uri: https://www.cnblogs.com
            predicates:
            - Between=2020-08-01T23:34:00.000+08:00[Asia/Shanghai],2020-08-01T23:36:00.000+08:00[Asia/Shanghai]
View Code

注意!以上三种转发方式如果uri是这样的 http://localhost:9100/order1 本地路径会转发失败,我在测试时只能转发到域名地址。

route之 - Cookie

请求需要带指定的cookie,否则404,需要多个就设置多个-Cookie value可以用Java正则表达式(PS:正则不太会就不做例子了)

server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: consumerPath
            uri: lb://order/
            predicates:
            - Path=/gateway/o/**
            - Cookie=cookiekey1,cookievalue1
            filters:
            - StripPrefix=2
View Code

route之 - Header

请求需要带指定的header,否则404,需要多个就设置多个-Header。value可以用Java正则表达式(PS:正则不太会就不做例子了)

server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: consumerPath
            uri: lb://order/
            predicates:
            - Path=/gateway/o/**
            - Header=headkey1, headvalue1
            - Header=headkey2, headvalue2
            filters:
            - StripPrefix=2
View Code

route之 - Host(get不到用法)

server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: consumerPath
            uri: lb://order/
            predicates:
            - Path=/gateway/o/**
            - Host=www.datang.com**
            filters:
            - StripPrefix=2
View Code

route之 - Method

这个route就比较霸道了,下面代码片段order服务有三个接口分别是GET,POST,PUT,但是gateway设置只允许

GET,POST请求可以放行,PUT请求就是404。

server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: consumerPath
            uri: lb://order/
            predicates:
            - Path=/gateway/o/**
            - Method=GET,POST
            filters:
            - StripPrefix=2
View Code
package com.dfsn.cloud.order.controller;

import com.dfsn.cloud.order.vo.Person;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @GetMapping(value = "order")
    public String order1(Person person) {
        return "order-9100";
    }

    @PostMapping(value = "order")
    public String order2() {
        return "order-9100";
    }
    
    @PutMapping(value="order")
    public String order3() {
        return "order-9100";
    }

}
View Code

route之 - Path

这个是最经常用到的,请求包含某个路径则转发。以下代码片段主要路径包括 o**或者p**就转发到order。

order服务中有三个接口分别是order,porder.sorder。最后一个则不会被转发。

package com.dfsn.cloud.order.controller;

import com.dfsn.cloud.order.vo.Person;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @GetMapping(value = "order")
    public String order1(Person person) {
        return "order-9100";
    }

    @GetMapping(value = "porder")
    public String order2() {
        return "order-9100";
    }
    
    @GetMapping(value = "sorder")
    public String order3() {
        return "order-9100";
    }
}
View Code
server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: consumerPath
            uri: lb://order/
            predicates:
            - Path=/o**,/p**
View Code

route之 - Query

规定参数必须包含一些参数,也可以指定参数的值是多少,值可以用Java正则表达式。

以下代码片段指定必须有name和age参数否则404。

server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: consumerPath
            uri: lb://order/
            predicates:
            - Path=/gateway/o/**
            - Query=name
            - Query=age
            filters:
            - StripPrefix=2
View Code
package com.dfsn.cloud.order.controller;

import com.dfsn.cloud.order.vo.Person;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @GetMapping(value = "order")
    public String order1(Person person) {
        return person.toString();
    }

    @PostMapping(value = "order")
    public String order2(@RequestBody Person person) {
        return person.toString();
    }
}
View Code

我们也可以指定参数的值范围。以下代码片段指定了name的值是张开头,age是2开头,否则404。

以下代码片段指定name必须是A开头并且后边只能有一个字符,age只能是数字(我这蹩脚的正则水平。。。)

server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: consumerPath
            uri: lb://order/
            predicates:
            - Path=/gateway/o/**
            - Query=name,A.
            - Query=age,d+
            filters:
            - StripPrefix=2
View Code

注意参数在请求体不可以,比如JSON格式是不行的。

package com.dfsn.cloud.order.controller;

import com.dfsn.cloud.order.vo.Person;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @GetMapping(value = "order")
    public String order1(Person person) {
        return person.toString();
    }

    @PostMapping(value = "order")
    public String order2(@RequestBody Person person) {
        return person.toString();
    }
}
View Code

route之 - RemoteAddr

限制请求IP,这个也可以用的。如果是内部系统只开放自己局域网内的。

以下代码片段只允许我本机的真实IP访问,所以127.0.0.1就不行。

route之 - Weight

在开头我们看过,如果两个route都匹配则会使用第一个。weight设置权重,如果权重越大

越容易被转发,但是权重小的也是有机会的。以下代码片段order和consumer都有get请求

consumer的权重是2order是1,实验得出转发到consumer几率确实大很多。

server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: consumerPath1
            uri: lb://order/
            predicates:
            - Path=/gateway/g/**
            - Weight=group1, 1
            filters:
            - StripPrefix=2
            
         -  id: consumerPath2
            uri: lb://consumer/
            predicates:
            - Path=/gateway/g/**
            - Weight=group1, 2
            filters:
            - StripPrefix=2
View Code
package com.dfsn.cloud.order.controller;

import com.dfsn.cloud.order.vo.Person;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @GetMapping(value = "get")
    public String order1(Person person) {
        return "order-9100";
    }

    
}
View Code
package com.dfsn.cloud.consumer.controller;

import com.dfsn.cloud.consumer.vo.Person;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class ConsumerController {

    @GetMapping(value = "get")
    public String order1(Person person) {
        return "consumer-8001";
    }


}
View Code

31个Filter

filter之 - AddRequestHeader

这个过滤器用于给请求增加header,以下代码片段增加了两个请求头,head1,head2值是value1,value2。

server:
  port: 6001

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka




spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: consumerPath
          uri: lb://order
          predicates:
            - Path=/gateway/o/**
          filters:
            - StripPrefix=2
            - AddRequestHeader=head1,value1
            - AddRequestHeader=head2,value2
View Code
package com.dfsn.cloud.order.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

@RestController
public class OrderController {

    @GetMapping(value = "order1")
    public String order1(HttpServletRequest request) {
        String head1 = request.getHeader("head1");
        String head2 = request.getHeader("head2");
        return head1 + head2;
    }

}
View Code

filter之 - AddRequestParameter

这个过滤器用于给请求增加参数,以下代码示例增加参数name和age,值为zhangsan,22。

server:
  port: 6001

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka




spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: consumerPath
          uri: lb://order
          predicates:
            - Path=/gateway/o/**
          filters:
            - StripPrefix=2
            - AddRequestParameter=name,zhangsan
            - AddRequestParameter=age,22
View Code
package com.dfsn.cloud.order.controller;

import com.dfsn.cloud.order.vo.Person;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @GetMapping(value = "order1")
    public String order1(Person person) {

        return person.getName() + person.getAge();
    }

}
View Code

filter之 - AddResponseHeader

这个过滤器给请求响应增加header,以下代码片段增加了两个header,header1,header2,值分别是value1,value2。

server:
  port: 6001

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka




spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: consumerPath
          uri: lb://order
          predicates:
            - Path=/gateway/o/**
          filters:
            - StripPrefix=2
            - AddResponseHeader=head1,value1
            - AddResponseHeader=head2,value2
View Code

filter之 - DedupeResponseHeader

这个过滤器用于去除重复的响应header,以下代码片段再order的服务中设置了两个header,再gateway又设置两个名称相同但值不同的header。

使用这个过滤器则可以去掉重复的header。

package com.dfsn.cloud.order.controller;


import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;

@RestController
public class OrderController {

    @GetMapping(value = "order1")
    public String order1(HttpServletResponse response) {
        response.setHeader("header1", "zhangsan");
        response.setHeader("header2", "22");
        return "order-9100";
    }

}
View Code
server:
  port: 6001

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka




spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: consumerPath
          uri: lb://order
          predicates:
            - Path=/gateway/o/**
          filters:
            - StripPrefix=2
            - AddResponseHeader=header1,lisi
            - AddResponseHeader=header2,11
View Code

server:
  port: 6001

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka




spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: consumerPath
          uri: lb://order
          predicates:
            - Path=/gateway/o/**
          filters:
            - StripPrefix=2
            - AddResponseHeader=header1,lisi
            - AddResponseHeader=header2,11
            - DedupeResponseHeader=header1 header2
View Code

filter之 - Hystrix

这个filter可以引入hystrix的熔断可降级。以下代码片段中设置了一个HystrexCommand就是之前讲过的

hystrix.command.default 之前用default搞全局的现在换成一个具体的名字。其他的设置一样,在filter中

引用。设置超时时间为2秒,order服务休眠3秒就报错了。需要引入hystrix的依赖。经过测试只有请求超时

时才会走降级,如果是转发的服务内部异常则不会降级。

server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
hystrix:
   command:
      orderCommand:
         execution:
            timeout:
               enabled: true
            isolation:
               thread:
                  timeoutInMilliseconds: 2000
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: consumerPath1
            uri: lb://order/
            predicates:
            - Path=/gateway/g/**
            filters:
            - StripPrefix=2
            - Hystrix=orderCommand
View Code
package com.dfsn.cloud.order.controller;

import com.dfsn.cloud.order.vo.Person;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @GetMapping(value = "get")
    public String order1(Person person) {
        try {
            Thread.sleep(3000);
        }catch(Exception e) {}
        return "order-9100";
    }

    
}
View Code
    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
View Code

同时也可以设置对应的fallback降级方法,这需要我们在当前gateway工程内写一个可被转发的Controller,为了让所有的order接口都适用

我们用@RequestMapping注解方法,不使用具体的method。

package com.dfsn.cloud.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableEurekaClient
@RestController
public class Gateway_6001 {

    public static void main(String[] args) {
        SpringApplication.run(Gateway_6001.class, args);
    }

    @RequestMapping(value = "/orderFallback")
    public String fallback() {
        return "orderback";
    }
}
View Code
server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
hystrix:
   command:
      orderCommand:
         execution:
            timeout:
               enabled: true
            isolation:
               thread:
                  timeoutInMilliseconds: 2000
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: consumerPath1
            uri: lb://order/
            predicates:
            - Path=/gateway/g/**
            filters:
            - StripPrefix=2
            -  name: Hystrix
               args:
                  name: orderCommand
                  fallbackUri: forward:/orderFallback
View Code

fallbackUri 也可以使用 lb: 做负载均衡,需要我们在做一个route,route匹配fallbackUri的值即可。

以下代码片段最终转发到了consumer服务中。

server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
hystrix:
   command:
      orderCommand:
         execution:
            timeout:
               enabled: true
            isolation:
               thread:
                  timeoutInMilliseconds: 2000
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: consumerPath1
            uri: lb://order/
            predicates:
            - Path=/gateway/g/**
            filters:
            - StripPrefix=2
            -  name: Hystrix
               args:
                  name: orderCommand
                  fallbackUri: forward:/orderFallbacklb
         -  id: fallbackPath
            uri: lb://consumer/
            predicates:
            - Path=/orderFallbacklb
            
            
            
            
            
            
            
            
View Code
package com.dfsn.cloud.consumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@EnableFeignClients
@SpringBootApplication
@EnableHystrix
@RestController
public class Consumer_8001 {

    public static void main(String[] args)  {
        SpringApplication.run(Consumer_8001.class, args);
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    

    @RequestMapping(value = "/orderFallbacklb")
    public String fallback() {
        return "orderback";
    }
}
View Code

filter之 - CircuitBreaker

这个过滤器对转发的服务做预熔断处理,设置格式和 - Hystrix 类似。以下代码片段设置了熔断计划,一个时间窗(10秒)

最低请求10个并且错误率100%则开启熔断,此后一分钟内的请求都会被熔断。这里有个问题是当-CircuitBreaker和-Hystrix

同时使用会出现-CircuitBreaker不生效。同样的,降级方法可以是再本项目,也可以是任意一个 lb: 这取决于你的代码设置。

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
hystrix:
  command:
    orderCircuitBreaker:
      circuitBreaker:
        enabled: true
        requestVolumeThreshold: 10
        errorThresholdPercentage: 50
        sleepWindowInMilliseconds: 60000
spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: consumerPath1
          uri: lb://order/
          predicates:
            - Path=/gateway/g/**
          filters:
            - StripPrefix=2
            - name: CircuitBreaker
              args:
                name: orderCircuitBreaker
                fallbackUri: forward:/orderFallback
View Code

 

filter之 - FallbackHeaders

无论是熔断还是超时都可以把降级方法写道别的服务里,通过gateway转发。FallbackHeaders

会帮我生成一个header,这个header包含具体的错误信息。

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



hystrix:
  command:
    orderTimeout:
      execution:
        timeout:
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 2000




spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: consumerPath1
          uri: lb://order/
          predicates:
            - Path=/gateway/g/**
          filters:
            - StripPrefix=2
            - name: Hystrix
              args:
                name: orderTimeout
                fallbackUri: forward:/exeorderFallback

        - id: fallbackpath
          uri: lb://consumer/
          predicates:
            - Path=/exeorderFallback
          filters:
            - name: FallbackHeaders
              args:
                executionExceptionTypeHeaderName: Test-Header
View Code
package com.dfsn.cloud.consumer.controller;

import com.dfsn.cloud.consumer.feigns.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;


@RestController
public class ConsumerController {

    @Autowired
    private OrderService orderService;

    @GetMapping(value = "consumer1")
    public String consumer1() {
        return orderService.order1();
    }


    @RequestMapping(value = "/exeorderFallback")
    public String s(HttpServletRequest httpServletRequest) {
        String header = httpServletRequest.getHeader("Test-Header");
        System.out.println(header);
        return "转发到consumer处理熔断" + header;
    }

}
View Code

filter之 - MapRequestHeader

给请求添加header但是它和 -AddRequestHeader不同,它接收两个参数,A,B如果请求头中不包含B则会增加一个B头

B值是A头的值。若A头也不存在则什么都不做,若B头存在则增强B头,也就是一个头两个值。

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka


spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: consumerPath1
          uri: lb://order/
          predicates:
            - Path=/gateway/o/**
          filters:
            - StripPrefix=2
            - MapRequestHeader=name,nikename
View Code
package com.dfsn.cloud.order.controller;


import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;


@RestController
public class OrderController {

    @GetMapping(value = "order1")
    public String order1(HttpServletRequest request) {
        String name = request.getHeader("name");
        Enumeration<String> nikename1 = request.getHeaders("nikename");
        StringBuilder sb = new StringBuilder();
        while (nikename1.hasMoreElements()) {
            String s = nikename1.nextElement();
            sb.append(s);
        }
        return name + sb.toString();
    }

}
View Code

 

 

 

filter之 - PrefixPath

向下游转发时增加一个路径。如下代码,order中的controller的path是/mypath/order1。而请求的是 http://localhost:6001/gateway/o/order1

通过 - StripPrefix 去掉两级后剩下order1,在加上PrefixPath=/mypath 刚好够。

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka


spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: consumerPath1
          uri: lb://order/
          predicates:
            - Path=/gateway/o/**
          filters:
            - StripPrefix=2
            - PrefixPath=/mypath
View Code
package com.dfsn.cloud.order.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class OrderController {

    @GetMapping(value = "/mypath/order1")
    public String order1() {
        return "order-9100";
    }

}
View Code

filter之 - PreserveHostHeader

此筛选器设置一个请求属性,路由筛选器将对该属性进行检查,以确定是否应该发送原始主机标头,而不是由HTTP客户机确定的主机标头。

翻译的结果Q_Q 我这边做测试添加这个过滤器请求头都一样,不知道是干啥的。

filter之 - RequestRateLimiter

使用这个filter对请求做限流处理。目前文档上提供了一种令牌桶限流策略,需要和Redis结合使用,给出限定请求的规则,在配置令牌策略

如下代码片段,从获取每一个请求的路径,RequestRateLimiter会根据访问的路径生成对应的令牌。当前设置每秒生成5个令牌(replenishRate),

最多存储5个(burstCapacity),每个请求消耗1个令牌(requestedTokens也就限制了一个请求每秒最多5次访问。

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  redis:
    host: redisIP
    password: redis密码
    port: 6379
    database: 0
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: consumerPath1
          uri: lb://order/
          predicates:
            - Path=/gateway/o/**
          filters:
            - StripPrefix=2
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 5
                redis-rate-limiter.burstCapacity: 5
                redis-rate-limiter.requestedTokens: 1
View Code
package com.dfsn.cloud.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;


@SpringBootApplication
@RestController
public class Gateway_6001 {


    public static void main(String[] args) {
        SpringApplication.run(Gateway_6001.class, args);
    }

    @Bean
    KeyResolver userKeyResolver() {
        return new KeyResolver() {
            @Override
            public Mono<String> resolve(ServerWebExchange exchange) {
                return Mono.just(exchange.getRequest().getPath().value());
            }
        };
    }


}
View Code

filter之 -  RedirectTo

对转发做重定向,但是我只测试出了怎么转发到外部网站,转发到内部,不知道怎么弄。访问http://localhost:6001/redirect 会转发到 www.cnblogs.com

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/redirect
          filters:
            - RedirectTo=302,www.cnblogs.com
View Code 

filter之 - RemoveRequestHeader

删除请求头

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/gateway/o/*
          filters:
            - StripPrefix=2
            - RemoveRequestHeader=age
            - RemoveRequestHeader=name
View Code

filter之  - RemoveResponseHeader

删除响应头

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/gateway/o/*
          filters:
            - StripPrefix=2
            - RemoveResponseHeader=a
            - RemoveResponseHeader=b
View Code

filter之 - RemoveRequestParameter

删除表单参数

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/gateway/o/*
          filters:
            - StripPrefix=2
            - RemoveRequestParameter=name
            - RemoveRequestParameter=age
View Code

filter之 - RewritePath

如果匹配到路径则替换匹配到的路径。以下代码示例,如果路径包含/gateway/o/,则将它替换为/gateway/o/mypath

最终成功转发到order服务的/mypath/order1

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/gateway/o/*
          filters:
            - RewritePath=/gateway/o/,/gateway/o/mypath/
            - StripPrefix=2
View Code
package com.dfsn.cloud.order.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;


@RestController
public class OrderController {

    @GetMapping(value = "/mypath/order1")
    public String order1(HttpServletResponse response) {
        return "order1-9100";
    }


}
View Code

filter之 - RewriteLocationResponseHeader

filter之 - RewriteLocationResponseHeader
可接收参数 stripVersionMode,locationHeaderName,hostValue,protocolsRegex

stripVersionMode:可选值有

         NEVER_STRIP:即使原始请求路径不包含版本,版本也不会被剥离。

         AS_IN_REQUEST:只有当原始请求路径不包含版本时,版本才会被剥离。 

         ALWAYS_STRIP:版本总是被剥离,即使原始请求路径包含版本。

locationHeaderName:固定值 Location

hostValue: 如果提供了“hostValue”参数,则用于替换响应“Location”头部的“host:port”部分。如果没有提供,则使用“Host”请求头的值。

protocolsRegex:参数必须是一个有效的regex“字符串”,协议名必须与之匹配。如果不匹配,过滤器什么也不做。默认是“http|https|ftp|ftps”。

这么多参数我只测试除了一个有用,就是 hostValue,可能我没有彻底理解透。

以下代码示例order1在增加响应头Location,增加状态码301,浏览器接到响应后会重定向到该服务的order2。但是增加了filter后,使用hostValue

参数替换了Host:Port,最终重定向到了consumer服务。 

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/gateway/o/*
          filters:
            - StripPrefix=2
            - RewriteLocationResponseHeader=AS_IN_REQUEST, Location,localhost:8001,
View Code
package com.dfsn.cloud.order.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;


@RestController
public class OrderController {

    @GetMapping(value = "/order1")
    public String order1(HttpServletResponse response) {
        response.setHeader("Location", "http://localhost:6001/gateway/o/order2");
        response.setStatus(301);
        return "order1-9100";
    }

    @GetMapping(value = "/order2")
    public String order2() {
        return "order2-9100";
    }

}
View Code
package com.dfsn.cloud.consumer.controller;

import com.dfsn.cloud.consumer.feigns.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;



@RestController
public class ConsumerController {

    @Autowired
    private OrderService orderService;

    @GetMapping(value = "consumer1")
    public String consumer1() {
        return "consumer1";
    }


    @GetMapping(value = "/gateway/o/order2")
    public String consumer2() {
        return "我是consumer8001";
    }




}
View Code

filter之 - RewriteResponseHeader

替换响应头的值,三个参数 1 需要替换header,2 替换值的哪一部分(正则表达式)3 替换为

如下,替换color响应头的RRRR值为TTTT。

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/gateway/o/*
          filters:
            - StripPrefix=2
            - RewriteResponseHeader=color,R,T
View Code
package com.dfsn.cloud.order.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;


@RestController
public class OrderController {

    @GetMapping(value = "/order1")
    public String order1(HttpServletResponse response) {
        response.setHeader("color","RRRR");
        return "order1-9100";
    }


}
View Code

filter之 - SaveSession

在转发请求之前,强制执行WebSession::save操作主要用在那种像 Spring Session 延迟数据存储(数据不是立刻持久化)的,

并希望在请求转发前确保session状态保存情况。如果你将Spring Secutiry于Spring Session集成使用,并想确保安全信息都传到下游机器,

你就需要配置这个filter。

filter之 - SecureHeaders 

为原始响应添加了一系列起安全作用的响应头(PS这一个和上一个不做示例,关于session和Secure)

filter之 SetPath 

它允许我们将路径的替换为指定的值。如下示例,我们的路径本是需要去掉/gateway/o 这一部分

但现在不用 -StripPrefix 而是将路径全部替换为/{path}的值。

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/gateway/o/{path}
          filters:
            #- StripPrefix=2
            - SetPath=/{path}
View Code

filter之 - SetRequestHeader

替换请求头header的值,如下代码片段,把用户传递的X-Request-Red请求头的值替换成了 Blue

package com.dfsn.cloud.order.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;


@RestController
public class OrderController {

    @GetMapping(value = "/order1")
    public String order1(HttpServletRequest request) {
        Enumeration<String> headers = request.getHeaders("X-Request-Red");

        StringBuilder sb = new StringBuilder();

        while (headers.hasMoreElements()){
            String s = headers.nextElement();
            sb.append(s);
        }

        return sb.toString();
    }


}
View Code
server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/gateway/o/{path}
          filters:
            - SetPath=/{path}
            - SetRequestHeader=X-Request-Red, Blue
View Code

filter之 - SetResponseHeader

修改响应头的值

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/gateway/o/{path}
          filters:
            - SetPath=/{path}
            - SetResponseHeader=X-Response-Red, Blue
View Code
package com.dfsn.cloud.order.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;


@RestController
public class OrderController {

    @GetMapping(value = "/order1")
    public String order1(HttpServletResponse response) {
        response.setHeader("X-Response-Red","Green");
        return "order1";
    }


}
View Code

filter之 - SetStatus

修改响应状态

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/gateway/o/{path}
          filters:
            - SetPath=/{path}
            - SetStatus=201
View Code

filter之 - StripPrefix

如果是从头看到这里,这个过滤器不用说了吧。转发到下游之前去掉几个 /xx 

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/gateway/o/**
          filters:
            - StripPrefix=2
View Code

filter之 - Retry

对于请求失败的做重试。以下配置规定了只有GET请求会重试,Get请求总共发了四次,三次为重试。POST没有重试。

server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
hystrix:
   command:
      orderCommand:
         execution:
            timeout:
               enabled: true
            isolation:
               thread:
                  timeoutInMilliseconds: 2000
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: orderid
            uri: lb://order/
            predicates:
            - Path=/gateway/o/**
            filters:
            - StripPrefix=2
            -  name: Retry
               args:
                  retries: 3
                  statuses: BAD_GATEWAY
                  methods: GET
                  exceptions:
                  - java.lang.ArithmeticException
                  
                 
View Code
order1请求进来了
2020-08-09 09:18:38.989 ERROR 5268 --- [nio-9100-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root cause

java.lang.ArithmeticException: / by zero
    at com.dfsn.cloud.order.controller.OrderController.order1(OrderController.java:14) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_251]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_251]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_251]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_251]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108) ~[spring-boot-actuator-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_251]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_251]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_251]

order1请求进来了
2020-08-09 09:18:39.063 ERROR 5268 --- [nio-9100-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root cause

java.lang.ArithmeticException: / by zero
    at com.dfsn.cloud.order.controller.OrderController.order1(OrderController.java:14) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_251]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_251]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_251]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_251]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108) ~[spring-boot-actuator-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_251]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_251]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_251]

order1请求进来了
2020-08-09 09:18:39.085 ERROR 5268 --- [nio-9100-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root cause

java.lang.ArithmeticException: / by zero
    at com.dfsn.cloud.order.controller.OrderController.order1(OrderController.java:14) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_251]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_251]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_251]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_251]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108) ~[spring-boot-actuator-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_251]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_251]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_251]

order1请求进来了
2020-08-09 09:18:39.109 ERROR 5268 --- [nio-9100-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root cause

java.lang.ArithmeticException: / by zero
    at com.dfsn.cloud.order.controller.OrderController.order1(OrderController.java:14) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_251]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_251]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_251]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_251]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108) ~[spring-boot-actuator-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_251]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_251]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_251]

2020-08-09 09:20:21.331  INFO 5268 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver      : Resolving eureka endpoints via configuration
View Code
order21请求进来了
2020-08-09 09:20:46.521 ERROR 5268 --- [nio-9100-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root cause

java.lang.ArithmeticException: / by zero
    at com.dfsn.cloud.order.controller.OrderController.order2(OrderController.java:21) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_251]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_251]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_251]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_251]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108) ~[spring-boot-actuator-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_251]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_251]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.29.jar:9.0.29]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_251]
View Code

上面的重试过滤器,有5个配置:

retries:默认为3,用来标识重试次数

series:用来指定哪些段的状态码需要重试,默认SERVER_ERROR,即5xx。

statuses:用于指定哪些状态需要重试,默认为空,它跟series至少得指定一个。一般不怎么配置这个。

methods:于指定那些方法的请求需要重试,默认为GET

exceptions:用于指定哪些异常需要重试,默认为java.io.IOException

backoff:为重试机制的时间设置,默认为disabled不配置。

除了retriesexceptions,其他3项的对应的枚举类为:

series:org.springframework.http.HttpStatus.Series

statuses:org.springframework.http.HttpStatus

methods:org.springframework.http.HttpMethod

最后贴一个官方的配置。
spring:
  cloud:
    gateway:
      routes:
      - id: retry_test
        uri: http://localhost:8080/flakey
        predicates:
        - Host=*.retry.com
        filters:
        - name: Retry
          args:
            retries: 3
            statuses: BAD_GATEWAY
            methods: GET,POST
            backoff:
              firstBackoff: 10ms
              maxBackoff: 50ms
              factor: 2
              basedOnPreviousValue: false
View Code

filter之 - RequestSize

限制请求大小。

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/gateway/o/**
          filters:
            - StripPrefix=2
            - name: RequestSize
              args:
                maxSize: 1
View Code

filter之 - SetRequestHost

写入报错,用JAVA编码方式,发现没有此方法,可能此方法已经移除,但文档未修改。

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/gateway/o/**
          filters:
            - StripPrefix=2
            - name: SetRequestHost
              args:
                host: example.org
View Code

filter之 - ModifyRequestBody 

之前有修改请求参数的,但是那只限于表单请求,而这个filter是可以修改 requestBody请求体里的内容的。

官方文档给出的方式是通过JAVA代码形式,没有yml方式的,所以我这里也是代码形式。其实是可以用yml我在

一篇博客上看到过。另外代码形式使用的lambda表达式如果看不明白,可以先看这个章节以上yml配置都可以用JAVA编程方式实现

在这里我将lambda方式转换成了Java方式提供给你参考。

尽管如此这里我还是要大概说下,modifyRequestBody(Class<T> inClass, Class<R> outClass,String newContentType, RewriteFunction<T, R> rewriteFunction)方法

接收四个参数第一个是body体里的参数类型,无疑它是String.class,第二个是过滤器发往下游的类型,在我的Order

服务中,接口使用Person对象接收,而我的过滤器仅仅是对参数进行了一些改变,并没有直接改变类型,所以这里也是

Person.class,第三个是ContentType我也没有改变使用的还是application/json第四个则是具体处理的方法。这里是一个

Function接口,这个接口就是处理具体的逻辑的。

package com.dfsn.cloud.gateway;

import java.util.function.Function;

import org.reactivestreams.Publisher;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.Route.AsyncBuilder;
import org.springframework.cloud.gateway.route.builder.GatewayFilterSpec;
import org.springframework.cloud.gateway.route.builder.PredicateSpec;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.route.builder.UriSpec;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;

import com.alibaba.fastjson.JSONObject;
import com.dfsn.cloud.vo.Person;

import reactor.core.publisher.Mono;

@SpringBootApplication
@EnableEurekaClient
@RestController
public class Gateway_6001 {

    public static void main(String[] args) {
        SpringApplication.run(Gateway_6001.class, args);
    }

    // 這是lambda
    @Bean
    public RouteLocator routes(RouteLocatorBuilder builder) {
        return builder.routes().route("orderid", p -> p.path("/gateway/o/**").filters(g -> g.stripPrefix(2)
                .modifyRequestBody(String.class, Person.class, MediaType.APPLICATION_JSON_VALUE, (e, s) -> {
                    Person oldPerson = JSONObject.parseObject(s, Person.class);
                    Person newPerson = new Person();
                    newPerson.setAge(oldPerson.getAge() + 100);
                    newPerson.setName(oldPerson.getAge() + "---");
                    return Mono.just(newPerson);
                })).uri("lb://order/")).build();
    }

    // 这是对应的JAVA编码
    // @Bean
    public RouteLocator routes2(RouteLocatorBuilder builder) {
        return builder.routes().route("orderid", new Function<PredicateSpec, Route.AsyncBuilder>() {
            @Override
            public Route.AsyncBuilder apply(PredicateSpec t) {
                t.path("/gateway/o/**").filters(new Function<GatewayFilterSpec, UriSpec>() {
                    @Override
                    public UriSpec apply(GatewayFilterSpec t) {
                        return t.stripPrefix(2).modifyRequestBody(String.class, Person.class,
                                MediaType.APPLICATION_JSON_VALUE, new RewriteFunction<String, Person>() {
                                    @Override
                                    public Publisher<Person> apply(ServerWebExchange s, String t) {
                                        Person oldPerson = JSONObject.parseObject(t, Person.class);
                                        Person newPerson = new Person();
                                        newPerson.setAge(oldPerson.getAge() + 100);
                                        newPerson.setName(oldPerson.getAge() + "---");
                                        return Mono.just(newPerson);
                                    }
                                });
                    }
                });
                return t.uri("lb://order/");
            }
        }).build();
    }


}
View Code
server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
hystrix:
   command:
      orderCommand:
         execution:
            timeout:
               enabled: true
            isolation:
               thread:
                  timeoutInMilliseconds: 2000
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
#         -  id: orderid
#            uri: lb://order/
#            predicates:
#            - Path=/gateway/o/**
#            filters:
#           - StripPrefix=2
            
            
            
            
            
            
            
            
View Code
package com.dfsn.cloud.order.controller;

import com.dfsn.cloud.order.vo.Person;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @PostMapping(value = "person")
    public String order1(@RequestBody Person person) {
        return person.getName()+"!!!!!"+person.getAge();
    }

    
}
View Code

filter之 - ModifyResponseBody 

修改返回值body中的信息,这个写法就和以上类似了。

package com.dfsn.cloud.gateway;

import java.util.function.Function;

import org.reactivestreams.Publisher;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.Route.AsyncBuilder;
import org.springframework.cloud.gateway.route.builder.GatewayFilterSpec;
import org.springframework.cloud.gateway.route.builder.PredicateSpec;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.route.builder.UriSpec;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.web.server.ServerWebExchange;

import com.alibaba.fastjson.JSONObject;
import com.dfsn.cloud.vo.Person;

import reactor.core.publisher.Mono;

@SpringBootApplication
@EnableEurekaClient
public class Gateway_6001 {

    public static void main(String[] args) {
        SpringApplication.run(Gateway_6001.class, args);
    }

    // 這是lambda
    @Bean
    public RouteLocator routes(RouteLocatorBuilder builder) {
        return builder.routes().route("orderid", p -> p.path("/gateway/o/**").filters(g -> g.stripPrefix(2)
                .modifyResponseBody(String.class, String.class, MediaType.APPLICATION_JSON_VALUE, (e, s) -> {
                    Person oldPserson = JSONObject.parseObject(s, Person.class);
                    Person newPerson = new Person();
                    newPerson.setAge(oldPserson.getAge() + 50);
                    newPerson.setName(oldPserson.getName() + "%%%");
                    String jsonString = JSONObject.toJSONString(newPerson);
                    return Mono.just(jsonString);
                })).uri("lb://order/")).build();
    }

    // 这是对应的JAVA编码
    //@Bean
    public RouteLocator routes2(RouteLocatorBuilder builder) {
        return builder.routes().route("orderid", new Function<PredicateSpec, Route.AsyncBuilder>() {
            @Override
            public Route.AsyncBuilder apply(PredicateSpec t) {
                t.path("/gateway/o/**").filters(new Function<GatewayFilterSpec, UriSpec>() {
                    @Override
                    public UriSpec apply(GatewayFilterSpec t) {
                        return t.stripPrefix(2).modifyResponseBody(String.class, String.class,
                                MediaType.APPLICATION_JSON_VALUE, new RewriteFunction<String, String>() {
                                    @Override
                                    public Publisher<String> apply(ServerWebExchange t, String u) {
                                        Person oldPserson = JSONObject.parseObject(u, Person.class);
                                        Person newPerson = new Person();
                                        newPerson.setAge(oldPserson.getAge() + 50);
                                        newPerson.setName(oldPserson.getName() + "%%%");
                                        String jsonString = JSONObject.toJSONString(newPerson);
                                        return Mono.just(jsonString);
                                    }
                                });
                    }
                });
                return t.uri("lb://order/");
            }
        }).build();
    }

}
View Code
package com.dfsn.cloud.order.controller;

import com.dfsn.cloud.order.vo.Person;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @GetMapping(value = "person")
    public Person order1() {
        Person person = new Person();
        person.setAge(22);
        person.setName("小名");
        return person;
    }

    
}
View Code

filter之  default-filters

如果有多个route每个里边都有filter,必然会有一两种过滤器是通用的。所以可以抽成公用组件。

以下代码片段把 - StripPrefix=2 抽成默认的,所有route都可以用。

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka



spring:
  application:
    name: gateway
  cloud:
    gateway:
      default-filters:
        - StripPrefix=2
      enabled: true
      routes:
        - id: orderPath1
          uri: lb://order
          predicates:
            - Path=/gateway/o/**
View Code

以上yml配置都可以用JAVA编程方式实现

不熟悉Lambda的可以参考我的另一篇博客 https://www.cnblogs.com/zumengjie/p/11613043.html

以下代码片段分别是yml配置和JAVA配置,JAVA配置的方式官方文档以及大部分博客都使用的是 route2() Lambda

server:
  port: 6001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka

a: /gateway/o/**

spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: orderPath
          uri: lb://order
          predicates:
            - Path=/gateway/o/**
          filters:
            - StripPrefix=2


        - id: consumerPath
          uri: lb://consumer
          predicates:
            - Path=/gateway/c/**
          filters:
            - StripPrefix=2
View Code
package com.dfsn.cloud.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.GatewayFilterSpec;
import org.springframework.cloud.gateway.route.builder.PredicateSpec;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.route.builder.UriSpec;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RestController;

import java.util.function.Function;


@SpringBootApplication
@RestController
public class Gateway_6001 {


    public static void main(String[] args) {
        SpringApplication.run(Gateway_6001.class, args);
    }


    @Bean
    public RouteLocator routes1(RouteLocatorBuilder builder) {
        return builder.routes().route("orderPath", new Function<PredicateSpec, Route.AsyncBuilder>() {
            @Override
            public Route.AsyncBuilder apply(PredicateSpec predicateSpec) {
                return predicateSpec.path("/gateway/o/**").filters(new Function<GatewayFilterSpec, UriSpec>() {
                    @Override
                    public UriSpec apply(GatewayFilterSpec gatewayFilterSpec) {
                        return gatewayFilterSpec.filters().stripPrefix(2);
                    }
                }).uri("lb://order");
            }

        }).route("consumerPath", new Function<PredicateSpec, Route.AsyncBuilder>() {
            @Override
            public Route.AsyncBuilder apply(PredicateSpec predicateSpec) {
                return predicateSpec.path("/gateway/c/**").filters(new Function<GatewayFilterSpec, UriSpec>() {
                    @Override
                    public UriSpec apply(GatewayFilterSpec gatewayFilterSpec) {
                        return gatewayFilterSpec.filters().stripPrefix(2);
                    }
                }).uri("lb://consumer");
            }

        }).build();
    }

    @Bean
    public RouteLocator routes2(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("orderPath", p -> p.path("/gateway/o/**").filters(g -> g.stripPrefix(2)).uri("lb://order"))
                .route("consumerPath", p -> p.path("/gateway/c/**").filters(g -> g.stripPrefix(2)).uri("lb://consumer"))
                .build();
    }
}
View Code

全局过滤器

我们可以设定全局过滤器,给每一个route使用。多个全局过滤器指定order,值越小,越先触发。

geteway默认的也有很多过滤器,比如我们使用的 lb:// 这是一个LoadBalancerClientFilter

以下代码片段声明了两个自定义全局过滤器,这需要用@Bean配置。

server:
   port: 6001
eureka:
   client:
      register-with-eureka: true
      fetch-registry: true
      service-url:
         defaultZone: http://localhost:7002/eureka,http://localhost:7001/eureka
hystrix:
   command:
      orderCommand:
         execution:
            timeout:
               enabled: true
            isolation:
               thread:
                  timeoutInMilliseconds: 2000
spring:
   application:
      name: gateway
   cloud:
      gateway:
         enabled: true
         routes:
         -  id: orderid
            uri: lb://order/
            predicates:
            - Path=/gateway/o/**
            filters:
            - StripPrefix=2
         -  id: consumerid
            uri: lb://consumer/
            predicates:
            - Path=/gateway/c/**
            filters:
            - StripPrefix=2
View Code
package com.dfsn.cloud.gateway;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Mono;

public class CustomGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("------------------------我是全局过滤器---------------------------------------");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1;
    }
}
View Code
package com.dfsn.cloud.gateway;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Mono;

public class CustomGlobalFilter2 implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("---------------------------------------我是全局过滤器222222222222222---------------------------------------");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 1;
    }
}
View Code
package com.dfsn.cloud.gateway;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;



@SpringBootApplication
@EnableEurekaClient
public class Gateway_6001 {

    public static void main(String[] args) {
        SpringApplication.run(Gateway_6001.class, args);
    }

    @Bean
    public GlobalFilter customFilter() {
        return new CustomGlobalFilter();
    }

    @Bean
    public GlobalFilter customFilter2() {
        return new CustomGlobalFilter2();
    }

}
View Code

原文地址:https://www.cnblogs.com/zumengjie/p/13357863.html