1. 程式人生 > >Spring Cloud----Zuul動態路由

Spring Cloud----Zuul動態路由

轉至:http://blog.csdn.net/u013815546/article/details/68944039

前言

Zuul 是Netflix 提供的一個開源元件,致力於在雲平臺上提供動態路由,監控,彈性,安全等邊緣服務的框架。也有很多公司使用它來作為閘道器的重要組成部分,碰巧今年公司的架構組決定自研一個閘道器產品,集動態路由,動態許可權,限流配額等功能為一體,為其他部門的專案提供統一的外網呼叫管理,最終形成產品(這方面阿里其實已經有成熟的閘道器產品了,但是不太適用於個性化的配置,也沒有整合許可權和限流降級)。

不過這裡並不想介紹整個閘道器的架構,而是想著重於討論其中的一個關鍵點,並且也是經常在交流群中聽人說起的:動態路由怎麼做?

再闡釋什麼是動態路由之前,需要介紹一下架構的設計。

傳統網際網路架構圖

這裡寫圖片描述 
上圖是沒有閘道器參與的一個最典型的網際網路架構(本文中統一使用book代表應用例項,即真正提供服務的一個業務系統)

加入eureka的架構圖

這裡寫圖片描述 
book註冊到eureka註冊中心中,zuul本身也連線著同一個eureka,可以拉取book眾多例項的列表。服務中心的註冊發現一直是值得推崇的一種方式,但是不適用與閘道器產品。因為我們的閘道器是面向眾多的其他部門已有或是異構架構的系統,不應該強求其他系統都使用eureka,這樣是有侵入性的設計。

最終架構圖

這裡寫圖片描述 
要強調的一點是,gateway最終也會部署多個例項,達到分散式的效果,在架構圖中沒有畫出,請大家自行腦補。

本部落格的示例使用最後一章架構圖為例,帶來動態路由的實現方式,會有具體的程式碼。

動態路由

動態路由需要達到可持久化配置,動態重新整理的效果。如架構圖所示,不僅要能滿足從spring的配置檔案properties載入路由資訊,還需要從資料庫載入我們的配置。另外一點是,路由資訊在容器啟動時就已經載入進入了記憶體,我們希望配置完成後,實施釋出,動態重新整理記憶體中的路由資訊,達到不停機維護路由資訊的效果。

zuul–HelloWorldDemo

專案結構

    <groupId>com.sinosoft</groupId>
    <artifactId>
zuul-gateway-demo</artifactId> <packaging>pom</packaging> <version>1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> </parent> <modules> <module>gateway</module> <module>book</module> </modules> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Camden.SR6</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

tip:springboot-1.5.2對應的springcloud的版本需要使用Camden.SR6,一開始想專門寫這個demo時,只替換了springboot的版本1.4.0->1.5.2,結果啟動就報錯了,最後發現是版本不相容的鍋。

gateway專案: 
啟動類:GatewayApplication.java

@EnableZuulProxy
@SpringBootApplication
public class GatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

配置:application.properties

