webentwicklung-frage-antwort-db.com.de

Sie müssen den asynchronen Dienst aufgeben, ohne die Ausführungszeit zu erhöhen

meine Anwendungen sollten 2 Kernendpunkte haben: Push , Pull zum Senden und Abrufen von Daten.

Die Pull-Operation sollte asynchron arbeiten und DeferredResult ergeben. Wenn der Benutzer den Pull-Service über einen Rest anruft, wird neues DefferedResult erstellt und in Map<Long, DefferedResult> results = new ConcurrentHashMap<>() gespeichert, wo auf neue Daten gewartet wird oder bis das Zeitlimit abgelaufen ist.

Benutzer des Push-Operationsaufrufs auch über Ruhe, und diese Operation prüft die Ergebniskarte nach dem Empfänger der Daten, die von dieser Operation gepusht werden. Wenn die Karte das Ergebnis des Empfängers enthält, werden diese Daten auf sein Ergebnis gesetzt, und DefferedResult wird zurückgegeben.

Hier ist der Basiscode:

@Service
public class FooServiceImpl {
    Map<Long, DefferedResult> results = new ConcurrentHashMap<>();

    @Transactional
    @Override
    public DeferredResult<String> pull(Long userId) {
        // here is database call, String data = fooRepository.getNewData(); where I check if there are some new data in database, and if there are, just return it, if not add deferred result into collection to wait for it
        DeferredResult<String> newResult = new DeferredResult<>(5000L);
        results.putIfAbsent(userId, newResult);
        newResult.onCompletion(() -> results.remove(userId));

        // if (data != null)
        //      newResult.setResult(data);

        return newResult;
    }

    @Transactional
    @Override
    public void Push(String data, Long recipientId) {
        // fooRepository.save(data, recipientId);
        if (results.containsKey(recipientId)) {
            results.get(recipientId).setResult(data);
        }
    }
}

Code funktioniert wie ich erwartet Problem ist, dass sollte auch für mehrere Benutzer funktionieren. Ich schätze, die maximal aktiven Benutzer, die den Pull-Vorgang aufrufen, werden maximal 1000 sein. Daher dauert jeder Aufruf von Pull maximal 5 Sekunden, wenn ich DefferedResult setze. Dies ist jedoch nicht der Fall.

Wie Sie im Bild sehen können, können Sie, wenn ich sofort mehrmals den Rest des Pull-Vorgangs von meinem Javascript-Client anrufe, sehen, dass Aufgaben nacheinander statt gleichzeitig ausgeführt werden. Aufgaben, die ich zuletzt ausgelöst habe, dauern etwa 25 Sekunden, aber ich brauche, wenn 1000 Benutzer gleichzeitig den Pull-Vorgang ausführen, sollte dieser Vorgang maximal 5 Sekunden + Latenzzeit in Anspruch nehmen.

 enter image description here

Wie kann ich meine App so konfigurieren, dass sie diese Aufgaben gleichzeitig ausführt und sicherstellen, dass jede Aufgabe etwa 5 Sekunden oder weniger dauert (wenn ein anderer Benutzer etwas an den wartenden Benutzer sendet)? Ich habe versucht, diese Konfiguration in die Eigenschaftsdatei einzufügen:

server.Tomcat.max-threads=1000

und auch diese Konfiguration:

@Configuration
public class AsyncConfig extends AsyncSupportConfigurer {

    @Override
    protected AsyncTaskExecutor getTaskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(1000);
        taskExecutor.initialize();
        return taskExecutor;
    }
}

Aber es hat nicht geholfen, immer noch dasselbe Ergebnis. Kannst du mir bitte bei der Konfiguration helfen? Danke im Rat.

EDIT:

So nenne ich diesen Service von eckig:

this.http.get<any>(this.url, {params})
  .subscribe((data) => {
    console.log('s', data);
  }, (error) => {
    console.log('e', error);
  });

Wenn ich es versucht habe, rufen Sie es mehrmals mit reinem JS-Code wie folgt an:

