SpringCloudGateway開發詳解


  1. 路由簡介:

        SpringCloudGateWay 是用於替代zuul作為API網關,在gateway中有三個重要的名詞:過濾器,斷言,路由

      過濾器與斷言是路由的一部分,路由便是將請求進行一系列的處理后分發到各個服務的一個過程。

      路由的過程:首先會加載斷言以及路由,在接受到請求后根據斷言加載的順序會匹配到先加載的斷言,只有與斷言匹配了的請求才會進入路由,沒有匹配到的服務會將請求當成普通的訪問請求。

2:路由加載斷言的方式:

      斷言加載的方式有四種,分別是配置文件,java編碼,數據庫以及注冊中心

      第一種配置文件:

            在官方文檔中主要介紹的就是配置文件的加載方式

            官方地址:https://cloud.spring.io/spring-cloud-gateway/reference/html/#gateway-starter

                一般的斷言有三種要素:id,uri,predicate.

            id是斷言的標識,uri是ip+端口,predicate則是斷言匹配的規則

  3:示例:

       新建一個springboot項目,並且引入springcloudgateway的依賴

 1 <dependencies>
 3         <dependency>
 4             <groupId>org.springframework.boot</groupId>
 5             <artifactId>spring-boot-starter-test</artifactId>
 6             <scope>test</scope>
 7         </dependency>
 8         <dependency>
 9             <groupId>org.springframework.cloud</groupId>
10             <artifactId>spring-cloud-starter-gateway</artifactId>
11         </dependency>
12     </dependencies>

      在啟動類注冊三個全局過濾器

 1 @SpringBootApplication
 2 public class GateWayApplication {
 3 
 4     public static void main(String[] args) {
 5         SpringApplication.run(GateWayApplication.class, args);
 6     }
 7 
 8     @Bean
 9     @Order(-1)
10     public GlobalFilter a() {
11         return (exchange, chain) -> {
12 
13             return chain.filter(exchange).then(Mono.fromRunnable(() -> {
14                 System.out.println(-1);
15             }));
16         };
17     }
18 
19     @Bean
20     @Order(0)
21     public GlobalFilter b() {
22         return (exchange, chain) -> {
23 
24             return chain.filter(exchange).then(Mono.fromRunnable(() -> {
25                 System.out.println(0);
26             }));
27         };
28     }
29 
30     @Bean
31     @Order(1)
32     public GlobalFilter c() {
33         return (exchange, chain) -> {
34 
35             return chain.filter(exchange).then(Mono.fromRunnable(() -> {
36                 System.out.println(1);
37             }));
38         };
39     }
40 }

在配置文件類配置兩條路由

 1 
server.port: 7777
spring: 2 application: 3 name: gateway 4 cloud: 5 gateway: 6 discovery: 7 locator: 8 enabled: true 9 lower-case-service-id: true 10 routes: 11 - id: method_route 12 uri: http://127.0.0.1:9999 13 predicates: 14 - Method=GET 15 - id: method_route 16 uri: http://127.0.0.1:8006 17 predicates: 18 - Method=GET

 發送請求,請求到達后匹配的是第一條路由,由此可以知道路由匹配的順序會根據加載的順序來

 

  4:SpringCloudGateWay從注冊中心獲得路由

        在官方文檔中,我們可以看到有這樣的一段話

Configuring Predicates and Filters For DiscoveryClient Routes

By default the Gateway defines a single predicate and filter for routes created via a DiscoveryClient.

