成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

基于Redis實(shí)現(xiàn)Spring Cloud Gateway的動(dòng)態(tài)管理

開發(fā) 架構(gòu) Redis
Spring Cloud Gateway是當(dāng)前使用非常廣泛的一種API網(wǎng)關(guān)。它本身能力并不能完全滿足企業(yè)對(duì)網(wǎng)關(guān)的期望,人們希望它可以提供更多的服務(wù)治理能力。

引言:

Spring Cloud Gateway是當(dāng)前使用非常廣泛的一種API網(wǎng)關(guān)。它本身能力并不能完全滿足企業(yè)對(duì)網(wǎng)關(guān)的期望,人們希望它可以提供更多的服務(wù)治理能力。但Spring Cloud Gateway并不提供數(shù)據(jù)的動(dòng)態(tài)管理,甚至修改個(gè)路由都需要重啟。我們?nèi)绾谓鉀Q它這個(gè)短板,同時(shí)實(shí)現(xiàn)治理配置數(shù)據(jù)的高效動(dòng)態(tài)管理呢?本文將帶來我們網(wǎng)關(guān)與Redis組合的實(shí)踐。

[[274496]]

目錄:

1.Spring Cloud Gateway 簡(jiǎn)介

2.網(wǎng)關(guān)數(shù)據(jù)管理

3.實(shí)現(xiàn)細(xì)節(jié)

1.Spring Cloud Gateway 簡(jiǎn)介

API 網(wǎng)關(guān)

API 網(wǎng)關(guān)出現(xiàn)的原因是微服務(wù)架構(gòu)的出現(xiàn),不同的微服務(wù)一般會(huì)有不同的網(wǎng)絡(luò)地址,而外部客戶端可能需要調(diào)用多個(gè)服務(wù)的接口才能完成一個(gè)業(yè)務(wù)需求,如果讓客戶端直接與各個(gè)微服務(wù)通信,會(huì)有以下的問題:

  • 客戶端會(huì)多次請(qǐng)求不同的微服務(wù),增加了客戶端的復(fù)雜性。
  • 存在跨域請(qǐng)求,在一定場(chǎng)景下處理相對(duì)復(fù)雜。
  • 認(rèn)證復(fù)雜,每個(gè)服務(wù)都需要獨(dú)立認(rèn)證。
  • 難以重構(gòu),隨著項(xiàng)目的迭代,可能需要重新劃分微服務(wù)。例如,可能將多個(gè)服務(wù)合并成一個(gè)或者將一個(gè)服務(wù)拆分成多個(gè)。如果客戶端直接與微服務(wù)通信,那么重構(gòu)將會(huì)很難實(shí)施。
  • 某些微服務(wù)可能使用了防火墻 / 瀏覽器不友好的協(xié)議,直接訪問會(huì)有一定的困難。

以上這些問題可以借助 API 網(wǎng)關(guān)解決。API 網(wǎng)關(guān)是介于客戶端和服務(wù)器端之間的中間層,所有的外部請(qǐng)求都會(huì)先經(jīng)過 API 網(wǎng)關(guān)這一層。也就是說,API 的實(shí)現(xiàn)方面更多的考慮。 

 

使用 API 網(wǎng)關(guān)后的優(yōu)點(diǎn)如下:

  • 易于監(jiān)控。可以在網(wǎng)關(guān)收集監(jiān)控?cái)?shù)據(jù)并將其推送到外部系統(tǒng)進(jìn)行分析。
  • 易于認(rèn)證。可以在網(wǎng)關(guān)上進(jìn)行認(rèn)證,然后再將請(qǐng)求轉(zhuǎn)發(fā)到后端的微服務(wù),而無須在每個(gè)微服務(wù)中進(jìn)行認(rèn)證。
  • 減少了客戶端與各個(gè)微服務(wù)之間的交互次數(shù)。

Spring Cloud Gateway

Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技術(shù)開發(fā)的網(wǎng)關(guān),Spring Cloud Gateway旨在為微服務(wù)架構(gòu)提供一種簡(jiǎn)單而有效的統(tǒng)一的API路由管理方式。

