当前位置:   article > 正文

springboot整合spring cloud gateway搭建网关服务_springboot gateway

springboot gateway

spring cloud netflix zuul、spring cloud gateway是最常见的微服务网关,通过网关,我们可以在请求到达后端指定服务之前/后端服务处理完业务响应数据之后对响应进行对请求/响应进行处理。

比如常见的参数校验、接口鉴权等等,在后端服务的拦截器和过滤器能做的事在网关都可以做。

网关的主要功能是请求的转发以及负载均衡,和nginx的功能类似,只是底层实现不同。

这篇文章就详细介绍一下spring cloud gateway的使用,包括了各种断言及过滤器的相关配置,帮助初学者更好的了解gateway的使用。

目录

Springboot整合gateway

第一步:创建网关服务

第二步:在pom.yml添加依赖

第三步:修改application.yml文件

gateway相关配置说明

predicates断言工厂

After

Before

Between

Cookile

Header

Host

Method

Path

Query

RemoteAddr

Weight

filters过滤器工厂

AddRequestHeader

AddRequestParameter

AddResponseHeader

RemoveResponseHeader

第四步:使用网关服务

扩展:配置动态路由


Springboot整合gateway

在讲解之前,先搭建起一个网关服务,通过Springboot整合spring cloud gateway搭建一个微服务网关的案例。

第一步:创建网关服务

创建一个springboot项目,并命名为springboot-gateway

第二步:在pom.yml添加依赖

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.2.6.RELEASE</version>
  9. <relativePath />
  10. </parent>
  11. <groupId>cn.edu.sgu.www</groupId>
  12. <artifactId>springboot-gateway</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <properties>
  15. <java.version>1.8</java.version>
  16. <lombok.version>1.18.22</lombok.version>
  17. <spring-cloud.version>2.2.6.RELEASE</spring-cloud.version>
  18. </properties>
  19. <dependencies>
  20. <!--lombok-->
  21. <dependency>
  22. <groupId>org.projectlombok</groupId>
  23. <artifactId>lombok</artifactId>
  24. <version>${lombok.version}</version>
  25. </dependency>
  26. <!--gateway网关-->
  27. <dependency>
  28. <groupId>org.springframework.cloud</groupId>
  29. <artifactId>spring-cloud-starter-gateway</artifactId>
  30. <version>${spring-cloud.version}</version>
  31. </dependency>
  32. <!--网关负载均衡-->
  33. <dependency>
  34. <groupId>org.springframework.cloud</groupId>
  35. <artifactId>spring-cloud-starter-loadbalancer</artifactId>
  36. <version>${spring-cloud.version}</version>
  37. </dependency>
  38. </dependencies>
  39. <build>
  40. <plugins>
  41. <plugin>
  42. <groupId>org.springframework.boot</groupId>
  43. <artifactId>spring-boot-maven-plugin</artifactId>
  44. </plugin>
  45. </plugins>
  46. </build>
  47. </project>

第三步:修改application.yml文件

