news 2026/10/24 1:39:38

Gateway - 入门教程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Gateway - 入门教程

一、基本概念

1.网关基本概念

客户端和服务端中间的一堵墙,可以起到的作用有:请求转发、负载均衡、权限控制等等

个人理解:网关解决从客户端(如8080端口)进入服务端时调用哪个微服务的问题(请求转发功能),Feign解决各微服务之间的相互调用问题

API网关介绍
API 网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:

  1. 客户端会多次请求不同的微服务,增加了客户端的复杂性。
  2. 存在跨域请求,在一定场景下处理相对复杂。
  3. 认证复杂,每个服务都需要独立认证。
  4. 难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将会很难实施。
  5. 某些微服务可能使用了防火墙 / 浏览器不友好的协议,直接访问会有一定的困难。

以上这些问题可以借助 API 网关解决。

API 网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过 API 网关这一层。
也就是说,API 的实现方面更多的考虑业务逻辑,而安全、性能、监控可以交由 API 网关来做,这样既提高业务灵活性又不缺安全性。

2.什么是Gateway网关

Spring Cloud Gateway核心概念
网关提供API全托管服务,丰富的API管理功能,辅助企业管理大规模的API,以降低管理成本和安全风险,包括协议适配、协议转发、安全策略、防刷、流量、监控日志等功能。

一般来说网关对外暴露的URL或者接口信息,我们统称为路由信息。

如果研发过网关中间件或者使用过Zuul的人,会知道网关的核心是Filter以及Filter Chain(Filter责任链)。Sprig Cloud Gateway也具有路由和Filter的概念。

Spring Cloud Gateway中几个重要的概念

  1. 路由:路由是网关最基础的部分,路由信息有一个ID、一个目的URL、一组断言和一组Filter组成。如果断言路由为真,则说明请求的URL和配置匹配
  2. 断言:匹配的规则。Java8中的断言函数。Spring Cloud Gateway中的断言函数输入类型是Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自于httprequest中的任何信息,比如请求头和参数等。
  3. 过滤器:一个标准的Spring webFilter。Spring cloud gateway中的filter分为两种类型的Filter,分别是Gateway Filter和Global Filter。过滤器Filter将会对请求和响应进行修改处理

3.网关实现流程

  1. 客户端发送请求先到网关
  2. 将网关服务和其它要访问的服务在注册中心进行注册
  3. 通过网关访问到注册的其它服务


    GateWay Handler Mapping:访问之后做映射(路径)匹配
    GateWay Web Handler:处理匹配
    Filter:过滤器,实现权限管理、跨域等

二、项目整合

1.引入依赖

在api_gateway中引入依赖

<!--nacos注册中心--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--Gateway网关依赖--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--gson--><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId></dependency><!--服务调用--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>

2.编写配置文件

properties写法

# nacos中注册的服务名spring.application.name=service-gateway# nacos服务地址spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848#使用服务发现路由,服务调用spring.cloud.gateway.discovery.locator.enabled=true#服务路由名小写#spring.cloud.gateway.discovery.locator.lower-case-service-id=true#设置路由id,下标从[0]开始#理论随便写,建议写服务的名字spring.cloud.gateway.routes[0].id=service-acl#设置路由的url#格式lb://+nacos中注册的服务名spring.cloud.gateway.routes[0].uri=lb://service-acl#设置路由断言(匹配规则),代理servicerId为auth-service的/auth/路径#格式Path=spring.cloud.gateway.routes[0].predicates=Path=/*/acl/** spring.cloud.gateway.routes[1].id=service-edu spring.cloud.gateway.routes[1].uri=lb://service-edu spring.cloud.gateway.routes[1].predicates=Path=/eduservice/**

yml写法

server: port:8222spring: application: cloud: gateway: discovery: locator: enabled:trueroutes: - id: service-acl uri: lb://service-acl predicates: -Path=/*/acl/**# 路径匹配- id: service-edu uri: lb://service-edu predicates: -Path=/eduservice/**# 路径匹配nacos: discovery: server-addr:127.0.0.1:8848

