Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
sl-gateway
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
zhouwei
sl-gateway
Commits
0745b64b
Commit
0745b64b
authored
Apr 16, 2025
by
zhouwei
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
新增超时转移逻辑
parent
e8c85f2b
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
47 additions
and
28 deletions
+47
-28
AppConfig.java
src/main/java/com/nanyan/securitylink/config/AppConfig.java
+6
-0
FailoverGatewayFilterFactory.java
...yan/securitylink/filter/FailoverGatewayFilterFactory.java
+41
-28
No files found.
src/main/java/com/nanyan/securitylink/config/AppConfig.java
View file @
0745b64b
...
...
@@ -12,6 +12,12 @@ import org.springframework.context.annotation.Configuration;
public
class
AppConfig
{
@Value
(
"${alert.host}"
)
String
alertHost
;
@Value
(
"${gateway.request.timeout:100000}"
)
private
int
requestTimeout
;
public
int
getRequestTimeout
()
{
return
requestTimeout
;
}
public
String
getAlertHost
()
{
return
alertHost
;
...
...
src/main/java/com/nanyan/securitylink/filter/FailoverGatewayFilterFactory.java
View file @
0745b64b
...
...
@@ -2,6 +2,7 @@ package com.nanyan.securitylink.filter;
import
com.alibaba.fastjson.JSON
;
import
com.alibaba.fastjson.JSONObject
;
import
com.nanyan.securitylink.config.AppConfig
;
import
com.nanyan.securitylink.service.AlertService
;
import
com.nanyan.securitylink.service.TokenRouteMappingService
;
import
lombok.Data
;
...
...
@@ -21,7 +22,9 @@ import org.springframework.web.reactive.function.BodyInserters;
import
org.springframework.web.reactive.function.client.WebClient
;
import
org.springframework.web.server.ServerWebExchange
;
import
reactor.core.publisher.Mono
;
import
java.nio.charset.StandardCharsets
;
import
java.time.Duration
;
import
java.util.Map
;
import
java.util.concurrent.ConcurrentHashMap
;
...
...
@@ -36,8 +39,11 @@ public class FailoverGatewayFilterFactory extends AbstractGatewayFilterFactory<F
private
static
final
int
WINDOW_SIZE_SECONDS
=
100
;
// 1分钟窗口
private
static
final
double
ERROR_THRESHOLD
=
0.5
;
// 75%错误率阈值
private
static
Long
lastAlarmTime
=
System
.
currentTimeMillis
();
@Autowired
AlertService
alertService
;
@Autowired
AppConfig
appConfig
;
private
final
Map
<
String
,
CircularQueue
>
errorStatsMap
=
new
ConcurrentHashMap
<>();
public
FailoverGatewayFilterFactory
(
WebClient
.
Builder
webClientBuilder
,
...
...
@@ -98,7 +104,7 @@ public class FailoverGatewayFilterFactory extends AbstractGatewayFilterFactory<F
private
boolean
shouldSkipPrimary
(
String
primaryUrl
)
{
CircularQueue
stats
=
getOrCreateErrorStats
(
primaryUrl
);
double
errorRate
=
stats
.
getErrorRate
();
log
.
info
(
"rate: {}%, url: {}"
,
errorRate
*
100
,
primaryUrl
);
log
.
info
(
"rate: {}%, url: {}"
,
errorRate
*
100
,
primaryUrl
);
boolean
skip
=
errorRate
>=
ERROR_THRESHOLD
;
if
(
skip
)
{
// 发送告警
...
...
@@ -108,13 +114,15 @@ public class FailoverGatewayFilterFactory extends AbstractGatewayFilterFactory<F
}
return
skip
;
}
private
final
Map
<
String
,
Long
>
alarmSentMap
=
new
ConcurrentHashMap
<>();
private
void
sendAlarmIfNeeded
(
String
primaryUrl
,
double
errorRate
)
{
long
currentTime
=
System
.
currentTimeMillis
();
Long
lastAlarmTime
=
alarmSentMap
.
get
(
primaryUrl
);
// 如果从未发送过告警,或者距离上次告警已经超过冷却时间
if
(
lastAlarmTime
==
null
||
(
currentTime
-
lastAlarmTime
)
>
20
*
60
*
1000
)
{
if
(
lastAlarmTime
==
null
||
(
currentTime
-
lastAlarmTime
)
>
20
*
60
*
1000
)
{
String
alarmMessage
=
String
.
format
(
"Service degradation detected: %s has high error rate (%.2f%%), switching to fallback service"
,
primaryUrl
,
errorRate
*
100
);
...
...
@@ -146,6 +154,7 @@ public class FailoverGatewayFilterFactory extends AbstractGatewayFilterFactory<F
log
.
info
(
"Found matching URI config for token: {}, sourceUri: {}"
,
token
,
sourceUri
);
String
primaryUrl
=
uriConfig
.
getPrimaryUrl
();
CircularQueue
errorStats
=
getOrCreateErrorStats
(
primaryUrl
);
long
startTime
=
System
.
currentTimeMillis
();
// 检查是否应该跳过主地址
if
(
shouldSkipPrimary
(
uriConfig
.
getPrimaryUrl
()))
{
//告警日志
...
...
@@ -172,10 +181,14 @@ public class FailoverGatewayFilterFactory extends AbstractGatewayFilterFactory<F
exchange
,
true
,
errorStats
).
onErrorResume
(
primaryError
->
{
).
timeout
(
Duration
.
ofMillis
(
appConfig
.
getRequestTimeout
()))
.
onErrorResume
(
primaryError
->
{
log
.
error
(
"Primary endpoint failed: {}, error: {}"
,
uriConfig
.
getPrimaryUrl
()
+
sourceUri
,
primaryError
.
getMessage
());
long
duration
=
System
.
currentTimeMillis
()
-
startTime
;
log
.
info
(
"Source request timed out after {}ms, switching to target: {}"
,
duration
,
uriConfig
.
getTargetUri
());
// 记录错误
getOrCreateErrorStats
(
uriConfig
.
getPrimaryUrl
()).
add
(
false
);
...
...
@@ -275,7 +288,7 @@ public class FailoverGatewayFilterFactory extends AbstractGatewayFilterFactory<F
private
Mono
<
Void
>
handleResponse
(
ServerWebExchange
exchange
,
org
.
springframework
.
web
.
reactive
.
function
.
client
.
ClientResponse
clientResponse
,
boolean
isSourceRequest
,
CircularQueue
errorStats
)
{
boolean
isSourceRequest
,
CircularQueue
errorStats
)
{
HttpStatus
responseStatus
=
clientResponse
.
statusCode
();
if
(
isSourceRequest
)
{
...
...
@@ -297,19 +310,19 @@ public class FailoverGatewayFilterFactory extends AbstractGatewayFilterFactory<F
JSONObject
jsonBody
=
JSON
.
parseObject
(
body
);
boolean
success
=
false
;
String
bodyStatus
=
""
;
if
(
jsonBody
.
containsKey
(
"status"
))
{
if
(
jsonBody
.
containsKey
(
"status"
))
{
Integer
status
=
jsonBody
.
getInteger
(
"status"
);
bodyStatus
=
status
+
""
;
bodyStatus
=
status
+
""
;
}
if
(
jsonBody
.
containsKey
(
"answer"
))
{
if
(
jsonBody
.
containsKey
(
"answer"
))
{
success
=
true
;
}
if
(
jsonBody
.
containsKey
(
"data"
))
{
if
(
jsonBody
.
containsKey
(
"data"
))
{
JSONObject
data
=
jsonBody
.
getJSONObject
(
"data"
);
if
(
data
.
containsKey
(
"status"
)){
if
(
data
.
containsKey
(
"status"
))
{
String
status
=
data
.
getString
(
"status"
);
if
(
"succeeded"
.
equalsIgnoreCase
(
status
))
{
if
(
"succeeded"
.
equalsIgnoreCase
(
status
))
{
success
=
true
;
}
bodyStatus
=
status
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment