Wang's Blog头像
Java 项目实战:交友项目 - 项目架构:网关的概述封面图

Java 项目实战:交友项目 - 项目架构:网关的概述

概述

多服务架构在接入层暴露出的问题,本文给出解法的全貌:网关。先把「为什么引入、引入后放哪、工程里长什么样」讲清楚,具体搭建和鉴权代码留到后续文章展开。

纲要

  • 架构问题回顾:项目多服务架构的三个接入层痛点
  • 网关是什么:路由 + 过滤器两大核心能力
  • 网关的三大职责:统一入口/路由、统一鉴权、统一跨域/限流/日志
  • 网关在项目架构中的位置:接入层落点
  • 工程落点:实读 mf-gateway 模块(pom / bootstrap.yml / 路由规则)
  • Spring Cloud Gateway 的角色:响应式 Netty,与 MVC 应用的差异
  • 统一鉴权预告:TokenInterceptor 的去留问题
  • 常见误解澄清

架构问题回顾

前文在架构问题分析中铺过三个痛点,这里快速收拢,因为它们正好对应网关的三项职责:

痛点现状后果
多后端地址C 端 App 直连 mf-app-server,管理端 Vue 直连 mf-admin前端维护多套地址,服务一多就散架
鉴权重复每个服务各自写 JWT 校验(项目用拦截器 TokenInterceptor)同一份校验逻辑拷 N 份,改规则要全量回归
跨域分散CORS 配置散落在各服务管理端 Vue 和 App 两边口径不一致时,排查成本高

这三个问题的共同点:它们都是「接入层」的事,却被迫摊派给了每个业务服务。把接入层的职责从各服务里抽出来、集中到一处,这个「一处」就是网关。

网关是什么:路由 + 过滤器

网关(Gateway)处在所有客户端请求的必经之路上:请求先到网关,网关按路由配置把请求转发给后端的具体服务。它有两项核心能力:

  • 路由转发:按路径、请求头等条件(谓词,Predicate)决定请求去哪个服务,转发前还能改写路径(Filter)。
  • 过滤器:请求转发前后都可以被过滤器拦截,借这个位置可以挂授权、限流、登录校验、日志记录这类横切逻辑。

用一张时序图看请求的走向:

