【问题标题】:Retrieving routes and their paths from Spring Cloud Gateway (vs Zuul)从 Spring Cloud Gateway 检索路由及其路径(vs Zuul)
【发布时间】:2020-06-01 18:38:26
【问题描述】:

我正在尝试将 JHipster 从使用 Zuul 迁移到 Spring Cloud Gateway。在当前的 Zuul 实现中,有一个 GatewayResource 用于获取路由列表及其服务实例。

package com.mycompany.myapp.web.rest;

import com.mycompany.myapp.web.rest.vm.RouteVM;

import java.util.ArrayList;
import java.util.List;

import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.http.*;
import org.springframework.security.access.annotation.Secured;
import com.mycompany.myapp.security.AuthoritiesConstants;
import org.springframework.web.bind.annotation.*;

/**
 * REST controller for managing Gateway configuration.
 */
@RestController
@RequestMapping("/api/gateway")
public class GatewayResource {

    private final RouteLocator routeLocator;

    private final DiscoveryClient discoveryClient;

    public GatewayResource(RouteLocator routeLocator, DiscoveryClient discoveryClient) {
        this.routeLocator = routeLocator;
        this.discoveryClient = discoveryClient;
    }

    /**
     * {@code GET  /routes} : get the active routes.
     *
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the list of routes.
     */
    @GetMapping("/routes")
    @Secured(AuthoritiesConstants.ADMIN)
    public ResponseEntity<List<RouteVM>> activeRoutes() {
        List<Route> routes = routeLocator.getRoutes();
        List<RouteVM> routeVMs = new ArrayList<>();
        routes.forEach(route -> {
            RouteVM routeVM = new RouteVM();
            routeVM.setPath(route.getFullPath());
            routeVM.setServiceId(route.getId());
            routeVM.setServiceInstances(discoveryClient.getInstances(route.getLocation()));
            routeVMs.add(routeVM);
        });
        return ResponseEntity.ok(routeVMs);
    }
}

/api/gateway/routes 端点返回如下数据:

[
  {
    "path": "/services/blog/**",
    "serviceId": "blog",
    "serviceInstances": [
      {
        "serviceId": "BLOG",
        "secure": false,
        "instanceId": "blog:17c5482e0ccf49f19efb6dba8c5e5aa1",
        "instanceInfo": {
          "instanceId": "blog:17c5482e0ccf49f19efb6dba8c5e5aa1",
          "app": "BLOG",
          "appGroupName": null,
          "ipAddr": "192.168.0.20",
          "sid": "na",
          "homePageUrl": "http://192.168.0.20:8081/",
          "statusPageUrl": "http://192.168.0.20:8081/management/info",
          "healthCheckUrl": "http://192.168.0.20:8081/management/health",
          "secureHealthCheckUrl": null,
          "vipAddress": "blog",
          "secureVipAddress": "blog",
          "countryId": 1,
          "dataCenterInfo": {
            "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
            "name": "MyOwn"
          },
          "hostName": "192.168.0.20",
          "status": "UP",
          "overriddenStatus": "UNKNOWN",
          "leaseInfo": {
            "renewalIntervalInSecs": 5,
            "durationInSecs": 10,
            "registrationTimestamp": 1581934876730,
            "lastRenewalTimestamp": 1581935287273,
            "evictionTimestamp": 0,
            "serviceUpTimestamp": 1581934876214
          },
          "isCoordinatingDiscoveryServer": false,
          "metadata": {
            "zone": "primary",
            "profile": "dev",
            "management.port": "8081",
            "version": "0.0.1-SNAPSHOT"
          },
          "lastUpdatedTimestamp": 1581934876730,
          "lastDirtyTimestamp": 1581934876053,
          "actionType": "ADDED",
          "asgName": null
        },
        "port": 8081,
        "host": "192.168.0.20",
        "metadata": {
          "zone": "primary",
          "profile": "dev",
          "management.port": "8081",
          "version": "0.0.1-SNAPSHOT"
        },
        "uri": "http://192.168.0.20:8081",
        "scheme": null
      }
    ]
  }
]

如何在 Spring Cloud Gateway 中实现相同的端点?我在application.yml 中的配置如下:

spring:
  application:
    name: jhipster
  cloud:
    gateway:
      default-filters:
        - TokenRelay
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
          predicates:
            - name: Path
              args:
                pattern: "'/services/'+serviceId.toLowerCase()+'/**'"
          filters:
            - name: RewritePath
              args:
                regexp: "'/services/' + serviceId.toLowerCase() + '/(?<remaining>.*)'"
                replacement: "'/${remaining}'"
          route-id-prefix: ""
      httpclient:
        pool:
          max-connections: 1000

我尝试如下使用RouteLocator

package com.mycompany.myapp.web.rest;

import com.mycompany.myapp.security.AuthoritiesConstants;
import com.mycompany.myapp.web.rest.vm.RouteVM;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.route.*;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.util.ArrayList;
import java.util.List;

/**
 * REST controller for managing Gateway configuration.
 */
@RestController
@RequestMapping("/api/gateway")
public class GatewayResource {

    private final RouteLocator routeLocator;

    private final DiscoveryClient discoveryClient;

    public GatewayResource(RouteLocator routeLocator, DiscoveryClient discoveryClient) {
        this.routeLocator = routeLocator;
        this.discoveryClient = discoveryClient;
    }

