Skip to content

Commit a964e0e

Browse files
committed
Update test cases and demo for async support
Signed-off-by: Eric Zhao <[email protected]>
1 parent d798794 commit a964e0e

File tree

7 files changed

+613
-36
lines changed

7 files changed

+613
-36
lines changed

sentinel-core/src/main/java/com/alibaba/csp/sentinel/context/ContextUtil.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public static Context getContext() {
190190
* @return old context
191191
* @since 0.2.0
192192
*/
193-
private static Context replaceContext(Context newContext) {
193+
static Context replaceContext(Context newContext) {
194194
Context backupContext = contextHolder.get();
195195
if (newContext == null) {
196196
contextHolder.remove();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/*
2+
* Copyright 1999-2018 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.alibaba.csp.sentinel;
17+
18+
import java.util.Set;
19+
import java.util.concurrent.ExecutorService;
20+
import java.util.concurrent.Executors;
21+
import java.util.concurrent.TimeUnit;
22+
23+
import com.alibaba.csp.sentinel.context.ContextUtil;
24+
import com.alibaba.csp.sentinel.node.DefaultNode;
25+
import com.alibaba.csp.sentinel.node.EntranceNode;
26+
import com.alibaba.csp.sentinel.node.Node;
27+
import com.alibaba.csp.sentinel.slots.block.BlockException;
28+
29+
import org.junit.After;
30+
import org.junit.Test;
31+
32+
import static org.junit.Assert.*;
33+
34+
/**
35+
* Integration test for asynchronous entry, including common scenarios.
36+
*
37+
* @author Eric Zhao
38+
*/
39+
public class AsyncEntryIntegrationTest {
40+
41+
private final ExecutorService pool = Executors.newFixedThreadPool(10);
42+
43+
private void anotherAsync() {
44+
try {
45+
final AsyncEntry entry = SphU.asyncEntry("test-another-async");
46+
47+
runAsync(new Runnable() {
48+
@Override
49+
public void run() {
50+
ContextUtil.runOnContext(entry.getAsyncContext(), new Runnable() {
51+
@Override
52+
public void run() {
53+
try {
54+
TimeUnit.SECONDS.sleep(2);
55+
anotherSyncInAsync();
56+
System.out.println("Async result: 666");
57+
} catch (InterruptedException e) {
58+
// Ignore.
59+
} finally {
60+
entry.exit();
61+
}
62+
}
63+
});
64+
}
65+
});
66+
} catch (BlockException ex) {
67+
ex.printStackTrace();
68+
}
69+
}
70+
71+
private void fetchSync() {
72+
Entry entry = null;
73+
try {
74+
entry = SphU.entry("test-sync");
75+
} catch (BlockException ex) {
76+
ex.printStackTrace();
77+
} finally {
78+
if (entry != null) {
79+
entry.exit();
80+
}
81+
}
82+
}
83+
84+
private void fetchSyncInAsync() {
85+
Entry entry = null;
86+
try {
87+
entry = SphU.entry("test-sync-in-async");
88+
} catch (BlockException ex) {
89+
ex.printStackTrace();
90+
} finally {
91+
if (entry != null) {
92+
entry.exit();
93+
}
94+
}
95+
}
96+
97+
public void anotherSyncInAsync() {
98+
Entry entry = null;
99+
try {
100+
entry = SphU.entry("test-another-in-async");
101+
} catch (BlockException ex) {
102+
ex.printStackTrace();
103+
} finally {
104+
if (entry != null) {
105+
entry.exit();
106+
}
107+
}
108+
}
109+
110+
private void doAsyncThenSync() {
111+
try {
112+
// First we call an asynchronous resource.
113+
final AsyncEntry entry = SphU.asyncEntry("test-async");
114+
this.invoke("abc", new Consumer<String>() {
115+
@Override
116+
public void accept(final String resp) {
117+
// The thread is different from original caller thread for async entry.
118+
// So we need to wrap in the async context so that nested sync invocation entry
119+
// can be linked to the parent asynchronous entry.
120+
ContextUtil.runOnContext(entry.getAsyncContext(), new Runnable() {
121+
@Override
122+
public void run() {
123+
try {
124+
// In the callback, we do another async invocation under the async context.
125+
anotherAsync();
126+
127+
System.out.println(resp);
128+
129+
// Then we do a sync entry under current async context.
130+
fetchSyncInAsync();
131+
} finally {
132+
// Exit the async entry.
133+
entry.exit();
134+
}
135+
}
136+
});
137+
}
138+
});
139+
// Then we call a sync resource.
140+
fetchSync();
141+
} catch (BlockException ex) {
142+
// Request blocked, handle the exception.
143+
ex.printStackTrace();
144+
}
145+
}
146+
147+
@Test
148+
public void testAsyncEntryUnderSyncEntry() throws Exception {
149+
// Expected invocation chain:
150+
// EntranceNode: machine-root
151+
// -EntranceNode: async-context
152+
// --test-top
153+
// ---test-async
154+
// ----test-sync-in-async
155+
// ----test-another-async
156+
// -----test-another-in-async
157+
// ---test-sync
158+
ContextUtil.enter(contextName, origin);
159+
Entry entry = null;
160+
try {
161+
entry = SphU.entry("test-top");
162+
doAsyncThenSync();
163+
} catch (BlockException ex) {
164+
ex.printStackTrace();
165+
} finally {
166+
if (entry != null) {
167+
entry.exit();
168+
}
169+
ContextUtil.exit();
170+
}
171+
172+
TimeUnit.SECONDS.sleep(10);
173+
testTreeCorrect();
174+
}
175+
176+
private void testTreeCorrect() {
177+
DefaultNode root = Constants.ROOT;
178+
Set<Node> childListForRoot = root.getChildList();
179+
assertEquals(1, childListForRoot.size());
180+
for (Node node : childListForRoot) {
181+
EntranceNode en = (EntranceNode) node;
182+
assertEquals(contextName, en.getId().getName());
183+
}
184+
// TODO: check child tree
185+
}
186+
187+
@After
188+
public void shutdown() {
189+
pool.shutdownNow();
190+
ContextUtil.exit();
191+
}
192+
193+
private void runAsync(Runnable f) {
194+
// In Java 8, we can use CompletableFuture.runAsync(f) instead.
195+
pool.submit(f);
196+
}
197+
198+
private void invoke(final String arg, final Consumer<String> handler) {
199+
runAsync(new Runnable() {
200+
@Override
201+
public void run() {
202+
try {
203+
TimeUnit.SECONDS.sleep(3);
204+
String resp = arg + ": " + System.currentTimeMillis();
205+
handler.accept(resp);
206+
207+
} catch (Exception ex) {
208+
ex.printStackTrace();
209+
}
210+
}
211+
});
212+
}
213+
214+
private interface Consumer<T> {
215+
void accept(T t);
216+
}
217+
218+
private final String contextName = "async-context";
219+
private final String origin = "originA";
220+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.alibaba.csp.sentinel;
2+
3+
import com.alibaba.csp.sentinel.context.Context;
4+
import com.alibaba.csp.sentinel.context.ContextUtil;
5+
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
6+
7+
import org.junit.Test;
8+
9+
import static org.junit.Assert.*;
10+
11+
/**
12+
* Test cases for {@link AsyncEntry}.
13+
*
14+
* @author Eric Zhao
15+
* @since 0.2.0
16+
*/
17+
public class AsyncEntryTest {
18+
19+
@Test
20+
public void testCleanCurrentEntryInLocal() {
21+
final String contextName = "abc";
22+
try {
23+
ContextUtil.enter(contextName);
24+
Context curContext = ContextUtil.getContext();
25+
AsyncEntry entry = new AsyncEntry(new StringResourceWrapper("testCleanCurrentEntryInLocal", EntryType.OUT),
26+
null, curContext);
27+
28+
assertSame(entry, curContext.getCurEntry());
29+
30+
entry.cleanCurrentEntryInLocal();
31+
assertNotSame(entry, curContext.getCurEntry());
32+
} finally {
33+
ContextUtil.getContext().setCurEntry(null);
34+
ContextUtil.exit();
35+
}
36+
}
37+
38+
@Test
39+
public void testInitAndGetAsyncContext() {
40+
final String contextName = "abc";
41+
final String origin = "xxx";
42+
try {
43+
ContextUtil.enter(contextName, origin);
44+
Context curContext = ContextUtil.getContext();
45+
AsyncEntry entry = new AsyncEntry(new StringResourceWrapper("testInitAndGetAsyncContext", EntryType.OUT),
46+
null, curContext);
47+
assertNull(entry.getAsyncContext());
48+
49+
entry.initAsyncContext();
50+
System.out.println(curContext.getName());
51+
System.out.println(curContext.getOrigin());
52+
53+
Context asyncContext = entry.getAsyncContext();
54+
assertNotNull(asyncContext);
55+
assertEquals(contextName, asyncContext.getName());
56+
assertEquals(origin, asyncContext.getOrigin());
57+
assertSame(curContext.getEntranceNode(), asyncContext.getEntranceNode());
58+
assertSame(entry, asyncContext.getCurEntry());
59+
assertTrue(asyncContext.isAsync());
60+
} finally {
61+
ContextUtil.getContext().setCurEntry(null);
62+
ContextUtil.exit();
63+
}
64+
}
65+
66+
@Test(expected = IllegalStateException.class)
67+
public void testDuplicateInitAsyncContext() {
68+
Context context = new Context(null, "abc");
69+
AsyncEntry entry = new AsyncEntry(new StringResourceWrapper("testDuplicateInitAsyncContext", EntryType.OUT),
70+
null, context);
71+
entry.initAsyncContext();
72+
73+
// Duplicate init.
74+
entry.initAsyncContext();
75+
}
76+
}

sentinel-core/src/test/java/com/alibaba/csp/sentinel/ContextTest.java

-35
This file was deleted.

0 commit comments

Comments
 (0)