Spring Cloud Gateway作為Spring Cloud生態(tài)系中的網(wǎng)關(guān),目標(biāo)是替代Netflix ZUUL,其不僅提供統(tǒng)一的路由方式,并且基于Filter鏈的方式提供了網(wǎng)關(guān)基本的功能,例如:安全,監(jiān)控/埋點(diǎn),和限流等。 

如圖所示,SCG的架構(gòu)看起來很簡(jiǎn)單。

首先,它內(nèi)部包含了一個(gè)高性能的Netty Server,用來接收各類網(wǎng)絡(luò)請(qǐng)求。請(qǐng)求進(jìn)來之后,會(huì)根據(jù)配置的各個(gè)路由進(jìn)行匹配并處理請(qǐng)求。每個(gè)路由都可以定義多個(gè)斷言(Predicate),用于路由匹配。

SCG默認(rèn)提供了10多個(gè)內(nèi)建的斷言,可以基于請(qǐng)求的各個(gè)方面(請(qǐng)求頭,路徑,路徑,時(shí)間,Cookie,http方法等)進(jìn)行路由匹配。如果還不夠,用戶還可以自已擴(kuò)展。

請(qǐng)求匹配到了合適的路由之后,就會(huì)按照路由中配置的各過濾器(filter),按順序?qū)φ?qǐng)求進(jìn)行處理。Filter也基本上可以對(duì)請(qǐng)求的所有屬性做處理,修改,添加或者除請(qǐng)求頭,修改請(qǐng)求數(shù)據(jù),修改返回的數(shù)據(jù)等,幾乎無所不能。當(dāng)然,修改請(qǐng)求也只是一方面的用途,認(rèn)證,鑒權(quán),記錄日志等也都可以在網(wǎng)關(guān)中統(tǒng)一來做。

所有filter形成處理鏈,直到所有的filter處理完,才會(huì)交給最后面的 Netty Client,由它將處理過的請(qǐng)求發(fā)送至對(duì)應(yīng)的微服務(wù)。

在請(qǐng)求發(fā)送至微服務(wù)之前,還可以定義它的負(fù)載均衡策略(LoadBalancerRule),以決定請(qǐng)求至底發(fā)往微服務(wù)的哪個(gè)實(shí)例。

Filter 與 LoadBalancerRule 都支持自行擴(kuò)展。

2.網(wǎng)關(guān)數(shù)據(jù)管理

實(shí)現(xiàn)一個(gè)適合自已的網(wǎng)關(guān),對(duì)數(shù)據(jù)管理需要考慮哪些方面的東西呢?

(1)首先,我們要考慮一下,我們需要管理些什么數(shù)據(jù)。

SCG本身對(duì)數(shù)據(jù)管理的管理是很弱的。它沒有提供數(shù)據(jù)的持久化方案,它所有的數(shù)據(jù)都來自初始化,來自它的配置文件(application.yml)。它本身雖然也對(duì)外提供了一些管理接口(Actuator API)能力不夠,但能力不夠,且這些修改都是暫時(shí)的,網(wǎng)關(guān)一停,數(shù)據(jù)就消失了。這就要求我們要用一套更完善的方案,把網(wǎng)關(guān)的這些數(shù)據(jù)管理起來,不能讓它只能寫在配置文件中,而要支持持久化,支持動(dòng)態(tài)變更。再有就是我們對(duì)各微服務(wù)的治理數(shù)據(jù)。網(wǎng)關(guān)只用來做路由轉(zhuǎn)發(fā),那就太浪費(fèi)了,統(tǒng)一認(rèn)證,統(tǒng)一鑒權(quán),訪問日志記錄,應(yīng)用訪問統(tǒng)計(jì),黑白名單過濾,API訂閱管理,流量限制,甚至數(shù)據(jù)格式轉(zhuǎn)換,網(wǎng)絡(luò)協(xié)議轉(zhuǎn)換,都可以在網(wǎng)關(guān)中來做。而所有的這些能力,無不需要數(shù)據(jù)的支持。因此,這些服務(wù)的治理配置,也是網(wǎng)關(guān)需要管理的數(shù)據(jù)。

(2)數(shù)據(jù)有了,我們還得考慮怎么把它保存起來,不能網(wǎng)關(guān)一重啟,所有數(shù)據(jù)就沒了。