3.启动类

importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication @EnableDiscoveryClient public class ApiGatewayApplication{public static void main(String[]args){SpringApplication.run(ApiGatewayApplication.class, args);}}

4.测试

  1. 启动nacos,启动Gateway,启动需要调用的服务
  2. 打开naocs,地址:http://localhost:8848/nacos,默认账号密码都为nacos
  3. 点开服务列表检查Gateway等服务是否注册到nacos
  4. 先通过原服务端口号访问,然后通过网关端口号访问

三、工具类

1.跨域配置

importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.cors.CorsConfiguration;importorg.springframework.web.cors.reactive.CorsWebFilter;importorg.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;importorg.springframework.web.util.pattern.PathPatternParser;@ConfigurationpublicclassCorsConfig{@BeanpublicCorsWebFiltercorsFilter(){CorsConfigurationconfig=newCorsConfiguration();config.addAllowedMethod("*");config.addAllowedOrigin("*");config.addAllowedHeader("*");UrlBasedCorsConfigurationSourcesource=newUrlBasedCorsConfigurationSource(newPathPatternParser());source.registerCorsConfiguration("/**",config);returnnewCorsWebFilter(source);}}

2.全局Filter

统一处理会员登录与外部不允许访问的服务

importcom.google.gson.JsonObject;importorg.springframework.cloud.gateway.filter.GatewayFilterChain;importorg.springframework.cloud.gateway.filter.GlobalFilter;importorg.springframework.core.Ordered;importorg.springframework.core.io.buffer.DataBuffer;importorg.springframework.http.server.reactive.ServerHttpRequest;importorg.springframework.http.server.reactive.ServerHttpResponse;importorg.springframework.stereotype.Component;importorg.springframework.util.AntPathMatcher;importorg.springframework.web.server.ServerWebExchange;importreactor.core.publisher.Mono;importjava.nio.charset.StandardCharsets;importjava.util.List;@ComponentpublicclassAuthGlobalFilterimplementsGlobalFilter,Ordered{privateAntPathMatcherantPathMatcher=newAntPathMatcher();@OverridepublicMono<Void>filter(ServerWebExchangeexchange,GatewayFilterChainchain){ServerHttpRequestrequest=exchange.getRequest();Stringpath=request.getURI().getPath();//api接口,校验用户必须登录if(antPathMatcher.match("/api/**/auth/**",path)){List<String>tokenList=request.getHeaders().get("token");if(null==tokenList){ServerHttpResponseresponse=exchange.getResponse();returnout(response);}else{// Boolean isCheck = JwtUtils.checkToken(tokenList.get(0));// if(!isCheck) {ServerHttpResponseresponse=exchange.getResponse();returnout(response);// }}}//内部服务接口,不允许外部访问if(antPathMatcher.match("/**/inner/**",path)){ServerHttpResponseresponse=exchange.getResponse();returnout(response);}returnchain.filter(exchange);}@OverridepublicintgetOrder(){return0;}privateMono<Void>out(ServerHttpResponseresponse){JsonObjectmessage=newJsonObject();message.addProperty("success",false);message.addProperty("code",28004);message.addProperty("data","鉴权失败");byte[]bits=message.toString().getBytes(StandardCharsets.UTF_8);DataBufferbuffer=response.bufferFactory().wrap(bits);//response.setStatusCode(HttpStatus.UNAUTHORIZED);//指定编码,否则在浏览器中会中文乱码response.getHeaders().add("Content-Type","application/json;charset=UTF-8");returnresponse.writeWith(Mono.just(buffer));}}

3.异常处理

importorg.springframework.beans.factory.ObjectProvider;importorg.springframework.boot.autoconfigure.web.ResourceProperties;importorg.springframework.boot.autoconfigure.web.ServerProperties;importorg.springframework.boot.context.properties.EnableConfigurationProperties;importorg.springframework.boot.web.reactive.error.ErrorAttributes;importorg.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.core.Ordered;importorg.springframework.core.annotation.Order;importorg.springframework.http.codec.ServerCodecConfigurer;importorg.springframework.web.reactive.result.view.ViewResolver;importjava.util.Collections;importjava.util.List;@Configuration@EnableConfigurationProperties({ServerProperties.class,ResourceProperties.class})publicclassErrorHandlerConfig{privatefinalServerPropertiesserverProperties;privatefinalApplicationContextapplicationContext;privatefinalResourcePropertiesresourceProperties;privatefinalList<ViewResolver>viewResolvers;privatefinalServerCodecConfigurerserverCodecConfigurer;publicErrorHandlerConfig(ServerPropertiesserverProperties,ResourcePropertiesresourceProperties,ObjectProvider<List<ViewResolver>>viewResolversProvider,ServerCodecConfigurerserverCodecConfigurer,ApplicationContextapplicationContext){this.serverProperties=serverProperties;this.applicationContext=applicationContext;this.resourceProperties=resourceProperties;this.viewResolvers=viewResolversProvider.getIfAvailable(Collections::emptyList);this.serverCodecConfigurer=serverCodecConfigurer;}@Bean@Order(Ordered.HIGHEST_PRECEDENCE)publicErrorWebExceptionHandlererrorWebExceptionHandler(ErrorAttributeserrorAttributes){JsonExceptionHandlerexceptionHandler=newJsonExceptionHandler(errorAttributes,this.resourceProperties,this.serverProperties.getError(),this.applicationContext);exceptionHandler.setViewResolvers(this.viewResolvers);exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());returnexceptionHandler;}}

4.返回

packagecom.athly.gateway.handler;importorg.springframework.boot.autoconfigure.web.ErrorProperties;importorg.springframework.boot.autoconfigure.web.ResourceProperties;importorg.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;importorg.springframework.boot.web.reactive.error.ErrorAttributes;importorg.springframework.context.ApplicationContext;importorg.springframework.web.reactive.function.server.*;importjava.util.HashMap;importjava.util.Map;/** * 自定义异常处理 * <p>异常时用JSON代替HTML异常信息<p> */publicclassJsonExceptionHandlerextendsDefaultErrorWebExceptionHandler{publicJsonExceptionHandler(ErrorAttributeserrorAttributes,ResourcePropertiesresourceProperties,ErrorPropertieserrorProperties,ApplicationContextapplicationContext){super(errorAttributes,resourceProperties,errorProperties,applicationContext);}/** * 获取异常属性 */@OverrideprotectedMap<String,Object>getErrorAttributes(ServerRequestrequest,booleanincludeStackTrace){Map<String,Object>map=newHashMap<>();map.put("success",false);map.put("code",20005);map.put("message","网关失败");map.put("data",null);returnmap;}/** * 指定响应处理方法为JSON处理的方法 * @param errorAttributes */@OverrideprotectedRouterFunction<ServerResponse>getRoutingFunction(ErrorAttributeserrorAttributes){returnRouterFunctions.route(RequestPredicates.all(),this::renderErrorResponse);}/** * 根据code获取对应的HttpStatus * @param errorAttributes */@OverrideprotectedintgetHttpStatus(Map<String,Object>errorAttributes){return200;}}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/24 1:39:38

副队长JavaScript教程(23)-选择结构与代码块

点此查看 所有教程、项目、源码导航 文章目录1. 背景2. 基础 if else 条件判断3. 缩进规范4. if else 的作用范围陷阱5. 代码块 {}6. 选择结构搭配代码块7. 编码规范建议8. 小结1. 背景 上一篇我们讲解了 JS 语言最基本的结构 —— 顺序结构&#xff0c;JS 代码在一般情况下&a…

作者头像 李华
网站建设 2026/10/24 1:38:27

51单片机引脚详解:AT89S51与AT89C2051接线避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 1:38:13

FastCopy v4.02+ 文件加速复制:原理、调优与场景实测

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 1:38:08

知不足而奋进:个人成长操作系统实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 1:38:08

电网基波与谐波深度解析:从产生机理到治理方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 1:37:19

文件巴士3.0:端到端状态同步驱动的实时协作底座

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华