function httpGet()
{
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.open( "GET", 'http://localhost:8080/api/pull?id=1', true );
    xmlHttp.send( null );
    return xmlHttp.responseText;
}
setInterval(httpGet, 500);

es wird jeden Anforderungsaufruf viel schneller ausführen (etwa 7 Sekunden). Ich hatte erwartet, dass das Erhöhen von Datenbankaufrufen im Dienst verursacht wird, aber immer noch besser als 25 Sekunden. Habe ich etwas falsch, wenn Sie diesen Service in eckig anrufen?

EDIT 2:

Ich habe eine andere Testform ausprobiert und anstelle des Browsers jMeter verwendet. Ich führe 100 Anfragen in 100 Threads aus und hier ist das Ergebnis:

 enter image description here

Wie Sie sehen, werden Anforderungen um 10 fortgesetzt, und nach Erreichen von 50 Anforderungen löst die Anwendung eine Ausnahme aus:

Java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms.
    at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.Java:667) ~[HikariCP-2.7.8.jar:na]
    at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.Java:183) ~[HikariCP-2.7.8.jar:na]
    at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.Java:148) ~[HikariCP-2.7.8.jar:na]
    at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.Java:128) ~[HikariCP-2.7.8.jar:na]
    at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.Java:122) ~[hibernate-core-5.2.16.Final.jar:5.2.16.Final]
    at org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.Java:35) ~[hibernate-core-5.2.16.Final.jar:5.2.16.Final]
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.Java:106) ~[hibernate-core-5.2.16.Final.jar:5.2.16.Final]
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getPhysicalConnection(LogicalConnectionManagedImpl.Java:136) ~[hibernate-core-5.2.16.Final.jar:5.2.16.Final]
    at org.hibernate.internal.SessionImpl.connection(SessionImpl.Java:523) ~[hibernate-core-5.2.16.Final.jar:5.2.16.Final]
    at Sun.reflect.GeneratedMethodAccessor61.invoke(Unknown Source) ~[na:na]
    at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43) ~[na:1.8.0_171]
    at Java.lang.reflect.Method.invoke(Method.Java:498) ~[na:1.8.0_171]
    at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.Java:223) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.Java:207) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle.doGetConnection(HibernateJpaDialect.Java:391) ~[spring-orm-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.beginTransaction(HibernateJpaDialect.Java:154) ~[spring-orm-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.Java:400) ~[spring-orm-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.Java:378) ~[spring-tx-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.Java:474) ~[spring-tx-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.Java:289) ~[spring-tx-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.Java:98) ~[spring-tx-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:185) ~[spring-aop-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.Java:92) ~[spring-aop-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:185) ~[spring-aop-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.Java:689) ~[spring-aop-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at sk.moe.zoya.service.impl.FooServiceImpl$$EnhancerBySpringCGLIB$$ebab570a.pull(<generated>) ~[classes/:na]
    at sk.moe.zoya.web.FooController.pull(FooController.Java:25) ~[classes/:na]
    at Sun.reflect.GeneratedMethodAccessor60.invoke(Unknown Source) ~[na:na]
    at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43) ~[na:1.8.0_171]
    at Java.lang.reflect.Method.invoke(Method.Java:498) ~[na:1.8.0_171]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.Java:209) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.Java:136) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.Java:102) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.Java:877) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.Java:783) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.Java:87) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.Java:991) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.Java:925) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.Java:974) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.Java:866) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.Java:635) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.Java:851) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.Java:742) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:231) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:166) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.Tomcat.websocket.server.WsFilter.doFilter(WsFilter.Java:52) ~[Tomcat-embed-websocket-8.5.29.jar:8.5.29]
    at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:193) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:166) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.Java:99) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:193) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:166) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.Java:109) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:193) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:166) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.Java:81) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:193) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:166) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.Java:200) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
    at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:193) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:166) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.Java:198) ~[Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.catalina.core.StandardContextValve.invoke(StandardContextValve.Java:96) [Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.Java:496) [Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.catalina.core.StandardHostValve.invoke(StandardHostValve.Java:140) [Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.Java:81) [Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.Java:87) [Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.Java:342) [Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.coyote.http11.Http11Processor.service(Http11Processor.Java:803) [Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.Java:66) [Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.Java:790) [Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.Tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.Java:1459) [Tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.Apache.Tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.Java:49) [Tomcat-embed-core-8.5.29.jar:8.5.29]
    at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1149) [na:1.8.0_171]
    at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:624) [na:1.8.0_171]
    at org.Apache.Tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.Java:61) [Tomcat-embed-core-8.5.29.jar:8.5.29]
    at Java.lang.Thread.run(Thread.Java:748) [na:1.8.0_171]

2018-06-02 13:21:47.163  WARN 26978 --- [io-8080-exec-48] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 0, SQLState: null
2018-06-02 13:21:47.163  WARN 26978 --- [io-8080-exec-40] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 0, SQLState: null
2018-06-02 13:21:47.163 ERROR 26978 --- [io-8080-exec-48] o.h.engine.jdbc.spi.SqlExceptionHelper   : HikariPool-1 - Connection is not available, request timed out after 30000ms.
2018-06-02 13:21:47.163 ERROR 26978 --- [io-8080-exec-40] o.h.engine.jdbc.spi.SqlExceptionHelper   : HikariPool-1 - Connection is not available, request timed out after 30000ms.
2018-06-02 13:21:47.164 ERROR 26978 --- [io-8080-exec-69] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection] with root cause

Ich kommentiere auch den Code, bei dem ich Repositorys verwende, um sicherzustellen, dass nichts mit der Datenbank und demselben Ergebnis verbunden ist. Ich habe auch eine einzige Benutzer-ID für jede Anfrage mit AtomicLong-Klasse festgelegt.

EDIT 3:

Ich erfahre, wenn ich auch @Transactional kommentiere, alles funktioniert gut! Können Sie mir also sagen, wie Sie die Transaktionen von spring für eine große Anzahl von Operationen ohne Verzögerung festlegen können?

Ich habe spring.datasource.maximumPoolSize=1000 hinzugefügt, um die Poolgröße zu erhöhen, was meiner Meinung nach sollte. Das einzige Problem ist, wie Methoden mit @Transactional beschleunigt werden können.

Jede Aufruf-zu-Pull-Methode wird mit @Transactional annotiert, da ich zunächst Daten aus der Datenbank laden und prüfen muss, ob neue Daten vorhanden sind, da ja, ich muss kein verzögertes Ergebnis erstellen. Push-Methoden müssen ebenfalls mit @Transaction annotiert werden, da ich zuerst empfangene Daten in der Datenbank speichern und als nächsten Wert auf wartende Ergebnisse setzen muss. Für meine Daten verwende ich Postgres.

10
Denis Stephanov

Hier scheint das Problem zu sein, dass Ihnen die Verbindungen im Datenbankpool ausgehen.

Sie haben Ihre Methode mit @Transaction markiert, aber Ihr Controller erwartet auch, dass das Ergebnis der Methode, d. H. Die DeferredResult, so bald wie möglich übermittelt wird, sodass der Thread freigegeben wird.

Folgendes passiert nun, wenn Sie eine Anfrage ausführen:

  • Die Funktion @Transaction ist in einem Spring-Proxy implementiert, der eine Verbindung öffnen, die Methode subject aufrufen und dann die Transaktion festschreiben oder zurücksetzen muss.
  • Wenn Ihr Controller die fooService.pull -Methode aufruft, ruft er daher tatsächlich einen Proxy auf.
  • Der Proxy muss zuerst eine Verbindung aus dem Pool anfordern und ruft dann Ihre Dienstmethode auf, die innerhalb dieser Transaktion einige Datenbankoperationen ausführt. Schließlich muss die Transaktion festgeschrieben oder zurückgesetzt und die Verbindung zum Pool zurückgegeben werden.
  • Nach alledem gibt Ihre Methode eine DeferredResult zurück, die dann zur Rückgabe an den Controller übergeben wird.

