最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Boot 3 整合 Spring Cloud Gateway實踐過程

 更新時間:2025年02月26日 16:29:16   作者:別惹CC  
本文介紹了如何使用SpringCloudAlibaba2023.0.0.0版本構(gòu)建一個微服務(wù)網(wǎng)關(guān),包括統(tǒng)一路由、限流防刷和登錄鑒權(quán)等功能,并通過一個項目實例進(jìn)行詳細(xì)說明,感興趣的朋友一起看看吧

引子

當(dāng)前微服務(wù)架構(gòu)已成為中大型系統(tǒng)的標(biāo)配,但在享受拆分帶來的敏捷性時,流量治理與安全管控的復(fù)雜度也呈指數(shù)級上升。因此,我們需要構(gòu)建微服務(wù)網(wǎng)關(guān)來為系統(tǒng)“保駕護(hù)航”。本文將會通過一個項目(核心模塊包含 鑒權(quán)服務(wù)、文件服務(wù)、主服務(wù) 共 3 個微服務(wù)),采用 Spring Cloud Alibaba 2023.0.0.0 版本技術(shù)棧(核心組件:Nacos 2.5.0 注冊中心與配置中心),分享如何構(gòu)建一個微服務(wù)網(wǎng)關(guān)。

為什么需要微服務(wù)網(wǎng)關(guān)

我們當(dāng)前模擬的這個項目中包含了三個業(yè)務(wù)服務(wù),如果部署到線上的話,每個服務(wù)都有自己的ip(或域名)以及端口號。因此,我們的業(yè)務(wù)入口是分散的且暴露在外的,我們無法統(tǒng)一攔截異常流量以及限制接口訪問等。但有了微服務(wù)網(wǎng)關(guān),我們就可以將所有的請求都先集中在網(wǎng)關(guān)這里(有點類似于一個房子的大門口),由網(wǎng)關(guān)對所有請求進(jìn)行統(tǒng)一的管理。

實踐

在知曉了網(wǎng)關(guān)的作用后,我們將實踐如何在一個現(xiàn)成的微服務(wù)項目中整合gateway網(wǎng)關(guān)以及做功能開發(fā)。當(dāng)然,在這之前,我們需要先完成整合。首先,我們需要建一個網(wǎng)關(guān)模塊,如下:

完成模塊的創(chuàng)建后,導(dǎo)入gateway相關(guān)的依賴,如下:

    <dependencies>
        <dependency>
            <groupId>com.pitayafruits</groupId>
            <artifactId>wechat-pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    </dependencies>

說明一下:這里引入的pojo包含了項目中常用的方法、工具類等;web則是因為網(wǎng)關(guān)本身也是一個可以訪問的服務(wù),所以需要引入;gateway則是這里需要使用的網(wǎng)關(guān)的依賴。然后來對它進(jìn)行基礎(chǔ)的配置,如下:

server:
  port: 1000
  tomcat:
    uri-encoding: UTF-8
    max-swallow-size: -1 # 不限制請求體大小
spring:
  application:
    name: gateway
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        username: nacos
        password: naocs
# 日志級別
logging:
  level:
    root: info

我們使用了nacos來管理服務(wù),網(wǎng)關(guān)自然也是一個服務(wù),因此也需要把它注冊到nacos

1.統(tǒng)一路由

引入網(wǎng)關(guān)的首要作用是統(tǒng)一訪問的入口,所有的服務(wù)訪問都要先經(jīng)過網(wǎng)關(guān)。因此,第一個要實現(xiàn)的功能就是統(tǒng)一路由。而它的實現(xiàn)也是非常簡單,只需要在配置文件中做下簡單配置即可:

