15. SpringCloudAlibaba 实践笔记:SpringCloud Gateway项目集成

我们需要在项目中增加一个服务网关模块 shop-gateway,在服务网关模块中实现网关的能力。此时,我们的项目中就会有用户微服务、商品微服务、订单微服务和服务网关。
image

新建网关模块

引入依赖

在项目中新建 shop-gateway 模块,在服务网关 shop-gateway 模块的 pom.xml 文件中添加如下依赖。

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
</dependencies>

配置应用

在服务网关 shop-gateway 模块的 resources 目录下新建 application.yml 文件,并在文件中添加如下配置信息。

server:
  port: 10001  # 设置服务器端口为 10001
spring:
  application:
    name: server-gateway  # 设置应用名称为 server-gateway
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':  # 全局跨域配置的路径匹配规则,表示匹配所有路径
            allowedOrigins: "*"  # 允许所有来源的请求
            allowedMethods: "*"  # 允许所有的 HTTP 方法
            allowCredentials: true  # 允许携带凭证(如 Cookie)
            allowedHeaders: "*"  # 允许所有的请求头
      routes:
        - id: user-gateway  # 路由的唯一标识为 user-gateway
          uri: http://localhost:8060  # 路由的目标地址是本地的 8060 端口
          order: 1  # 路由的优先级为 1
          predicates:
            - Path=/server-user/**  # 路由的路径匹配规则,匹配以/server-user/开头的路径
          filters:
            - StripPrefix=1  # 去除请求路径中的一级前缀
        - id: product-gateway
          uri: http://localhost:8070
          order: 1
          predicates:
            - Path=/server-product/**
          filters:
            - StripPrefix=1
        - id: order-gateway
          uri: http://localhost:8080
          order: 1
          predicates:
            - Path=/server-order/**
          filters:
            - StripPrefix=1
  • globalcors:此节点下的配置是为了解决SpringCloud Gateway跨域的问题。
  • routes:表示一个路由数组,可以在此节点下配置多个路由信息。
  • id:当前路由的唯一标识。
  • order:路由的优先级,数字越小表示优先级越高。
  • predicates:网关断言,也就是路由转发的条件,也是一个数组,可以配置多个路由转发条件。
  • Path:当客户端请求的路径满足Path的规则时,进行路由转发操作。
  • filters:网关过滤器,在过滤器中可以修改请求的参数和header信息,以及响应的结果和header信息,网关过滤器也是一个数组,可以配置多个过滤规则。
  • StripPrefix:网关在进行路由转发之前,会去掉1层访问路径。

启动类

在服务网关 shop-gateway 模块下新建 GatewayStarter 类,表示服务网关的启动类

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

网关整合 Nacos

引入依赖

在服务网关 shop-gateway 模块的 pom.xml 文件中继续添加如下依赖。

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

在服务网关 shop-gateway 模块的 GatewayStarter 类添加 @EnableDiscoveryClient 注解。

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

修改配置应用

server:
  port: 10001  # 设置服务器监听的端口为 10001。
spring:
  application:
    name: server-gateway  # 定义应用的名称为 server-gateway。
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848  # 配置 Nacos 服务发现的地址为本地的 127.0.0.1:8848。
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':  # 全局跨域配置,匹配所有路径。
            allowedOrigins: "*"  # 允许任何来源的请求。
            allowedMethods: "*"  # 允许任何 HTTP 方法。
            allowCredentials: true  # 允许携带凭证。
            allowedHeaders: "*"  # 允许任何请求头。
      discovery:
        locator:
          enabled: true  # 启用从服务发现中获取路由的功能。
      routes:
        - id: user-gateway  # 第一个路由的唯一标识。
          uri: lb://server-user  # 使用服务发现的负载均衡器来定位名为 server-user 的服务。
          order: 1  # 路由的优先级为 1。
          predicates:
            - Path=/server-user/**  # 路由的路径匹配规则,匹配以 /server-user/ 开头的路径。
          filters:
            - StripPrefix=1  # 去除请求路径中的一级前缀。
        - id: product-gateway
          uri: lb://server-product
          order: 1
          predicates:
            - Path=/server-product/**
          filters:
            - StripPrefix=1
        - id: order-gateway
          uri: lb://server-order
          order: 1
          predicates:
            - Path=/server-order/**
          filters:
            - StripPrefix=1

网关整合 Sentinel 限流

引入依赖

在服务网关 shop-gateway 模块的 pom.xml 文件中添加如下依赖。

<dependencies>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
    </dependency>
</dependencies>

在服务网关 shop-gateway 模块中下新建 GatewayConfig 类。基于 Sentinel 的 Gateway 限流是通过其提供的 Filter 来完成的,使用时只需注入对应的 SentinelGatewayFilter 实例以及 SentinelGatewayBlockExceptionHandler 实例即可。

@Configuration
public class GatewayConfig {

    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

    @Value("${spring.cloud.gateway.discovery.locator.route-id-prefix}")
    private String routeIdPrefix;

    public GatewayConfig(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /**
     * 初始化一个限流的过滤器
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void init() {
       this.initGatewayRules();
       this.initBlockHandlers();
    }

    /**
     * 配置初始化的限流参数
     */
    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();

        /**
         * Sentinel整合SpringCloud Gateway使用的API类型为Route ID类型,也就是基于route维度时,
         * 由于Sentinel为SpringCloud Gateway网关生成的API名称规则如下:
         * 生成的规则为:${spring.cloud.gateway.discovery.locator.route-id-prefix}后面直接加上目标微服务的名称,如下所示。
         * ${spring.cloud.gateway.discovery.locator.route-id-prefix}目标微服务的名称
         * 其中,${spring.cloud.gateway.discovery.locator.route-id-prefix}是在yml文件中配置的访问前缀
         *
         * 为了让通过服务网关访问目标微服务链接后,请求链路中生成的API名称与流控规则中生成的API名称一致,以达到启动项目即可实现访问链接的限流效果,
         * 而无需登录Setinel管理界面手动配置限流规则,可以将
         * resource参数设置为${spring.cloud.gateway.discovery.locator.route-id-prefix}目标微服务的名称
         *
         * 当然,如果不按照上述配置,也可以在项目启动后,通过服务网关访问目标微服务链接后,在Sentinel管理界面的请求链路中找到对应的API名称所代表的请求链路,
         * 然后手动配置限流规则。
         **/
