spring cloud netflix zuul、spring cloud gateway是最常見的微服務(wù)網(wǎng)關(guān),通過網(wǎng)關(guān),我們可以在請求到達后端指定服務(wù)之前/后端服務(wù)處理完業(yè)務(wù)響應(yīng)數(shù)據(jù)之后對響應(yīng)進行對請求/響應(yīng)進行處理。
比如常見的參數(shù)校驗、接口鑒權(quán)等等,在后端服務(wù)的攔截器和過濾器能做的事在網(wǎng)關(guān)都可以做。
網(wǎng)關(guān)的主要功能是請求的轉(zhuǎn)發(fā)以及負(fù)載均衡,和nginx的功能類似,只是底層實現(xiàn)不同。
這篇文章就詳細介紹一下spring cloud gateway的使用,包括了各種斷言及過濾器的相關(guān)配置,幫助初學(xué)者更好的了解gateway的使用。
目錄
Springboot整合gateway
第一步:創(chuàng)建網(wǎng)關(guān)服務(wù)
第二步:在pom.yml添加依賴
第三步:修改application.yml文件
gateway相關(guān)配置說明
predicates斷言工廠
After
Before
Between
Cookile
Header
Host
Method
Path
Query
RemoteAddr
Weight
filters過濾器工廠
AddRequestHeader
AddRequestParameter
AddResponseHeader
RemoveResponseHeader
第四步:使用網(wǎng)關(guān)服務(wù)
擴展:配置動態(tài)路由
Springboot整合gateway
在講解之前,先搭建起一個網(wǎng)關(guān)服務(wù),通過Springboot整合spring cloud gateway搭建一個微服務(wù)網(wǎng)關(guān)的案例。
第一步:創(chuàng)建網(wǎng)關(guān)服務(wù)
創(chuàng)建一個springboot項目,并命名為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網(wǎng)關(guān)-->
<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>
<!--網(wǎng)關(guān)負(fù)載均衡-->
<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文件
參數(shù)說明:- StripPrefix=2表示刪除前面兩級路徑(如:/api/mhxysy),比如http://localhost:9091/api/mhxysy/user/login對應(yīng)服務(wù)器端的地址為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
這基本上是最簡單的配置,配置了服務(wù)名為gateway,然后配置了一個路由,是在idea里啟動的一個后端項目。
gateway相關(guān)配置說明
在學(xué)習(xí)gateway的配置之前,需要了解gateway中的路由這個概念,路由一般又路由ID、目標(biāo)URL、一組斷言和一組過濾器組成。
比如上面我們配置的一個路由,routes中可以配置多個路由。
spring:
cloud:
gateway:
routes:
- id: mhxysy
uri: http://localhost:8080
predicates:
- Path=/api/mhxysy/**
filters:
- StripPrefix=2
其中uri稱為目標(biāo)地址,也就是我們需要轉(zhuǎn)發(fā)請求到uri后面配置的路徑,當(dāng)我們訪問ip地址:端口號/api/mhxysy/**時,請求將會被轉(zhuǎn)發(fā)到http://localhost:8080/**
predicates斷言工廠
斷言,其實就是一組條件,熟悉java.util.function.Predicate的應(yīng)該對這個概念有一定的了解,其實就是設(shè)置了一些條件,通過matches()方法的返回值來判斷是否滿足設(shè)置的條件。
gateway里的斷言predicates指的是路由斷言工廠,在predicates里可以配置各種條件,只有全部條件都滿足,請求才能被轉(zhuǎn)發(fā)。
接下來介紹gateway里都有哪些斷言工廠。
After
配置在指定時間之后才能轉(zhuǎn)發(fā)請求,后面指定的值的格式和LocalDateTime很像,只是多了毫秒和時區(qū)信息。
2023-09-08T08:31:59.789+08:00[Asia/Shanghai]
前半部分:2023-09-08T08:31:59這是一個LocalDateTime的字符串格式,789表示的是789毫秒,1秒=1000毫秒。
后半部分:+08:00[Asia/Shanghai]表示東八區(qū)的亞洲/上海。
- 東時區(qū)用+表示,如+6:00表示東6區(qū)
- 西時區(qū)用-表示,如-5:00表示西5區(qū)
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
比如當(dāng)前是2023年9月8日早上8:30分,此時訪問服務(wù)的/chongwu/selectAll接口返回404
過了那個時間再訪問,成功返回查詢結(jié)果
Before
限制在指定時間之前才能訪問,比如上面的配置改成Before就訪問不到了,因為已經(jīng)過了那個時間點
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
配置只能在指定時間段訪問,上面的時間配置后移一天,依舊能得到返回結(jié)果。
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這個服務(wù)的配置文件中設(shè)置了session的名稱為MHXYSY_JSESSIONID,默認(rèn)是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 {
/**
* 設(shè)置SESSION_ID
* @return ServletContextInitializer
*/
@Bean
public ServletContextInitializer servletContextInitializer() {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) {
servletContext.getSessionCookieConfig().setName("MHXYSY_JSESSIONID");
}
};
}
}
session值通過瀏覽器中查看得到
然后配置cookie
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名稱改為默認(rèn)的JSESSIONID之后,請求返回404。
Header
只有攜帶指定請求頭的請求會被轉(zhuǎn)發(fā),在這里配置需要攜帶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
為了方便測試,使用postman來測試效果
不攜帶請求頭時
攜帶請求頭,正常響應(yīng),這里返回401,是因為沒有通過接口鑒權(quán),瀏覽器登錄了,所以沒有返回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
配置請求方式,只有規(guī)定的請求方式才會被轉(zhuǎn)發(fā)。
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,一樣是因為沒有通過鑒權(quán)流程。
post
get
Path
這是基礎(chǔ)的斷言,只有將指定的路徑轉(zhuǎn)發(fā)到目標(biāo)URL,本篇文章的http://localhost:9091/api/mhxysy/chongwu/selectAll會被轉(zhuǎn)發(fā)到http://localhost:8080/chongwu/selectAll
Query
限制需要攜帶指定參數(shù)的請求才能正常轉(zhuǎn)發(fā)
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
上面配置了要攜帶參數(shù)?acess=true
未攜帶參數(shù)時
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
這個用于配置權(quán)重,同一個分組中,權(quán)重配置的越大,請求時被選擇訪問的概率越高。這個不方便演示,跳過。
filters過濾器工廠
網(wǎng)關(guān)的過濾器,網(wǎng)關(guān)的處理流程由一組過濾器組成的過濾器鏈組成,是責(zé)任鏈設(shè)計模式的典型應(yīng)用。
為了演示過濾器效果,在mhxysy的服務(wù)添加一個切面類,通過日志在控制臺打印請求信息。
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("請求類方法參數(shù):{}", Arrays.toString(joinPoint.getArgs()));
log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~AuthenticationAop.before()執(zhí)行完成");
}
}
@After(value = "requestAspect()")
public void after() throws Throwable {
HttpServletResponse response = UserUtils.getResponse();
log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~AuthenticationAop.after()開始執(zhí)行");
// 得到所有響應(yīng)頭的名稱
Collection<String> headerNames = response.getHeaderNames();
for (String headerName : headerNames) {
String header = response.getHeader(headerName);
log.debug("響應(yīng)頭 => {}:{}", headerName, header);
}
log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~AuthenticationAop.after()執(zhí)行完成");
}
}
接下來介紹gateway里的幾種常用的過濾器,因為過濾器實在是太多了,只講解4種,其他的過濾器使用類似,感興趣的可以通過文章末尾的spring cloud gateway鏈接進一步學(xué)習(xí)。
AddRequestHeader
這個過濾器的作用是在請求轉(zhuǎn)發(fā)之前為當(dāng)前請求添加請求頭。
然后給請求添加一個請求頭
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發(fā)起一次請求,mhxysy服務(wù)后臺成功打印出請求頭request_header的值
AddRequestParameter
這個過濾器的作用是給請求添加參數(shù),相當(dāng)于在請求后面添加?參數(shù)名=參數(shù)值。
因為之前的方法沒有定義參數(shù),這里改一個接口,這次用/chongwu/selectById這個接口。
如下圖,給請求添加參數(shù)?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參數(shù)時,因為ID查詢條件通過@RequestParam注解設(shè)置為必填,會發(fā)生異常。
取消上面的- AddRequestParameter=id, CW20230727095358注釋之后,成功查詢到了指定ID的寵物數(shù)據(jù)
AddResponseHeader
這個過濾器的作用是,服務(wù)端返回請求之后,在網(wǎng)關(guān)返回數(shù)據(jù)給客戶端之前為響應(yīng)添加響應(yīng)頭。
在這里添加一個響應(yīng)頭name,值為heyunlin
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
然后我們觀察一下效果,返回的響應(yīng)中確實有一個自定義的響應(yīng)頭name
RemoveResponseHeader
這個過濾器的作用是刪除響應(yīng)頭,例如上面的請求中,總是會返回一個響應(yīng)頭Date,在這里配置移除這個響應(yīng)頭。
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里查看響應(yīng)信息,確實已經(jīng)沒有了Date這個響應(yīng)頭。
第四步:使用網(wǎng)關(guān)服務(wù)
項目的前端封裝了ajax請求,在每個請求的路徑前加上網(wǎng)關(guān)里配置的對應(yīng)路由,控制通過每個請求都走網(wǎng)關(guān)。
let base = "http://localhost:9091/api/mhxysy";
/**
* 封裝的ajax get請求
* @param url 請求url
* @param params 請求參數(shù)
* @param success 成功回調(diào)函數(shù)
* @param error 失敗回調(diào)函數(shù)
*/
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 請求參數(shù)
* @param success 成功回調(diào)函數(shù)
* @param error 失敗回調(diào)函數(shù)
*/
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 提交數(shù)據(jù)
* @param success 成功回調(diào)
* @param error 失敗回調(diào)
*/
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兩個服務(wù),在測試的控制器接口上加上自定義的匿名注解跳過鑒權(quán)(因為存在跨域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;
}
/**
* 查詢?nèi)繉櫸? * @return List<Chongwu>
*/
@AnonymityAccess
@ApiOperation("通過ID修改寵物信息")
@RequestMapping(path = "/selectAll", method = RequestMethod.GET)
public List<Chongwu> selectAll() {
return service.selectAll();
}
}
瀏覽器訪問如下地址,成功返回查詢結(jié)果
localhost:9091/api/mhxysy/chongwu/selectAll
訪問以上地址,請求會被轉(zhuǎn)發(fā)到 localhost:8080/chongwu/selectAll ,如圖
通過網(wǎng)關(guān)轉(zhuǎn)發(fā)請求
直接訪問
擴展:配置動態(tài)路由
如果像上面一樣,每個應(yīng)用都要寫ip:端口號,也太麻煩了。gateway支持動態(tài)路由,通過配置動態(tài)路由,可以實現(xiàn)負(fù)載均衡的功能,只需要把上面的配置稍微修改一下。
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
上面的配置文件對比前面的配置文件,其實就多了一個開啟動態(tài)路由的配置
# 開啟自動路由
discovery:
locator:
enabled: true
然后斷言改成了以下格式,其中l(wèi)b表示load balance(負(fù)載均衡),后面加上我們注冊到注冊中心的服務(wù)名,也就是服務(wù)提供者的的spring.application.name配置的值
lb://服務(wù)名稱
好了,文章就分享到這里了,代碼已開源,可按需獲取,看完不要忘了點贊+收藏哦~
gateway網(wǎng)關(guān)服務(wù)項目https://gitee.com/he-yunlin/gateway.git文章來源:http://www.zghlxwxcb.cn/news/detail-726612.html
如果想要更深入學(xué)習(xí)gateway,可以訪問Spring官網(wǎng)Spring Cloud Gateway文章來源地址http://www.zghlxwxcb.cn/news/detail-726612.html
到了這里,關(guān)于springboot整合spring cloud gateway搭建網(wǎng)關(guān)服務(wù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!