(3)還得再考慮一下數(shù)據(jù)的讀取。網(wǎng)關(guān)對(duì)性能的要求是很高的,每次對(duì)過關(guān)的數(shù)據(jù)進(jìn)行治理,都需要去讀取這些配置信息。如果配置信息讀取太消耗資源,無疑對(duì)網(wǎng)關(guān)是不利的。所以,我們還得考慮數(shù)據(jù)如何緩存,以提高數(shù)據(jù)的讀取性能。

(4)單個(gè)網(wǎng)關(guān),可以處理的請(qǐng)求量是有上限的。為了應(yīng)對(duì)大的流量,我們可能會(huì)需要對(duì)網(wǎng)關(guān)做水平擴(kuò)容。當(dāng)多個(gè)網(wǎng)關(guān)實(shí)例共存時(shí),如何保障對(duì)網(wǎng)關(guān)的修改,能快速同步到每個(gè)網(wǎng)關(guān)實(shí)例呢?數(shù)據(jù)變更通知也得考慮。

(5)最多,我們還得考慮一下方案的擴(kuò)展,數(shù)據(jù)存儲(chǔ)能不能改個(gè)地方,通知能不能換種方式?

綜合考慮了這些方面之后,我們的網(wǎng)關(guān)的架構(gòu)如下: 

 

如圖,以上就是我們網(wǎng)關(guān)的整體設(shè)計(jì)。方案設(shè)計(jì)要點(diǎn)如下:

  • 網(wǎng)關(guān)對(duì)外提供治理數(shù)據(jù)管理接口, 微服務(wù)治理平臺(tái)可通過這些接口, 將治理配置推送到網(wǎng)關(guān)
  • 網(wǎng)關(guān)通過治理數(shù)據(jù)統(tǒng)一存儲(chǔ)接口, 將治理配置數(shù)據(jù)保持至治理數(shù)據(jù)持久存儲(chǔ)(這里我們默認(rèn)為Redis)
  • Redis通過發(fā)布訂閱能力, 將數(shù)據(jù)的變更通知到各網(wǎng)關(guān)實(shí)例
  • 各網(wǎng)關(guān)實(shí)例收到通知后, 將數(shù)據(jù)從持久存儲(chǔ)同步至內(nèi)部高速緩存
  • 內(nèi)部緩存在網(wǎng)關(guān)啟動(dòng)時(shí), 會(huì)自動(dòng)從持久存儲(chǔ)加載對(duì)應(yīng)配置進(jìn)入緩存. 同時(shí)它也支持清空, 以及按需加載
  • 外部業(yè)務(wù)請(qǐng)求經(jīng)過網(wǎng)關(guān)時(shí), 對(duì)數(shù)據(jù)執(zhí)行鑒權(quán),處理轉(zhuǎn)換, 以及灰度策略時(shí),所需要治理配置,都從內(nèi)部緩存中獲取, 以提升性能
  • 方案中, 外部持久存儲(chǔ)(默認(rèn)用的Redis, 可以換成Mysql, 文件, Appolo等), 以及數(shù)據(jù)變更通知(默認(rèn)使用的是Redis的發(fā)布訂閱, 可以換成Appolo通知, 消息隊(duì)列, 定時(shí)掃描等), 都是可以擴(kuò)展的

3.實(shí)現(xiàn)細(xì)節(jié)

動(dòng)態(tài)路由管理