The default predicate is a path predicate defined with the pattern /serviceId/**, where serviceId is the id of the service from the DiscoveryClient.

The default filter is rewrite path filter with the regex /serviceId/(?<remaining>.*) and the replacement /${remaining}. This just strips the service id from the path before the request is sent downstream.

If you would like to customize the predicates and/or filters used by the DiscoveryClient routes you can do so by setting spring.cloud.gateway.discovery.locator.predicates[x] and spring.cloud.gateway.discovery.locator.filters[y]. When doing so you need to make sure to include the default predicate and filter above, if you want to retain that functionality. Below is an example of what this looks like.

地址 :https://cloud.spring.io/spring-cloud-gateway/reference/html/#_global_filters

       

1 spring.cloud.gateway.discovery.locator.predicates[0].name: Path
2 spring.cloud.gateway.discovery.locator.predicates[0].args[pattern]: "'/'+serviceId+'/**'"
3 spring.cloud.gateway.discovery.locator.predicates[1].name: Host
4 spring.cloud.gateway.discovery.locator.predicates[1].args[pattern]: "'**.foo.com'"
5 spring.cloud.gateway.discovery.locator.filters[0].name: Hystrix
6 spring.cloud.gateway.discovery.locator.filters[0].args[name]: serviceId
7 spring.cloud.gateway.discovery.locator.filters[1].name: RewritePath
8 spring.cloud.gateway.discovery.locator.filters[1].args[regexp]: "'/' + serviceId + '/(?<remaining>.*)'"
9 spring.cloud.gateway.discovery.locator.filters[1].args[replacement]: "'/${remaining}'"

根據文檔介紹,依照這種方式,可以從注冊中心獲得斷言與過濾器的配置

  5:SpringGateWay從數據庫配置路由

      

public class DBRouteDefinitionRepository  implements RouteDefinitionRepository

 項目中實現了RouteDefinitionRepository后,springgateway會采用你實現的這個類去加載路由,如果不實現則采用他默認的方式加載路由

 1     
 2 public class DBRouteDefinitionRepository  implements RouteDefinitionRepository {
    //保存路由
    private final Map<String, RouteDefinition> routes = synchronizedMap(new LinkedHashMap<String, RouteDefinition>());
    
    private Logger log = LoggerFactory.getLogger(DBRouteDefinitionRepository.class);
    //初始標準
    private boolean init_flag = true;
    //
    private final GatewayProperties properties;
    private DynamicRouteServiceImpl service;
    

    public DBRouteDefinitionRepository(GatewayProperties properties) {
        this.properties = properties;
        this.service = new DynamicRouteServiceImpl();

    }
    
    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        if(init_flag) {
            List<RouteDefinition> routeDefinitions = new ArrayList<>();
            List<RouteDefinition> rs = new ArrayList<>();
            try {
                routeDefinitions = service.quertAllRoutes();//從數據庫中加載route
                rs = this.properties.getRoutes();//獲得配置文件的route
                for (RouteDefinition rse : rs) {
                    routeDefinitions.add(rse);
                }
                routes.clear();
                routeDefinitions.forEach(x->routes.put(x.getId(), x));
                init_flag=false;
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                log.error("Init Route Fail,Can't get Routes.",e);
            }
            return Flux.fromIterable(routeDefinitions);
        }else {
            return Flux.fromIterable(routes.values());
        }
        
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return routeId.flatMap(id -> {
            if (routes.containsKey(id)) {
                routes.remove(id);
                return Mono.empty();
            }
            return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: "+routeId)));
        });
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return route.flatMap( r -> {
            routes.put(r.getId(), r);
            return Mono.empty();
        });
    }

}

這個是我自己實現的類,這個類可以從數據庫與配置文件中獲得路由配置,從數據庫中獲得路由配置可以根據個人的要求來

@Validated
public class RouteDefinition {

    @NotEmpty
    private String id = UUID.randomUUID().toString();

    @NotEmpty
    @Valid
    private List<PredicateDefinition> predicates = new ArrayList<>();

    @Valid
    private List<FilterDefinition> filters = new ArrayList<>();

    @NotNull
    private URI uri;

    private int order = 0;

    public RouteDefinition() {
    }

    public RouteDefinition(String text) {
        int eqIdx = text.indexOf('=');
        if (eqIdx <= 0) {
            throw new ValidationException("Unable to parse RouteDefinition text '" + text
                    + "'" + ", must be of the form name=value");
        }

        setId(text.substring(0, eqIdx));

        String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ",");

        setUri(URI.create(args[0]));

        for (int i = 1; i < args.length; i++) {
            this.predicates.add(new PredicateDefinition(args[i]));
        }
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public List<PredicateDefinition> getPredicates() {
        return predicates;
    }

    public void setPredicates(List<PredicateDefinition> predicates) {
        this.predicates = predicates;
    }

    public List<FilterDefinition> getFilters() {
        return filters;
    }

    public void setFilters(List<FilterDefinition> filters) {
        this.filters = filters;
    }

    public URI getUri() {
        return uri;
    }

    public void setUri(URI uri) {
        this.uri = uri;
    }

    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        RouteDefinition routeDefinition = (RouteDefinition) o;
        return Objects.equals(id, routeDefinition.id)
                && Objects.equals(predicates, routeDefinition.predicates)
                && Objects.equals(order, routeDefinition.order)
                && Objects.equals(uri, routeDefinition.uri);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, predicates, uri);
    }

    @Override
    public String toString() {
        return "RouteDefinition{" + "id='" + id + '\'' + ", predicates=" + predicates
                + ", filters=" + filters + ", uri=" + uri + ", order=" + order + '}';
    }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM