Spring Cloud Gateway整合Nacos實(shí)現(xiàn)服務(wù)路由及集群負(fù)載均衡

一、序言

我們都知道Spring Cloud Gateway是一個(gè)基于Spring BootSpring WebFluxProject Reactor構(gòu)建的高性能網(wǎng)關(guān)驰弄,旨在提供簡(jiǎn)單、高效的API路由速客。

Spring Cloud Gateway基于Netty運(yùn)行戚篙,因此在傳統(tǒng)Servlet容器中或者打成war包是不能正常運(yùn)行的。




二溺职、代碼示例

這里我們注冊(cè)中心選型的是Nacos已球,如果還沒(méi)有安裝Nacos,請(qǐng)參考:Nacos快速安裝部署辅愿。

1、父工程spring-cloud-gateway-learning

    <modules>
            <module>spring-cloud-api-gateway</module>
            <module>spring-cloud-user-service</module>
            <module>spring-cloud-message-service</module>
    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.boot.version>2.3.7.RELEASE</spring.boot.version>
        <spring.cloud.version>Hoxton.SR12</spring.cloud.version>
        <spring.cloud.alibaba.version>2.2.6.RELEASE</spring.cloud.alibaba.version>
        <commons.lang3.version>3.12.0</commons.lang3.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>${commons.lang3.version}</version>
        </dependency>
    </dependencies>

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

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

備注:具體==Spring Cloud==各版本說(shuō)明請(qǐng)參考Spring Cloud Alibaba版本說(shuō)明忆某。

2点待、子工程spring-cloud-api-gateway

(1) pom.xml

<parent>
        <groupId>com.universe</groupId>
        <artifactId>spring-cloud-gateway-learning</artifactId>
        <version>1.0-SNAPSHOT</version>
</parent>

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
</dependencies>

(2) 配置文件和代碼示例

  1. bootstrap.yml
spring:
  application:
    name: api-gateway
