Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
zhangjian1949
apollo
提交
f9c69dbb
apollo
项目概览
zhangjian1949
/
apollo
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
apollo
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
f9c69dbb
编写于
7月 11, 2021
作者:
J
Jason Song
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix the issue that release messages might be missed in certain scenarios
上级
9bd47037
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
143 addition
and
4 deletion
+143
-4
CHANGES.md
CHANGES.md
+1
-0
apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/message/ReleaseMessageScanner.java
...p/framework/apollo/biz/message/ReleaseMessageScanner.java
+54
-4
apollo-biz/src/test/java/com/ctrip/framework/apollo/biz/message/ReleaseMessageScannerTest.java
...amework/apollo/biz/message/ReleaseMessageScannerTest.java
+88
-0
未找到文件。
CHANGES.md
浏览文件 @
f9c69dbb
...
...
@@ -59,6 +59,7 @@ Apollo 1.9.0
*
[
support release apollo-client-config-data
](
https://github.com/ctripcorp/apollo/pull/3822
)
*
[
Reduce bootstrap time in the situation with large properties
](
https://github.com/ctripcorp/apollo/pull/3816
)
*
[
docs: English catalog in sidebar
](
https://github.com/ctripcorp/apollo/pull/3831
)
* [fix the issue that release messages might be missed in certain scenarios](https://github.com/ctripcorp/apollo/pull/3819)
------------------
All issues and pull requests are
[
here
](
https://github.com/ctripcorp/apollo/milestone/6?closed=1
)
apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/message/ReleaseMessageScanner.java
浏览文件 @
f9c69dbb
...
...
@@ -16,7 +16,12 @@
*/
package
com.ctrip.framework.apollo.biz.message
;
import
com.google.common.collect.Maps
;
import
java.util.Iterator
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map.Entry
;
import
java.util.Set
;
import
java.util.concurrent.Executors
;
import
java.util.concurrent.ScheduledExecutorService
;
import
java.util.concurrent.TimeUnit
;
...
...
@@ -40,19 +45,22 @@ import com.google.common.collect.Lists;
*/
public
class
ReleaseMessageScanner
implements
InitializingBean
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
ReleaseMessageScanner
.
class
);
private
static
final
int
missingReleaseMessageMaxAge
=
10
;
// hardcoded to 10, could be configured via BizConfig if necessary
@Autowired
private
BizConfig
bizConfig
;
@Autowired
private
ReleaseMessageRepository
releaseMessageRepository
;
private
int
databaseScanInterval
;
private
List
<
ReleaseMessageListener
>
listeners
;
private
ScheduledExecutorService
executorService
;
private
final
List
<
ReleaseMessageListener
>
listeners
;
private
final
ScheduledExecutorService
executorService
;
private
final
Map
<
Long
,
Integer
>
missingReleaseMessages
;
// missing release message id => age counter
private
long
maxIdScanned
;
public
ReleaseMessageScanner
()
{
listeners
=
Lists
.
newCopyOnWriteArrayList
();
executorService
=
Executors
.
newScheduledThreadPool
(
1
,
ApolloThreadFactory
.
create
(
"ReleaseMessageScanner"
,
true
));
missingReleaseMessages
=
Maps
.
newHashMap
();
}
@Override
...
...
@@ -62,6 +70,7 @@ public class ReleaseMessageScanner implements InitializingBean {
executorService
.
scheduleWithFixedDelay
(()
->
{
Transaction
transaction
=
Tracer
.
newTransaction
(
"Apollo.ReleaseMessageScanner"
,
"scanMessage"
);
try
{
scanMissingMessages
();
scanMessages
();
transaction
.
setStatus
(
Transaction
.
SUCCESS
);
}
catch
(
Throwable
ex
)
{
...
...
@@ -108,10 +117,51 @@ public class ReleaseMessageScanner implements InitializingBean {
}
fireMessageScanned
(
releaseMessages
);
int
messageScanned
=
releaseMessages
.
size
();
maxIdScanned
=
releaseMessages
.
get
(
messageScanned
-
1
).
getId
();
long
newMaxIdScanned
=
releaseMessages
.
get
(
messageScanned
-
1
).
getId
();
// check id gaps, possible reasons are release message not committed yet or already rolled back
if
(
newMaxIdScanned
-
maxIdScanned
>
messageScanned
)
{
recordMissingReleaseMessageIds
(
releaseMessages
,
maxIdScanned
);
}
maxIdScanned
=
newMaxIdScanned
;
return
messageScanned
==
500
;
}
private
void
scanMissingMessages
()
{
Set
<
Long
>
missingReleaseMessageIds
=
missingReleaseMessages
.
keySet
();
Iterable
<
ReleaseMessage
>
releaseMessages
=
releaseMessageRepository
.
findAllById
(
missingReleaseMessageIds
);
fireMessageScanned
(
releaseMessages
);
releaseMessages
.
forEach
(
releaseMessage
->
{
missingReleaseMessageIds
.
remove
(
releaseMessage
.
getId
());
});
growAndCleanMissingMessages
();
}
private
void
growAndCleanMissingMessages
()
{
Iterator
<
Entry
<
Long
,
Integer
>>
iterator
=
missingReleaseMessages
.
entrySet
()
.
iterator
();
while
(
iterator
.
hasNext
())
{
Entry
<
Long
,
Integer
>
entry
=
iterator
.
next
();
if
(
entry
.
getValue
()
>
missingReleaseMessageMaxAge
)
{
iterator
.
remove
();
}
else
{
entry
.
setValue
(
entry
.
getValue
()
+
1
);
}
}
}
private
void
recordMissingReleaseMessageIds
(
List
<
ReleaseMessage
>
messages
,
long
startId
)
{
for
(
ReleaseMessage
message
:
messages
)
{
long
currentId
=
message
.
getId
();
if
(
currentId
-
startId
>
1
)
{
for
(
long
i
=
startId
+
1
;
i
<
currentId
;
i
++)
{
missingReleaseMessages
.
putIfAbsent
(
i
,
1
);
}
}
startId
=
currentId
;
}
}
/**
* find largest message id as the current start point
* @return current largest message id
...
...
@@ -125,7 +175,7 @@ public class ReleaseMessageScanner implements InitializingBean {
* Notify listeners with messages loaded
* @param messages
*/
private
void
fireMessageScanned
(
List
<
ReleaseMessage
>
messages
)
{
private
void
fireMessageScanned
(
Iterable
<
ReleaseMessage
>
messages
)
{
for
(
ReleaseMessage
message
:
messages
)
{
for
(
ReleaseMessageListener
listener
:
listeners
)
{
try
{
...
...
apollo-biz/src/test/java/com/ctrip/framework/apollo/biz/message/ReleaseMessageScannerTest.java
浏览文件 @
f9c69dbb
...
...
@@ -18,12 +18,15 @@ package com.ctrip.framework.apollo.biz.message;
import
com.ctrip.framework.apollo.biz.config.BizConfig
;
import
com.google.common.collect.Lists
;
import
com.google.common.collect.Sets
;
import
com.google.common.util.concurrent.SettableFuture
;
import
com.ctrip.framework.apollo.biz.AbstractUnitTest
;
import
com.ctrip.framework.apollo.biz.entity.ReleaseMessage
;
import
com.ctrip.framework.apollo.biz.repository.ReleaseMessageRepository
;
import
java.util.ArrayList
;
import
org.awaitility.Awaitility
;
import
org.junit.Before
;
import
org.junit.Test
;
import
org.mockito.Mock
;
...
...
@@ -31,7 +34,9 @@ import org.springframework.test.util.ReflectionTestUtils;
import
java.util.concurrent.TimeUnit
;
import
static
org
.
awaitility
.
Awaitility
.
await
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertSame
;
import
static
org
.
mockito
.
Mockito
.
when
;
/**
...
...
@@ -54,6 +59,10 @@ public class ReleaseMessageScannerTest extends AbstractUnitTest {
databaseScanInterval
=
100
;
//100 ms
when
(
bizConfig
.
releaseMessageScanIntervalInMilli
()).
thenReturn
(
databaseScanInterval
);
releaseMessageScanner
.
afterPropertiesSet
();
Awaitility
.
reset
();
Awaitility
.
setDefaultTimeout
(
databaseScanInterval
*
5
,
TimeUnit
.
MILLISECONDS
);
Awaitility
.
setDefaultPollInterval
(
databaseScanInterval
,
TimeUnit
.
MILLISECONDS
);
}
@Test
...
...
@@ -91,7 +100,86 @@ public class ReleaseMessageScannerTest extends AbstractUnitTest {
assertEquals
(
anotherMessage
,
anotherListenerMessage
.
getMessage
());
assertEquals
(
anotherId
,
anotherListenerMessage
.
getId
());
}
@Test
public
void
testScanMessageWithGapAndNotifyMessageListener
()
throws
Exception
{
String
someMessage
=
"someMessage"
;
long
someId
=
1
;
ReleaseMessage
someReleaseMessage
=
assembleReleaseMessage
(
someId
,
someMessage
);
String
someMissingMessage
=
"someMissingMessage"
;
long
someMissingId
=
2
;
ReleaseMessage
someMissingReleaseMessage
=
assembleReleaseMessage
(
someMissingId
,
someMissingMessage
);
String
anotherMessage
=
"anotherMessage"
;
long
anotherId
=
3
;
ReleaseMessage
anotherReleaseMessage
=
assembleReleaseMessage
(
anotherId
,
anotherMessage
);
String
anotherMissingMessage
=
"anotherMissingMessage"
;
long
anotherMissingId
=
4
;
ReleaseMessage
anotherMissingReleaseMessage
=
assembleReleaseMessage
(
anotherMissingId
,
anotherMissingMessage
);
long
someRolledBackId
=
5
;
String
yetAnotherMessage
=
"yetAnotherMessage"
;
long
yetAnotherId
=
6
;
ReleaseMessage
yetAnotherReleaseMessage
=
assembleReleaseMessage
(
yetAnotherId
,
yetAnotherMessage
);
ArrayList
<
ReleaseMessage
>
receivedMessage
=
Lists
.
newArrayList
();
SettableFuture
<
ReleaseMessage
>
someListenerFuture
=
SettableFuture
.
create
();
ReleaseMessageListener
someListener
=
(
message
,
channel
)
->
receivedMessage
.
add
(
message
);
releaseMessageScanner
.
addMessageListener
(
someListener
);
when
(
releaseMessageRepository
.
findFirst500ByIdGreaterThanOrderByIdAsc
(
0L
)).
thenReturn
(
Lists
.
newArrayList
(
someReleaseMessage
));
await
().
untilAsserted
(()
->
{
assertEquals
(
1
,
receivedMessage
.
size
());
assertSame
(
someReleaseMessage
,
receivedMessage
.
get
(
0
));
});
when
(
releaseMessageRepository
.
findFirst500ByIdGreaterThanOrderByIdAsc
(
someId
)).
thenReturn
(
Lists
.
newArrayList
(
anotherReleaseMessage
));
await
().
untilAsserted
(()
->
{
assertEquals
(
2
,
receivedMessage
.
size
());
assertSame
(
someReleaseMessage
,
receivedMessage
.
get
(
0
));
assertSame
(
anotherReleaseMessage
,
receivedMessage
.
get
(
1
));
});
when
(
releaseMessageRepository
.
findAllById
(
Sets
.
newHashSet
(
someMissingId
)))
.
thenReturn
(
Lists
.
newArrayList
(
someMissingReleaseMessage
));
await
().
untilAsserted
(()
->
{
assertEquals
(
3
,
receivedMessage
.
size
());
assertSame
(
someReleaseMessage
,
receivedMessage
.
get
(
0
));
assertSame
(
anotherReleaseMessage
,
receivedMessage
.
get
(
1
));
assertSame
(
someMissingReleaseMessage
,
receivedMessage
.
get
(
2
));
});
when
(
releaseMessageRepository
.
findFirst500ByIdGreaterThanOrderByIdAsc
(
anotherId
)).
thenReturn
(
Lists
.
newArrayList
(
yetAnotherReleaseMessage
));
await
().
untilAsserted
(()
->
{
assertEquals
(
4
,
receivedMessage
.
size
());
assertSame
(
someReleaseMessage
,
receivedMessage
.
get
(
0
));
assertSame
(
anotherReleaseMessage
,
receivedMessage
.
get
(
1
));
assertSame
(
someMissingReleaseMessage
,
receivedMessage
.
get
(
2
));
assertSame
(
yetAnotherReleaseMessage
,
receivedMessage
.
get
(
3
));
});
when
(
releaseMessageRepository
.
findAllById
(
Sets
.
newHashSet
(
anotherMissingId
,
someRolledBackId
)))
.
thenReturn
(
Lists
.
newArrayList
(
anotherMissingReleaseMessage
));
await
().
untilAsserted
(()
->
{
assertEquals
(
5
,
receivedMessage
.
size
());
assertSame
(
someReleaseMessage
,
receivedMessage
.
get
(
0
));
assertSame
(
anotherReleaseMessage
,
receivedMessage
.
get
(
1
));
assertSame
(
someMissingReleaseMessage
,
receivedMessage
.
get
(
2
));
assertSame
(
yetAnotherReleaseMessage
,
receivedMessage
.
get
(
3
));
assertSame
(
anotherMissingReleaseMessage
,
receivedMessage
.
get
(
4
));
});
}
private
ReleaseMessage
assembleReleaseMessage
(
long
id
,
String
message
)
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录