跨境出口电商网站,代做论文网站,网站后台 刷新,枣庄网站建设哪家强gateway网关 系列博客背景一、什么是Spring Cloud Gateway二、为什么要使用Spring Cloud Gateway三、 Spring Cloud Gateway 三大核心概念4.1 Route#xff08;路由#xff09;4.2 Predicate#xff08;断言#xff09;4.3 Filter#xff08;过滤#xff09; 五、Spring … gateway网关 系列博客背景一、什么是Spring Cloud Gateway二、为什么要使用Spring Cloud Gateway三、 Spring Cloud Gateway 三大核心概念4.1 Route路由4.2 Predicate断言4.3 Filter过滤 五、Spring Cloud Gateway是如何工作的四、 如何使用Spring Cloud Gateway进行服务路由4.1搭建服务Apom文件yml配置文件启动类controller类 4.2 搭建服务Bpom文件yml配置文件启动类controller类 4.3搭建Spring Cloud Gateway服务pom文件yml配置文件启动类 4.4启动项目4.5动态路由 五、gateway过滤器5.1自定义全局过滤器 六、使用gateway做token校验6.1实现的大致流程示意图6.2实现代码gateway服务pom.xml文件TokenCheckFilter类 login-service服务pom.xml文件LoginController类 访问测试 总结升华 系列博客
【Spring Cloud一】微服务基本知识 【Spring Cloud 三】Eureka服务注册与服务发现 【Spring Cloud 四】Ribbon负载均衡 【Spring Cloud 五】OpenFeign服务调用 【Spring Cloud 六】Hystrix熔断 【Spring Cloud 七】SleuthZipkin 链路追踪
背景
在项目中是使用了Gateway做统一的请求的入口以及统一的跨域处理以及统一的token校验。但是这些工作都是之前的同事来做的正好在新项目中也需要使用其进行统一的token校验。本着对Gateway更精进一步所以博主就对Gateway进行了较为全面的学习了解包括动态路由、自定义过滤器、token校验和续活。
一、什么是Spring Cloud Gateway
Spring Cloud Gateway提供一种简单有效的方法来路由到对应的API上并可以提供一些额外的功能安全性、监控、度量、负载等等。
我们可以这样理解Spring Cloud Gateway将该项目中所有服务对外提供的API聚集起来并向外提供唯一的入口同时提供了一些额外的功能安全性、监控、度量、负载等等。
没使用Spring Cloud Gateway 之前的示意图 使用Spring Cloud Gateway之后的示意图
二、为什么要使用Spring Cloud Gateway
统一入口并解除客户端与服务端的耦合在微服务架构中每个微服务都有自己入口在没有它的时候客户端需要大量微服务的地址会照成复杂的客户端代码。并且会暴露服务端的细节如果服务端发生变化可能会影响到客户端代码导致维护困难。动态路由和负载均衡Spring Cloud GateWay 提供动态路由来应对当网关来能够在多个服务实例之间进行负载均衡提供整个系统的高可用。请求过滤在微服务架构中可能需要对请求进行鉴权、身份验证一个中心化的地方来处理这些请求过滤和处理逻辑可以减少重复的代码和逻辑。反应式和高性能由于微服务架构的复杂性需要一个能够处理高并发请求的解决方案以确保系统在高负载情况下保持稳定。Spring Cloud Gateway是基于webFlux框架实现的而webFlux框架底层使用了高性能Reactor模式通信框架的Netty。
三、 Spring Cloud Gateway 三大核心概念
4.1 Route路由
路由是由一个ID、一个目的URI、一组断言、一组Filter组成。 如果路由断言为真那么说明这个路由被匹配上了。
4.2 Predicate断言
是一个java8函数断言。输入类型是一个Spring Framewordk ServerWebExchange。可以让你匹配HTTP上的任何请求。比如请求头和参数。
4.3 Filter过滤
是Spring WebFilter的实例Spring Cloud Gateway中的Filter分为两种分贝是Gateway Filter和Global Filter一个是针对某一个路由的filter例如对某一个接口做限流一个是针对全局的filter例如token校验ip黑名单。过滤器Filter将会对请求和响应进行修改处理。
五、Spring Cloud Gateway是如何工作的
Spring 官网 客户端向Spring Cloud Gateway发出请求。如果网关处理器映射器确定请求与路由匹配则会将其发送到网关web处理器。它通过特定的过滤器链来运行请求。过滤器被虚线分割的原因是过滤器可以在发送代理请求之前和之后运行对应的逻辑。
四、 如何使用Spring Cloud Gateway进行服务路由
示例项目示意图 备注Eureka的搭建可以参考这篇博客【Spring Cloud 三】Eureka服务注册与服务发现
这里之所以使用Eureka是为了之后做动态路由和负载均衡。
4.1搭建服务A
pom文件
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.12.RELEASE/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.wangwei/groupIdartifactIdlogin-service/artifactIdversion0.0.1-SNAPSHOT/versionnamelogin-service/namedescriptionlogin-service/descriptionpropertiesjava.version8/java.versionspring-cloud.versionHoxton.SR12/spring-cloud.version/propertiesdependencies!-- dependency--
!-- groupIdorg.springframework.boot/groupId--
!-- artifactIdspring-boot-starter-data-redis/artifactId--
!-- /dependency--dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency/dependenciesdependencyManagementdependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversion${spring-cloud.version}/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagementbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project
yml配置文件
server:port: 8081spring:application:name: login-serviceeureka:client:service-url:defaultZone: http://localhost:8761/eurekaregister-with-eureka: true #设置为fasle 不往eureka-server注册默认为truefetch-registry: true #应用是否拉取服务列表到本地registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题时间越短脏读越少 性能相应的消耗回答instance: #实例的配置instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}hostname: localhost #主机名称或者服务ipprefer-ip-address: true #以ip的形式显示具体的服务信息lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔
启动类
SpringBootApplication
EnableEurekaClient
public class LoginServiceApplication {public static void main(String[] args) {SpringApplication.run(LoginServiceApplication.class, args);}}controller类
RestController
public class LoginController {GetMapping(doLogin)public String doLogin(){return 登陆成功;}
}4.2 搭建服务B
pom文件
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.12.RELEASE/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.wangwei/groupIdartifactIdteacher-service/artifactIdversion0.0.1-SNAPSHOT/versionnameteacher-service/namedescriptionteacher-service/descriptionpropertiesjava.version8/java.versionspring-cloud.versionHoxton.SR12/spring-cloud.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency/dependenciesdependencyManagementdependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversion${spring-cloud.version}/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagementbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project
yml配置文件
server:port: 8082spring:application:name: teacher-serviceeureka:client:service-url:defaultZone: http://localhost:8761/eurekaregister-with-eureka: true #设置为fasle 不往eureka-server注册默认为truefetch-registry: true #应用是否拉取服务列表到本地registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题时间越短脏读越少 性能相应的消耗回答instance: #实例的配置instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}hostname: localhost #主机名称或者服务ipprefer-ip-address: true #以ip的形式显示具体的服务信息lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔
启动类
SpringBootApplication
EnableEurekaClient
public class TeacherServiceApplication {public static void main(String[] args) {SpringApplication.run(TeacherServiceApplication.class, args);}}controller类
RestController
public class TeacherController {GetMapping(teach)public String teach(){return 教书学习;}
}
4.3搭建Spring Cloud Gateway服务
pom文件
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.12.RELEASE/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.wangwei/groupIdartifactIdgateway-server/artifactIdversion0.0.1-SNAPSHOT/versionnamegateway-server/namedescriptiongateway-server/descriptionpropertiesjava.version8/java.versionspring-cloud.versionHoxton.SR12/spring-cloud.version/propertiesdependencies
!-- dependency--
!-- groupIdorg.springframework.boot/groupId--
!-- artifactIdspring-boot-starter-data-redis/artifactId--
!-- /dependency--
!-- dependency--
!-- groupIdorg.springframework.boot/groupId--
!-- artifactIdspring-boot-starter-data-redis-reactive/artifactId--
!-- /dependency--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-gateway/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency/dependenciesdependencyManagementdependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversion${spring-cloud.version}/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagementbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project
yml配置文件
server:port: 81 #?????80spring:application:name: gateway-servercloud:gateway:enabled: trueroutes:- id: login-service-route # 路由的id 保持唯一uri: http://localhost:8081 #uri统一资源标识符 url 统一资源定位符#uri: lb://login-service #??lb负载均衡predicates: # 断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上- Path/doLogin # 匹配规则 只要你Path配置上了/doLogin 就往uri转发并将路径带上- id: teacher-service-routeurl: http://localhost:8082predicates:- Path/teacheureka:client:service-url:defaultZone: http://localhost:8761/eurekaregister-with-eureka: true #设置为fasle 不往eureka-server注册默认为truefetch-registry: true #应用是否拉取服务列表到本地registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题时间越短脏读越少 性能相应的消耗回答instance: #实例的配置instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}hostname: localhost #主机名称或者服务ipprefer-ip-address: true #以ip的形式显示具体的服务信息lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔
启动类
SpringBootApplication
EnableEurekaClient
public class GatewayServerApplication {public static void main(String[] args) {SpringApplication.run(GatewayServerApplication.class, args);}}4.4启动项目
依次启动Eureka服务gateway服务和两个服务A和服务B。
进行调用服务A和服务B 如下图所示通过gateway的ip端口路径调用到对应的服务A中和服务B中。 4.5动态路由
在微服务中通常一个服务的实例有多个那我们网关如何做负载均衡呢 gateway帮我们做了很多东西只要我们集成注册中心nacos、Eureka、zoomkeeper并添加对应的配置gateway就可以自动帮我们进行负载均衡。 方式一 添加对应的配置信息来开启动态路由 gateway:enabled: trueroutes:- id: login-service-route # 路由的id 保持唯一uri: http://localhost:8081 #uri统一资源标识符 url 统一资源定位符#uri: lb://login-service #??lb负载均衡predicates: # 断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上- Path/doLogin # 匹配规则 只要你Path配置上了/doLogin 就往uri转发并将路径带上- id: teacher-service-routeuri: http://localhost:8082predicates:- Path/teachdiscovery:locator:enabled: true #开启动态路由 开启通过应用名称找到服务的功能lower-case-service-id: true # 开启服务名称小写请求服务时需要带上对应的服务名称 方式二 添加对应的配置uri: lb://login-service #lb负载均衡 gateway:enabled: trueroutes:- id: login-service-route # 路由的id 保持唯一#uri: http://localhost:8081 #uri统一资源标识符 url 统一资源定位符uri: lb://login-service #lb负载均衡predicates: # 断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上- Path/doLogin # 匹配规则 只要你Path配置上了/doLogin 就往uri转发并将路径带上- id: teacher-service-routeuri: http://localhost:8082predicates:- Path/teach推荐使用方式一进行统一的配置负载均衡。
五、gateway过滤器
过滤器按照作用范围可以分为两种Gateway Filter和Global Filter。
Gateway Filter网关过滤器需要通过 spring.cloud.routes.filters 配置在具体路由下只作用在当前路由上或通过 spring.cloud.default-filters 配置在全局作用在所有路由上。
Global Filter全局过滤器不需要配置路由系统初始化作用在所有路由上。
全局过滤器一般用于统计请求次数、限流、token校验、ip黑名单拦截等。
5.1自定义全局过滤器
Component
public class MyGlobalFilter implements GlobalFilter, Ordered {/*** 过滤的方法* 职责链模式* 网关里面有使用 mybatis的二级缓存有变种职责链模式* param exchange* param chain* return*/Overridepublic MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) {//针对请求的过滤 拿到请求 header url 参数。。。ServerHttpRequest request exchange.getRequest();//HttpServletRequest 这个是web里面的//ServerHttpRequest webFlux里面 响应式里面的String pathrequest.getURI().getPath();System.out.println(path);HttpHeaders headersrequest.getHeaders();System.out.println(headers);String name request.getMethod().name();String hostString request.getHeaders().getHost().getHostString();System.out.println(hostString);//响应相关数据ServerHttpResponse response exchange.getResponse();//微服务 肯定是前后端分离的 一般前后端通过数据传输是json格式//{code:200,msg:ok}//设置编码 响应头response.getHeaders().set(content-type,application/json;charsetutf-8);//组装业务返回值HashMapString ,Object mapnew HashMap(4);map.put(code, HttpStatus.UNAUTHORIZED.value());map.put(msg,你未授权);ObjectMapper objectMappernew ObjectMapper();//把一个map转换为字节byte[] bytes new byte[0];try {bytes objectMapper.writeValueAsBytes(map);} catch (JsonProcessingException e) {throw new RuntimeException(e);}//通过buffer工厂将字节数组包装成一个数据报DataBuffer wrap response.bufferFactory().wrap(bytes);return response.writeWith(Mono.just(wrap));//放行到下一个过滤器//return chain.filter(exchange);}/*** 制定顺序的方法* 越小越先执行* return*/Overridepublic int getOrder() {return 0;}
}请求接口进行访问可以发现当前请求已经被拦截下来。 六、使用gateway做token校验
6.1实现的大致流程示意图
6.2实现代码
gateway服务
pom.xml文件
在pom文件中新增redis的依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency注意博主是在本机安装的redis所以不需要在服务中添加对应的redis配置。
TokenCheckFilter类
新建TokenCheckFilter类并实现全局过滤器和Ordered
package com.wangwei.gatewayserver.filter;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
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.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.Arrays;
import java.util.HashMap;
import java.util.List;Component
public class TokenCheckFilter implements GlobalFilter, Ordered {/*** 指定好放行的路径白名单*/public static final ListString ALLOW_URL Arrays.asList(/doLogin,/myUrl);Autowiredprivate StringRedisTemplate redisTemplate;/*** 和前端约定好 一般放在请求头类里面一般key为 Authorization value bearer token* 1.拿到请求url* 2.判断放行* 3.拿到请求头* 4.拿到token* 5.校验* 6.放行/拦截* param exchange* param chain* return*/Overridepublic MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request exchange.getRequest();String path request.getURI().getPath();if (ALLOW_URL.contains(path)) {return chain.filter(exchange);}//检查ListString authorization request.getHeaders().get(Authorization);if (!CollectionUtils.isEmpty(authorization)){String token authorization.get(0);if(StringUtils.hasText(token)){//约定好的有前缀的bearer tokenString realToken token.replaceFirst(bearer , );if (StringUtils.hasText(realToken)redisTemplate.hasKey(realToken)){return chain.filter(exchange);}}}//拦截ServerHttpResponse response exchange.getResponse();response.getHeaders().set(content-type,application/json;charsetutf-8);//组装业务返回值HashMapString ,Object mapnew HashMap(4);map.put(code, HttpStatus.UNAUTHORIZED);map.put(msg,未授权);ObjectMapper objectMappernew ObjectMapper();//把一个map转换为字节byte[] bytes new byte[0];try {bytes objectMapper.writeValueAsBytes(map);} catch (JsonProcessingException e) {throw new RuntimeException(e);}//通过buffer工厂将字节数组包装成一个数据报DataBuffer wrap response.bufferFactory().wrap(bytes);return response.writeWith(Mono.just(wrap));}Overridepublic int getOrder() {return 0;}
}
login-service服务
pom.xml文件
在pom文件中新增redis的依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency注意博主是在本机安装的redis所以不需要在服务中添加对应的redis配置。
LoginController类
在LoginController类中新增doLogin方法
Autowiredpublic StringRedisTemplate redisTemplate;PostMapping(doLogin)public String doLogin(RequestBody User user){//tokenString token UUID.randomUUID().toString();//存起来redisTemplate.opsForValue().set(token,user.toString(), Duration.ofSeconds(7200));return token;}访问测试
http://localhost:81/doLogin
由于在gateway将该/doLogin放入了白名单所以该请求不会进行token校验发送请求成功之后会返回token 访问http://localhost:81/teach并在请求头中添加token最后可以看到请求访问成功。 如果我们不在请求头中添加对应的token或者token为错误token那么gateway会将请求进行拦截。 总结
gateway在微服务中起到了很重要的作用作为项目请求的统一入口。gateway也体现了我们软件设计的复用思想可以统一进行跨域处理进行token校验。gateway比较重要的一块是关于他的过滤器通过实现过滤器的接口可以自定义过滤器提供了很强大的可扩展支持
升华
通过学习gateway不光是学习gateway更重要的是学习软件设计思想。