spring:
    gateway:
      discovery:
        locator:
          enabled: true # 開啟從注冊中心動態(tài)創(chuàng)建路由的功能,利用微服務(wù)名進(jìn)行路由
      routes:           # 路由配置信息(數(shù)組/list)
        - id: authRoute # 每項路由規(guī)則都有一個唯一的id編號,可以自定義
          uri: lb://auth-service # lb=負(fù)載均衡,會動態(tài)尋址
          predicates:
            - Path=/a/**
        - id: fileRoute
          uri: lb://file-service
          predicates:
            - Path=/f/**
        - id: mainRoute
          uri: lb://main-service
          predicates:
            - Path=/m/**
      globalcors: # 允許跨域的相關(guān)配置
        cors-configurations:
          '[/**]':
            allowedOriginPatterns: "*"
            allowedHeaders: "*"
            allowedMethods: "*"
            allowCredentials: true

這里對routes下的相關(guān)配置說明下:id是給每個服務(wù)的路由一個唯一編號,保證唯一即可,通常我們采用的寫法是服務(wù)名+route;uri則是服務(wù)名稱,如果寫成ip或者域名,那么地址發(fā)生變化,我們還需要重新修改配置,但是服務(wù)名稱是可以固定不變的;接下來是predicates,它可以配置多個值,我們一個服務(wù)里會有多個controller,把每個controller的路由配置在這里即可,/**表示指定的controller下的所有方法。

另外,如果負(fù)載均衡這個寫法無法被識別,說明你當(dāng)前使用的spring-cloud版本中默認(rèn)并不包含相關(guān)依賴,我們需要手動引入它。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

完成上述配置后,我們此時其他服務(wù)的API將無法直接訪問,而統(tǒng)一通過網(wǎng)關(guān)來訪問。例如原本main-service中的127.0.0.1:88/m/hello 變成了 127.0.0.1:1000/m/hello

2.限流防刷

提到網(wǎng)關(guān),一個繞不開的話題就是限流。如果有人惡意刷我們的接口,我們就需要對某些IP進(jìn)行訪問限制,比如在XX秒內(nèi)訪問同一接口超過XX次,就需要限制訪問。它的實現(xiàn)非常簡單,聲明一個處理類繼承g(shù)ateway的相關(guān)過濾接口即可。代碼如下:

@Component
public class IPLimitFilter implements GlobalFilter {
    private static final Integer continueCounts = 3;
    private static final Integer timeInterval = 20;
    private static final Integer limitTimes = 30;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        return doLimit(exchange, chain);
    }
    /**
     * 限制ip請求次數(shù)的判斷
     *
     * @param exchange 請求交換器
     * @param chain     過濾器鏈
     * @return 返回值
     */
    public Mono<Void> doLimit(ServerWebExchange exchange,
                              GatewayFilterChain chain) {
        // 獲取ip
        ServerHttpRequest request = exchange.getRequest();
        String ip = IPUtil.getIP(request);
        // 正常ip定義
        final String ipRedisKey = "gateway-ip" + ip;
        // 被攔截的黑名單,如果在redis中存在,那么就不允許訪問
        final String ipRedisLimitKey = "gateway-ip:limit" + ip;
        // 判斷當(dāng)前ip的剩余時間,如果大于0,則表示還處于黑名單
        long limitLeftTimes = redis.ttl(ipRedisLimitKey);
        if ( limitLeftTimes > 0 ) {
            return renderErrorMsg(exchange, ResponseStatusEnum.SYSTEM_ERROR_BLACK_IP);
        }
        // 在redis中更新次數(shù)
        long requestCounts = redis.increment(ipRedisKey, 1);
        // 如果第一次訪問,就需要設(shè)置間隔時間
        if (requestCounts == 1) {
            redis.expire(ipRedisKey, timeInterval);
        }
        // 如果還能獲得正常請求次數(shù),說明用戶的正常請求落在正常時間內(nèi),超過則限制
        if (requestCounts > continueCounts) {
            redis.set(ipRedisLimitKey, ipRedisLimitKey, limitTimes);
            return renderErrorMsg(exchange, ResponseStatusEnum.SYSTEM_ERROR_BLACK_IP);
        }
        // 放行請求
        return chain.filter(exchange);
    }
    //過濾器的順序,數(shù)字越小優(yōu)先級越大.
    @Override
    public int getOrder() {
        return 1;
    }

我們需要借助redis來實現(xiàn)根據(jù)時間對指定ip的控制,這里的邏輯是:如果某個ip在30秒訪問超過三次,就限制訪問,如果限制了,則20秒后再恢復(fù)。

3.登錄鑒權(quán)

關(guān)于登錄鑒權(quán),我們目前通常會采用無狀態(tài)的做法:即用戶登錄后,后端返回token給前端,前端后續(xù)所有的請求都在headers中攜帶token,后端服務(wù)不存儲token,只對前端發(fā)來的token進(jìn)行校驗和解析。而網(wǎng)關(guān)作為所有服務(wù)的入口,自然而然地也就可以承擔(dān)起這個職責(zé)了。

