编程技术分享平台

网站首页 > 技术教程 正文

从0开始学习springcloudgateway

xnh888 2025-01-20 17:28:35 技术教程 126 ℃ 0 评论

什么是网关?

随着微服务架构的普及,单体应用被拆分成多个服务,需要一个统一的访问入口。

搭建网关

两种方法:

  1. github上拉一个
  2. springcloud为用户准备好了网关的依赖,我们只需要在自己的springboot工程中引入网关依赖,就拥有了网关。

网关依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <version>2.2.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    <version>2.2.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.4.RELEASE</version>
</dependency>

启动类 Application.java

@SpringBootApplication
@EnableDiscoveryClient
@EnableEurekaClient
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

配置文件 application.yml

server:
  port: 10000

spring:
  application:
    name: my-gateway
  cloud:
    gateway:
      httpclient:
        pool:
          max-idle-time: 2000
      discovery:
        locator:
          enabled: true
          lowerCaseServiceId: true
          filters:
          - StripPrefix=1

eureka:
  client:
    registry-fetch-interval-seconds: 10
    fetch-registry: true
    registry-with-eureka: true
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    lease-expiration-duration-in-seconds: 10
    lease-renewal-interval-in-seconds: 3
    prefer-ip-address: true
    metadata-map:
      version: release

这样一个spring cloud gateway就创建好了。

网关设计

下图为网关内部架构设计,可以看到网关由一个netty-server,一个netty-client,多个route组成。

网关的核心:

● Route(路由):网关配置的基本组成模块,和Zuul网关的路由配置模块类似。一个Route模块由一个ID、一个目标URI、一组断言和一组过滤器组成。如果断言为真,则路由匹配,目标URI会被访问。

● Predicate(断言):Predicate来自Java 8的接口,它可以用来匹配来自HTTP请求的任何内容,例如headers或参数。接口包含多种默认方法,并将Predicate组合成复杂的逻辑(与、或、非),可以用于接口参数校验、路由转发判断等。

● Filter(过滤器):和Zuul的过滤器在概念上类似,可以使用Filter拦截和修改请求,实现对上游的响应,进行二次处理,实现横切与应用无关的功能,如安全、访问超时设置、限流等功能。

网关配置route的方式有两种,一个是配置文件,另一个是通过代码实现。

下面用一个静态路由做例子,

配置文件:

spring:
  cloud:
    gateway:
      routes:
      - id: my_route
        uri: http://localhost:9011/
        predicates:
          - Path= /eureka-client2/**
        filters:
          - StripPrefix=1

代码:

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
            .route("my_route", r -> r.path("/eureka-client2/**")
                    .filters(f -> f.stripPrefix(1))
                    .uri("http://localhost:9011/"))
            .build();
}

分别实现并展示,结果相同。

工作原理


自定义filter

网关自身提供了多个filter,可以通过配置文件进行装配,上面用到的- StripPrefix=1就是filter。

StripPrefixGatewayFilterFactory.java

public class StripPrefixGatewayFilterFactory
      extends AbstractGatewayFilterFactory<StripPrefixGatewayFilterFactory.Config> {
    ...
    @Override
    public GatewayFilter apply(Config config) { // apply方法写filter逻辑 
       return new GatewayFilter() {
          @Override
          public Mono<Void> filter(ServerWebExchange exchange,
                GatewayFilterChain chain) {
             ServerHttpRequest request = exchange.getRequest();
             addOriginalRequestUrl(exchange, request.getURI());
             String path = request.getURI().getRawPath();
             String newPath = "/"
                   + Arrays.stream(StringUtils.tokenizeToStringArray(path, "/"))
                         .skip(config.parts).collect(Collectors.joining("/"));
             newPath += (newPath.length() > 1 && path.endsWith("/") ? "/" : "");
             ServerHttpRequest newRequest = request.mutate().path(newPath).build();
    
             exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR,
                   newRequest.getURI());
    
             return chain.filter(exchange.mutate().request(newRequest).build());
          }
    
          @Override
          public String toString() {
             return filterToStringCreator(StripPrefixGatewayFilterFactory.this)
                   .append("parts", config.getParts()).toString();
          }
       };
    }
    ...
 }

通过工厂模式实现的过滤器是需要在配置文件中手动装配的,这种过滤器可以根据自己的需要在不同的routes上自由装配。

spring:
  cloud:
    gateway:
      routes:
      - id: my_route
        uri: http://localhost:9011/
        predicates:
          - Path= /eureka-client2/**
        filters:
          - StripPrefix=1

还有一种实现过滤器的方式是实现GlobalFilter接口,这个方式不需要添加配置,所有routes都生效。

拿网关自带的filter举例子,RouteToRequestUrlFilter.java

public class RouteToRequestUrlFilter implements GlobalFilter, Ordered {
    ...
    @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);
    }
    ...
}

我们可以通过断点,查看一条route有哪些filter,GatewayFilterChain对象里包含了filter信息。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表