参数说明:- StripPrefix=2表示删除前面两级路径(如:/api/mhxysy),比如http://localhost:9091/api/mhxysy/user/login对应服务器端的地址为http://localhost:8080/user/login

  1. server:
  2. port: 9091
  3. spring:
  4. application:
  5. name: gateway
  6. cloud:
  7. gateway:
  8. enabled: true
  9. routes:
  10. - id: mhxysy
  11. uri: http://localhost:8080
  12. predicates:
  13. - Path=/api/mhxysy/**
  14. filters:
  15. - StripPrefix=2
  16. logging:
  17. file:
  18. name: D:/log/gateway.log
  19. level:
  20. springfox: error
  21. cn.edu.sgu.www.gateway: debug

这基本上是最简单的配置,配置了服务名为gateway,然后配置了一个路由,是在idea里启动的一个后端项目。

gateway相关配置说明

在学习gateway的配置之前,需要了解gateway中的路由这个概念,路由一般又路由ID、目标URL、一组断言和一组过滤器组成。

比如上面我们配置的一个路由,routes中可以配置多个路由。

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: mhxysy
  6. uri: http://localhost:8080
  7. predicates:
  8. - Path=/api/mhxysy/**
  9. filters:
  10. - StripPrefix=2

其中uri称为目标地址,也就是我们需要转发请求到uri后面配置的路径,当我们访问ip地址:端口号/api/mhxysy/**时,请求将会被转发到http://localhost:8080/**

predicates断言工厂

断言,其实就是一组条件,熟悉java.util.function.Predicate的应该对这个概念有一定的了解,其实就是设置了一些条件,通过matches()方法的返回值来判断是否满足设置的条件。

gateway里的断言predicates指的是路由断言工厂,在predicates里可以配置各种条件,只有全部条件都满足,请求才能被转发。

接下来介绍gateway里都有哪些断言工厂。

After

配置在指定时间之后才能转发请求,后面指定的值的格式和LocalDateTime很像,只是多了毫秒和时区信息。

2023-09-08T08:31:59.789+08:00[Asia/Shanghai]

前半部分:2023-09-08T08:31:59这是一个LocalDateTime的字符串格式,789表示的是789毫秒,1秒=1000毫秒。

后半部分:+08:00[Asia/Shanghai]表示东八区的亚洲/上海。

- 东时区用+表示,如+6:00表示东6区

- 西时区用-表示,如-5:00表示西5区

  1. spring:
  2. application:
  3. name: gateway
  4. cloud:
  5. gateway:
  6. enabled: true
  7. routes:
  8. - id: gateway-mhxysy
  9. uri: http://localhost:8080
  10. predicates:
  11. - Path=/api/mhxysy/**
  12. - After=2023-09-08T08:31:59.789+08:00[Asia/Shanghai]
  13. filters:
  14. - StripPrefix=2
  15. server:
  16. port: 9091

比如当前是2023年9月8日早上8:30分,此时访问服务的/chongwu/selectAll接口返回404

过了那个时间再访问,成功返回查询结果

Before

限制在指定时间之前才能访问,比如上面的配置改成Before就访问不到了,因为已经过了那个时间点

  1. spring:
  2. application:
  3. name: gateway
  4. cloud:
  5. gateway:
  6. enabled: true
  7. routes:
  8. - id: gateway-mhxysy
  9. uri: http://localhost:8080
  10. predicates:
  11. - Path=/api/mhxysy/**
  12. - Before=2023-09-08T08:31:59.789+08:00[Asia/Shanghai]
  13. filters:
  14. - StripPrefix=2
  15. server:
  16. port: 9091

Between

配置只能在指定时间段访问,上面的时间配置后移一天,依旧能得到返回结果。

  1. spring:
  2. application:
  3. name: gateway
  4. cloud:
  5. gateway:
  6. enabled: true
  7. routes:
  8. - id: gateway-mhxysy
  9. uri: http://localhost:8080
  10. predicates:
  11. - Path=/api/mhxysy/**
  12. - Between=2023-09-08T08:31:59.789+08:00[Asia/Shanghai],2023-09-09T08:31:59.789+08:00[Asia/Shanghai]
  13. filters:
  14. - StripPrefix=2
  15. server:
  16. port: 9091

Cookile

限制只有请求中携带了指定的cookie才能访问,比如配置需要携带MHXYSY_JSESSIONID。

因为在mhxysy这个服务的配置文件中设置了session的名称为MHXYSY_JSESSIONID,默认是JSESSIONID,登录之后,浏览器每次请求都会携带MHXYSY_JSESSIONID的cookie。

  1. package cn.edu.sgu.www.mhxysy.config;
  2. import org.springframework.boot.web.servlet.ServletContextInitializer;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  6. import javax.servlet.ServletContext;
  7. /**
  8. * springmvc配置类
  9. * @author heyunlin
  10. * @version 1.0
  11. */
  12. @Configuration
  13. public class SpringMvcConfig implements WebMvcConfigurer {
  14. /**
  15. * 设置SESSION_ID
  16. * @return ServletContextInitializer
  17. */
  18. @Bean
  19. public ServletContextInitializer servletContextInitializer() {
  20. return new ServletContextInitializer() {
  21. @Override
  22. public void onStartup(ServletContext servletContext) {
  23. servletContext.getSessionCookieConfig().setName("MHXYSY_JSESSIONID");
  24. }
  25. };
  26. }
  27. }

session值通过浏览器中查看得到

