前言:在微服务架构中,有一个组件可以说是必不可少的,那就是微服务网关。微服务网关处理了路由转发,负载均衡,缓存,权限校验,监控,限流控制,日志等。Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,目标是取代 Netflix Zuul 网关。它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全、监控、埋点和限流等。

一、API 网关简介

服务网关可以这样简单理解:服务网关 = 路由转发 + 过滤器

1、路由转发:接收一切外界请求,转发到后端的微服务上去;

2、过滤器:在服务网关中可以完成一系列的横切功能,例如权限校验、限流以及监控等,这些都可以通过过滤器完成(其实路由转发也是通过过滤器实现的)。

1.1、为什么要用微服务网关

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

(1)客户端会维护成百上千个微服务URL地址,增加了客户端的复杂性。

(2)存在跨域请求,在一定场景下处理相对复杂。

(3)认证复杂,每个服务都需要独立认证。

(4)难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将会很难实施。

(5)某些微服务可能使用了防火墙 / 浏览器不友好的协议,直接访问会有一定的困难。

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

②而且 API网关 是任何微服务架构的重要组成部分。有了它我们可以在一个独立的模块上方便的处理一些非业务逻辑,可以让微服务本身专注在自身特定的功能上,使得每个微服务的开发更容易和更快速。

其实在我们的项目曾经还是单体应用的时候,虽然没有「 API网关 」的概念,但是一般在项目中都会用到filter过滤器之类的东西,filter的作用就是把项目中的一些非业务逻辑的功能抽离出来独立处理,避免与业务逻辑混在一起增加代码复杂度。比如 鉴权认证功能、Session处理、安全检查、日志处理等等。

现在我们采用微服务架构了,在一个项目中微服务节点很多,如果让每一个节点都去处理上面这些 “鉴权认证功能、Session处理、安全检查、日志处理等” 会多出很多冗余的代码,也会给增加业务代码的复杂度,因此我们就需要有一个「 API网关 」把这些公共的功能独立出来成为一个服务来统一的处理这些事情。

我个人理解API 网关就是一个增强版业务Nginx,它支持请求转发,负载均衡,统一埋点,限流降级、安全认证等等很多功能,Gateway网关收集所有请求根据路由规则转发请求,使用统一的过滤器处理请求参数等,是不是听起来有点像Nginx,但是比Nginx拥有更多功能。

1.2、API网关主要功能

API网关就像是微服务的大门守卫一样,是连通外部客户端与内部微服务之间的一个桥梁。

其主要功能有:

  • 路由转发

    之前说了「API网关」是内部微服务的对外唯一入口,所以外面全部的请求都会先发到这个「API网关」上,然后由「API网关」来根据不同的请求去路由到不同的微服务节点上。例如可以 根据路径 来转发、也可以 根据参数来转发。

    并且由于内部微服务实例也会随着业务调整不停的变更,增加或者删除节点,「API网关」可以与「服务注册」模块进行协同工作,保证将外部请求转发到最合适的微服务实例上面去。

  • 负载均衡

    既然「API网关」是内部微服务的单一入口,所以「API网关」在收到外部请求之后,还可以根据内部微服务每个实例的负荷情况进行动态的负载均衡调节。一旦内部的某个微服务实例负载很高,甚至是不能及时响应,则「API网关」就通过负载均衡策略减少或停止向这个实例转发请求。当所有的内部微服务实例都处理不过来的时候,「API网关」还可以采用限流或熔断的形式阻止外部请求,以保障整个系统的可用性。

  • 安全认证

    「API网关」就像是微服务的大门守卫,每一个请求进来之后,都必须先在「API网关」上进行身份验证,身份验证通过后才转发给后面的服务,转发的时候一般也会带上身份信息。

    同时「API网关」也需要对每一个请求进行安全性检查,例如参数的安全性、传输的安全性等等。

  • 日志记录

    既然所有的请求都需要走「API网关」,那么我们就可以在「API网关」上统一集中的记录下这些行为日志。这些日志既可以作为我们后续事件查询使用,也可以作为系统的性能监控使用。

  • 数据转换

    因为「API网关」对外是面向多种不同的客户端,不同的客户端所传输的数据类型可能是不一样的。因此「API网关」还需要具备数据转换的功能,将不同客户端传输进来的数据转换成同一种类型再转发给内部微服务上,这样,兼容了这些请求的多样性,保证了微服务的灵活性。


二、Spring Cloud Gateway简介