#配置在配置檔案中的路由資訊
zuul.routes.books.url=http://localhost:8090
zuul.routes.books.path=/books/**
#不使用註冊中心,會帶來侵入性
ribbon.eureka.enabled=false
#閘道器埠
server.port=8080
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

book專案: 
啟動類:BookApplication.java

@RestController
@SpringBootApplication
public class BookApplication {

    @RequestMapping(value = "/available")
    public String available() {
        System.out.println("Spring in Action");
        return "Spring in Action";
    }

    @RequestMapping(value = "/checked-out")
    public String checkedOut() {
        return "Spring Boot in Action";
    }

    public static void main(String[] args) {
        SpringApplication.run(BookApplication.class, args);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

配置類:application.properties

server.port=8090
  • 1

上述demo是一個簡單的靜態路由,簡單看下原始碼,zuul是怎麼做到轉發,路由的。

@Configuration
@EnableConfigurationProperties({ ZuulProperties.class })
@ConditionalOnClass(ZuulServlet.class)
@Import(ServerPropertiesAutoConfiguration.class)
public class ZuulConfiguration {

    @Autowired
    //zuul的配置檔案,對應了application.properties中的配置資訊
    protected ZuulProperties zuulProperties;

    @Autowired
    protected ServerProperties server;

    @Autowired(required = false)
    private ErrorController errorController;

    @Bean
    public HasFeatures zuulFeature() {
        return HasFeatures.namedFeature("Zuul (Simple)", ZuulConfiguration.class);
    }

    //核心類,路由定位器,最最重要
    @Bean
    @ConditionalOnMissingBean(RouteLocator.class)
    public RouteLocator routeLocator() {
        //預設配置的實現是SimpleRouteLocator.class
        return new SimpleRouteLocator(this.server.getServletPrefix(),
                this.zuulProperties);
    }

    //zuul的控制器,負責處理鏈路呼叫
    @Bean
    public ZuulController zuulController() {
        return new ZuulController();
    }

    //MVC HandlerMapping that maps incoming request paths to remote services.
    @Bean
    public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
        ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
        mapping.setErrorController(this.errorController);
        return mapping;
    }

    //註冊了一個路由重新整理監聽器,預設實現是ZuulRefreshListener.class,這個是我們動態路由的關鍵
    @Bean
    public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() {
        return new ZuulRefreshListener();
    }

    @Bean
    @ConditionalOnMissingBean(name = "zuulServlet")
    public ServletRegistrationBean zuulServlet() {
        ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet(),
                this.zuulProperties.getServletPattern());
        // The whole point of exposing this servlet is to provide a route that doesn't
        // buffer requests.
        servlet.addInitParameter("buffer-requests", "false");
        return servlet;
    }

    // pre filters

    @Bean
    public ServletDetectionFilter servletDetectionFilter() {
        return new ServletDetectionFilter();
    }

    @Bean
    public FormBodyWrapperFilter formBodyWrapperFilter() {
        return new FormBodyWrapperFilter();
    }

    @Bean
    public DebugFilter debugFilter() {
        return new DebugFilter();
    }

    @Bean
    public Servlet30WrapperFilter servlet30WrapperFilter() {
        return new Servlet30WrapperFilter();
    }

    // post filters

    @Bean
    public SendResponseFilter sendResponseFilter() {
        return new SendResponseFilter();
    }

    @Bean
    public SendErrorFilter sendErrorFilter() {
        return new SendErrorFilter();
    }

    @Bean
    public SendForwardFilter sendForwardFilter() {
        return new SendForwardFilter();
    }

    @Configuration
    protected static class ZuulFilterConfiguration {

        @Autowired
        private Map<String, ZuulFilter> filters;

        @Bean
        public ZuulFilterInitializer zuulFilterInitializer() {
            return new ZuulFilterInitializer(this.filters);
        }

    }

    //上面提到的路由重新整理監聽器
    private static class ZuulRefreshListener
            implements ApplicationListener<ApplicationEvent> {

        @Autowired
        private ZuulHandlerMapping zuulHandlerMapping;

        private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor();

        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ContextRefreshedEvent
                    || event instanceof RefreshScopeRefreshedEvent
                    || event instanceof RoutesRefreshedEvent) {
                //設定為髒,下一次匹配到路徑時,如果發現為髒,則會去重新整理路由資訊
                this.zuulHandlerMapping.setDirty(true);
            }
            else if (event instanceof HeartbeatEvent) {
                if (this.heartbeatMonitor.update(((HeartbeatEvent) event).getValue())) {
                    this.zuulHandlerMapping.setDirty(true);
                }
            }
        }

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140

我們要解決動態路由的難題,第一步就得理解路由定位器的作用。 
這裡寫圖片描述 
很失望,因為從介面關係來看,spring考慮到了路由重新整理的需求,但是預設實現的SimpleRouteLocator沒有實現RefreshableRouteLocator介面,看來我們只能借鑑DiscoveryClientRouteLocator去改造SimpleRouteLocator使其具備重新整理能力。

public interface RefreshableRouteLocator extends RouteLocator {
    void refresh();
}
  • 1
  • 2
  • 3

DiscoveryClientRouteLocator比SimpleRouteLocator多了兩個功能,第一是從DiscoveryClient(如Eureka)發現路由資訊,之前的架構圖已經給大家解釋清楚了,我們不想使用eureka這種侵入式的閘道器模組,所以忽略它,第二是實現了RefreshableRouteLocator介面,能夠實現動態重新整理。 
對SimpleRouteLocator.class的原始碼加一些註釋,方便大家閱讀:

public class SimpleRouteLocator implements RouteLocator {

    //配置檔案中的路由資訊配置
    private ZuulProperties properties;
    //路徑正則配置器,即作用於path:/books/**
    private PathMatcher pathMatcher = new AntPathMatcher();

    private String dispatcherServletPath = "/";
    private String zuulServletPath;

    private AtomicReference<Map<String, ZuulRoute>> routes = new AtomicReference<>();

    public SimpleRouteLocator(String servletPath, ZuulProperties properties) {
        this.properties = properties;
        if (servletPath != null && StringUtils.hasText(servletPath)) {
            this.dispatcherServletPath = servletPath;
        }

        this.zuulServletPath = properties.getServletPath();
    }

    //路由定位器和其他元件的互動,是最終把定位的Routes以list的方式提供出去,核心實現
    @Override
    public List<Route> getRoutes() {
        if (this.routes.get() == null) {
            this.routes.set(locateRoutes());
        }
        List<Route> values = new ArrayList<>();
        for (String url : this.routes.get().keySet()) {
            ZuulRoute route = this.routes.get().get(url);
            String path = route.getPath();
            values.add(getRoute(route, path));
        }
        return values;
    }

    @Override
    public Collection<String> getIgnoredPaths() {
        return this.properties.getIgnoredPatterns();
    }

    //這個方法在閘道器產品中也很重要,可以根據實際路徑匹配到Route來進行業務邏輯的操作,進行一些加工
    @Override
    public Route getMatchingRoute(final String path) {

        if (log.isDebugEnabled()) {
            log.debug("Finding route for path: " + path);
        }

        if (this.routes.get() == null) {
            this.routes.set(locateRoutes());
        }

        if (log.isDebugEnabled()) {
            log.debug("servletPath=" + this.dispatcherServletPath);
            log.debug("zuulServletPath=" + this.zuulServletPath);
            log.debug("RequestUtils.isDispatcherServletRequest()="
                    + RequestUtils.isDispatcherServletRequest());
            log.debug("RequestUtils.isZuulServletRequest()="
                    + RequestUtils.isZuulServletRequest());
        }

        String adjustedPath = adjustPath(path);

        ZuulRoute route = null;
        if (!matchesIgnoredPatterns(adjustedPath)) {
            for (Entry<String, ZuulRoute> entry : this.routes.get().entrySet()) {
                String pattern = entry.getKey();
                log.debug("Matching pattern:" + pattern);
                if (this.pathMatcher.match(pattern, adjustedPath)) {
                    route = entry.getValue();
                    break;
                }
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("route matched=" + route);
        }

        return getRoute(route, adjustedPath);

    }

    private Route getRoute(ZuulRoute route, String path) {
        if (route == null) {
            return null;
        }
        String targetPath = path;
        String prefix = this.properties.getPrefix();
        if (path.startsWith(prefix) && this.properties.isStripPrefix()) {
            targetPath = path.substring(prefix.length());
        }
        if (route.isStripPrefix()) {
            int index = route.getPath().indexOf("*") - 1;
            if (index > 0) {
                String routePrefix = route.getPath().substring(0, index);
                targetPath = targetPath.replaceFirst(routePrefix, "");
                prefix = prefix + routePrefix;
            }
        }
        Boolean retryable = this.properties.getRetryable();
        if (route.getRetryable() != null) {
            retryable = route.getRetryable();
        }
        return new Route(route.getId(), targetPath, route.getLocation(), prefix,
                retryable,
                route.isCustomSensitiveHeaders() ? route.getSensitiveHeaders() : null);
    }

    //注意這個類並沒有實現refresh介面,但是卻提供了一個protected級別的方法,旨在讓子類不需要重複維護一個private AtomicReference<Map<String, ZuulRoute>> routes = new AtomicReference<>();也可以達到重新整理的效果
    protected void doRefresh() {
        this.routes.set(locateRoutes());
    }


    //具體就是在這兒定位路由資訊的,我們之後從資料庫載入路由資訊,主要也是從這兒改寫
    /**
     * Compute a map of path pattern to route. The default is just a static map from the
     * {@link ZuulProperties}, but subclasses can add dynamic calculations.
     */
    protected Map<String, ZuulRoute> locateRoutes() {
        LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>();
        for (ZuulRoute route : this.properties.getRoutes().values()) {
            routesMap.put(route.getPath(), route);
        }
        return routesMap;
    }

    protected boolean matchesIgnoredPatterns(String path) {
        for (String pattern : this.properties.getIgnoredPatterns()) {
            log.debug("Matching ignored pattern:" + pattern);
            if (this.pathMatcher.match(pattern, path)) {
                log.debug("Path " + path + " matches ignored pattern " + pattern);
                return true;
            }
        }
        return false;
    }

    private String adjustPath(final String path) {
        String adjustedPath = path;

        if (RequestUtils.isDispatcherServletRequest()
                && StringUtils.hasText(this.dispatcherServletPath)) {
            if (!this.dispatcherServletPath.equals("/")) {
                adjustedPath = path.substring(this.dispatcherServletPath.length());
                log.debug("Stripped dispatcherServletPath");
            }
        }
        else if (RequestUtils.isZuulServletRequest()) {
            if (StringUtils.hasText(this.zuulServletPath)
                    && !this.zuulServletPath.equals("/")) {
                adjustedPath = path.substring(this.zuulServletPath.length());
                log.debug("Stripped zuulServletPath");
            }
        }
        else {
            // do nothing
        }

        log.debug("adjustedPath=" + path);
        return adjustedPath;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 相關推薦

    Spring Cloud —— Zuul 動態路由

    推薦 Spring Boot/Cloud 視訊: 前言 Zuul 是Netflix 提供的一個開源元件,致力於在雲平臺上提供動態路由,監控,彈性,安全等邊緣服務的框架。也有很多公司使用它來作為閘道器的重要組成部分,碰巧今年公司的架構組決定自研一個閘道器產品,集

    Spring Cloud----Zuul動態路由

    轉至:http://blog.csdn.net/u013815546/article/details/68944039 前言 Zuul 是Netflix 提供的一個開源元件,致力於在雲平臺上提供動態路由,監控,彈性,安全等邊緣服務的框架。也有很多公司使用它來作為閘道器

    Spring Cloud Zuul 實現路由,獲取參數

    esp 參數 實現 bsp pos 舉例 nbsp 傳遞 重寫 Spring Cloud Zuul作為網關,可實現請求驗證過濾,路由到具體服務的作用。 舉例: 比如請求訂單接口,報文包括驗證信息及具體請求參數。 通過配置路由到具體接口,通過參數調用接口,返回具體報文。 參數

    基於spring-cloud-zuul路由閘道器設定

    由於微服務的日益增多,管理也會不方便,所以需要一個可以集中管理所有服務的功能(類似sevelet的filter),可以在此做同一的許可權入口管理 新建一個模組spring-cloud-gateway 增加zuul及相關依賴如下: pom.xml <?xml vers

    Spring Cloud Zuul路由配置細節(14)

    轉自 https://blog.csdn.net/u012702547/article/details/77823434 這個系列我感覺真的太好了,可以一步一步的瞭解spring cloud 的搭建以及更深層次的東西,對想學這門技術的朋友真的入門特別的快,感謝這位大哥的分享,我也會持續

    Spring Cloud Zuul路由限流配置

    Zuul配置: pom.xml 新增 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-clou

    Spring Cloud Zuul路由配置細節

    上篇文章我們介紹了API閘道器的基本構建方式以及請求過濾,小夥伴們對Zuul的作用應該已經有了一個基本的認識,但是對於路由的配置我們只是做了一個簡單的介紹,本文我們就來看看路由配置的其他一些細節。 本文是Spring Cloud系列的第二十篇文章,瞭解前十九篇文章內容有

    拓展spring cloud gateway 動態路由解析及302問題

    開發十年,就只剩下這套架構體系了! >>>   

    spring-cloud-gateway動態路由

    # 概述 線上專案釋出一般有以下幾種方案: 1. 停機發布 2. 藍綠部署 3. 滾動部署 4. 灰度釋出 **停機發布** 這種釋出一般在夜裡或者進行大版本升級的時候釋出,因為需要停機,所以現在大家都在研究 `Devops` 方案。 **藍綠部署** 需要準備兩個相同的環境。一個環境新版本,一個環境

    Nacos+Spring Cloud Gateway動態路由配置

    前言   Nacos最近專案一直在使用,其簡單靈活,支援更細粒度的命令空間,分組等為麻煩複雜的環境切換提供了方便;同時也很好支援動態路由的配置,只需要簡單的幾步即可。在國產的註冊中心、配置中心中比較突出,容易上手,本文通過gateway、nacos-consumer、nacos-provider三個簡單模組

    spring cloud zuul 實現動態路由(可以收藏儲存)

    spring cloud zuul(實現動態路由) 路由 分為靜態路由和動態路由 下面介紹下動態的路由的實現方式:(一共2中,bus和快取的方式,下面採用的是資料庫的方式demo) pom 引入路由jar <dependency> <

    使用Spring Cloud Zuul實現動態路由

    Zuul 是提供動態路由,監控,彈性,安全等的邊緣服務。Zuul 相當於是裝置和 Netflix 流應用的 Web 網站後端所有請求的前門。Zuul 可以適當的對多個 Amazon Auto Scal

    Spring Cloud Zuul實現動態路由

    大致看看 ,看個思路即可轉載:https://mp.weixin.qq.com/s/4d-epBiq5b69fZTCSkiOzAZuul 是Netflix 提供的一個開源元件,致力於在雲平臺上提供動態路由,監控,彈性,安全等邊緣服務的框架。也有很多公司使用它來作為閘道器的重要

    Spring Cloud Zuul動態路由怎樣做?整合Nacos實現很簡單

    一、說明 閘道器的核心概念就是路由配置和路由規則,而作為所有請求流量的入口,在實際生產環境中為了保證高可靠和高可用,是儘量要避免重啟的,所以實現動態路由是非常有必要的;本文主要介紹實現的思路,並且以Nacos為資料來源來講解   二、實現要點 要實現動態路由只需關注下面4個點 閘道器啟動時,動態路

    spring cloud: zuul: 微閘道器-簡單使用與路由配置

    spring cloud: zuul: 微閘道器-簡單使用與路由配置 首先引入依賴 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-clo

    spring cloud: zuul: 微網關-簡單使用與路由配置

    true art ble localhost server cli 圖片 tid zuul spring cloud: zuul: 微網關-簡單使用與路由配置 首先引入依賴 <dependency> <groupId>org.springfra

    Spring Cloud系列教程 | 第六篇:Spring Cloud Zuul作為API閘道器實現請求路由轉發教程

    推薦 Spring Cloud 視訊: Spring Cloud Zuul作為API閘道器實現請求路由轉發教程      當我們的架構實現前後端分離以後,前端和後端之間互動就是通過API閘道器進行,API閘道器兩個職責: 1.設計上的適配層,或稱Facade模

    springcloud系列—Zuul—第5章-4: Spring Cloud Zuul 異常處理、禁用過濾器、動態載入

    資料參考:《Spring Cloud 微服務實戰》 目錄 異常處理 try-catch處理 ErrorFilter處理 不足與優化 自定義異常資訊 禁用過濾器 動態載入          動態路由  

    springcloud系列—Zuul—第5章-2: Spring Cloud Zuul 路由詳解

    資料參考:《Spring Cloud 微服務實戰》 目錄 路由詳解 傳統路由配置 服務路由配置 服務路由的預設規則 自定義路由對映關係 路徑匹配 忽略表示式 路由字首 本地跳轉 cookie與頭資訊 重定向問題 路由詳解 傳統路由配置

    第二十二章 Spring cloud Zuul使用正則表示式指定路由規則

    Zuul使用正則表示式指定路由規則 EurekaApplication類 package com.example.demo; import org.springframework.boot.SpringApplication; import org.springfra