//        //用户微服务网关
//        rules.add(this.getGatewayFlowRule("user-gateway"));
//        //商品微服务网关
//        rules.add(this.getGatewayFlowRule("product-gateway"));
//        //订单微服务网关
//        rules.add(this.getGatewayFlowRule("order-gateway"));
        //用户微服务网关
        rules.add(this.getGatewayFlowRule(getResource("server-user")));
        //商品微服务网关
        rules.add(this.getGatewayFlowRule(getResource("server-product")));
        //订单微服务网关
        rules.add(this.getGatewayFlowRule(getResource("server-order")));
        //加载规则
        GatewayRuleManager.loadRules(rules);
    }

    private String getResource(String targetServiceName){
        if (routeIdPrefix == null){
            routeIdPrefix = "";
        }
        return routeIdPrefix.concat(targetServiceName);
    }

    private GatewayFlowRule getGatewayFlowRule(String resource){
        //传入资源名称生成GatewayFlowRule
        GatewayFlowRule gatewayFlowRule = new GatewayFlowRule(resource);
        //限流阈值
        gatewayFlowRule.setCount(1);
        //统计的时间窗口,单位为
        gatewayFlowRule.setIntervalSec(1);
        return gatewayFlowRule;
    }

    /**
     * 配置限流的异常处理器
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    /**
     * 自定义限流异常页面
     */
    private void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap<>();
                map.put("code", 1001);
                map.put("codeMsg", "接口被限流了");
                return ServerResponse.status(HttpStatus.OK).
                        contentType(MediaType.APPLICATION_JSON_UTF8).
                        body(BodyInserters.fromObject(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

修改配置应用

server:
  port: 10001
spring:
  application:
    name: server-gateway
  main:
    allow-bean-definition-overriding: true
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    sentinel:
      transport:
        port: 7777
        dashboard: 127.0.0.1:8888
      web-context-unify: false
      eager: true
      
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods: "*"
            allowCredentials: true
            allowedHeaders: "*"
      discovery:
        locator:
          enabled: true
          route-id-prefix: gateway-
posted @ 2024-11-06 23:29  Jacob-Chen  阅读(372)  评论(0)    收藏  举报