然后配置cookie

  1. spring:
  2. application:
  3. name: gateway
  4. cloud:
  5. gateway:
  6. enabled: true
  7. routes:
  8. - id: gateway-mhxysy
  9. uri: http://localhost:8080
  10. predicates:
  11. - Path=/api/mhxysy/**
  12. - Cookie=MHXYSY_JSESSIONID,831B175D25150131A3F3017116369CAE
  13. filters:
  14. - StripPrefix=2
  15. server:
  16. port: 9091

把上面的配置中的cookie名称改为默认的JSESSIONID之后,请求返回404。

只有携带指定请求头的请求会被转发,在这里配置需要携带token的请求头

  1. spring:
  2. application:
  3. name: gateway
  4. cloud:
  5. gateway:
  6. enabled: true
  7. routes:
  8. - id: gateway-mhxysy
  9. uri: http://localhost:8080
  10. predicates:
  11. - Path=/api/mhxysy/**
  12. - Header=token,mhxy1218
  13. filters:
  14. - StripPrefix=2
  15. server:
  16. port: 9091

为了方便测试,使用postman来测试效果

不携带请求头时

携带请求头,正常响应,这里返回401,是因为没有通过接口鉴权,浏览器登录了,所以没有返回401

Host

这个是指定域名访问,如果不是指定的域名,将访问失败,这个不好测试,就跳过了。

  1. spring:
  2. application:
  3. name: gateway
  4. cloud:
  5. gateway:
  6. enabled: true
  7. routes:
  8. - id: gateway-mhxysy
  9. uri: http://localhost:8080
  10. predicates:
  11. - Path=/api/mhxysy/**
  12. - Host=taobao.com
  13. filters:
  14. - StripPrefix=2
  15. server:
  16. port: 9091

Method

配置请求方式,只有规定的请求方式才会被转发。

  1. spring:
  2. application:
  3. name: gateway
  4. cloud:
  5. gateway:
  6. enabled: true
  7. routes:
  8. - id: gateway-mhxysy
  9. uri: http://localhost:8080
  10. predicates:
  11. - Path=/api/mhxysy/**
  12. - Method=POST
  13. filters:
  14. - StripPrefix=2
  15. server:
  16. port: 9091

这时候通过get请求访问会404(post请求返回401是因为没有登录,鉴权失败了)。

post

get

Path

这是基础的断言,只有将指定的路径转发到目标URL,本篇文章的http://localhost:9091/api/mhxysy/chongwu/selectAll会被转发到http://localhost:8080/chongwu/selectAll

Query

限制需要携带指定参数的请求才能正常转发

  1. spring:
  2. application:
  3. name: gateway
  4. cloud:
  5. gateway:
  6. enabled: true
  7. routes:
  8. - id: gateway-mhxysy
  9. uri: http://localhost:8080
  10. predicates:
  11. - Path=/api/mhxysy/**
  12. - Query=acess,true
  13. filters:
  14. - StripPrefix=2
  15. server:
  16. port: 9091

上面配置了要携带参数?acess=true

未携带参数时

RemoteAddr

RemoteAddr用于指定IP地址,只有配置的IP地址才能访问。

  1. spring:
  2. application:
  3. name: gateway
  4. cloud:
  5. gateway:
  6. enabled: true
  7. routes:
  8. - id: gateway-mhxysy
  9. uri: http://localhost:8080
  10. predicates:
  11. - Path=/api/mhxysy/**
  12. - RemoteAddr=176.176.4.127
  13. filters:
  14. - StripPrefix=2
  15. server:
  16. port: 9091

上面配置了本机的IP地址,所以通过localhost访问时返回404

Weight

这个用于配置权重,同一个分组中,权重配置的越大,请求时被选择访问的概率越高。这个不方便演示,跳过。

filters过滤器工厂

网关的过滤器,网关的处理流程由一组过滤器组成的过滤器链组成,是责任链设计模式的典型应用。

为了演示过滤器效果,在mhxysy的服务添加一个切面类,通过日志在控制台打印请求信息。

  1. package cn.edu.sgu.www.mhxysy.aop;
  2. import cn.edu.sgu.www.mhxysy.util.UserUtils;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.aspectj.lang.JoinPoint;
  5. import org.aspectj.lang.annotation.After;
  6. import org.aspectj.lang.annotation.Aspect;
  7. import org.aspectj.lang.annotation.Before;
  8. import org.aspectj.lang.annotation.Pointcut;
  9. import org.springframework.stereotype.Component;
  10. import javax.servlet.http.HttpServletRequest;
  11. import javax.servlet.http.HttpServletResponse;
  12. import java.util.Arrays;
  13. import java.util.Collection;
  14. import java.util.Enumeration;
  15. @Slf4j
  16. @Aspect
  17. @Component
  18. public class AuthenticationAop {
  19. @Pointcut("execution(public * cn.edu.sgu.www.mhxysy.controller..*(..))")
  20. public void requestAspect(){}
  21. @Before(value = "requestAspect()")
  22. public void before(JoinPoint joinPoint) throws Throwable {
  23. synchronized (AuthenticationAop.class) {
  24. log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~进入方法AuthenticationAop.before()");
  25. HttpServletRequest request = UserUtils.getRequest();
  26. String requestURI = request.getRequestURI();
  27. //打印请求信息
  28. log.debug("request_header:{}", request.getHeader("request_header"));
  29. log.info("请求ip:{}", request.getRemoteAddr());
  30. log.info("请求地址:{}", requestURI);
  31. log.info("请求方式:{}", request.getMethod());
  32. log.info("请求类方法:{}", joinPoint.getSignature());
  33. log.info("请求类方法参数:{}", Arrays.toString(joinPoint.getArgs()));
  34. log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~AuthenticationAop.before()执行完成");
  35. }
  36. }
  37. @After(value = "requestAspect()")
  38. public void after() throws Throwable {
  39. HttpServletResponse response = UserUtils.getResponse();
  40. log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~AuthenticationAop.after()开始执行");
  41. // 得到所有响应头的名称
  42. Collection<String> headerNames = response.getHeaderNames();
  43. for (String headerName : headerNames) {
  44. String header = response.getHeader(headerName);
  45. log.debug("响应头 => {}:{}", headerName, header);
  46. }
  47. log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~AuthenticationAop.after()执行完成");
  48. }
  49. }

接下来介绍gateway里的几种常用的过滤器,因为过滤器实在是太多了,只讲解4种,其他的过滤器使用类似,感兴趣的可以通过文章末尾的spring cloud gateway链接进一步学习。

AddRequestHeader

这个过滤器的作用是在请求转发之前为当前请求添加请求头。

然后给请求添加一个请求头

  1. spring:
  2. application:
  3. name: gateway
  4. cloud:
  5. gateway:
  6. enabled: true
  7. routes:
  8. - id: gateway-mhxysy
  9. uri: http://localhost:8080
  10. predicates:
  11. - Path=/api/mhxysy/**
  12. filters:
  13. - StripPrefix=2
  14. - AddRequestHeader=request_header, mhxy1218
  15. server:
  16. port: 9091

通过postman发起一次请求,mhxysy服务后台成功打印出请求头request_header的值

AddRequestParameter

这个过滤器的作用是给请求添加参数,相当于在请求后面添加?参数名=参数值。

因为之前的方法没有定义参数,这里改一个接口,这次用/chongwu/selectById这个接口。

如下图,给请求添加参数?id=CW20230727095358

  1. spring:
  2. application:
  3. name: gateway
  4. cloud:
  5. gateway:
  6. enabled: true
  7. routes:
  8. - id: gateway-mhxysy
  9. uri: http://localhost:8080
  10. predicates:
  11. - Path=/api/mhxysy/**
  12. filters:
  13. - StripPrefix=2
  14. # - AddRequestParameter=id, CW20230727095358
  15. server:
  16. port: 9091

没有添加id参数时,因为ID查询条件通过@RequestParam注解设置为必填,会发生异常。

取消上面的- AddRequestParameter=id, CW20230727095358注释之后,成功查询到了指定ID的宠物数据

AddResponseHeader

这个过滤器的作用是,服务端返回请求之后,在网关返回数据给客户端之前为响应添加响应头。

在这里添加一个响应头name,值为heyunlin

  1. spring:
  2. application:
  3. name: gateway
  4. cloud:
  5. gateway:
  6. enabled: true
  7. routes:
  8. - id: gateway-mhxysy
  9. uri: http://localhost:8080
  10. predicates:
  11. - Path=/api/mhxysy/**
  12. filters:
  13. - StripPrefix=2
  14. - AddResponseHeader=name, heyunlin
  15. server:
  16. port: 9091

然后我们观察一下效果,返回的响应中确实有一个自定义的响应头name

RemoveResponseHeader

这个过滤器的作用是删除响应头,例如上面的请求中,总是会返回一个响应头Date,在这里配置移除这个响应头。

  1. spring:
  2. application:
  3. name: gateway
  4. cloud:
  5. gateway:
  6. enabled: true
  7. routes:
  8. - id: gateway-mhxysy
  9. uri: http://localhost:8080
  10. predicates:
  11. - Path=/api/mhxysy/**
  12. filters:
  13. - StripPrefix=2
  14. - RemoveResponseHeader=Date
  15. server:
  16. port: 9091

在postman里查看响应信息,确实已经没有了Date这个响应头。

第四步:使用网关服务

项目的前端封装了ajax请求,在每个请求的路径前加上网关里配置的对应路由,控制通过每个请求都走网关。

  1. let base = "http://localhost:9091/api/mhxysy";
  2. /**
  3. * 封装的ajax get请求
  4. * @param url 请求url
  5. * @param params 请求参数
  6. * @param success 成功回调函数
  7. * @param error 失败回调函数
  8. */
  9. function get(url, params, success, error) {
  10. $.ajax({
  11. type: "GET",
  12. url: base + url,
  13. data: params,
  14. cache: false,
  15. async: true,
  16. dataType: 'json',
  17. processData: true,
  18. success: success,
  19. error: error
  20. });
  21. }
  22. /**
  23. * 封装的ajax post请求
  24. * @param url 请求url
  25. * @param params 请求参数
  26. * @param success 成功回调函数
  27. * @param error 失败回调函数
  28. */
  29. function post(url, params, success, error) {
  30. $.ajax({
  31. type: "POST",
  32. url: base + url,
  33. data: params,
  34. async: true,
  35. cache: false,
  36. dataType: 'json',
  37. processData: true,
  38. success: success,
  39. error: error
  40. });
  41. }
  42. let error = (res) => {
  43. console.log(res);
  44. if (res && res.responseJSON) {
  45. let response = res.responseJSON;
  46. if (res.status && res.status === 404) {
  47. $.messager.alert("提示", "路径" + response.path + "不存在。", "error");
  48. } else {
  49. $.messager.alert("提示", response.message, "error");
  50. }
  51. }
  52. }
  53. /**
  54. * 文件上传
  55. * @param url 上传路径
  56. * @param data 提交数据
  57. * @param success 成功回调
  58. * @param error 失败回调
  59. */
  60. function ajaxUpload(url, data, success, error) {
  61. $.ajax({
  62. url: base + url,
  63. data: data,
  64. cache: false,
  65. async: true,
  66. type: "POST",
  67. dataType: 'json',
  68. processData: false,
  69. contentType: false,
  70. success: success,
  71. error: error
  72. });
  73. }

