大家好,我是不才陳某~
最近知識星球的球友在學習星球中的《精盡Spring Cloud Alibaba》專欄提到一個問題,相信也有很多人在線上環(huán)境遇到過,或許也因此被批過:一個集群中有某個服務突然下線,但是網(wǎng)關(guān)還是會去請求這個實例,所以線上就報錯了,報錯信息如下圖:

究其原因到底為何呢?有沒有一種靠譜的解決方案呢?別著急,往下看
產(chǎn)生原因
Gateway中有個緩存 CachingRouteLocator ,而網(wǎng)關(guān)服務使用的是lb模式,服務在上線或者下線之后,未能及時刷新這個緩存,相應的源碼如下:
public?class?CachingRouteLocator?implements?Ordered,?RouteLocator,
??ApplicationListener<RefreshRoutesEvent>,?ApplicationEventPublisherAware?{
?private?static?final?Log?log?=?LogFactory.getLog(CachingRouteLocator.class);
?private?static?final?String?CACHE_KEY?=?"routes";
?private?final?RouteLocator?delegate;
?private?final?Flux<Route>?routes;
?private?final?Map<String,?List>?cache?=?new?ConcurrentHashMap<>();
?private?ApplicationEventPublisher?applicationEventPublisher;
?public?CachingRouteLocator(RouteLocator?delegate)?{
??this.delegate?=?delegate;
??routes?=?CacheFlux.lookup(cache,?CACHE_KEY,?Route.class)
????.onCacheMissResume(this::fetch);
?}
?private?Flux<Route>?fetch()?{
??return?this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE);
?}
?@Override
?public?Flux<Route>?getRoutes()?{
??return?this.routes;
?}
?/**
??*?Clears?the?routes?cache.
??*?@return?routes?flux
??*/
?public?Flux<Route>?refresh()?{
??this.cache.clear();
??return?this.routes;
?}
?@Override
?public?void?onApplicationEvent(RefreshRoutesEvent?event)?{
??try?{
???fetch().collect(Collectors.toList()).subscribe(list?->?Flux.fromIterable(list)
?????.materialize().collect(Collectors.toList()).subscribe(signals?->?{
??????applicationEventPublisher
????????.publishEvent(new?RefreshRoutesResultEvent(this));
??????cache.put(CACHE_KEY,?signals);
?????},?throwable?->?handleRefreshError(throwable)));
??}
??catch?(Throwable?e)?{
???handleRefreshError(e);
??}
?}
?private?void?handleRefreshError(Throwable?throwable)?{
??if?(log.isErrorEnabled())?{
???log.error("Refresh?routes?error?!!!",?throwable);
??}
??applicationEventPublisher
????.publishEvent(new?RefreshRoutesResultEvent(this,?throwable));
?}
?@Deprecated
?/*?for?testing?*/?void?handleRefresh()?{
??refresh();
?}
?@Override
?public?int?getOrder()?{
??return?0;
?}
?@Override
?public?void?setApplicationEventPublisher(
???ApplicationEventPublisher?applicationEventPublisher)?{
??this.applicationEventPublisher?=?applicationEventPublisher;
?}
}
那么解決方案就自然能夠想出來,只需要在服務下線時能夠去實時的刷新這個緩存自然就解決了
解決方案
這里通過去監(jiān)聽 Nacos 實例刷新事件,一旦出現(xiàn)實例發(fā)生變化馬上刪除緩存。在刪除負載均衡緩存后,Spring Cloud Gateway 在處理請求時發(fā)現(xiàn)沒有緩存會重新拉取一遍服務列表,這樣之后都是用的是最新的服務列表了,也就達到了我們動態(tài)感知上下線的目的。
代碼如下:
@Component
@Slf4j
public??class?NacosInstancesChangeEventListener?extends?Subscriber<InstancesChangeEvent>?{
????@Resource
????private?CacheManager?defaultLoadBalancerCacheManager;
????@Override
????public?void?onEvent(InstancesChangeEvent?event)?{
????????log.info("Spring?Gateway?接收實例刷新事件:{},?開始刷新緩存",?JacksonUtils.toJson(event));
????????Cache?cache?=?defaultLoadBalancerCacheManager.getCache(SERVICE_INSTANCE_CACHE_NAME);
????????if?(cache?!=?null)?{
????????????cache.evict(event.getServiceName());
????????}
????????log.info("Spring?Gateway?實例刷新完成");
????}
????@Override
????public?Class<??extends?com.alibaba.nacos.common.notify.Event>?subscribeType()?{
????????return?InstancesChangeEvent.class;
????}
}
這里通過繼承的方式監(jiān)聽 Nacos 的 InstancesChangeEvent
,在 onEvent 接收到實例刷新的信息后直接刪除對應服務的負載均衡緩存,緩存的名字是定義在 Spring Gateway 的相關(guān)代碼中的,直接引入即可,Cache
則是繼承自 Spring Cache 接口,負載均衡緩存也繼承了 Cache 接口,有了 Cache 接口就可以直接使用其接口定義的 evict 方法即可,而緩存的 key 名就則就是服務名,在 InstancesChangeEvent 中,通過 getServiceName 就可以得到服務名。
這里就不演示了,有興趣的小伙伴可以測試一下
最后說一句(別白嫖,求關(guān)注)
陳某每一篇文章都是精心輸出,如果這篇文章對你有所幫助,或者有所啟發(fā)的話,幫忙點贊、在看、轉(zhuǎn)發(fā)、收藏,你的支持就是我堅持下去的最大動力!
另外陳某的知識星球開通了,公眾號回復關(guān)鍵詞:知識星球 獲取限量30元優(yōu)惠券加入只需89元,一頓飯錢,但是星球回饋的價值卻是巨大,目前更新了Spring全家桶實戰(zhàn)系列、億級數(shù)據(jù)分庫分表實戰(zhàn)、DDD微服務實戰(zhàn)專欄、我要進大廠、Spring,Mybatis等框架源碼、架構(gòu)實戰(zhàn)22講等....每增加一個專欄價格將上漲20元文章來源:http://www.zghlxwxcb.cn/news/detail-617700.html

關(guān)注公眾號:【碼猿技術(shù)專欄】,公眾號內(nèi)有超贊的粉絲福利,回復:加群,可以加入技術(shù)討論群,和大家一起討論技術(shù),吹牛逼!文章來源地址http://www.zghlxwxcb.cn/news/detail-617700.html
到了這里,關(guān)于Spring Cloud Gateway + Nacos 實現(xiàn)服務上下線無縫切換的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!