2.1、Spring Cloud Gateway概述

Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,目标是取代 Netflix Zuul 网关。该项目是基于Netty、Reactor以及WebFlux构建,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全、监控、埋点和限流等。

优点

  • 性能强劲,是Zuul的1.6倍

  • 功能强大,内置了很多实用的功能,例如转发、监控、限流等

  • 设计优雅,容易扩展

缺点

  • 依赖Netty与WebFlux,不是传统的Servlet编程模型,有一定的学习成本

  • 不能在Servlet容器下工作,也不能构建成WAR包,即不能将其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包执行

  • 不支持Spring Boot 1.x,需2.0及更高的版本

2.2、Spring Cloud Gateway核心概念

网关的核心概念就是路由配置和路由规则,而作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,是尽量要避免重启的,所以实现动态路由是非常有必要的。

GateWay的主要功能之一是转发请求,转发规则的定义主要包含三个部分:

Route(路由)

这是Spring Cloud Gateway的基本构建块,可简单理解成一条转发规则。包含:ID、目标URL、一组断言和一组过滤器

Predicate(断言)

这是一个 Java 8 的 Predicate,即java.util.function.Predicate这个接口,Gateway使用Predicate实现路由的匹配条件。断言,路径相匹配的进行路由。

Filter(过滤器)

一个标准的Spring WebFilter。Spring cloud gateway中的filter分为两种类型
的Filter,分别是Gateway Filter和Global Filter。过滤器Filter将会对请求和响应进行修改处理。

Gateway路由配置示例

spring:cloud:gateway:routes:- id: order-service  # 路由唯一标识,没有固定规则但要求唯一,建议使用服务名#匹配后提供服务的目标路由地址uri: lb://order-service  # 目标URL,lb代表从注册中心获取服务,lb是Load Balance的缩写predicates:# Predicate集合- Path=/findBy  # 匹配转发路径:断言,路径相匹配的进行路由filters:# Filter集合- StripPrefix=4  # 从第几级开始转发#配置示例
spring:application:name: gateway-servicecloud:gateway:routes:- id: data-service1  #请求 http://localhost:8100/data-service1/test会转发到data-producer服务uri: lb://data-producer  #在服务注册中心找服务名为 data-producer的服务predicates:- Path=/data-service1/*  #设置路由断言,代理servicerId为data-service1的/ data-service1 /路径filters:- StripPrefix=1- id: data-service2  # 请求 http://localhost:8100/data-service2/test转发到 http://localhost:8080/testuri: http://localhost:8080predicates:- Path=/data-service2/*filters:- StripPrefix=1  

2.3、Gateway转发规则

${GATEWAY_URL}/{微服务名}/{请求路径}

2.4、Spring Cloud Gateway工作流程


三、Spring Cloud Alibaba整合Spring Cloud Gateway

Geteway网关的核心功能是:过滤和路由转发

3.1、新建api-gateway子Moudle

3.2、添加Spring Cloud Gateway依赖

网关组件一般都配合服务发现组件使用,我这里使用Nacos作为服务发现组件,具体的依赖如下:

       <!--gateway依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId><version>${spring-cloud-alibaba.version}</version></dependency><!--nacos依赖 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>

注意:Spring cloud gateway是基于webflux的,如果非要web支持的话需要导入spring-boot-starter-webflux而不是spring-boot-start-web。Gateway组件已经依赖了 starter-webflux,所以这里 千万不要依赖spring-boot-start-web

3.3 、修改配置文件application.yml文件

server:port: 8001 #服务端口spring:application:name: gatewaycloud:nacos:discovery:server-addr: 127.0.0.1:8848 #nacos服务地址gateway:discovery:locator:enabled: true #使用服务发现路由(开启从注册中心动态创建路由的功能,利用微服务名进行路由)routes:- id: my-service #设置路由iduri: lb://order-service #设置路由的url  lb://nacos服务注册名称predicates:- Path=/api/order/* #路径匹配规则

3.4 、应用启动类添加注解 @EnableDiscoveryClient

3.5  启动GateWay网关服务,访问测试

通过服务名访问是可以的:http://localhost:8002/order-service/order

通过网关断言方式也可以:http://localhost:8002/api/order

3.6、解决跨域问题

package com.caiweiwei.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 {@Beanpublic CorsWebFilter corsFilter() {CorsConfiguration config = new CorsConfiguration();config.addAllowedMethod("*");config.addAllowedOrigin("*");config.addAllowedHeader("*");UrlBasedCorsConfigurationSource source = newUrlBasedCorsConfigurationSource(new PathPatternParser());source.registerCorsConfiguration("/**", config);return new CorsWebFilter(source);}
}

