Gateway网关

SpringCloudGateway:
    底层使用的是webFlux技术(java),内部使用的服务器为Netty.
  WebFlux是一个响应式的技术.
    稍后我们在学习网关的过滤器时,使用的都是webFlux的过滤器,与我们之前学的稍有不同.
  Netty服务器默认端口为8080
我们之前学过的所有组件,底层均依赖SpringMVC-Servlet,依赖Tomcat服务器.
  Tomcat: 200

网关入门

1.创建maven项目,导入启动器

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>day04-cloud-demo</artifactId>
        <groupId>com.bw</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gateway-service</artifactId>
    <!-- gateway启动器 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.配置网关服务

server:
  port: 10010
spring:
  application:
    name: gateway-service # 服务名称
  cloud:
    gateway: # 网关配置
      routes: # 路由(分发请求)
        - id: user-service-name # 当前路由的唯一标识(自定义,唯一即可)
          uri: http://127.0.0.1:8081 # 路由的目标微服务地址
          predicates: # 断言,匹配规则
            - Path=/user/** # 按照路径匹配的规则

面向注册中心服务-网关

使用Eureka做注册中心

1.导入Eureka启动器

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>day04-cloud-demo</artifactId>
        <groupId>com.bw</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gateway-service</artifactId>
    <!-- gateway启动器 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!-- eureka客户端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.yml配置

spring:
  cloud:
    gateway:
      routes:
        - id: user-service-name # 当前路由的唯一标识(自定义,唯一即可)
#          uri: http://127.0.0.1:8081 # 路由的目标微服务地址
          uri: lb://user-service # 路由的目标 微服务名称
          predicates: # 断言,匹配规则
            - Path=/user/** # 按照路径匹配的规则
  application:
    name: gateway-service
server:
  port: 10010
# eureka服务地址
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

3.其他路由方式(感兴趣可以去看)

Spring Cloud Gateway

网关过滤器

 路由/局部过滤器: 只对某一个路由规则生效
 默认过滤器: 对所有的路由规则都生效
 全局/自定义过滤器: 作用与默认过滤器一致,对所有的路由规则都生效 
     需要手动写代码实现过滤器(webflux技术)

局部/路由过滤器

添加请求头

# 注意格式 
spring:
  cloud:
    gateway:
      routes:
        - id: user-service-name # 当前路由的唯一标识(自定义,唯一即可)
#          uri: http://127.0.0.1:8081 # 路由的目标微服务地址
          uri: lb://user-service # 路由的目标 微服务名称
          predicates: # 断言,匹配规则
            - Path=/user-service/** # 按照路径匹配的规则
          filters:
            - AddRequestHeader=info, java is best!
  application:
    name: gateway-service
server:
  port: 10010
# eureka服务地址
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

Hystrix服务降级

测试方式: 制造问题,将下游服务宕机(停止服务),然后通过网关访问下游服务,就会出现降级效果

1.导入Hystrix启动器
 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
 </dependency>
2.配置Hystrix降级工厂
spring:
  cloud:
    gateway:
      default-filters: # 默认过滤项
        - name: Hystrix # 指定过滤工厂名称
          args: # 指定过滤的参数
            name: fallbackcmd  # hystrix的指令名
            fallbackUri: forward:/fallbackTest # 失败后的跳转路径
      routes:
        - id: user-service-name # 当前路由的唯一标识(自定义,唯一即可)
#          uri: http://127.0.0.1:8081 # 路由的目标微服务地址
          uri: lb://user-service # 路由的目标 微服务名称
          predicates: # 断言,匹配规则
            - Path=/user-service/** # 按照路径匹配的规则
          filters:
            - AddRequestHeader=info, java is best!
  application:
    name: gateway-service
server:
  port: 10010
# eureka服务地址
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
3.编写降级方法

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FallbackController {
    @RequestMapping("/fallbackTest")
    public String fallbackTest(){
        return "网关服务降级: 服务器繁忙,稍后重试...";
    }
}

server:
  port: 10010
spring:
  application:
    name: gateway-service # 服务名称
  cloud:
    gateway: # 网关配置
      default-filters: # 默认过滤 全局过滤,对所有的路由规则都生效
        - StripPrefix=1 # 截去路由的前缀(1个前缀)
        - name: Hystrix # 指定过滤工厂名称(不能乱写)
          args: # 指定过滤的参数
            name: fallbackcmd  # hystrix的指令名(固定值)
            fallbackUri: forward:/fallbackTest # 失败后的跳转路径
      routes: # 路由(分发请求)
        - id: user-service-name # 当前路由的唯一标识(自定义,唯一即可)
         # uri: http://127.0.0.1:8081 # 路由的目标微服务地址
          uri: lb://user-service # 路由的目标微服务名称
          predicates: # 断言,匹配规则
            - Path=/user-service/** # 按照路径匹配的规则
          filters: # 添加局部过滤器
            - AddRequestHeader=info,java is best

# eureka服务地址
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

全局过滤器

方式1:

import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;


@Order(0) // 通过注解声明过滤器顺序
@Component // 创建类对象存放到IOC容器中
public class LoginFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取请求携带的username值
        String username = exchange.getRequest().getQueryParams().toSingleValueMap().get("username");
        // 判断请求参数是否正确
        if(StringUtils.equals(username, "admin")){
            // 正确,放行
            return chain.filter(exchange);
        }
        // 错误,需要拦截,设置状态码
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        // 结束任务
        return exchange.getResponse().setComplete();
    }
}

方式2:

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import reactor.core.publisher.Mono;


@Slf4j
@Configuration
public class FilterConfiguration {

    @Bean
    @Order(-2)
    public GlobalFilter globalFilter1(){
        return ((exchange, chain) -> {
            log.info("过滤器1的pre阶段!");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("过滤器1的post阶段!");
            }));
        });
    }

    @Bean
    @Order(-1)
    public GlobalFilter globalFilter2(){
        return ((exchange, chain) -> {
            log.info("过滤器2的pre阶段!");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("过滤器2的post阶段!");
            }));
        });
    }

    @Bean
    @Order(0)
    public GlobalFilter globalFilter3(){
        return ((exchange, chain) -> {
            log.info("过滤器3的pre阶段!");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("过滤器3的post阶段!");
            }));
        });
    }
}

过滤器执行顺序

请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter

请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器

排序的规则是什么呢?

  • 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前

  • GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定

  • 路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。

  • 当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。

网关限流

限流: 限制单位时间内请求的数量

1.导入redis的启动器

<!--SpringCloudGateway整合redis后的启动器-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

2.配置令牌工厂过滤器对象

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.InetSocketAddress;

/**
 * 限流的原理:
 *     当并发请求来时,解析当前请求中携带的用户信息(ip,token),将用户的唯一标记作为redis的key
 *     并给key设置超时时间和有效次数,每访问一次次数减1
 *
 *     每个ip5秒内访问5次
 *     第一次过来:   127.0.0.1    4
 *     第二次过来:   127.0.0.1    3
 *     第三次过来:   127.0.0.1    2
 *     第四次过来:   127.0.0.1    1
 *     第五次过来:   127.0.0.1    0
 *     第六次过来:   127.0.0.1    0  忽略当前请求
 */