mf-admin mf-app-server mf-gateway 客户端(App / Vue) mf-admin mf-app-server mf-gateway 客户端(App / Vue) 请求 /app/xx 路由匹配 Path=/app/** 转发(StripPrefix 去掉 /app) 响应 响应 请求 /admin/xx 路由匹配 Path=/admin/** 转发(StripPrefix 去掉 /admin) 响应 响应

关键收益在过滤器上:功能加在网关一次,所有后端服务自动获得。比如在网关做了 token 统一校验,mf-app-server 和 mf-admin 就都不用再各自校验了,这正是「接入层职责上收」的含义。

网关的三大职责

把上面的两个能力和项目的问题对齐,得到三大职责:

职责解决的问题项目里的体现
统一入口 / 路由前端维护多个后端地址前端只认识网关地址 ip:8888,按路径前缀 /app/**、/admin/** 分流
统一鉴权每个服务重复写 JWT 校验JWT 校验在网关做一次,后端服务不再重复鉴权
统一跨域 / 限流 / 日志CORS、限流、日志分散在各服务CORS 在网关配一次,全服务生效

第二项值得多说两句。项目此前用 TokenInterceptor 做过鉴权,那是在每个服务内部用 Spring MVC 拦截器做统一身份鉴权,「统一」只是统一到了「一个服务内部的所有接口」,服务和服务之间仍然是重复的。引入网关后,鉴权位置整体上移:从「每个服务的拦截器」移到「所有服务的共同入口」。同样叫统一鉴权,粒度完全不同。

引入网关后

App

mf-gateway
统一 JWT 校验

Vue

mf-app-server

mf-admin

引入网关前

App

mf-app-server
JWT 拦截器

Vue

mf-admin
JWT 拦截器

网关的位置

对照项目的分层图(表现层 → 应用层 → 域服务层),网关落在哪里一目了然:应用层的最前面,即「接入层」。

域服务层(Dubbo RPC)

应用层

表现层

/app/**

/admin/**

Dubbo

Dubbo

C 端 App

管理端 Vue

mf-gateway(接入层)

mf-app-server

mf-admin

mf-dubbo-db / mf-dubbo-mongo

三个层次各干各的:网关管「进来」,mf-app-server / mf-admin 管业务编排,Dubbo 域服务管数据。注意网关不做业务逻辑,它是交通枢纽,不是处理车间。

工程落点:实读 mf-gateway

看 09 章终态工程的 mf-gateway 模块,结构非常轻:

mf
├── mf-gateway
│   ├── pom.xml
│   └── src/main
│       ├── java/com/mf/gateway
│       │   ├── GatewayApplication.java      # 引导类
│       │   ├── config/CorsConfig.java       # 跨域支持
│       │   └── filters/AuthFilter.java      # 统一鉴权过滤器(后篇展开)
│       └── resources/bootstrap.yml
├── mf-app-server
├── mf-admin
├── mf-dubbo(db / mongo / interface)
├── mf-model / mf-commons / mf-autoconfig
└── pom.xml

pom:三件套依赖

<dependencies>
    <!-- 网关核心:基于响应式 WebFlux/Netty 实现 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <!-- 注册中心:网关要从 Nacos 拉服务列表做负载均衡转发 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

    <!-- 配置中心:路由规则托管在 Nacos,启动时经 bootstrap.yml 引导加载 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>

    <dependency>
        <groupId>com.wb</groupId>
        <artifactId>mf-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

引导类

package com.mf.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GatewayApplication {

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

没有任何特殊注解,网关的能力全部由 starter 自动装配,这也是「网关是配置驱动的接入组件」的直观体现。

配置文件:路由规则在哪

这里有个素材辨证点要交代。讲义(教学进度)给的是本地 application.yml 配路由,端口 8888,规则长这样:

server:
  port: 8888
spring:
  application:
    name: mf-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.136.160:8848
    gateway:
      routes:
        # 手机端访问
        - id: mf-app-server
          uri: lb://mf-app-server
          predicates:
            - Path=/app/**
          filters:
            - StripPrefix=1
        # 管理后台
        - id: mf-admin
          uri: lb://mf-admin
          predicates:
            - Path=/admin/**
          filters:
            - StripPrefix=1

四行规则值得逐条拆:

  • uri: lb://mf-app-server:lb 即 load balance,转发目标不是写死的 http://localhost:18080,而是服务名,从 Nacos 拉实例列表做负载均衡。对比 SpringCloud 章最初的 cloud-demo,URL 写死的痛点在这里被顺带解决了。
  • predicates: Path=/app/**:断言条件,路径以 /app 开头才命中这条路由。
  • filters: StripPrefix=1:转发前剥掉第一级路径前缀,/app/user/info 到了 mf-app-server 就变成 /user/info,后端服务感知不到网关的存在。
  • 自定义的 gateway.excludedUrls(/user/login、/user/loginVerification、/system/users/verification、/system/users/login):放行不需要校验 token 的登录类接口,这是鉴权篇的伏笔。

而实读终态工程,bootstrap.yml 里并没有路由配置:

spring:
  profiles:
    active: dev
  application:
    name: mf-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.136.160:8848
      config:
        server-addr: 192.168.136.160:8848
        file-extension: yml

因为工程最终把路由规则托管到了 Nacos 配置中心(nacos-config + file-extension: yml,bootstrap.yml 只负责引导连接配置中心)。讲义同步给了 Nacos 侧的同名配置,内容与上面一致。两者不冲突:教学阶段先写在本地 yml 里跑通,工程终态挪去 Nacos 以获得动态刷新能力。这里以讲义的本地版规则为准讲原理,Nacos 托管留到 Nacos 篇延伸。

CorsConfig:跨域配一次

package com.mf.gateway.config;

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;
import org.springframework.web.util.pattern.PathPatternParser;

/**
 * 跨域支持
 */
@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource source =
                new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(source);
    }
}

