spring cloud netflix zuul、spring cloud gateway是最常见的微服务网关,通过网关,我们可以在请求到达后端指定服务之前/后端服务处理完业务响应数据之后对响应进行对请求/响应进行处理。
这篇文章就详细介绍一下spring cloud gateway的使用,包括了各种断言及过滤器的相关配置,帮助初学者更好的了解gateway的使用。
Springboot整合gateway
在讲解之前,先搭建起一个网关服务,通过Springboot整合spring cloud gateway搭建一个微服务网关的案例。
第一步:创建网关服务
第二步:在pom.yml添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/>
</parent>
<groupId>cn.edu.sgu.www</groupId>
<artifactId>gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<lombok.version>1.18.22</lombok.version>
<sentinel.version>2.2.9.RELEASE</sentinel.version>
<spring-cloud.version>2.2.6.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--gateway网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>${spring-cloud.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--nacos注册中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring-cloud.version}</version>
</dependency>
<!--nacos配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${spring-cloud.version}</version>
</dependency>
<!--网关负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>${spring-cloud.version}</version>
</dependency>
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
<version>${sentinel.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>${sentinel.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
第三步:修改application.yml文件
参数说明:- StripPrefix=2表示删除前面两级路径(如:/api/mhxysy),比如http://localhost:9091/api/mhxysy/user/login对应服务器端的地址为http://localhost:8080/user/login
server:
port: 9091
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
routes:
- id: mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
filters:
- StripPrefix=2
logging:
file:
name: D:/log/gateway.log
level:
springfox: error
cn.edu.sgu.www.gateway: debug
这基本上是最简单的配置,配置了服务名为gateway,然后配置了一个路由,是在idea里启动的一个后端项目。
gateway相关配置说明
在学习gateway的配置之前,需要了解gateway中的路由这个概念,路由一般又路由ID、目标URL、一组断言和一组过滤器组成。
比如上面我们配置的一个路由,routes中可以配置多个路由。
spring:
cloud:
gateway:
routes:
- id: mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
filters:
- StripPrefix=2
其中uri称为目标地址,也就是我们需要转发请求到uri后面配置的路径,当我们访问ip地址:端口号/api/mhxysy/**时,请求将会被转发到http://localhost:8080/**
predicates断言工厂
断言,其实就是一组条件,熟悉java.util.function.Predicate的应该对这个概念有一定的了解,其实就是设置了一些条件,通过matches()方法的返回值来判断是否满足设置的条件。
gateway里的断言predicates指的是路由断言工厂,在predicates里可以配置各种条件,只有全部条件都满足,请求才能被转发。
After
配置在指定时间之后才能转发请求,后面指定的值的格式和LocalDateTime很像,只是多了毫秒和时区信息。
2023-09-08T08:31:59.789+08:00[Asia/Shanghai]
前半部分:2023-09-08T08:31:59这是一个LocalDateTime的字符串格式,789表示的是789毫秒,1秒=1000毫秒。
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
routes:
- id: gateway-mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
- After=2023-09-08T08:31:59.789+08:00[Asia/Shanghai]
filters:
- StripPrefix=2
server:
port: 9091
比如当前是2023年9月8日早上8:30分,此时访问服务的/chongwu/selectAll接口返回404
Before
限制在指定时间之前才能访问,比如上面的配置改成Before就访问不到了,因为已经过了那个时间点
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
routes:
- id: gateway-mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
- Before=2023-09-08T08:31:59.789+08:00[Asia/Shanghai]
filters:
- StripPrefix=2
server:
port: 9091
Between
配置只能在指定时间段访问,上面的时间配置后移一天,依旧能得到返回结果。
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
routes:
- id: gateway-mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
- Between=2023-09-08T08:31:59.789+08:00[Asia/Shanghai],2023-09-09T08:31:59.789+08:00[Asia/Shanghai]
filters:
- StripPrefix=2
server:
port: 9091
Cookile
限制只有请求中携带了指定的cookie才能访问,比如配置需要携带MHXYSY_JSESSIONID。
因为在mhxysy这个服务的配置文件中设置了session的名称为MHXYSY_JSESSIONID,默认是JSESSIONID,登录之后,浏览器每次请求都会携带MHXYSY_JSESSIONID的cookie。
package cn.edu.sgu.www.mhxysy.config;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.servlet.ServletContext;
/**
* springmvc配置类
* @author heyunlin
* @version 1.0
*/
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
/**
* 设置SESSION_ID
* @return ServletContextInitializer
*/
@Bean
public ServletContextInitializer servletContextInitializer() {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) {
servletContext.getSessionCookieConfig().setName("MHXYSY_JSESSIONID");
}
};
}
}
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
routes:
- id: gateway-mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
- Cookie=MHXYSY_JSESSIONID,831B175D25150131A3F3017116369CAE
filters:
- StripPrefix=2
server:
port: 9091
把上面的配置中的cookie名称改为默认的JSESSIONID之后,请求返回404。
Header
只有携带指定请求头的请求会被转发,在这里配置需要携带token的请求头
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
routes:
- id: gateway-mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
- Header=token,mhxy1218
filters:
- StripPrefix=2
server:
port: 9091
不携带请求头时
携带请求头,正常响应,这里返回401,是因为没有通过接口鉴权,浏览器登录了,所以没有返回401
Host
这个是指定域名访问,如果不是指定的域名,将访问失败,这个不好测试,就跳过了。
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
routes:
- id: gateway-mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
- Host=taobao.com
filters:
- StripPrefix=2
server:
port: 9091
Method
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
routes:
- id: gateway-mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
- Method=POST
filters:
- StripPrefix=2
server:
port: 9091
这时候通过get请求访问会404,post请求则返回401,一样是因为没有通过鉴权流程。
Path
这是基础的断言,只有将指定的路径转发到目标URL,本篇文章的http://localhost:9091/api/mhxysy/chongwu/selectAll会被转发到http://localhost:8080/chongwu/selectAll
Query
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
routes:
- id: gateway-mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
- Query=acess,true
filters:
- StripPrefix=2
server:
port: 9091
上面配置了要携带参数?acess=true
未携带参数时
RemoteAddr
RemoteAddr用于指定IP地址,只有配置的IP地址才能访问。
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
routes:
- id: gateway-mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
- RemoteAddr=176.176.4.127
filters:
- StripPrefix=2
server:
port: 9091
上面配置了本机的IP地址,所以通过localhost访问时返回404
Weight
这个用于配置权重,同一个分组中,权重配置的越大,请求时被选择访问的概率越高。这个不方便演示,跳过。
filters过滤器工厂
package cn.edu.sgu.www.mhxysy.aop;
import cn.edu.sgu.www.mhxysy.util.UserUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
@Slf4j
@Aspect
@Component
public class AuthenticationAop {
@Pointcut("execution(public * cn.edu.sgu.www.mhxysy.controller..*(..))")
public void requestAspect(){}
@Before(value = "requestAspect()")
public void before(JoinPoint joinPoint) throws Throwable {
synchronized (AuthenticationAop.class) {
log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~进入方法AuthenticationAop.before()");
HttpServletRequest request = UserUtils.getRequest();
String requestURI = request.getRequestURI();
//打印请求信息
log.debug("request_header:{}", request.getHeader("request_header"));
log.info("请求ip:{}", request.getRemoteAddr());
log.info("请求地址:{}", requestURI);
log.info("请求方式:{}", request.getMethod());
log.info("请求类方法:{}", joinPoint.getSignature());
log.info("请求类方法参数:{}", Arrays.toString(joinPoint.getArgs()));
log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~AuthenticationAop.before()执行完成");
}
}
@After(value = "requestAspect()")
public void after() throws Throwable {
HttpServletResponse response = UserUtils.getResponse();
log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~AuthenticationAop.after()开始执行");
// 得到所有响应头的名称
Collection<String> headerNames = response.getHeaderNames();
for (String headerName : headerNames) {
String header = response.getHeader(headerName);
log.debug("响应头 => {}:{}", headerName, header);
}
log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~AuthenticationAop.after()执行完成");
}
}
接下来介绍gateway里的几种常用的过滤器,因为过滤器实在是太多了,只讲解4种,其他的过滤器使用类似,感兴趣的可以通过文章末尾的spring cloud gateway链接进一步学习。
AddRequestHeader
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
routes:
- id: gateway-mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
filters:
- StripPrefix=2
- AddRequestHeader=request_header, mhxy1218
server:
port: 9091
通过postman发起一次请求,mhxysy服务后台成功打印出请求头request_header的值
AddRequestParameter
这个过滤器的作用是给请求添加参数,相当于在请求后面添加?参数名=参数值。
因为之前的方法没有定义参数,这里改一个接口,这次用/chongwu/selectById这个接口。
如下图,给请求添加参数?id=CW20230727095358
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
routes:
- id: gateway-mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
filters:
- StripPrefix=2
# - AddRequestParameter=id, CW20230727095358
server:
port: 9091
没有添加id参数时,因为ID查询条件通过@RequestParam注解设置为必填,会发生异常。
取消上面的- AddRequestParameter=id, CW20230727095358注释之后,成功查询到了指定ID的宠物数据
AddResponseHeader
这个过滤器的作用是,服务端返回请求之后,在网关返回数据给客户端之前为响应添加响应头。
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
routes:
- id: gateway-mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
filters:
- StripPrefix=2
- AddResponseHeader=name, heyunlin
server:
port: 9091
然后我们观察一下效果,返回的响应中确实有一个自定义的响应头name
RemoveResponseHeader
这个过滤器的作用是删除响应头,例如上面的请求中,总是会返回一个响应头Date,在这里配置移除这个响应头。
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
routes:
- id: gateway-mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
filters:
- StripPrefix=2
- RemoveResponseHeader=Date
server:
port: 9091
在postman里查看响应信息,确实已经没有了Date这个响应头。
第四步:使用网关服务
项目的前端封装了ajax请求,在每个请求的路径前加上网关里配置的对应路由,控制通过每个请求都走网关。
let base = "http://localhost:9091/api/mhxysy";
/**
* 封装的ajax get请求
* @param url 请求url
* @param params 请求参数
* @param success 成功回调函数
* @param error 失败回调函数
*/
function get(url, params, success, error) {
$.ajax({
type: "GET",
url: base + url,
data: params,
cache: false,
async: true,
dataType: 'json',
processData: true,
success: success,
error: error
});
}
/**
* 封装的ajax post请求
* @param url 请求url
* @param params 请求参数
* @param success 成功回调函数
* @param error 失败回调函数
*/
function post(url, params, success, error) {
$.ajax({
type: "POST",
url: base + url,
data: params,
async: true,
cache: false,
dataType: 'json',
processData: true,
success: success,
error: error
});
}
let error = (res) => {
console.log(res);
if (res && res.responseJSON) {
let response = res.responseJSON;
if (res.status && res.status === 404) {
$.messager.alert("提示", "路径" + response.path + "不存在。", "error");
} else {
$.messager.alert("提示", response.message, "error");
}
}
}
/**
* 文件上传
* @param url 上传路径
* @param data 提交数据
* @param success 成功回调
* @param error 失败回调
*/
function ajaxUpload(url, data, success, error) {
$.ajax({
url: base + url,
data: data,
cache: false,
async: true,
type: "POST",
dataType: 'json',
processData: false,
contentType: false,
success: success,
error: error
});
}
启动gateway和mhxysy两个服务,在测试的控制器接口上加上自定义的匿名注解跳过鉴权(因为存在跨域session丢失问题,暂时没有解决)
@RestController
@Api(value = "宠物控制器类", tags = "宠物控制器类")
@RequestMapping(path = "/chongwu", produces = "application/json; charset=utf-8")
public class ChongwuController {
private final ChongwuService service;
@Autowired
public ChongwuController(ChongwuService service) {
this.service = service;
}
/**
* 查询全部宠物
* @return List<Chongwu>
*/
@AnonymityAccess
@ApiOperation("通过ID修改宠物信息")
@RequestMapping(path = "/selectAll", method = RequestMethod.GET)
public List<Chongwu> selectAll() {
return service.selectAll();
}
}
访问以上地址,请求会被转发到 localhost:8080/chongwu/selectAll ,如图
通过网关转发请求
直接访问
扩展:配置动态路由
如果像上面一样,每个应用都要写ip:端口号,也太麻烦了。gateway支持动态路由,通过配置动态路由,可以实现负载均衡的功能,只需要把上面的配置稍微修改一下。
server:
port: 9091
spring:
application:
name: gateway
cloud:
gateway:
enabled: true
# 开启自动路由
discovery:
locator:
enabled: true
routes:
- id: gateway-mhxysy
uri: lb://mhxysy
predicates:
- Path=/api/mhxysy/**
filters:
- StripPrefix=2
logging:
level:
springfox: error
cn.edu.sgu.www.gateway: debug
上面的配置文件对比前面的配置文件,其实就多了一个开启动态路由的配置
# 开启自动路由
discovery:
locator:
enabled: true
然后断言改成了以下格式,其中lb表示load balance(负载均衡),后面加上我们注册到注册中心的服务名,也就是服务提供者的的spring.application.name配置的值
lb://服务名称
好了,文章就分享到这里了,代码已开源,可按需获取,看完不要忘了点赞+收藏哦~
gateway网关服务项目https://gitee.com/he-yunlin/gateway.git
如果想要更深入学习gateway,可以访问Spring官网Spring Cloud Gateway
原文地址:https://blog.csdn.net/heyl163_/article/details/127707450
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_43510.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!