需要注意的是,这个与@CrossOrigin不能同时存在

3.7、全局Filter,统一处理会员登录与外部不允许访问的服务

package com.caiweiwei.filter;import com.google.gson.JsonObject;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.nio.charset.StandardCharsets;
import java.util.List;public class AuthGlobalFilter implements GlobalFilter, Ordered {private AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();String path = request.getURI().getPath();//谷粒学院api接口,校验用户必须登录if (antPathMatcher.match("/api/**/auth/**", path)) {List<String> tokenList = request.getHeaders().get("token");if (null == tokenList) {ServerHttpResponse response = exchange.getResponse();return out(response);} else {// Boolean isCheck = JwtUtils.checkToken(tokenList.get(0));// if(!isCheck) {ServerHttpResponse response = exchange.getResponse();return out(response);// }}}//内部服务接口,不允许外部访问if (antPathMatcher.match("/**/inner/**", path)) {ServerHttpResponse response = exchange.getResponse();return out(response);}return chain.filter(exchange);}@Overridepublic int getOrder() {return 0;}private Mono<Void> out(ServerHttpResponse response) {JsonObject message = new JsonObject();message.addProperty("success", false);message.addProperty("code", 28004);message.addProperty("data", "鉴权失败");byte[] bits = message.toString().getBytes(StandardCharsets.UTF_8);DataBuffer buffer = response.bufferFactory().wrap(bits);//response.setStatusCode(HttpStatus.UNAUTHORIZED);//指定编码,否则在浏览器中会中文乱码response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");return response.writeWith(Mono.just(buffer));}
}

3.8、自定义异常处理

服务网关调用服务时可能会有一些异常或服务不可用,它返回错误信息不友好,需要我们覆盖处理
ErrorHandlerConfig:

package com.caiweiwei.handler;import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;
import java.util.Collections;
import java.util.List;/*** 覆盖默认的异常处理**/
@Configuration
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
public class ErrorHandlerConfig {private final ServerProperties serverProperties;private final ApplicationContext applicationContext;private final ResourceProperties resourceProperties;private final List<ViewResolver> viewResolvers;private final ServerCodecConfigurer serverCodecConfigurer;public ErrorHandlerConfig(ServerProperties serverProperties,ResourceProperties resourceProperties,ObjectProvider<List<ViewResolver>>viewResolversProvider,ServerCodecConfigurerserverCodecConfigurer,ApplicationContext applicationContext) {this.serverProperties = serverProperties;this.applicationContext = applicationContext;this.resourceProperties = resourceProperties;this.viewResolvers =viewResolversProvider.getIfAvailable(Collections::emptyList);this.serverCodecConfigurer = serverCodecConfigurer;}@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {JsonExceptionHandler exceptionHandler = new JsonExceptionHandler(errorAttributes,this.resourceProperties,this.serverProperties.getError(),this.applicationContext);exceptionHandler.setViewResolvers(this.viewResolvers);exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());return exceptionHandler;}
}

JsonExceptionHandler:

package com.caiweiwei.handler;import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.web.reactive.function.server.*;import java.util.HashMap;
import java.util.Map;public class JsonExceptionHandler extends DefaultErrorWebExceptionHandler {public JsonExceptionHandler(ErrorAttributes errorAttributes,ResourceProperties resourceProperties,ErrorProperties errorProperties,ApplicationContext applicationContext) {super(errorAttributes, resourceProperties, errorProperties,applicationContext);}/*** 获取异常属性*/@Overrideprotected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {Map<String, Object> map = new HashMap<>();map.put("success", false);map.put("code", 20005);map.put("message", "网关失败");map.put("data", null);return map;}/*** 指定响应处理方法为JSON处理的方法** @param errorAttributes*/@Overrideprotected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);}/*** 根据code获取对应的HttpStatus** @param errorAttributes*/@Overrideprotected int getHttpStatus(Map<String, Object> errorAttributes) {return 200;}}

参考链接:

若依微服务SpringCloud—使用Spring Cloud Gateway网关-pudn.com

微服务中网关的作用及搭建

从零搭建Spring-Cloud-Alibaba(五)- Gateway网关

Gateway网关服务搭建和配置