注意 import 走的是 org.springframework.web.cors.reactive 包,CorsWebFilter 是响应式环境的过滤器,和第七章 MVC 拦截器用的是两套 API。这份配置在网关注册一次,/** 全路径生效,mf-app-server 和 mf-admin 里就不用再配 CORS 了。

一个容易踩的坑:Spring Cloud Gateway 也支持在 yml 里用 globalcors 配跨域,但官网明确提示,globalcors 简单 URL 处理器对部分预检请求可能不生效,用自定义 CorsWebFilter 是更稳的写法。两种写法选其一即可,别同时配。

Spring Cloud Gateway 的角色

一句话:Spring Cloud Gateway 是基于响应式技术栈(Reactor + Netty)构建的 API 网关。它不是普通的 Spring MVC 应用:

维度Spring MVC 服务(如 mf-app-server)Spring Cloud Gateway(mf-gateway)
底层容器Tomcat(Servlet,同步阻塞)Netty(异步非阻塞)
编程模型Spring MVC,@ControllerWebFlux 风格,GlobalFilter 返回 Mono
职责业务逻辑接入层:路由、鉴权、限流、日志

对 MVC 应用而言请求是一问一答同步处理的,而网关要扛全量入口流量、大部分时间只做转发,异步非阻塞模型让它用少量线程支撑高并发转发。体现在代码上,就是 AuthFilter 里 filter() 方法的返回类型是 Mono<Void>,而不是传统拦截器的 boolean preHandle()。好在项目的过滤器只做「校验 token、决定放行还是返回 401」,理解成本不高,这是后两篇的事。

统一鉴权预告:TokenInterceptor 怎么办

网关接手 JWT 校验后,各服务里已有的 TokenInterceptor 面临取舍:保留,则网关和服务各验一次(网关挡 401,服务自己也要防御);移除,则服务内彻底信任网关的转发结果。两种口径在生产中都有人用,取舍取决于「内网是否可信」。

工程实读的结论先放在这:mf-gateway 里的 AuthFilter(GlobalFilter + Ordered)已经就位,读取 Authorization 请求头、剥掉 Bearer 前缀、调 JwtUtils.verifyToken 校验,失败返回 401 与 JSON 错误体,excludedUrls 放行登录接口。鉴权思路与代码的完整展开,见本章后续的网关:统一鉴权思路网关:统一鉴权代码实现两篇,那里会正面回答 TokenInterceptor 的去留。

常见误解澄清

误解澄清
微服务就必须有网关网关解决的是接入层问题。单体应用或服务极少、调用方单一的架构,直接暴露服务也未必是问题;服务变多、调用方变杂后网关才划算
网关可以做业务逻辑网关只承担接入层职责(路由、鉴权、限流、日志),业务逻辑放网关会让它变成新的耦合中心,升级和扩容都被拖累
有了网关鉴权,服务内部安全不用管网关挡的是外网流量。内网的服务直连端口仍然裸奔,服务内部安全(服务间调用鉴权、内网访问控制)是另一层防护
Gateway 和 Nginx 二选一两者定位不同,通常 Nginx 在最前面做静态资源与反向代理,Gateway 在其后按服务名路由。项目的部署方案正是 Nginx 转发到网关地址
加了网关,前端要改很多代码前端只需把请求地址从各服务换成网关地址,配合路径前缀 /app、/admin 即可,接口定义本身不变

官方文档

总结

  • 项目架构的三个接入层痛点(多后端地址、鉴权重复、跨域分散)指向同一个解法:把接入层职责上收到网关。
  • 网关两大能力是路由与过滤器;路由解决「进哪个服务」,过滤器解决「进来之前做点什么」。
  • 统一鉴权的本质是位置上移:从「每个服务内部的拦截器」变为「所有服务的共同入口」,粒度从单服务变为全项目。
  • mf-gateway 模块极轻:引导类零注解、路由走 lb://服务名、终态路由规则托管 Nacos、CORS 用响应式 CorsWebFilter 配一次。
  • Gateway 基于 Netty 响应式栈,与 MVC 服务的 Tomcat 同步模型是两套 API,写过滤器时别混用。
  • 后面会完善项目架构:搭建网关工程动手创建 mf-gateway 模块并跑通路由。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/Tyro_java/article/details/170454384

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--