import com.google.gson.Gson;
import com.pitayafruits.base.BaseInfoProperties;
import com.pitayafruits.grace.result.GraceJSONResult;
import com.pitayafruits.grace.result.ResponseStatusEnum;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Component
@Slf4j
@RefreshScope
public class SecurityFilterToken extends BaseInfoProperties implements GlobalFilter, Ordered {
    @Resource
    private ExcludeUrlProperties excludeUrlProperties;
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 獲取用戶請求路徑
        String url = exchange.getRequest().getURI().getPath();
        // 獲取所有需要排除校驗的url
        List<String> excludeList = excludeUrlProperties.getUrls();
        // 校驗并排除url
        if (excludeList != null && !excludeList.isEmpty()) {
            for (String excludeUrl : excludeList) {
                if (antPathMatcher.matchStart(excludeUrl, url)) {
                    return chain.filter(exchange);
                }
            }
        }
        // 從header中獲得用戶id和token
        String userId = exchange.getRequest().getHeaders().getFirst(HEADER_USER_ID);
        String userToken = exchange.getRequest().getHeaders().getFirst(HEADER_USER_TOKEN);
        // 校驗header中的token
        if (StringUtils.isNotBlank(userId) && StringUtils.isNotBlank(userToken)) {
            String redisToken = redis.get(REDIS_USER_TOKEN + ":" + userId);
            if (redisToken.equals(userToken)) {
                return chain.filter(exchange);
            }
        }
        // 默認(rèn)不放行
        return renderErrorMsg(exchange, ResponseStatusEnum.UN_LOGIN);
    }
    //過濾器的順序,數(shù)字越小優(yōu)先級越大.
    @Override
    public int getOrder() {
        return 0;
    }
    /**
     * 異常信息包裝
     *
     * @param exchange   交換器
     * @param statusEnum 狀態(tài)枚舉
     * @return 返回值
     */
    public Mono<Void> renderErrorMsg(ServerWebExchange exchange,
                                     ResponseStatusEnum statusEnum) {
        //1.獲得response
        ServerHttpResponse response = exchange.getResponse();
        //2.構(gòu)建jsonResult
        GraceJSONResult jsonResult = GraceJSONResult.exception(statusEnum);
        //3.設(shè)置header類型
        if (!response.getHeaders().containsKey("Content-Type")) {
            response.getHeaders().add("Content-Type",
                    MimeTypeUtils.APPLICATION_JSON_VALUE);
        }
        //4.設(shè)置狀態(tài)碼
        response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        //5.轉(zhuǎn)換json并向response寫數(shù)據(jù)
        String resultJson = new Gson().toJson(jsonResult);
        DataBuffer buffer = response.bufferFactory().wrap(resultJson.getBytes(StandardCharsets.UTF_8));
        //6.返回
        return response.writeWith(Mono.just(buffer));
    }
}

在我這個示例中,我做的校驗邏輯很簡單:只是用戶登錄的時候會在redis里存放生成的token,然后其他接口訪問的時候比對下傳來的tokenredis里存放的token是否一致。這里需要關(guān)注下過濾器的順序,目前的案例中我們已經(jīng)編寫了兩個過濾器-限流防刷和登錄鑒權(quán)。所以可以把登錄鑒權(quán)過濾器的執(zhí)行順序改為0,限流防抖改為1。

另外,我們需要對部分接口放行不攔截,比如登錄接口。而我這里的做法則是將放行接口寫在配置文件里,并聲明配置類進(jìn)行讀取。

exclude.urls[0] = /passport/getSMSCode
exclude.urls[1] = /passport/regist
exclude.urls[2] = /passport/login
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@Data
@PropertySource("classpath:excludeUrlPath.properties")
@ConfigurationProperties(prefix = "exclude")
public class ExcludeUrlProperties {
    private List<String> urls;
}

特別說明下:這里制定好過濾器的執(zhí)行順序后,內(nèi)部的驗證邏輯根據(jù)自己實際情況填寫,我這里沒用鑒權(quán)框架只是方便講解,要用也很簡單,引入之后把相關(guān)的鑒權(quán)邏輯寫進(jìn)對應(yīng)的過濾器就行。

小結(jié)

在本文中,我們完成了Spring Cloud Gateway微服務(wù)網(wǎng)關(guān)的整合,并完成了三個最基礎(chǔ)常見的實踐場景。如果你的項目有更多的業(yè)務(wù)需求,只需要加相應(yīng)的過濾器并制定好過濾器的執(zhí)行順序即可,希望對大家有所幫助!

