一、微服務架構-網關spring cloud gateway

這篇文章講述瞭如何簡單地使用Spring Cloud Gateway,來源於Spring Cloud官方案例,地址https://spring.io/guides/gs/gateway 

一、簡介

gateway是什麼:Spring Cloud Gateway是Spring官方基於Spring 5.0,Spring Boot 2.0和Project Reactor等技術開發的網關,Spring Cloud Gateway旨在爲微服務架構提供一種簡單而有效的統一的API路由管理方式。這裏需要注意一下gateway使用的netty+webflux實現,不要加入web依賴,需要加入webflux依賴。

gateway與zuul的區別的簡單比較:gateway使用的是異步請求,zuul是同步請求,gateway的數據封裝在ServerWebExchange裏,zuul封裝在RequestContext裏。
 

二、配置轉發路由

1、通過配置文件轉發路由

spring:
  cloud:
    gateway:
    # 配置所有路由的默認過濾器 這裏配置的是gatewayFilter
      default-filters:
      routes:
      - id: server-test  # 服務的id
        uri: lb://server-test #服務的application名稱
        order: 0 #路由級別
        predicates:
        - Path=/bus/**  #前綴
        filters:
        - StripPrefix=1 #去前綴 去幾層,1表示去bus **其他留下

2、通過修改啓動類轉發路由

/**
 * gateway 方式實現
 */
@SpringBootApplication
public class ServiceGatewayApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(ServiceGatewayApplication.class, args);
    }
 
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        StripPrefixGatewayFilterFactory.Config config =
                new StripPrefixGatewayFilterFactory.Config();
        config.setParts(1);
        return builder.routes()
                .route("test", a -> a.path("/test/**")
                        .filters(b -> b.stripPrefix(1))
                        .uri("http://localhost:8762"))
                .route("api-test2", r -> r.path("/api-test2/**")
                        .filters(f -> f.stripPrefix(1))
                        .uri("http://localhost:8762"))
                .route("api-test3", r -> r.path("/api-test3/**").
                        filters(f -> f.stripPrefix(1)).
                        uri("lb://service-test"))
                .build();
    }
}

3、使用Hystrix 

在spring cloud gateway中可以使用Hystrix。Hystrix是 spring cloud中一個服務熔斷降級的組件,在微服務系統有着十分重要的作用。
Hystrix是 spring cloud gateway中是以filter的形式使用的,代碼如下:

/**
 * gateway 方式實現
 */
@SpringBootApplication
public class ServiceGatewayApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(ServiceGatewayApplication.class, args);
    }
 
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        StripPrefixGatewayFilterFactory.Config config =
                new StripPrefixGatewayFilterFactory.Config();
        config.setParts(1);
        return builder.routes()
                .route("test", a -> a.path("/test/**")
                        .filters(b -> b.stripPrefix(1))
                        .uri("http://localhost:8762"))
                .route("api-test2", r -> r.path("/api-test2/**")
                        .filters(f -> f.stripPrefix(1))
                        .uri("http://localhost:8762"))
                .route("api-test3", r -> r.path("/api-test3/**").
                        filters(f -> f.stripPrefix(1)).
                        uri("lb://service-test"))
               .route(p -> p
                .host("*.hystrix.com")
                .filters(f -> f
                    .hystrix(config -> config
                        .setName("mycmd")
                        .setFallbackUri("forward:/fallback")))
                .uri(httpUri))
            .build();
    }
}

在上面的代碼中,我們使用了另外一個router,該router使用host去斷言請求是否進入該路由,當請求的host有“*.hystrix.com”,都會進入該router,該router中有一個hystrix的filter,該filter可以配置名稱、和指向性fallback的邏輯的地址,比如本案例中重定向到了“/fallback”。

現在寫的一個“/fallback”的l邏輯:


 @RequestMapping("/fallback")
    public Mono<String> fallback() {
        return Mono.just("fallback");
    }

因此帶xxx.hystrix.com的請求執行了hystrix的fallback的邏輯。

