本文針對於測試環境SpringCloud Gateway問題解決。html
本文遇到的問題都是在測試環境真正遇到的問題,不必定試用於全部人,僅作一次記錄,便於遇到一樣問題的幹掉這些問題。前端
使用版本:SpringCloud 2.0.0.RELEASEjava
以前系統是由阿里雲SLB直接分發到幾臺生產服務器,可是通過研究,決定在中間加一層網關,也就是阿里雲SLB分發流量到Gateway到下游服務。可是又因爲種種緣由,決定使用Host方式進行攔截處理,如下爲部分配置代碼:react
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: test_client
uri: lb://TEST-CLIENT
predicates:
- Host=www.dalaoyang.cn
order: 1
filters:
- DalaoyangAuth
複製代碼
注意,其中部份內容並不是真實環境內容,可是場景絕對真實,如:jquery
過濾器內容以下,稍後介紹:git
@Component
public class DalaoyangAuthFilterFactory extends AbstractGatewayFilterFactory<Object> {
private static final Logger logger = LoggerFactory.getLogger(DalaoyangAuthFilterFactory.class);
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest host = exchange.getRequest().mutate().headers(httpHeaders -> {
httpHeaders.remove("gate_way_auth");
httpHeaders.add("gate_way_auth", "yes");
}).build();
//將如今的request 變成 change對象
ServerWebExchange build = exchange.mutate().request(host).build();
return chain.filter(build);
};
}
}
複製代碼
下游服務攔截器大體內容以下,這段代碼是原有的代碼,這個功能大概就是加載公共的屬性basePath,用於加載靜態資源,好比前端的jquery.js,根據域名判斷,而後選擇是加載爲http://127.0.0.1:8080/jquery.js仍是https://www.dalaoyang.cn/jquery.js這種:github
public class GlobalInterceptorAdapter extends HandlerInterceptorAdapter {
private static Logger logger = LoggerFactory.getLogger(GlobalInterceptorAdapter.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
String scheme = request.getScheme();
String serverName = request.getServerName();
int port = request.getServerPort();
String path = request.getContextPath();
String basePath = "";
if(serverName.indexOf("www.dalaoyang.cn")!=-1){
basePath = "//" + serverName + path;
}else {
basePath = scheme + "://" + serverName + ":" + port + path;
}
if (logger.isDebugEnabled()) {
logger.debug(basePath);
}
request.setAttribute("basePath", basePath);
return true;
}
}
複製代碼
這段代碼也是原有的代碼,用戶Session過濾器,這個完整內容不少,只截取遇到問題的片斷,大體內容就是判斷用戶是否在其餘地方登陸,若是登陸了就彈出的固定的提示頁面,內容以下:spring
String url = null;
ApplicationConfig applicationConfig0 = getApplicationConfig();
if(applicationConfig0 != null) {
String scheme = applicationConfig0.getUrlScheme();
if(scheme != null) {
String requestUrl = request.getRequestURL().toString();
if(requestUrl != null && requestUrl.length() > 8) {
requestUrl = requestUrl.substring(requestUrl.indexOf(":"),
requestUrl.indexOf("/", 8));
url = scheme + requestUrl;
}
}
}
if(url != null) {
response.sendRedirect(url + request.getContextPath() + "/session-time-out");
} else {
response.sendRedirect(request.getContextPath() + "/session-time-out");
}
複製代碼
跳轉以下:瀏覽器
1.域名指向了Gateway地址。
2.在瀏覽器使用域名訪問Gateway,被Gateway轉發到下游服務,返回對應響應。bash
在使用上述配置後,使用request.getServerName()方法已經沒法獲取到域名了,通過測試,獲取到的是服務器的ip地址,致使雖然頁面能夠正常跳轉,可是沒法獲取到正確的域名,致使靜態資源加載有問題。
在網上請教了不少人,本想看看是否是什麼地方沒有設置對,可是後臺仍是採起大多數人的建議,在header中加入一個域名信息,修改後Gateway過濾器以下:
@Component
public class DalaoyangAuthFilterFactory extends AbstractGatewayFilterFactory<Object> {
private static final Logger logger = LoggerFactory.getLogger(DalaoyangAuthFilterFactory.class);
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest host = exchange.getRequest().mutate().headers(httpHeaders -> {
httpHeaders.remove("gate_way_auth");
httpHeaders.add("gate_way_auth", "yes");
httpHeaders.add("realServerName",
exchange.getRequest().getURI().getHost());
logger.info("headers:" + httpHeaders.toString());
}).build();
//將如今的request 變成 change對象
ServerWebExchange build = exchange.mutate().request(host).build();
return chain.filter(build);
};
}
}
複製代碼
很容易看到,就是以下這句話:
httpHeaders.add("realServerName",
exchange.getRequest().getURI().getHost());
複製代碼
下游服務過濾修改成:
public class GlobalInterceptorAdapter extends HandlerInterceptorAdapter {
private static Logger logger = LoggerFactory.getLogger(GlobalInterceptorAdapter.class);
private final String TEST_SERVERNAME = "www.dalaoyang.cn";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
String scheme = request.getScheme();
String serverName = request.getServerName();
String realServerName = request.getHeader("realServerName");
int port = request.getServerPort();
String path = request.getContextPath();
String basePath = "";
if((!StringUtils.isBlank(realServerName))){
if(realServerName.contains(TEST_SERVERNAME)){
basePath = "//" + realServerName + path;
}
}else {
basePath = scheme + "://" + serverName + ":" + port + path;
}
if (logger.isDebugEnabled()) {
logger.debug(basePath);
}
request.setAttribute("basePath", basePath);
return true;
}
}
複製代碼
其實大體內容就是,使用以下方式獲取域名:
String realServerName = request.getHeader("realServerName");
複製代碼
到此,問題解決了,大部份內容跳轉正常。
部分請求,通過路由訪問報以下錯誤。
2018-06-20 01:26:04.254 ERROR 1 --- [reactor-http-client-epoll-11] .a.w.r.e.DefaultErrorWebExceptionHandler : Failed to handle request [DELETE http://localhost:8080/entity/5b29ad2cb3cb1f00010a1546]
java.lang.NullPointerException: null
at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011) ~[na:1.8.0_111]
at java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006) ~[na:1.8.0_111]
at org.springframework.cloud.gateway.filter.NettyRoutingFilter.lambda$filter$3(NettyRoutingFilter.java:117) ~[spring-cloud-gateway-core-2.0.0.RELEASE.jar!/:2.0.0.RELEASE]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:177) ~[reactor-core-3.1.8.RELEASE.jar!/:3.1.8.RELEASE]
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:108) ~[reactor-core-3.1.8.RELEASE.jar!/:3.1.8.RELEASE]
at reactor.core.publisher.FluxRetryPredicate$RetryPredicateSubscriber.onNext(FluxRetryPredicate.java:81) ~[reactor-core-3.1.8.RELEASE.jar!/:3.1.8.RELEASE]
at reactor.core.publisher.MonoCreate$DefaultMonoSink.success(MonoCreate.java:146) ~[reactor-core-3.1.8.RELEASE.jar!/:3.1.8.RELEASE]
at reactor.ipc.netty.channel.PooledClientContextHandler.fireContextActive(PooledClientContextHandler.java:85) ~[reactor-netty-0.7.8.RELEASE.jar!/:0.7.8.RELEASE]
at reactor.ipc.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:578) ~[reactor-netty-0.7.8.RELEASE.jar!/:0.7.8.RELEASE]
at reactor.ipc.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:136) ~[reactor-netty-0.7.8.RELEASE.jar!/:0.7.8.RELEASE]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:438) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:310) ~[netty-codec-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:284) ~[netty-codec-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:808) ~[netty-transport-native-epoll-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:408) ~[netty-transport-native-epoll-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:308) ~[netty-transport-native-epoll-4.1.25.Final.jar!/:4.1.25.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884) ~[netty-common-4.1.25.Final.jar!/:4.1.25.Final]
at java.lang.Thread.run(Thread.java:745) ~[na:1.8.0_111]
複製代碼
遇到問題後,沒有很慌,打開了百度查了查(微笑)。百度沒讓我很失望,基本上沒啥答覆,而後谷歌了一下,看到了github上的一個issues,大體內容感受是SpringCloud Gateway 2.0.0.RELEASE版本有些問題,升級一下版本就行了,如圖。
Github issues地址:
github.com/spring-clou… github.com/spring-clou…
說實話,感受是版本問題,可是又看到了一篇國人的文章,地址是:xiaoqiangge.com/aritcle/154…,問題大體相似,加了一下博主的微信,請教了一下,大體瞭解到了,升級了一下版本,問題解決。
感謝小強哥!!!
問題是這樣的,剛剛介紹了,用戶在其餘地方登陸會自動跳轉至一個界面提示給用戶,發現問題是沒法跳轉。
查看gateway日誌,大概提示了這樣一句話,以下:
Unhandled failure: Connection has been closed, response already set (status=302)
複製代碼
從內容大體能夠看出,重定向有問題,想到了在用戶過濾器中最後的重定向,決定在這裏下手,修改後內容以下:
String scheme = request.getScheme();
String serverName = request.getServerName();
String realServerName = request.getHeader("realServerName");
int port = request.getServerPort();
String path = request.getContextPath();
String basePath = "";
if((!StringUtils.isEmpty(realServerName))){
if(realServerName.contains(TEST_SERVERNAME)){
basePath = "https://" + realServerName + path;
}else {
basePath = scheme + "://" + serverName + ":" + port + path;
}
response.sendRedirect(basePath + "/session-time-out");
複製代碼
問題也解決了,目前還在踩坑測試中,若是你們有相似經驗能夠一塊兒探討。