Das Problem ist nun, dass DeferredResult so konzipiert ist, dass es asynchron verwendet werden sollte. Mit anderen Worten, es wird erwartet, dass das Versprechen später in einem anderen Thread gelöst wird, und wir sollen den Anfragethread so bald wie möglich freigeben.

In der Tat, Frühling Dokumentation auf DeferredResult sagt:

@GetMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
    DeferredResult<String> deferredResult = new DeferredResult<String>();
    // Save the deferredResult somewhere..
    return deferredResult;
}

// From some other thread...
deferredResult.setResult(data);

Das Problem in Ihrem Code besteht genau darin, dass die DeferredResult im selben Anfragethread gelöst wird.

Wenn der Spring-Proxy eine Verbindung zum Datenbankpool anfordert und Sie Ihre Tests mit hoher Auslastung durchführen, stellen viele Anforderungen fest, dass der Pool voll ist und keine Verbindungen verfügbar sind. Die Anforderung wird in die Warteschleife gestellt, aber zu diesem Zeitpunkt wurde Ihre DeferredResult noch nicht erstellt, sodass die Timeout-Funktion nicht vorhanden ist.

Ihre Anfrage wartet im Wesentlichen darauf, dass eine Verbindung aus dem Datenbankpool verfügbar wird. Nehmen wir an, es vergehen 5 Sekunden, dann erhält die Anfrage eine Verbindung und Sie erhalten DeferredResult, mit der der Controller die Antwort verarbeitet. Irgendwann, 5 Sekunden später, tritt ein Timeout auf. Sie müssen also Ihre Zeit für das Warten auf eine Verbindung aus dem Pool und Ihre Zeit für das Warten auf die Auflösung der DeferredResult addieren.

Aus diesem Grund stellen Sie wahrscheinlich fest, dass sich die Anforderungszeit beim Testen mit JMeter allmählich erhöht, wenn Verbindungen aus dem Datenbankpool entfernt werden.

Sie können die Protokollierung für den Thread-Pool aktivieren, indem Sie die folgende Datei application.properties hinzufügen:

logging.level.com.zaxxer.hikari=DEBUG

Sie können auch die Größe Ihres Datenbankpools konfigurieren und sogar eine gewisse JMX-Unterstützung hinzufügen, damit Sie ihn von Java Mission Control aus überwachen können:

spring.datasource.hikari.maximumPoolSize=10
spring.datasource.hikari.registerMbeans=true

Mithilfe der JMX-Unterstützung können Sie sehen, wie der Datenbankpool erschöpft wird.

Der Trick hier besteht darin, die Logik, die das Versprechen löst, in einen anderen Thread zu verschieben:

@Override
public DeferredResult pull(Long previousId, String username) {


    DeferredResult result = createPollingResult(previousId, username);

    CompletableFuture.runAsync(() -> {
        //this is where you encapsulate your db transaction
        List<MessageDTO> messages = messageService.findRecents(previousId, username); // should be final or effective final
        if (messages.isEmpty()) {
           pollingResults.putIfAbsent(username, result);
        } else {
           result.setResult(messages);
        }
    });

    return result;
}

Auf diese Weise wird Ihre DeferredResult sofort zurückgegeben, und Spring kann die Magie der asynchronen Anforderungsverarbeitung ausführen, während der wertvolle Tomcat-Thread freigegeben wird.

2
Edwin Dalorzo

Wie schon viele erwähnt haben, ist es nicht richtig, die Leistung zu testen. Sie haben zu einem bestimmten Zeitpunkt wie in XMLHttpRequest darum gebeten, automatisierte Anforderungen auszuführen. Sie können interval von Observable verwenden als:

{Observable} aus "rxjs/Observable" importieren;

{Subscription} aus "rxjs/Subscription" importieren;

private _intervalSubscription: Subscription;

ngOnInit() {
    this._intervalSubscription = Observable.interval(500).subscribe(x => {
        this.getDataFromServer();
    });
}