server:
  port: 9000
  1. application.yml
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user/**
        - id: message-service
          uri: lb://message-service
          predicates:
            - Path=/message/**
    nacos:
      discovery:
        server-addr: localhost:8848

如果URI以==lb==開頭,比如如上配置中的lb://user-service弃舒,Spring Cloud Gateway會(huì)用ReactiveLoadBalancerClientFilter解析服務(wù)名為user-service的實(shí)例對(duì)應(yīng)的實(shí)際host和端口癞埠,并做集群負(fù)載均衡。

這項(xiàng)功能通過(guò)全局過(guò)濾器ReactiveLoadBalancerClientFilter實(shí)現(xiàn)聋呢,官網(wǎng)描述如下:
[圖片上傳失敗...(image-b13395-1655546589820)]

  1. RouteRecordGlobalFilter
@Slf4j
@Component
public class RouteRecordGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // RouteToRequestUrlFilter會(huì)把實(shí)際路由的URL通過(guò)該屬性保存
        URI proxyRequestUri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        long start = System.currentTimeMillis();

        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            long end = System.currentTimeMillis();
            log.info("實(shí)際調(diào)用地址為:{}苗踪,調(diào)用耗時(shí)為:{}ms", proxyRequestUri, (end - start));
        }));
    }

    @Override
    public int getOrder() {
        // 優(yōu)先級(jí)設(shè)為最低,先讓RouteToRequestUrlFilter先調(diào)用
        return Ordered.LOWEST_PRECEDENCE;
    }
}

RouteRecordGlobalFilter 這個(gè)全局過(guò)濾器我們主要用來(lái)記錄路由后的實(shí)際代理地址削锰,以及調(diào)用耗時(shí)通铲。

我們看下RouteToRequestUrlFilter的描述會(huì)發(fā)現(xiàn)實(shí)際路由地址會(huì)通過(guò)ServerWebExchange中名為ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的屬性保存。

[圖片上傳失敗...(image-a33940-1655546589820)]

關(guān)于RouteToRequestUrlFilter的部分源碼如下:

@Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
        if (route == null) {
            return chain.filter(exchange);
        }
        log.trace("RouteToRequestUrlFilter start");
        URI uri = exchange.getRequest().getURI();
        boolean encoded = containsEncodedParts(uri);
        URI routeUri = route.getUri();

        if (hasAnotherScheme(routeUri)) {
            // this is a special url, save scheme to special attribute
            // replace routeUri with schemeSpecificPart
            exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR,
                    routeUri.getScheme());
            routeUri = URI.create(routeUri.getSchemeSpecificPart());
        }

        if ("lb".equalsIgnoreCase(routeUri.getScheme()) && routeUri.getHost() == null) {
            // Load balanced URIs should always have a host. If the host is null it is
            // most
            // likely because the host name was invalid (for example included an
            // underscore)
            throw new IllegalStateException("Invalid host: " + routeUri.toString());
        }

        URI mergedUrl = UriComponentsBuilder.fromUri(uri)
                // .uri(routeUri)
                .scheme(routeUri.getScheme()).host(routeUri.getHost())
                .port(routeUri.getPort()).build(encoded).toUri();
        exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, mergedUrl);
        return chain.filter(exchange);
    }

備注:更多關(guān)于全局過(guò)濾器的介紹請(qǐng)參考 Spring Cloud Gateway全局過(guò)濾器器贩。

3颅夺、子工程spring-cloud-user-service

(1) pom.xml

<parent>
        <groupId>com.universe</groupId>
        <artifactId>spring-cloud-gateway-learning</artifactId>
        <version>1.0-SNAPSHOT</version>
</parent>

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
</dependencies>

(2) 配置文件

  1. bootstrap.yml
spring:
  application:
    name: user-service
server:
  servlet:
    context-path: /user

---
spring:
  profiles: user-service-master
server:
  port: 9091

---
spring:
  profiles: user-service-slave
server:
  port: 9092

  1. application.yml
spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  1. UserController
@RestController
public class UserController {

    @GetMapping("/info")
    public Map<String, Object> getUserInfo() {
    Random random = new Random();
        int waitTime = random.nextInt(1000);
        LockSupport.parkNanos(1000 * 1000 * waitTime);
        
        Map<String, Object> result = new HashMap<>();
        result.put("name", "Nick");
        result.put("age", 25);
        return result;
    }
}

4、子工程spring-cloud-message-service

(1) pom.xml

<parent>
        <groupId>com.universe</groupId>
        <artifactId>spring-cloud-gateway-learning</artifactId>
        <version>1.0-SNAPSHOT</version>
</parent>

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
</dependencies>

(2) 配置文件和代碼示例

  1. bootstrap.yml
spring:
  application:
    name: message-service
server:
  servlet:
    context-path: /message

---
spring:
  profiles: message-service-master
server:
  port: 9093

---
spring:
  profiles: message-service-slave
server:
  port: 9094
  1. application.yml
spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  1. MessageController
@RestController
public class MessageController {

    @GetMapping("/info")
    public Map<String, Object> getMessageInfo() {
    Random random = new Random();
        int waitTime = random.nextInt(1000);
        LockSupport.parkNanos(1000 * 1000 * waitTime);
    
        Map<String, Object> result = new HashMap<>();
        result.put("id", 1);
        result.put("title", "我愛(ài)中國(guó)");
        return result;
    }
}




三蛹稍、測(cè)試結(jié)果

分別啟動(dòng)api-gateway吧黄、指定概要文件啟動(dòng)兩個(gè)user-service服務(wù)實(shí)例、和兩個(gè)message-service服務(wù)實(shí)例唆姐,查看Nacos控制臺(tái)拗慨。
[圖片上傳失敗...(image-34bdc6-1655546589820)]

可以看到,api-gateway啟動(dòng)了一個(gè)服務(wù)實(shí)例,user-service和message-service都啟動(dòng)了兩個(gè)服務(wù)實(shí)例赵抢。

備注:IDEA運(yùn)行時(shí)指定Active Profiles即可剧蹂。

1、集群負(fù)載均衡測(cè)試

連續(xù)訪問(wèn)http://localhost:9000/user/info昌讲,可以看到user-service集群服務(wù)實(shí)例被輪詢調(diào)用国夜。

image.png

2、服務(wù)路由測(cè)試

分別訪問(wèn) http://localhost:9000/user/info短绸、http://localhost:9000/message/info车吹,我們可以看到基于路徑匹配的服務(wù)路由分發(fā)是成功的。

image.png

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末醋闭,一起剝皮案震驚了整個(gè)濱河市窄驹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌证逻,老刑警劉巖乐埠,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異囚企,居然都是意外死亡丈咐,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門龙宏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)棵逊,“玉大人,你說(shuō)我怎么就攤上這事银酗×居埃” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵黍特,是天一觀的道長(zhǎng)蛙讥。 經(jīng)常有香客問(wèn)我,道長(zhǎng)灭衷,這世上最難降的妖魔是什么次慢? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮今布,結(jié)果婚禮上经备,老公的妹妹穿的比我還像新娘。我一直安慰自己部默,他們只是感情好侵蒙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著傅蹂,像睡著了一般纷闺。 火紅的嫁衣襯著肌膚如雪算凿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天犁功,我揣著相機(jī)與錄音氓轰,去河邊找鬼。 笑死浸卦,一個(gè)胖子當(dāng)著我的面吹牛署鸡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播限嫌,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼靴庆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了怒医?” 一聲冷哼從身側(cè)響起炉抒,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎稚叹,沒(méi)想到半個(gè)月后焰薄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扒袖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年塞茅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片季率。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凡桥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蚀同,到底是詐尸還是另有隱情,我是刑警寧澤啊掏,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布蠢络,位于F島的核電站,受9級(jí)特大地震影響迟蜜,放射性物質(zhì)發(fā)生泄漏刹孔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一娜睛、第九天 我趴在偏房一處隱蔽的房頂上張望髓霞。 院中可真熱鬧,春花似錦畦戒、人聲如沸方库。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)纵潦。三九已至徐鹤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間邀层,已是汗流浹背返敬。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寥院,地道東北人劲赠。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像秸谢,于是被迫代替她去往敵國(guó)和親凛澎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容