Spring Cloud Gateway作為所有請(qǐng)求流量的入口,在實(shí)際生產(chǎn)環(huán)境中為了保證高可靠和高可用,盡量避免重啟, 需要實(shí)現(xiàn)Spring Cloud Gateway動(dòng)態(tài)路由配置。實(shí)現(xiàn)動(dòng)態(tài)路由其實(shí)很簡(jiǎn)單, 重點(diǎn)在于 RouteDefinitionRepository 這個(gè)接口. 這個(gè)接口繼承自兩個(gè)接口, 其中 RouteDefinitionLocator 是用來加載路由的. 它有很多實(shí)現(xiàn)類,其中的 PropertiesRouteDefinitionLocator 就用來實(shí)現(xiàn)從yml中加載路由。另一個(gè) RouteDefinitionWriter 用來實(shí)現(xiàn)路由的添加與刪除。通過查看spring cloud gateway的源碼可以發(fā)現(xiàn), 在 org.springframework.cloud.gateway.config.GatewayAutoConfiguration中這么一段: 

  1. @Bean 
  2. @ConditionalOnMissingBean(RouteDefinitionRepository.class) 
  3. public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() { 
  4.     return new InMemoryRouteDefinitionRepository(); 

可以看出, 網(wǎng)關(guān)中如果沒有RouteDefinitionRepository的Bean, 就會(huì)采用InMemoryRouteDefinitionRepository做為實(shí)現(xiàn)。這個(gè) InMemoryRouteDefinitionRepository有一個(gè)問題, 就是數(shù)據(jù)沒有持久化, 網(wǎng)關(guān)重啟之后,原來通過接口設(shè)置的路由就會(huì)丟失了。

這當(dāng)然是不可接受的, 所以我們需要實(shí)現(xiàn)自已的 RouteDefinitionRepository, 來提供路由配置信息。如使用redis做為存儲(chǔ), 來實(shí)現(xiàn)路由的存儲(chǔ)。實(shí)現(xiàn)請(qǐng)參考文章:https://dwz.cn/tsHfKwMe

除此以外, 每當(dāng)路由更改之后, 還需要通知網(wǎng)關(guān)刷新路由。這需要發(fā)送 RefreshRoutesEvent 來通知網(wǎng)關(guān)。如下列示例: 

  1. @Component 
  2. public class RouteDynamicService implements ApplicationEventPublisherAware { 
  3.     private ApplicationEventPublisher publisher; 
  4.  
  5.     @Override 
  6.     public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { 
  7.       this.publisher = publisher; 
  8.     } 
  9.  
  10.     /** 
  11.     * 刷新路由表 
  12.     */ 
  13.     public void refreshRoutes() { 
  14.       publisher.publishEvent(new RefreshRoutesEvent(this)); 
  15.     } 

刷新可以通過消息通知機(jī)制來觸發(fā), 當(dāng)然, 也可以對(duì)外接供rest接口, 手動(dòng)觸發(fā)。

數(shù)據(jù)存儲(chǔ) 

 

如上述類圖所示, IGovernDataRepository為治理數(shù)據(jù)統(tǒng)一存儲(chǔ)接口。RedisGovernDataRepository為實(shí)現(xiàn)的它的抽像類, 它需要依賴兩個(gè), 一個(gè)是StringRedisTemplate,用來實(shí)現(xiàn)redis數(shù)據(jù)的存儲(chǔ)。另一個(gè)為 RedisKeyGenerator, 用來為各治理對(duì)象生成對(duì)應(yīng)的key。RedisGovernDataRepository下面則為各個(gè)治理數(shù)據(jù)存儲(chǔ)的實(shí)現(xiàn)類。使用Redis做為持久存儲(chǔ)時(shí), 需要注意以下幾點(diǎn):

  • 為對(duì)象生成key時(shí), 建議為key添加一個(gè)命名空間(就是加一段有意義的前綴)
  • 在redis中進(jìn)行模糊搜索時(shí), 提供給Redis的pattern, 不能是一個(gè)正則的通配, 它支持三種通配 *(多個(gè)), ?(單個(gè))
  • 如果數(shù)據(jù)量比較大, 不建議使用keys進(jìn)行模糊查詢, 應(yīng)該使用scan方式

數(shù)據(jù)緩存

我們提供了內(nèi)部緩存,它處于使用者與持久存儲(chǔ)之間,緩存數(shù)據(jù)以提升性能。緩存的實(shí)現(xiàn)主要有如下幾點(diǎn):

  1. 實(shí)現(xiàn)了 InitializingBean 以實(shí)現(xiàn)在網(wǎng)關(guān)啟動(dòng)時(shí), 自動(dòng)加載數(shù)據(jù)
  2. 內(nèi)部使用了ConcurrentHashMap, 保證寫時(shí)的線程同步, 又保證了get時(shí)的高效(get整個(gè)過程不需要加鎖)
  3. 從緩存中取數(shù)據(jù)時(shí), 如果需要懶加載, 當(dāng)從持久存儲(chǔ)中加載不到數(shù)據(jù)時(shí), 建議使用空數(shù)據(jù), 或空集合占位, 避免每次都去持久存儲(chǔ)中查詢

代碼示例如下: 

  1. /** 
  2.   * 根據(jù) appCode 獲取流量策略 
  3.   *  
  4.   * @param appCode 
  5.   * @return 
  6.   */ 
  7. public Set<ApplicationTrafficPolicy> getAppTrafficPolicies(String appCode) { 
  8.   // 從緩存加載 
  9.   Map<String, ApplicationTrafficPolicy> map = policyMap.get(appCode); 
  10.   // 緩存中沒有 
  11.   if (map == null) { 
  12.     // 嘗試從持久存儲(chǔ)中加載所有此網(wǎng)關(guān)的流量策略 
  13.     Set<ApplicationTrafficPolicy> policies = trafficPolicyRepository.fuzzyQuery(); 
  14.     // 持久存儲(chǔ)中沒有任何流量策略,占個(gè)位置,防止緩存重復(fù)去加載 
  15.     if (policies == null || policies.size() == 0) { 
  16.       map = new ConcurrentHashMap<>(); 
  17.       policyMap.put(appCode, map); 
  18.     } else { 
  19.       // 持久存儲(chǔ)中有流量策略,放入緩存 
  20.       for (ApplicationTrafficPolicy policy : policies) { 
  21.         setTrafficPolicy(policy); 
  22.       } 
  23.       // 重新從緩存中加載一次 
  24.       map = policyMap.get(appCode); 
  25.       // 如果還是沒有,使用空 map 占位子 
  26.       if (map == null) { 
  27.         map = new ConcurrentHashMap<>(); 
  28.         policyMap.put(appCode, map); 
  29.       } 
  30.     } 
  31.   } 
  32.   return map.values().stream().collect(Collectors.toSet()); 

事件通知

事件通知,這里我們使用的是redis的發(fā)布與訂閱能力。Redis默認(rèn)是不發(fā)送事件的,要讓它發(fā)布事件,需要先修改它的配置文件redis.conf,添加一個(gè)配置: 

  1. notify-keyspace-events "K$g" 

上面的配置將使得Redis中發(fā)生數(shù)據(jù)的添加,修改或刪除時(shí),發(fā)送set或del事件。

然后,我們需要配置一個(gè)RedisMessageListenerContainer,用來訂閱我們感興趣的事件。 

  1. @Bean 
  2. RedisMessageListenerContainer container(MessageListenerAdapter listenerAdapter) { 
  3.   String gtwReidsPattern = "__keyspace@*__:" + GTW + keyGenerator.getGatewayCode() + "]*"
  4.   String cofRedisPattern = "__keyspace@*__:" + COF + cacheKey.getKeyNameSpace() + USER_NAME + "*"
  5.   log.info("Add gateway redis message listener, patternTopic is {}", gtwReidsPattern); 
  6.   log.info("Add coframe redis message listener, patternTopic is {}", cofRedisPattern); 
  7.   RedisMessageListenerContainer container = new RedisMessageListenerContainer(); 
  8.   container.setConnectionFactory(redisTemplate.getConnectionFactory()); 
  9.   // PatternTopic 參考:http://redisdoc.com/topic/notification.html 
  10.   container.addMessageListener(listenerAdapter, Arrays.asList(new PatternTopic(PatternUtil.fmt(gtwReidsPattern)), new PatternTopic(PatternUtil.fmt(cofRedisPattern)))); 
  11.   return container; 
  12. 當(dāng)redis事件訂閱好了之后, 每次其中我們關(guān)心的數(shù)據(jù)有變更, 都會(huì)發(fā)送set或del事件. 
  13. 我們需要定義一個(gè) MessageListener, 來接收事件: 
  14. @Service(value = RedisMessageListener.REDIS_LISTENER_NAME) 
  15. public class RedisMessageListener implements MessageListener { 
  16.   @Override 
  17.   public void onMessage(Message message, byte[] pattern) { 
  18.     String ops = new String(message.getBody()); 
  19.     String channel = new String(message.getChannel()); 
  20.     String key = channel.split(":")[1]; 
  21.  
  22.     if ("set".equals(ops)) { 
  23.       String value = redisTemplate.opsForValue().get(key); 
  24.       handleSet(key, value); 
  25.     } else if ("del".equals(ops)) { 
  26.       handleDel(key); 
  27.     } 
  28.   } 
  29.   ... 

接收到事件后,會(huì)調(diào)用相應(yīng)的內(nèi)部緩存,更新內(nèi)部緩存中的數(shù)據(jù),以實(shí)現(xiàn)治理數(shù)據(jù)變更的及時(shí)生效。

作者:

將曉漁,現(xiàn)任普元云計(jì)算架構(gòu)師。曾在PDM,云計(jì)算,數(shù)據(jù)備份,移動(dòng)互聯(lián)相關(guān)領(lǐng)域公司工作,十年以上IT工作經(jīng)驗(yàn)。曾為科企桌面虛擬化產(chǎn)品的核心工程師,愛數(shù)容災(zāi)備份云柜系統(tǒng)設(shè)計(jì)師,萬達(dá)信息的食安管理與追溯平臺(tái)開發(fā)經(jīng)理。國(guó)內(nèi)IAAS云計(jì)算的早期實(shí)踐者,容器技術(shù)專家。

 

責(zé)任編輯:華軒 來源: EAWorld
相關(guān)推薦

2024-05-13 18:35:06

負(fù)載均衡主機(jī)端口

2023-02-20 10:13:00

灰度發(fā)布實(shí)現(xiàn)

2022-01-07 07:29:08

Rbac權(quán)限模型

2024-01-29 08:00:00

架構(gòu)微服務(wù)開發(fā)

2023-02-28 08:57:06

Spring上下線緩存

2023-03-08 09:03:55

2022-08-15 09:22:12

JWT認(rèn)證系統(tǒng)

2025-03-04 08:59:16

2023-09-15 08:18:49

cookie網(wǎng)關(guān)代理

2023-02-13 08:10:40

Gateway網(wǎng)關(guān)Spring

2023-01-26 01:41:27

核心全局過濾器

2021-01-14 07:54:19

Spring Clou應(yīng)用路由

2023-07-24 08:00:56

客戶端訪問指定

2022-08-02 08:32:21

Spring項(xiàng)目網(wǎng)關(guān)

2018-06-01 23:08:01

Spring Clou微服務(wù)服務(wù)器

2022-04-11 07:34:46

OAuth2UAA節(jié)點(diǎn)

2025-02-10 00:23:11

Spring微服務(wù)架構(gòu)

2021-11-04 10:11:02

Sentinel網(wǎng)關(guān)限流

2023-02-15 08:12:19

http超時(shí)過濾器

2022-10-17 12:12:07

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 美人の美乳で授乳プレイ | 亚洲最大av网站 | 蜜桃在线一区二区三区 | 中文在线观看视频 | 国产成人精品一区二区三区四区 | 中文成人在线 | 亚洲三级在线观看 | 欧美一级大片 | 九九久久99| 欧美一区免费 | 免费h在线 | 999免费视频 | 国产成人99久久亚洲综合精品 | 精品欧美一区二区三区久久久小说 | 亚洲欧美日韩电影 | 久热久热| 中文字幕一区二区三区精彩视频 | 欧美综合一区二区 | 羞羞视频在线观看网站 | 免费黄色成人 | 国产精品美女久久久久aⅴ国产馆 | 亚洲一区二区三区 | 日韩精品专区在线影院重磅 | 久久久久久国产精品三区 | 欧美色综合一区二区三区 | 毛片区| 国产精品视频一区二区三区四蜜臂 | 国产激情91久久精品导航 | 国产一级一级 | 国产一级成人 | 欧美二区三区 | 精品粉嫩aⅴ一区二区三区四区 | 国产欧美视频一区 | 黄色一级片在线播放 | 一区二区视频在线观看 | 亚洲高清久久 | 青青激情网| 日本亚洲精品 | 在线观看成年视频 | 怡红院成人在线视频 | 亚洲天堂免费 |