06-搭建微服务-Gateway作为网关

0、前提

在上文的基础上,《05-搭建微服务-Nacos作为注册中心及动态刷新_汐小旅Shiory-CSDN博客


1、为什么使用网关SpringCloud-Gateway?

        目前主流的是微服务,一个项目会有很多服务。这些服务有各自的IP和端口,在没有网关之前,客户端维护IP、端口,管理效率太低。而且在集群的情况下,客户端无法实现负载均衡。
        虽然后来有Nginx可以解决这个问题,但还是存在很多的硬编码。
        而SpringCloud-Gateway,是基于Spring5、SpringBoot2.0和Project Reactor等技术开发的网关,目的是为微服务架构系统提供高性能且简单易用的API路由管理方式。它性能强劲,是第一代网关Zuul的1.6倍;它功能强大,内置很多实用的功能,如:路由、过滤、限流就、监控等...而且易于扩展。
        同时,SpringCloud-Gateway本身也是一个微服务,通过服务名称远程调用其他服务,客户端只需记住网关的IP和端口,需把请求发给Gateway,Gateway做一个路由转发,转发给对应的服务请求即可。

1、SpringCloud-Gateway的位置

        处于客户端和服务群之间,API网关

        客户端---Nginx---API网关---服务群

2、可以做的事:

1、路由转发

2、过滤,比如统一鉴权

3、限流

4、监控

5、....


2、核心概念(组件) 

1、Route(路由)

        路由是构建网关的基本模块,它由ID、目标URI、一系列的断言和过滤器组件,如果断言为true则匹配该路由

2、Predicate(断言、谓词)

        开发人员可匹配Http请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由

3、Filter(过滤)

        指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或之后队请求进行修改,比如token验证


3、工作流程 

1、客户端向SpringCloud Gateway发出请求

2、Gateway Handler Mapping中找到与请求相匹配的路由

3、将其发送到Gateway Web Handler

4、Gateway Web Handler再通过指定的过滤器链来将请求发送到实际的服务执行业务逻辑,然后返回(过滤器可能会在发送代理请求之前("pre")或之后("post")执行业务逻辑)。


4、搭建网关 

0、新建Module--cloud-gateway

1、加入依赖 

spring-cloud-starter-gateway 网关,底层基于Netty不要依赖spring-cloud-starter-web。因为spring-cloud-starter-web,是基于SpringMVC的,是servlet编程模型,运行服务器是tomcat,而spring-cloud-starter-gateway,基于Spring WebFlux,是reactor编程模型,运行服务器是netty容器。

        <!--gateway网关场景依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--端点监控场景依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--Nacos服务注册与发现场景依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--Nacos配置中心场景依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!--@ConfigurationProperties注解飘红去除-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

2、编写启动类

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * =====================================================
 * 网关
 * @author 汐小旅Shiory
 * @date 2021/12/14 21:24
 * =====================================================
 */
@Slf4j
@SpringBootApplication
@EnableDiscoveryClient // 服务注册与发现
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

3、编写配置文件

1、新建application.yml,配置服务名称和端口

server:
  port: 9999
spring:
  application:
    name: cloud-gateway

2、在Nacos上新建一个配置文件,编写Nacos服务注册与发现配置

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.2.102:8848 # nacos-server的地址
        username: nacos # nacos-server用户名
        password: nacos # nacos-server密码
        namespace: shiory # 命名空间ID
        group: DEFAULT_GROUP # 组

 3、新建一个bootstrap.yml,编写Naocs配置中心配置

spring:
  cloud:
    nacos:
      config:
        server-addr: 192.168.2.102:8848 # nacos-server的地址
        username: nacos # nacos-server用户
        password: nacos # nacos-server密码
        namespace: shiory # 命名空间ID
        group: DEFAULT_GROUP # 组
        # 文件名是通过公式来拼接的: ${prefix}-${spring.profiles.active}.${file-extension}
        prefix: shiory-gateway # 配置文件名前缀${prefix}
        file-extension: yml # 配置文件名扩展${file-extension}
  profiles:
    active: dev # 配置文件名扩展${spring.profiles.active}