@Component
public class IpKeyResolver implements KeyResolver {
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        // 获取请求信息对象
        ServerHttpRequest request = exchange.getRequest();
        // 获取请求者的地址信息
        InetSocketAddress remoteAddress = request.getRemoteAddress();
        // 获取请求者的主机名称  ip
        String hostName = remoteAddress.getHostName();
        System.out.println("请求者的地址信息: "+remoteAddress);
        System.out.println("请求者的主机名称: "+hostName);
        return Mono.just(hostName);
    }
}

3.配置令牌桶的大小和生成速率

server:
  port: 10010
spring:
  application:
    name: gateway-service # 服务名称
  cloud:
    gateway: # 网关配置
      default-filters: # 默认过滤 全局过滤,对所有的路由规则都生效
        - name: RequestRateLimiter #请求数限流 名字不能随便写
          args:
            key-resolver: "#{@ipKeyResolver}" # 指定一个key生成器
            redis-rate-limiter.replenishRate: 2 # 生成令牌的速率 每秒生成2个令牌
            redis-rate-limiter.burstCapacity: 2 # 桶的容量 
        - StripPrefix=1 # 截去路由的前缀(1个前缀)
        - name: Hystrix # 指定过滤工厂名称(不能乱写)
          args: # 指定过滤的参数
            name: fallbackcmd  # hystrix的指令名(固定值)
            fallbackUri: forward:/fallbackTest # 失败后的跳转路径
      routes: # 路由(分发请求)
        - id: user-service-name # 当前路由的唯一标识(自定义,唯一即可)
         # uri: http://127.0.0.1:8081 # 路由的目标微服务地址
          order: 0 # 设置过滤器的执行顺序 值越小优先级越高
          uri: lb://user-service # 路由的目标微服务名称
          predicates: # 断言,匹配规则
            - Path=/user-service/** # 按照路径匹配的规则
          filters: # 添加局部过滤器
            - AddRequestHeader=info,java is best

# eureka服务地址
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

网关跨域

同源策略: 浏览器默认遵循同源策略
    一个网页上展示的所有数据都应当来自同一台服务器

跨域: 协议、ip、端口三者有任何一者不同就是跨域
   违背同源策略就是跨域

server:
  # 配置netty服务器的端口号,netty服务器默认端口与tomcat端口一致 8080
  port: 10010
spring:
  application:
    name: gateway-service # 服务名称
  cloud:
    gateway: # 网关配置
      default-filters: # 默认过滤器: 对所有的路由规则都生效
        - StripPrefix=1 # 截去路由的前缀(1个前缀)  localhost:10010/userservice/user/1 --->  微服务ip地址/user/1
      routes: # 路由(分发请求) 配置分发的规则(路由断言)
        - id: devic-service-name # 当前路由的唯一标识(自定义,唯一即可)
          uri: lb://devic-service # 路由的目标微服务地址
          predicates: # 断言,匹配规则
            - Path=/deviceservice/** # 按照路径匹配的规则
        - id: type-service-name # 当前路由的唯一标识(自定义,唯一即可)
          uri: lb://type-service # 路由的目标微服务地址
          predicates: # 断言,匹配规则
            - Path=/typeservice/** # 按照路径匹配的规则
        - id: user-service-name # 当前路由的唯一标识(自定义,唯一即可)
          uri: lb://user-service # 路由的目标微服务地址
          predicates: # 断言,匹配规则
            - Path=/userservice/** # 按照路径匹配的规则
      # 配置网关允许跨域
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "*"
            allowedHeaders: "*"
            allowedMethods: "*"
            allowCredentials: true
            maxAge: 360000

Logo

一起探索未来云端世界的核心,云原生技术专区带您领略创新、高效和可扩展的云计算解决方案,引领您在数字化时代的成功之路。

更多推荐