    /**
     * {@code GET  /routes} : get the active routes.
     *
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the list of routes.
     */
    @GetMapping("/routes")
    @Secured(AuthoritiesConstants.ADMIN)
    public ResponseEntity<List<RouteVM>> activeRoutes() {
        Flux<Route> routes = routeLocator.getRoutes();
        List<RouteVM> routeVMs = new ArrayList<>();
        routes.subscribe(route -> {
            System.out.println("route: " + route.toString());
            RouteVM routeVM = new RouteVM();
            routeVM.setPath(route.getPredicate().toString());
            String serviceId = route.getId().substring(route.getId().indexOf("_") + 1).toLowerCase();
            routeVM.setServiceId(serviceId);
            routeVM.setServiceInstances(discoveryClient.getInstances(serviceId));
            routeVMs.add(routeVM);
        });
        return ResponseEntity.ok(routeVMs);
    }
}

这很接近,因为它返回以下内容:

[
  {
    "path": "Paths: [/services/blog/**], match trailing slash: true",
    "serviceId": "blog",
    "serviceInstances": [
      {
        "uri": "http://192.168.0.20:8081",
        "serviceId": "BLOG",
        "port": 8081,
        "host": "192.168.0.20",
        "instanceId": "blog:17c5482e0ccf49f19efb6dba8c5e5aa1",
        "secure": false,
        "instanceInfo": {
          "instanceId": "blog:17c5482e0ccf49f19efb6dba8c5e5aa1",
          "app": "BLOG",
          "appGroupName": null,
          "ipAddr": "192.168.0.20",
          "sid": "na",
          "homePageUrl": "http://192.168.0.20:8081/",
          "statusPageUrl": "http://192.168.0.20:8081/management/info",
          "healthCheckUrl": "http://192.168.0.20:8081/management/health",
          "secureHealthCheckUrl": null,
          "vipAddress": "blog",
          "secureVipAddress": "blog",
          "countryId": 1,
          "dataCenterInfo": {
            "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
            "name": "MyOwn"
          },
          "hostName": "192.168.0.20",
          "status": "UP",
          "overriddenStatus": "UNKNOWN",
          "leaseInfo": {
            "renewalIntervalInSecs": 5,
            "durationInSecs": 10,
            "registrationTimestamp": 1581934876730,
            "lastRenewalTimestamp": 1581965726885,
            "evictionTimestamp": 0,
            "serviceUpTimestamp": 1581934876214
          },
          "isCoordinatingDiscoveryServer": false,
          "metadata": {
            "zone": "primary",
            "profile": "dev",
            "management.port": "8081",
            "version": "0.0.1-SNAPSHOT"
          },
          "lastUpdatedTimestamp": 1581934876730,
          "lastDirtyTimestamp": 1581934876053,
          "actionType": "ADDED",
          "asgName": null
        },
        "metadata": {
          "zone": "primary",
          "profile": "dev",
          "management.port": "8081",
          "version": "0.0.1-SNAPSHOT"
        },
        "scheme": null
      }
    ]
  },
  {
    "path": "Paths: [/services/jhipster/**], match trailing slash: true",
    "serviceId": "jhipster",
    "serviceInstances": [{...}]
  }
]

路由打印如下:

route: Route{id='ReactiveCompositeDiscoveryClient_BLOG', uri=lb://BLOG, order=0, predicate=Paths: [/services/blog/**], match trailing slash: true, gatewayFilters=[[org.springframework.cloud.security.oauth2.gateway.TokenRelayGatewayFilterFactory$$Lambda$1404/0x0000000800a2dc40@2b9e183d, order = 1], [[RewritePath /services/blog/(?<remaining>.*) = '/${remaining}'], order = 1]], metadata={}}
route: Route{id='ReactiveCompositeDiscoveryClient_JHIPSTER', uri=lb://JHIPSTER, order=0, predicate=Paths: [/services/jhipster/**], match trailing slash: true, gatewayFilters=[[org.springframework.cloud.security.oauth2.gateway.TokenRelayGatewayFilterFactory$$Lambda$1404/0x0000000800a2dc40@59032a74, order = 1], [[RewritePath /services/jhipster/(?<remaining>.*) = '/${remaining}'], order = 1]], metadata={}}

几个问题:

  1. 如何获取谓词的路径? route.getPredicate().toString() 给了我"Paths: [/services/blog/**], match trailing slash: true" 而我只想要/services/blog/**
  2. 为什么spring.cloud.gateway.discovery.location.route-id-prefix: "" 不去掉默认前缀?我必须使用 route.getId().substring(route.getId().indexOf("_") + 1).toLowerCase() 手动剥离它。
  3. 为什么RouteLocator 返回/services/jhipster 路由?这是到网关的路由。使用 Zuul,只返回了一条路线。

【问题讨论】:

    标签: spring-boot jhipster netflix-zuul spring-cloud-gateway


    【解决方案1】:

    【讨论】:

    【解决方案2】:

    我们可以使用以下方法获取网关中配置的所有路由

    1. 为应用添加以下依赖项

                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
      
      
    2. 将以下配置添加到 application.properties 文件中

      
        management.endpoint.gateway.enabled=true
        management.endpoints.web.exposure.include=*
      
      
    3. 点击网址:/actuator/gateway/routes

    【讨论】:

      猜你喜欢
      • 2023-01-21
      • 2020-12-15
      • 2019-06-10
      • 2016-08-31
      • 2017-12-02
      • 2019-09-16
      • 2019-08-03
      • 2019-03-31
      • 1970-01-01
      相关资源
      最近更新 更多