4、路由配置(最终是使用动态路由

查看源码可知,路由是一个数组

注意事项:路由配置多个以后,会从上往下,一旦匹配到上面就不会再去匹配下面。所以如果有"/**",一定要放在最后 

情况1、路由到指定的URL

在Nacos上新建的配置文件中加入下面的内容

spring:
  cloud:
    # 网关配置
    gateway:
      discovery:
        locator:
          # 开启注册中心的路由定位器
          enabled: true
      # 路由配置
      routes:
        - id: baidu # ID保证唯一
          uri: http://www.baidu.com # 服务地址
          predicates:
            - Path=/** # 路径匹配规则

启动cloud-gateway服务

访问:localhost:9999,就会跳转到百度页面

访问:localhost:9999/a,就会出现404

        原因:localhost:9999 ===> http://www.baidu.com

                localhost:9999/a ===> http://www.baidu.com/a

情况2:路由到微服务---1、静态路由(有硬编码,IP和端口)

此处的"/**"放到了最后,不然无法匹配到"/demo3/**"

spring:
  cloud:
    # 网关配置
    gateway:
      discovery:
        locator:
          # 开启注册中心的路由定位器
          enabled: true
      # 路由配置
      routes:
        - id:cloud-demo3  # ID保证唯一
          uri: http://192.168.2.102:7003/ # 服务地址
          predicates:
            - Path=/demo3/**  # 路径断言,多个用逗号隔开
            
        - id: baidu # ID保证唯一
          uri: http://www.baidu.com # 服务地址
          predicates:
            - Path=/** # 路径匹配规则

情况2:路由到微服务---2、动态路由

spring:
  application:
    name: cloud-gateway
  cloud:
    # 网关配置
    gateway:
      discovery:
        locator:
          # 开启注册中心的路由定位器
          enabled: true
  
      # 路由配置
      routes:
        - id: cloud-demo3 # ID保证唯一
          uri: lb://cloud-demo3   # 服务名称 lb,LoadBalance,负载均衡,通过ribbon实现的
          predicates:
            - Path=/demo3/**,/testNacosRefresh/** # 路径断言,多个用逗号隔开
            
        - id: baidu # ID保证唯一
          uri: http://www.baidu.com # 服务地址
          predicates:
            - Path=/** # 路径匹配规则

访问:localhost:9999/demo3/xxx,就会访问到服务demo3的接口


5、谓词工厂

多个谓词工厂可以并存

1、内置谓词工厂

SpringCloudGateway提供了十来种路由谓词工厂(见官网),为网关实现灵活的转发提供了基石。官网上有使用例子

Spring Cloud Gateway

2、自定义谓词工厂

1、自定义谓词工厂的命名规范:后缀必须是RoutePredicateFactory

2、继承AbstractRoutePredicateFactory<配置的数据类型>


6、过滤器

1、内置过滤器

见官网,局部路由器,只针对某个路由生效。官网上有使用例子

Spring Cloud Gateway

2、自定义局部过滤器

与自定义谓词工厂差不多。局部路由器,只针对某个路由生效

1、命名规范:后缀为GatewayFilterFactory

2、继承AbstractGatewayFilterFactory<配置的数据类型>

// 前处理
chain.filter(exchange)放行
chain.filter(exchange).then()放行,then()中可以处理放行之后要处理的事
then(
	Mono.fromRunnable(() -> {
		// filter后处理
	})
)

3、全局过滤器 

1、内置全局过滤器:官网查看:Spring Cloud Gateway

 2、自定义全局过滤器

步骤:

1、实现GlobalFilter,Ordered
2、重写filter方法和getOrder方法
其中,filter方法负责前处理、后处理;getOrder方法,返回数字越小越先执行
自定义全局过滤器,比如登录认证过滤器
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * =====================================================
 *  全局Token 过滤器
 * @author 汐小旅Shiory
 * @date 2021/12/14 22:02
 * =====================================================
 */
@Slf4j
@Component
public class GlobalAccessTokenFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("全局过滤器============前执行");
        //Mono<Void> mono = chain.filter(exchange); // 直接放行
        Mono<Void> mono = chain.filter(exchange).then(// 放行之后再后执行
            Mono.fromRunnable(() -> {
                log.info("全局过滤器============后执行");
            })
        );
        return mono;
    }

    // 返回数字越小越先执行
    @Override
    public int getOrder() {
        return -2;
    }
}

另一种全局过滤器写法,在启动类中编写

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import reactor.core.publisher.Mono;

/**
 * =====================================================
 * 网关
 * @author 汐小旅Shiory
 * @date 2021/12/14 21:24
 * =====================================================
 */
@Slf4j
@SpringBootApplication
@EnableDiscoveryClient // 服务注册与发现
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }

    @Bean
    @Order(-1)
    public GlobalFilter gf1(){
        return (exchange, chain) -> {
            log.info("前执行1=================");
            return chain.filter(exchange).then(
                    Mono.fromRunnable(() -> {
                        log.info("后执行1=================");
                    })
            );
        };
    }
}

7、Gateway跨域问题

浏览器到网关:存在跨域

网关到其他服务:不存在跨域

springboot项目跨域解决方式:加注解@CrossOrigin

由于Gateway使用的是Webflux,而不是SpringMVC,所以需要先关闭SpringMVC的cors,再从Gateway的Filter里面设置cors

Gateway解决跨域

1、配置方式

spring:
  cloud:
    # 网关配置
    gateway:
      # 跨域配置
      globalcors:
        cors-configurations:
          '[/**]':
            allowCredentials: true # 开启支持用户凭据
            allowedOrigins: "*" # 远程服务器
            allowedMethods: "*" # 请求方法
            allowedHeaders: "*" # 请求头

 2、编码方式

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

/**
 * =====================================================
 * Gateway跨域配置
 * @author 汐小旅Shiory
 * @date 2021/12/15 23:22
 * =====================================================
 */
@Configuration
public class CorsConfig {
    /**
     * CorsFilter 是SpringMVC的cors
     * CorsWebFilter 是Webflux的cors
     * @return
     */
    @Bean
    public CorsWebFilter corsWebFilter(){
        // 跨域配置
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);// 开启支持用户凭据
        config.addAllowedOrigin("*");// 远程服务器
        config.addAllowedMethod("*");// 请求方法
        config.addAllowedHeader("*");// 请求头

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        // 为指定路径注册跨域配置
        source.registerCorsConfiguration("/**",config);
        CorsWebFilter corsWebFilter = new CorsWebFilter(source);
        return corsWebFilter;
    }
}

3、跨域测试前端代码 

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>跨域测试</title>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  </head>
  <body>
    <div id="app">
      {{ info }}
    </div>

    <script>
    const app = {
      data() {
        return {
          info: '请求失败!!'
        }
      },
      mounted () {
        axios
          .get('http://192.168.2.102:9999/demo3/gatewayTest')
          .then(response => (this.info = response.data))
          .catch(function (error) { // 请求失败处理
              console.log(error);
          });
      }
    }

    Vue.createApp(app).mount('#app')
    </script>
  </body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值