Spring Cloud 版本:
Dalston.SR5
今天使用Zuul發現一個和動態刷新相關的問題,動態刷新使用的是 /bus/refresh,即我的Zuul連着一個Rabbitmq,我這里是使用的總線刷新的方式,普通的刷新/refresh應該也是可以再現這個坑的。
我一共有兩個服務,服務名分別為one和all,刷新之前的路由規則:
zuul: ribbonIsolationStrategy: THREAD retryable: true add-host-header: true servlet-path: /zuul/* prefix: /test routes: #規則 n1: path: /* serviceId: all
改為:
zuul: ribbonIsolationStrategy: THREAD retryable: true add-host-header: true servlet-path: /zuul/* prefix: /test routes: #規則 n0: path: /one serviceId: one n1: path: /* serviceId: all
加了個n0的規則,並且放在了n1前邊,按說刷新之后,如果訪問/test/one,那么應該會由服務【one】來處理,而非服務【all】,結果卻是仍然會交由服務【all】來處理。
很坑是不是,我覺得這是個BUG,目前我還沒有研究源碼,但是通過實驗得出了幾個結論,並且也有了一個不用修改源碼的解決方案,詳情如下:
先說結論:
通過動態刷新的配置,對於路由規則的變動,只能新增和修改,不能刪除;並且新增的規則在匹配順序上,位於老規則的后邊,也就是說,一旦你老規則里配置了個/*,那么后邊的新規則你怎么改都不能生效,仍然會路由到/*對應的服務上。
再說解決辦法:
借助上邊描述的規則,我們可以將配置規則換一種寫法再刷新進去,新的寫法如下:
zuul: ribbonIsolationStrategy: THREAD retryable: true add-host-header: true servlet-path: /zuul/* prefix: /test routes: #規則 n1: path: /one serviceId: one n2: path: /* serviceId: all
這里修改了n1,然后新增了n2,巧妙的借助這個規則完成了,讓/one路由到服務【one】上,其他規則還繼續讓服務【all】來處理。
這中解決方式,感覺還是不完美,后續我們可以通過修改源碼,或者關注社區的方式,糾正這個坑(BUG)。
————————————————————2018年5月15日 追加——————————————————————————
經過調查發現:
zuul獲取路由規則的時候,會從所有路由規則中選擇第一個能正則匹配的,用來處理當前的請求,接着,我們需要看看刷新之后配置的順序是不是有問題。
這里看到順序是按照配置文件的順序進行加載,應該是沒有問題的,結果發現配置文件的順序並不能保證。這里我們先不去管為什么順序不能保證了,因為需要牽扯大量的refresh端點事件、配置中心相關代碼。
這里根據我們的需求直接決定改成優先精確匹配,精確匹配失敗再正則匹配即可,修改后:
修改非常簡單,重在調查問題的過程。
如果你想調查配置中心相關代碼,這里給個思路,或許有所幫助:
調用/bus/refresh時候走了這段。
繼續跟進代碼。
屬性對比的完整截圖,兩個Map,一個是之前的配置內容,一個是修改后的配置內容,最終結果是對比得出的兩者的不同點。
廣播了屬性替換事件,具體廣播的處理者在哪又得跟源碼了,時間有限,就寫到這里吧。歡迎指正和補充
----------------------------------------------------------2018年7月31日 ----------------------------------------------------------------
今天又發現一個新的坑,也是和上文同一個原因所致。
之前的結論:
通過動態刷新的配置,對於路由規則的變動,只能新增和修改,不能刪除;並且新增的規則在匹配順序上,位於老規則的后邊,也就是說,一旦你老規則里配置了個/*,那么后邊的新規則你怎么改都不能生效,仍然會路由到/*對應的服務上。
現在發現同一個路由規則的變動也是適用的,也就是說,如果你之前配了一個路由規則:
zuul: routes: route1: path: /a/** url: http://URL/PATH/
將所有/a/xxx的請求交給下邊這個url處理,之后你通過配刷新的方式改為了:
zuul: routes: route1: path: /a/** serviceId: service1
是沒有效果的,所有/a/xxx的請求還是會被之前那個url處理。
正確的姿勢是改為:
zuul: routes: route1: path: /a/** serviceId: service1 url:
再刷新配置。
總結一下原因:
1、配置刷新目前存在問題無法刪除已有的路由規則,只能修改已有規則和新增規則。
2、在路由的serviceId和url同時存在的時候,會以url為准,只有url為空的時候才會交給serviceId來處理。
該問題在
Edgware.SR3
版本中,仍然未得到解決。也許我該轉向spring cloud gateway了
完畢