Skip to content

bugfix: support async for spring mvc #2700

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

icodening
Copy link
Contributor

Describe what this PR does / why we need it

解决了Spring MVC下不支持异步的问题

Does this pull request fix one issue?

Fixes #2447

Describe how you did it

Sentinel在1.7.0之后支持异步方式的asyncEntry,会在asyncEntry之后移除Context但不exit。而在SpringMVC中,可以将asyncEntry方法返回值(一个AsyncEntry)放置HttpServletRequest的请求属性中,使其可以在AbstractSentinelInterceptor.afterCompletion时可以正常取出AsyncEntry,并正常exit。

PS:AsyncEntry会在初始化后移除线程上下文的Context,直接通过其内部字段"context"与之关联

// So we need to remove current async entry from current context.
asyncEntry.cleanCurrentEntryInLocal();

Describe how to verify it

1.构造两个异步处理器,以及一个同步处理器,例子如下

@RequestMapping("/async1")
public DeferredResult<String> async1() {
    DeferredResult<String> deferredResult = new DeferredResult<>();
    new Thread(() -> {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        deferredResult.setResult("Hello World from async1");
    }).start();
    return deferredResult;
}

@RequestMapping("/async2")
public DeferredResult<String> async2() {
    DeferredResult<String> stringDeferredResult = new DeferredResult<>();
    new Thread(() -> {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        stringDeferredResult.setResult("Hello World from async2");
    }).start();
    return stringDeferredResult;
}

@RequestMapping("/sync")
public Object sync(){
    return "sync success";
}

2.配置servlet容器线程池为1,目的是为了方便测试

#Tomcat例子
server:
  tomcat:
    max-threads: 1

3.不间断连续交替请求 "/async1","/async2","/sync", 观察日志。

  • 3.1 如果没有做任何修改,日志中不会出现任何报错,但可以通过以下命令强制触发一次GC获取存活的对象信息
jmap -dump:format=b,file=demo.hprof,live PID

image

image

可以看到FGC后依然存在以上对象

当只有increaseReferece -1而未使用SphU.asyncEntry时,将在exit会出现如下错误

com.alibaba.csp.sentinel.ErrorEntryFreeException: The order of entry exit can't be paired with the order of entry, current entry in context: </async2>, but expected: </async1>
	at com.alibaba.csp.sentinel.CtEntry.exitForContext(CtEntry.java:105) ~[sentinel-core-1.8.3.jar:1.8.3]
	at com.alibaba.csp.sentinel.CtEntry.trueExit(CtEntry.java:145) ~[sentinel-core-1.8.3.jar:1.8.3]
	at com.alibaba.csp.sentinel.CtEntry.exit(CtEntry.java:66) ~[sentinel-core-1.8.3.jar:1.8.3]
	at com.alibaba.csp.sentinel.Entry.exit(Entry.java:87) ~[sentinel-core-1.8.3.jar:1.8.3]
  • 3.2当完全修复后,日志不再出现报错,且heapdump文件也不会存在Context和Entry,正常结果如下.

image

image

image

Special notes for reviews

为防止因不同容器对Servlet异步的实现差异导致预期结果不一致,已在TomcatUndertowJetty三个Servlet容器下测试未发现问题

@sczyh30 sczyh30 added to-review To review area/integrations Issues or PRs related to integrations with open-source components kind/bug Category issues or prs related to bug. labels Apr 29, 2022
@Qsir-Q
Copy link

Qsir-Q commented Nov 11, 2024

可以看到FGC后依然存在以上对象
当只有increaseReferece -1而未使用SphU.asyncEntry时,将在exit会出现如下错误

请问一下:

  1. 为什么在FGC之后才会出现这个异常
  2. 当只有increaseReferece -1而未使用SphU.asyncEntry时 这是什么意思呢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/integrations Issues or PRs related to integrations with open-source components kind/bug Category issues or prs related to bug. to-review To review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

sentinel springwebmvc不支持异步async请求
3 participants