启动gateway和mhxysy两个服务,在测试的控制器接口上加上自定义的匿名注解跳过鉴权(因为存在跨域session丢失问题,暂时没有解决)

  1. @RestController
  2. @Api(value = "宠物控制器类", tags = "宠物控制器类")
  3. @RequestMapping(path = "/chongwu", produces = "application/json; charset=utf-8")
  4. public class ChongwuController {
  5. private final ChongwuService service;
  6. @Autowired
  7. public ChongwuController(ChongwuService service) {
  8. this.service = service;
  9. }
  10. /**
  11. * 查询全部宠物
  12. * @return List<Chongwu>
  13. */
  14. @AnonymityAccess
  15. @ApiOperation("通过ID修改宠物信息")
  16. @RequestMapping(path = "/selectAll", method = RequestMethod.GET)
  17. public List<Chongwu> selectAll() {
  18. return service.selectAll();
  19. }
  20. }

浏览器访问如下地址,成功返回查询结果

localhost:9091/api/mhxysy/chongwu/selectAll

访问以上地址,请求会被转发到 localhost:8080/chongwu/selectAll ,如图

通过网关转发请求

直接访问

扩展:配置动态路由

如果像上面一样,每个应用都要写ip:端口号,也太麻烦了。gateway支持动态路由,通过配置动态路由,可以实现负载均衡的功能,只需要把上面的配置稍微修改一下。

  1. server:
  2. port: 9091
  3. spring:
  4. application:
  5. name: gateway
  6. cloud:
  7. gateway:
  8. enabled: true
  9. # 开启自动路由
  10. discovery:
  11. locator:
  12. enabled: true
  13. routes:
  14. - id: gateway-mhxysy
  15. uri: lb://mhxysy
  16. predicates:
  17. - Path=/api/mhxysy/**
  18. filters:
  19. - StripPrefix=2
  20. logging:
  21. level:
  22. springfox: error
  23. cn.edu.sgu.www.gateway: debug

上面的配置文件对比前面的配置文件,其实就多了一个开启动态路由的配置

  1. # 开启自动路由
  2. discovery:
  3. locator:
  4. enabled: true

然后断言改成了以下格式,其中lb表示load balance(负载均衡),后面加上我们注册到注册中心的服务名,也就是服务提供者的的spring.application.name配置的值

lb://服务名称

好了,文章就分享到这里了,代码已开源,可按需获取,看完不要忘了点赞+收藏哦~

gateway网关服务项目icon-default.png?t=N7T8https://gitee.com/he-yunlin/springboot-gateway.git

如果想要更深入学习gateway,可以访问Spring官网Spring Cloud Gateway

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/317378
推荐阅读
相关标签
  

闽ICP备14008679号