三、配置過濾器
 

過濾器:gateway有兩種filter,一種是GlobalFilter一種是GatewayFilter,全局過濾器默認對所有路由有效,gatewayFilter需要進行指定。

filter的作用和生命週期:

       由filter工作流程點,可以知道filter有着非常重要的作用,在“pre”類型的過濾器可以做參數校驗、權限校驗、流量監控、日誌輸出、協議轉換等,在“post”類型的過濾器中可以做響應內容、響應頭的修改,日誌的輸出,流量監控等。首先需要弄清一點爲什麼需要網關這一層,這就不得不說下filter的作用了。

1、GlobalFilter 配置全局過濾器

/**
*  
*  配置全局過濾器
**/
@Configuration
@Slf4j
public class AccessGatewayFilter implements GlobalFilter{
  
      @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, GatewayFilterChain gatewayFilterChain) {
        //方法
        return gatewayFilterChain.filter(serverWebExchange.mutate().request(build).build());
    }
}

注意:在這裏可以實現記錄日誌和訪問權限校驗等

2.自定義GatewayFilter

GatewayFiltery有兩種類型的filter,分別爲pre和post類型,以下提供一個demo的配置

定義PreGatewayFilter:

public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory {
 
      public PreGatewayFilterFactory() {
          super(Config.class);
      }
 
      public GatewayFilter apply() {
          return apply(o -> {
          });
      }
      @Override
      public GatewayFilter apply(Config config) {
          // grab configuration from Config object
          return (exchange, chain) -> {
              //If you want to build a "pre" filter you need to manipulate the
              //request before calling change.filter  ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
              builder.header("GatewayFilter", "PreGatewayFilterFactory success");
              //use builder to manipulate the request
              return chain.filter(exchange.mutate().request(builder.build()).build());
          };
      }
        public static class Config {
            //Put the configuration properties for your filter here
      }
}

定義PostGatewayFilter:

public class PostGatewayFilterFactory extends AbstractGatewayFilterFactory {
 
    private Logger logger = LoggerFactory.getLogger(PostGatewayFilterFactory.class);
 
    public PostGatewayFilterFactory() {
        super(Config.class);
    }
 
    public GatewayFilter apply() {
        return apply(o -> {
        });
    }
 
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
          logger.info("PostGatewayFilter...");
          return chain.filter(exchange).then(Mono.fromRunnable(() -> {
              ServerHttpResponse response = exchange.getResponse();
            //Manipulate the response in some way
            }));
          };
    }
 
    public static class Config {
        //Put the configuration properties for your filter here
  }
}

四、跨域配置

import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator;
import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.codec.support.DefaultServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
 
/**
 * 跨域允許
 */
@Configuration
public class CorsConfig {
    private static final String MAX_AGE = "18000L";
 
    @Bean
    public WebFilter corsFilter() {
        return (ServerWebExchange ctx, WebFilterChain chain) -> {
            ServerHttpRequest request = ctx.getRequest();
            if (CorsUtils.isCorsRequest(request)) {
                HttpHeaders requestHeaders = request.getHeaders();
                ServerHttpResponse response = ctx.getResponse();
                HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
                HttpHeaders headers = response.getHeaders();
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
                headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders
                        .getAccessControlRequestHeaders());
                if(requestMethod != null){
                    headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
                }
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
                headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
                headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
                if (request.getMethod() == HttpMethod.OPTIONS) {
                    response.setStatusCode(HttpStatus.OK);
                    return Mono.empty();
                }
 
            }
            return chain.filter(ctx);
        };
    }
 
    @Bean
    public ServerCodecConfigurer serverCodecConfigurer() {
        return new DefaultServerCodecConfigurer();
    }
 
    /**
     * 如果使用了註冊中心(如:Eureka),進行控制則需要增加如下配置
     */
    @Bean
    public RouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient,
                                                                        DiscoveryLocatorProperties properties) {
        return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章