Spring Cloud Alibaba 快速入门(七):Gateway微服务网关相关推荐

  1. 【Spring Cloud Alibaba】(一)微服务介绍 及 Nacos注册中心实战

    文章目录 前言 I.微服务与Spring Cloud II.Nacos 注册中心 III.Spring Cloud Alibaba Nacos 实战 1.新建父工程 2.新建demo-a 服务 3.新 ...

  2. 【Spring Cloud Alibaba】(二)微服务调用组件Feign原理+实战

    系列目录 [Spring Cloud Alibaba](一)微服务介绍 及 Nacos注册中心实战 本文目录 系列目录 前言 什么是RPC? Feign和OpenFeign都是什么? HTTP调用 v ...

  3. Spring Cloud之(十八)微服务网关GateWay

    十八.微服务网关GateWay Zuul 1.x 是一个基于阻塞 IO 的 API Gateway 以及 Servlet:直到 2018 年 5 月,Zuul 2.x(基于Netty,也是非阻塞的,支 ...

  4. Spring Cloud Alibaba 极速运维:微服务与 DevOps

    前面我们反复强调微服务架构是将大的应用打散为多个小服务,这就必然导致打散后形成更多需要独立部署的应用程序,在大型互联网应用中,这些程序可能会达到上千个之多.频繁的测试.打包.发布,无疑会给运维部门带来 ...

  5. Spring Cloud Alibaba基础入门,一周学会基操!

    Spring Cloud Alibaba基础入门,一周学会基操! 服务拆分: 1.不同微服务,不要重复开发相同业务 2.微服务数据独立,不要访问其它微服务的数据库 3.微服务可以将自己的业务暴露为接口 ...

  6. 开放下载 | 和 4000+Java 开发者共读《Spring Cloud Alibaba 从入门到实战》

    Spring Cloud Alibaba 脱胎于阿里中间件团队内部,经受了阿里多年海量业务场景的考验,是目前最成熟.功能最丰富也最有前景的 Spring Cloud 实现.相信在未来 Spring C ...

  7. 【技术手册】Java 开发者必备手册《Spring Cloud Alibaba 从入门到实战》

    Java 开发者必备手册<Spring Cloud Alibaba 从入门到实战> 简介 大咖寄语 目录 精彩导读 基础知识篇 分布式配置 服务注册与发现 分布式服务调用 服务熔断和限流 ...

  8. Spring Cloud Alibaba - Sentinel入门案例(四)(热点规则 )

    Spring Cloud Alibaba - Sentinel入门案例(四)(热点规则 ) 回溯 什么是Sentinel? 正文 环境准备 简单使用 复杂使用 注意事项 回溯 什么是Sentinel? ...

  9. Spring Cloud 终于按捺不住推出了自己的服务网关 Gateway

    转载自  Spring Cloud 终于按捺不住推出了自己的服务网关 Gateway Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gateway ,相比之前我们使 ...

最新文章

  1. 命令模式的优点?_一篇目录复习完设计模式
  2. ORACLE约束总结
  3. MySQL数据库安全配置
  4. 计算机对环境的影响的案例,计算机环境下的呈现方式对学生样例学习的影响
  5. Linux 添加新硬盘
  6. robo3t 连接服务器数据库_mongodb开启公共IP访问及Robo 3T连接远程mongodb服务器
  7. 关于CVE-2020-14882,你需要知道的都在这里
  8. android工程怎么连手机,安卓手机怎么进工程模式 安卓手机进工程模式教程【详解】...
  9. Linux命令j解释之cp
  10. C++和C语言的前世今生,两者有什么血缘关系?
  11. L3-015 球队“食物链” (30 分)
  12. 2022春山东大学人工智能导论期末题库附答案
  13. stp网页浏览器2.0版本
  14. IOS微信分享调起微信后立刻返回到app中无法分享的问题
  15. dw css定位,Dreamweaver中层的定位
  16. 用python对S曲线加减速建模
  17. 华为、魅族手机不显示Log的原因
  18. Android第一行代码——第八章多媒体
  19. qt designer加载自定义组件
  20. Contest 2050 and Codeforces Round #718 B. Morning Jogging

热门文章

  1. 关于EXCEL中不能直接输入+、-、=(加号、减号、等号)的简单解决办法
  2. 如何用纯 CSS 创作一只单眼怪兽
  3. excel两列数据对照重复值
  4. 为app添加动态gif背景
  5. 5-15 QQ帐户的申请与登陆
  6. 趣好玩开发日志 03-day
  7. php 不失真缩略图,解决dedecms缩略图无水印以及不失真
  8. 微信小程序入门代码简介
  9. 串口通信——发送和接收数据(8位和16位数据之间的转换)
  10. 一段比较浅显易懂的java识别pdf 文字及图片