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>
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
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); } }
啟動項目后可以訪問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

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
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

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>
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); } }

需要注意的是做負載均衡的代碼屬性 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
服務實例配置項(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
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
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
手動清理已經關閉的服務
發送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
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
導入依賴后,配置yml配置。Post訪問 http://localhost:8001/actuator/shutdown 即可停止服務,但是這種方式再次開啟服務
需要手動到服務器啟動。

服務平滑上下線
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
shutdown:
enabled: true
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
配置如上,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>
一般來說,我們不需要單獨引入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"; } }
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); } }
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
<?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>
將以上代碼復制兩份,創建兩個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; } }
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(); } }
在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(); } }
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; } }
自定義負載均衡策略
當我們使用@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>
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
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); } }
首先我們在創建兩個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; } }
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 { }
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(); } }
package com.dfsn.cloud.consumer.config; import com.netflix.loadbalancer.RandomRule; public class MyRule extends RandomRule { @Override protected int chooseRandomInt(int serverCount) { return 0; } }




從結果看,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
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;
}
}
配置文件的格式為 服務名.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 { }
內置負載均衡策略
不依賴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
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
給每個服務配置 listOfServers屬性,該屬性的值為微服務集群的物理IP:Port。以上配置可以完全不依賴Eureka。
OpenFeign
openFeign不在SpringCloudNetflix中,而是單獨的一個項目 https://cloud.spring.io/spring-cloud-openfeign/2.2.x/reference/html
openFeign的使用
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
@SpringBootApplication
@EnableFeignClients
public class Consumer_8001 {
public static void main(String[] args) {
SpringApplication.run(Consumer_8001.class, args);
}
}
@FeignClient("order")
public interface OrderService {
@GetMapping(value = "orderHandler")
public String handler();
}
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(); }
@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; }
導入依賴后在啟動類上加上注解@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; }
@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);
@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); }
超時設置
feign: client: config: order: connectTimeout: 2000 readTimeout: 5000
@GetMapping(value = "orderHandler") public String handler() throws Exception { Thread.sleep(3000); return "order-9100"; }
@GetMapping(value = "producerHandler") public String handler()throws Exception { Thread.sleep(3000); return "producer-9001"; }
以上代碼設置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>
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); } }
@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 "我是降級處理哦"; }

請注意!方法級別的降級處理無論是在遠程調用中異常或者是方法本身異常,都會發生降級處理,這一點很重要。
服務級別的降級處理的兩種實現
服務級別的降級處理需要在feign中開啟使用hystrix
feign.hystrix.enable=true
1 fallbackFactory
feign: hystrix: enabled: true
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降級"; } }; } }
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(); }
2 fallback
feign: hystrix: enabled: true
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(); }
package com.dfsn.cloud.consumer.feigns; import org.springframework.stereotype.Component; @Component public class OrderServiceFallback implements OrderService{ @Override public String handler() { return "fallback降級"; } }
以上兩種方法都可以實現降級處理,區別是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



線程隔離 thread:Hystrix 的默認隔離策略。系統會創建一個依賴線程池,為每個依賴請 求分配一個獨立的線程,而每個依賴所擁有的線程數量是有上限的。
當對該依賴的調用 請求數量達到上限后再有請求,則該請求阻塞。所以對某依賴的並發量取決於為該依賴 所分配的線程數量。
hystrix: threadpool: default: coreSize: 5 maxQueueSize: -1 command: default: execution: timeout: enabled: true isolation: thread: timeoutInMilliseconds: 3000 strategy: THREAD
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"; } }
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降級"; } }; } }
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
以上代碼片段在遠程調用時必然的失敗,而回退方法中休眠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
public class Bean { private int val; public int getVal() { return val; } public void setVal(int val) { this.val = val; } }
@GetMapping(value = "toorder2") public String toOrder2(Integer val) throws Exception { Bean bean = new Bean(); bean.setVal(val); String forObject = orderService.handler(bean); return forObject; }
@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"; }
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>
2 添加配置
management:
endpoints:
web:
exposure:
include: hystrix.stream
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'"
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
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
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
<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>
新建一個項目,作為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>
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'"
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); } }


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
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
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
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
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']






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>
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
以上代碼片段引入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
以上代碼片段是一個 -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]


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]


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]



注意!以上三種轉發方式如果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


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


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


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
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"; } }



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"; } }
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**



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
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(); } }


我們也可以指定參數的值范圍。以下代碼片段指定了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


注意參數在請求體不可以,比如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(); } }

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
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"; } }
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"; } }

31個Filter
這個過濾器用於給請求增加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
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; } }

這個過濾器用於給請求增加參數,以下代碼示例增加參數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
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(); } }

這個過濾器給請求響應增加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

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"; } }
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

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

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
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"; } }
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

同時也可以設置對應的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"; } }
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

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
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"; } }

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


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
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; } }

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
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(); } }




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
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"; } }

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
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()); } }; } }
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
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

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

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

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
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"; } }

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,
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"; } }
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"; } }

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
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"; } }

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}

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(); } }
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

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
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"; } }

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

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

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
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
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]
上面的重試過濾器,有5個配置:
retries:默認為3,用來標識重試次數
series:用來指定哪些段的狀態碼需要重試,默認SERVER_ERROR,即5xx。
statuses:用於指定哪些狀態需要重試,默認為空,它跟series至少得指定一個。一般不怎么配置這個。
methods:於指定那些方法的請求需要重試,默認為GET
exceptions:用於指定哪些異常需要重試,默認為java.io.IOException
backoff:為重試機制的時間設置,默認為disabled不配置。
除了retries、exceptions,其他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
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

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

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(); } }
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
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(); } }
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(); } }
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; } }

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/**
以上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
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(); } }
全局過濾器
我們可以設定全局過濾器,給每一個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
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; } }
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; } }
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(); } }