到此這篇關(guān)于Spring Boot 3 整合 Spring Cloud Gateway實踐過程的文章就介紹到這了,更多相關(guān)Spring Boot 整合 Spring Cloud Gateway 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • idea如何實現(xiàn)引入外部jar包

    idea如何實現(xiàn)引入外部jar包

    文章介紹了如何在idea軟件中添加自定義jar依賴:打開idea,進(jìn)入項目依賴,選擇模塊,點擊+號選擇JARs of directories,找到并選擇jar文件,勾選并應(yīng)用更改,最后在項目依賴中確認(rèn)jar文件已成功引入
    2025-11-11
  • idea鼠標(biāo)控制放大縮小的操作

    idea鼠標(biāo)控制放大縮小的操作

    這篇文章主要介紹了idea鼠標(biāo)控制放大縮小的操作教程,具有很好的參考價值,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Java實現(xiàn)驗證文件名有效性的方法詳解

    Java實現(xiàn)驗證文件名有效性的方法詳解

    在本文中,我們將討論使用?Java?驗證一個給定的字符串是否具有操作系統(tǒng)的有效文件名的不同方法,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-09-09
  • java?http請求設(shè)置代理Proxy的兩種常見方法

    java?http請求設(shè)置代理Proxy的兩種常見方法

    代理是一種常見的設(shè)計模式,其目的就是為其他對象提供一個代理以控制對某個對象的訪問,這篇文章主要給大家介紹了關(guān)于java?http請求設(shè)置代理Proxy的兩種常見方法,需要的朋友可以參考下
    2023-11-11
  • 在IntelliJ IDEA中.idea文件是什么可以刪除嗎

    在IntelliJ IDEA中.idea文件是什么可以刪除嗎

    相信有很多小伙伴,在用idea寫java代碼的時候,創(chuàng)建工程總是會出現(xiàn).idea文件,該文件也從來沒去打開使用過,那么它在我們項目里面,扮演什么角色,到底能不能刪除它呢?這篇文章主要介紹了在IntelliJ IDEA中.idea文件是什么可以刪除嗎,需要的朋友可以參考下
    2024-01-01
  • 如何使用BeanUtils.copyProperties進(jìn)行對象之間的屬性賦值

    如何使用BeanUtils.copyProperties進(jìn)行對象之間的屬性賦值

    這篇文章主要介紹了使用BeanUtils.copyProperties進(jìn)行對象之間的屬性賦值,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • 基于SSM框架+Javamail發(fā)送郵件的代碼實例

    基于SSM框架+Javamail發(fā)送郵件的代碼實例

    本篇文章主要介紹了基于SSM框架+Javamail發(fā)送郵件的代碼實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2016-12-12
  • Java?Thread?類和Runnable?接口詳解

    Java?Thread?類和Runnable?接口詳解

    這篇文章主要介紹了Java?Thread?類和Runnable接口詳解,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下
    2022-08-08
  • SpringBoot中Bean拷貝及工具類封裝的實現(xiàn)

    SpringBoot中Bean拷貝及工具類封裝的實現(xiàn)

    本文主要介紹了SpringBoot中Bean拷貝及工具類封裝的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • SpringCloud中的灰度路由使用詳解

    SpringCloud中的灰度路由使用詳解

    這篇文章主要介紹了SpringCloud中的灰度路由使用詳解,在微服務(wù)中,?通常為了高可用,?同一個服務(wù)往往采用集群方式部署,?即同時存在幾個相同的服務(wù),而灰度的核心就?是路由,?通過我們特定的策略去調(diào)用目標(biāo)服務(wù)線路,需要的朋友可以參考下
    2023-08-08

最新評論

开化县| 两当县| 双桥区| 新巴尔虎右旗| 涞水县| 建始县| 崇信县| 聊城市| 彭阳县| 松江区| 灵川县| 白银市| 固阳县| 依安县| 新郑市| 监利县| 崇州市| 凤山市| 木里| 五寨县| 桓台县| 舞阳县| 海口市| 三亚市| 巴彦淖尔市| 刚察县| 华池县| 瑞昌市| 甘南县| 会理县| 湖北省| 宁波市| 香河县| 镇安县| 大方县| 吉安县| 长海县| 新乡县| 稷山县| 阿图什市| 犍为县|