Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
J
jiwei-api
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
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
马天浩
jiwei-api
Commits
a1b6e8e7
Commit
a1b6e8e7
authored
Mar 15, 2021
by
matianhao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[瑞成接口] <refactor> 解决一定并发下秘钥过期问题
- 将刷新秘钥的动作从各线程中提取成独立方法,并使用redis分布式锁控制线程独占 - 未获取到分布式锁的线程等待 1s 后重新发起请求
parent
5ee8f19e
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
136 additions
and
12 deletions
+136
-12
Constants.java
src/main/java/com/mth/requestsecret/constant/Constants.java
+7
-0
RequestSecretSchedulerTask.java
...h/requestsecret/scheduler/RequestSecretSchedulerTask.java
+1
-11
RestTemplateService.java
...va/com/mth/requestsecret/service/RestTemplateService.java
+91
-1
RedisUtils.java
src/main/java/com/mth/requestsecret/util/RedisUtils.java
+37
-0
No files found.
src/main/java/com/mth/requestsecret/constant/Constants.java
View file @
a1b6e8e7
...
...
@@ -32,4 +32,11 @@ public class Constants {
* sign参数错误:14
*/
public
static
final
String
RESPONSE_CODE_14
=
"14"
;
// 其他
//-----------------------------------
/**
* redis分布式锁的key
*/
public
static
final
String
SJJ_DISTRIBUTED_LOCK
=
"sjj.distributed.lock"
;
}
src/main/java/com/mth/requestsecret/scheduler/RequestSecretSchedulerTask.java
View file @
a1b6e8e7
...
...
@@ -18,7 +18,6 @@ import org.springframework.web.client.RestTemplate;
import
java.time.Instant
;
import
java.time.LocalDateTime
;
import
java.time.ZoneId
;
import
java.time.temporal.ChronoUnit
;
import
java.util.Date
;
import
java.util.HashMap
;
...
...
@@ -108,17 +107,8 @@ public class RequestSecretSchedulerTask implements SchedulingConfigurer {
requestSecretEndTime
=
Long
.
parseLong
(
datas
.
getString
(
"requestSecretEndTime"
));
refreshSecret
=
datas
.
getString
(
"refreshSecret"
);
refreshSecretEndTime
=
Long
.
parseLong
(
datas
.
getString
(
"refreshSecretEndTime"
));
// 设置过期时间,30秒内不重新刷新秘钥
String
expireTime
=
String
.
valueOf
(
requestSecretEndTime
-
(
14
*
60
*
1000
+
30
*
1000
));
// redis保存 请求秘钥 和 过期时间
log
.
info
(
"请求秘钥:{};请求秘钥过期时间:{};30s内不刷新秘钥:{}"
,
requestSecret
,
LocalDateTime
.
ofInstant
(
Instant
.
ofEpochMilli
(
requestSecretEndTime
),
ZoneId
.
systemDefault
()),
LocalDateTime
.
ofInstant
(
Instant
.
ofEpochMilli
(
Long
.
parseLong
(
expireTime
)),
ZoneId
.
systemDefault
())
);
// 存入redis
redisUtil
.
set
(
Constants
.
SJJ_REQUEST_SECRET_PREFIX
,
requestSecret
);
redisUtil
.
set
(
Constants
.
SJJ_REQUEST_EXPIRE_TIME_PREFIX
,
expireTime
);
}
}
...
...
src/main/java/com/mth/requestsecret/service/RestTemplateService.java
View file @
a1b6e8e7
...
...
@@ -19,6 +19,8 @@ import java.time.ZoneOffset;
import
java.util.Date
;
import
java.util.Map
;
import
java.util.Objects
;
import
java.util.UUID
;
import
java.util.concurrent.TimeUnit
;
import
static
com
.
mth
.
requestsecret
.
constant
.
Constants
.*;
...
...
@@ -78,7 +80,7 @@ public class RestTemplateService {
* @param apiMethod
* @return
*/
public
ResponseEntity
<
String
>
commonSendRequest
(
MultiValueMap
<
String
,
Object
>
paramMap
,
final
String
apiMethod
)
{
public
ResponseEntity
<
String
>
commonSendRequest
V1
(
MultiValueMap
<
String
,
Object
>
paramMap
,
final
String
apiMethod
)
{
// 请求url拼接api签名和公共参数
StringBuilder
url
=
new
StringBuilder
()
.
append
(
pathUrl
)
...
...
@@ -138,6 +140,94 @@ public class RestTemplateService {
return
responseEntity
;
}
/**
* 瑞成平台接口发送请求 V2.0
* <p>
* 将获取秘钥的动作从请求线程中拆分出来,并使用独占形式
*
* @param paramMap
* @param apiMethod
* @return
*/
public
ResponseEntity
<
String
>
commonSendRequest
(
MultiValueMap
<
String
,
Object
>
paramMap
,
final
String
apiMethod
)
{
// 请求url拼接api签名和公共参数
StringBuilder
url
=
new
StringBuilder
()
.
append
(
pathUrl
)
.
append
(
"/interface/public/service/risen-api/"
)
.
append
(
apiMethod
);
// 请求时间
String
requestTime
=
DSLUtils
.
dateToLong
(
new
Date
())
+
""
;
// redis中获取秘钥
String
requestSecret
=
redisUtil
.
get
(
SJJ_REQUEST_SECRET_PREFIX
);
log
.
info
(
"redis中请求秘钥:{}"
,
requestSecret
);
// 签名字符串
String
signStr
=
appKey
+
requestSecret
+
requestTime
;
// 组装请求参数
paramMap
.
set
(
"appKey"
,
appKey
);
paramMap
.
set
(
"sign"
,
MD5Utils
.
encoderByMd5
(
signStr
));
paramMap
.
set
(
"requestTime"
,
requestTime
);
// 日志记录
log
.
info
(
"纪委api:{}"
,
apiMethod
);
log
.
info
(
"api url:{}"
,
url
);
log
.
info
(
"api params:{}"
,
paramMap
);
HttpHeaders
headers
=
new
HttpHeaders
();
headers
.
setContentType
(
MediaType
.
APPLICATION_FORM_URLENCODED
);
// 发送请求
HttpEntity
<
MultiValueMap
<
String
,
Object
>>
request
=
new
HttpEntity
<>(
paramMap
,
headers
);
ResponseEntity
<
String
>
responseEntity
=
restTemplate
.
exchange
(
url
.
toString
(),
HttpMethod
.
POST
,
request
,
String
.
class
);
log
.
info
(
"api response:{}"
,
responseEntity
);
try
{
// 接口返回码
String
code
=
JSONObject
.
parseObject
(
responseEntity
.
getBody
()).
getString
(
"code"
);
Integer
count
=
threadLocal
.
get
();
String
threadName
=
Thread
.
currentThread
().
getName
();
// 如非成功返回码,则重新获取秘钥并发送请求,限制重新请求次数两次
if
(!
RESPONSE_CODE_00
.
equals
(
code
)
&&
count
<
2
)
{
threadLocal
.
set
(++
count
);
log
.
info
(
"线程名称:{},重新请求次数:{}"
,
threadName
,
count
);
// 刷新秘钥
getRequestSecret
();
// 重新发起调用请求
return
commonSendRequest
(
paramMap
,
apiMethod
);
}
}
finally
{
threadLocal
.
remove
();
}
return
responseEntity
;
}
/**
* 刷新瑞成接口秘钥
*/
private
void
getRequestSecret
()
{
// 唯一值,用于校验是否为同一个线程的锁
String
randomValue
=
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
);
// 获取锁
boolean
lock
=
redisUtil
.
getLock
(
SJJ_DISTRIBUTED_LOCK
,
randomValue
,
1000
);
if
(
lock
)
{
log
.
info
(
"获取到锁 - 刷新秘钥"
);
try
{
// 刷新秘钥
schedulerTask
.
getRequestSecret
();
}
finally
{
// 释放该线程的锁
redisUtil
.
releaseLock
(
SJJ_DISTRIBUTED_LOCK
,
randomValue
);
}
}
else
{
log
.
info
(
"未获取到锁 - 等待1000毫秒"
);
try
{
TimeUnit
.
MILLISECONDS
.
sleep
(
1000
);
}
catch
(
InterruptedException
e
)
{
e
.
printStackTrace
();
}
}
}
/**
* 招必得平台接口发送请求
...
...
src/main/java/com/mth/requestsecret/util/RedisUtils.java
View file @
a1b6e8e7
...
...
@@ -2,9 +2,11 @@ package com.mth.requestsecret.util;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.data.redis.core.RedisTemplate
;
import
org.springframework.data.redis.core.script.RedisScript
;
import
org.springframework.stereotype.Component
;
import
org.springframework.util.CollectionUtils
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
...
...
@@ -852,4 +854,39 @@ public class RedisUtils {
}
}
// 分布式锁
/**
* 获得锁
*/
public
boolean
getLock
(
String
lockId
,
String
value
,
long
millisecond
)
{
Boolean
success
=
redisTemplate
.
opsForValue
().
setIfAbsent
(
lockId
,
value
,
millisecond
,
TimeUnit
.
MILLISECONDS
);
return
success
!=
null
&&
success
;
}
/**
* 释放锁
*
* @param lockId
*/
public
void
releaseLock
(
String
lockId
)
{
redisTemplate
.
delete
(
lockId
);
}
/**
* 释放锁
*
* @param lockId
* @param value
* @return
*/
public
Long
releaseLock
(
String
lockId
,
String
value
)
{
// 查询如果是同一个锁则释放
String
script
=
"if redis.call('get', KEYS[1]) == KEYS[2] then return redis.call('del', KEYS[1]) else return 0 end"
;
RedisScript
<
Long
>
redisScript
=
RedisScript
.
of
(
script
,
Long
.
class
);
// 执行Lua脚本
return
redisTemplate
.
execute
(
redisScript
,
Arrays
.
asList
(
lockId
,
value
));
}
}
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