ngOnDestroy(): void {
    this._intervalSubscription.unsubscribe();
}

getDataFromServer() {
    // do your network call
    this.http.get<any>(this.url, {params})
                .subscribe((data) => {
                    console.log('s', data);
                }, (error) => {
                    console.log('e', error);
                }); 
}

Dies ist die bestmögliche Methode zum Abrufen von Clientseite.

EDIT 1

private prevRequestTime: number;

ngAfterViewInit(): void {
    this.getDataFromServer();
}

getDataFromServer() {
    this.prevRequestTime = Date.now();
    // do your network call
    this.http.get<any>(this.url, {params})
            .subscribe((data) => {
                console.log('s', data);
                this.scheduleRequestAgain();
            }, (error) => {
                console.log('e', error);
                this.scheduleRequestAgain();
            }); 
}

scheduleRequestAgain() {
    let diff = Date.now() - this.prevRequestTime;
    setTimeout(this.getDataFromServer(), diff);
}
1

Ich denke, Sie brauchen ein Produzenten- und Konsumentenstrukturmodell. Ich schreibe Code für dich. ich hoffe es hilft dir.

Dies ist Beispielcode:

DeferredResultStrore

@Component
public class DeferredResultStrore {

    private Queue<DeferredResult<String>> responseBodyQueue;
    private HashMap<String, List<DeferredResult<InterfaceModel>>> groupMap;
    private final long resultTimeOut;

    public DeferredResultStrore() {
        responseBodyQueue = new LinkedBlockingQueue<DeferredResult<String>>();
        groupMap = new HashMap<String, List<DeferredResult<InterfaceModel>>>();
        // write time.
        resultTimeOut = 1000 * 60 * 60;
    }

    public Queue<DeferredResult<String>> getResponseBodyQueue() {
        return responseBodyQueue;
    }

    public HashMap<String, List<DeferredResult<InterfaceModel>>> getGroupMap() {
        return groupMap;
    }

    public long getResultTimeOut() {
        return resultTimeOut;
    }

}

DeferredResultService

public interface DeferredResultService {

    public DeferredResult<?> biteResponse(HttpServletResponse resp, HttpServletRequest req);

    public DeferredResult<?> biteGroupResponse(String key, HttpServletResponse resp);

}

DeferredResultServiceImpl

@Service
public class DeferredResultServiceImpl implements DeferredResultService {

    @Autowired
    private DeferredResultStrore deferredResultStore;

    @Override
    public DeferredResult<?> biteResponse(final HttpServletResponse resp, HttpServletRequest req) {

        final DeferredResult<String> defResult = new DeferredResult<String>(deferredResultStore.getResultTimeOut());

        removeObserver(resp, defResult, null);

        deferredResultStore.getResponseBodyQueue().add(defResult);

        return defResult;
    }

    @Override
    public DeferredResult<?> biteGroupResponse(String key, final HttpServletResponse resp) {

        final DeferredResult<InterfaceModel> defResult = new DeferredResult<InterfaceModel>(
                deferredResultStore.getResultTimeOut());

        List<DeferredResult<InterfaceModel>> defResultList = null;

        removeObserver(resp, defResult, key);

        if (deferredResultStore.getGroupMap().containsKey(key)) {

            defResultList = deferredResultStore.getGroupMap().get(key);
            defResultList.add(defResult);

        } else {

            defResultList = new ArrayList<DeferredResult<InterfaceModel>>();
            defResultList.add(defResult);
            deferredResultStore.getGroupMap().put(key, defResultList);

        }

        return defResult;
    }

    private void removeObserver(final HttpServletResponse resp, final DeferredResult<?> defResult, final String key) {

        defResult.onCompletion(new Runnable() {
            public void run() {
                if (key != null) {
                    List<DeferredResult<InterfaceModel>> defResultList = deferredResultStore.getGroupMap().get(key);

                    if (defResultList != null) {
                        for (DeferredResult<InterfaceModel> deferredResult : defResultList) {
                            if (deferredResult.hashCode() == defResult.hashCode()) {
                                defResultList.remove(deferredResult);
                            }
                        }
                    }

                } else {
                    if (!deferredResultStore.getResponseBodyQueue().isEmpty()) {
                        deferredResultStore.getResponseBodyQueue().remove(defResult);
                    }
                }
            }
        });

        defResult.onTimeout(new Runnable() {
            public void run() {
                // 206
                resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

                if (key != null) {
                    List<DeferredResult<InterfaceModel>> defResultList = deferredResultStore.getGroupMap().get(key);

                    if (defResultList != null) {
                        for (DeferredResult<InterfaceModel> deferredResult : defResultList) {
                            if (deferredResult.hashCode() == defResult.hashCode()) {

                                InterfaceModel model = new InterfaceModel();
                                model.setId(key);
                                model.setMessage("onTimeout");

                                deferredResult.setErrorResult(model);
                                defResultList.remove(deferredResult);
                            }
                        }
                    }

                } else {
                    defResult.setErrorResult("onTimeout");
                    deferredResultStore.getResponseBodyQueue().remove(defResult);
                }
            }
        });
    }

}

PushService

public interface PushService {

    public boolean pushMessage(String message);

    public boolean pushGroupMessage(String key, String topic, HttpServletResponse resp);

}

PushServiceImpl

@Service
public class PushServiceImpl implements PushService {

    @Autowired
    private DeferredResultStrore deferredResultStore;

    @Override
    public boolean pushMessage(String message) {

        if (!deferredResultStore.getResponseBodyQueue().isEmpty()) {

            for (DeferredResult<String> deferredResult : deferredResultStore.getResponseBodyQueue()) {

                deferredResult.setResult(message);
            }

            deferredResultStore.getResponseBodyQueue().remove();
        }

        return true;
    }

    @Override
    public boolean pushGroupMessage(String key, String topic, HttpServletResponse resp) {
        List<DeferredResult<InterfaceModel>> defResultList = null;

        // select data in DB. that is sample group Push service. need to connect db.
        InterfaceModel model = new InterfaceModel();
        model.setMessage("write group message.");
        model.setId(key);

        if (deferredResultStore.getGroupMap().containsKey(key)) {
            defResultList = deferredResultStore.getGroupMap().get(key);

            for (DeferredResult<InterfaceModel> deferredResult : defResultList) {
                deferredResult.setResult(model);
            }

            deferredResultStore.getGroupMap().remove(key);
        }

        return true;
    }

}

InterfaceModel

public class InterfaceModel {

    private String message;

    private int idx;
    private String id;

    // DB Column

    public InterfaceModel() {
        // TODO Auto-generated constructor stub
    }

    public InterfaceModel(String message, int idx, String id) {
        this.message = message;
        this.idx = idx;
        this.id = id;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public int getIdx() {
        return idx;
    }

    public void setIdx(int idx) {
        this.idx = idx;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

}

web.xml

async-unterstützt sehr wichtig in einstellungen.

<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
</servlet>

Java-Basis

@Bean
public ServletRegistrationBean dispatcherServlet() {
    ServletRegistrationBean registration = new ServletRegistrationBean(
            new DispatcherServlet(), "/");
    registration.setAsyncSupported(true);
    return registration;
}

Eigentlich :

Ein DeferredResult ist einer offenen Anfrage zugeordnet. Wenn die Anforderung abgeschlossen ist, wird das DeferredResult aus der Zuordnung entfernt, und der Client gibt eine neue lange Abfrageanforderung aus, die eine neue DeferredResult-Instanz hinzufügt


Spring Boot registriert automatisch alle Servlet-Beans in Ihrem Anwendungskontext im Servlet-Container. Standardmäßig ist async supported auf true gesetzt, sodass Sie nichts weiter tun müssen, als eine Bean für Ihr Servlet zu erstellen.

@Aligtor, für Sie => public @interface EnableAsync Aktiviert die asynchrone Methodenausführungsfunktion von Spring, ähnlich den Funktionen im XML-Namespace von Spring.

1
Byeon0gam