Jak ve frontendu okamzite reagovat na udalost na backendu?

Potrebuju mit takovy life-reload <div> elementu na frontendu. Jamile se mi na backendu neco zmeni, melo by se to okamzite promitnout do toho <div>. A nechci to refreshovat nejakym timerem.

Zkousim na to jit cestou long pollingu:

Kód: [Vybrat]
<script>
    (function poll() {
        setTimeout(function() {
            $.ajax({
                url: "/events",
                type: "GET",
                success: function(data) {
                    $("#table").html(data);
                },
                complete: poll,
                timeout: 10000
            })
        }, 1000);
    })();
</script>

Na backendu na prichozim requestu potom zavolam wait(timeout) a tim zablokuju thread requestu:

Kód: [Vybrat]
        synchronized (lock) {
                lock.wait(waitMs);
        }

A kdyz se mi ve worker threadu vyvola patricna udalost, tak v nem zavolam:

Kód: [Vybrat]
        synchronized (lock) {
            lock.notifyAll();
        }

Tim dojde k tomu, ze se thread requestu probudi a na frontend se okamzite vrati vystup s aktualnimi daty, updatuje se <div> a pak se znovu udela z jquery request.


Tento pristup je docela jednoduchy, ma vsak jisty design leak:

Mezi 2 po sobe jdoucimi http requesty je slepe misto, kdy na udalost neceka zadny request - pokud by se zde vyvolala udalost, tak ji nic nezachyti. To je problem.

Potreboval bych nejakou javascriptovou knihovnu k tomuto urcenou, ktera se mi sama postara o to, aby na backendu vzdy naslouchal alespon 1 request.

Nicmene necham si poradit, jak to udelat vsechno nejak lip. Na backendu mam Javu a Spring MVC.


Re:Jak ve frontendu okamzite reagovat na udalost na backendu?
« Odpověď #1 kdy: 03. 08. 2019, 13:28:24 »
WebSockety. Ve Springu pro to máte podporu: Web on Servlet Stack – WebSockets nebo Web on Reactive Stack – WebSockets.

Re:Jak ve frontendu okamzite reagovat na udalost na backendu?
« Odpověď #2 kdy: 03. 08. 2019, 13:33:02 »
WebSockety. Ve Springu pro to máte podporu: Web on Servlet Stack – WebSockets nebo Web on Reactive Stack – WebSockets.

Ja se na to jak to udelat pres Websocket dival:
https://spring.io/guides/gs/messaging-stomp-websocket/

neni to zbytecne slozite? Ten polling dokaze byt docela jednoduchy, jenom k tomu mit vhodnou knihovnu. Jakou pridanou hodnotu mi daji Websockety?

gill

  • ****
  • 270
    • Zobrazit profil
    • E-mail
Re:Jak ve frontendu okamzite reagovat na udalost na backendu?
« Odpověď #3 kdy: 03. 08. 2019, 13:35:07 »
Zkousim na to jit cestou long pollingu:

to není long polling, pouze polling. Long polling je zdržení odpovědi na backendu. To co děláš, je nejhorší možný způsob, jak z uživatelského hlediska (je tam zdržení v závislosti na intervalu pollování), tak z hlediska zatížení serveru.
« Poslední změna: 03. 08. 2019, 13:40:24 od gill »

Re:Jak ve frontendu okamzite reagovat na udalost na backendu?
« Odpověď #4 kdy: 03. 08. 2019, 13:41:00 »
Zkousim na to jit cestou long pollingu:

to není long polling, pouze polling. Long polling je zdržení odpovědi na backendu. To co děláš, je nejhorší možný způsob, jak z uživatelského hlediska, tak z hlediska zatížení serveru.

Ale hovno, je to presne long polling.


gill

  • ****
  • 270
    • Zobrazit profil
    • E-mail
Re:Jak ve frontendu okamzite reagovat na udalost na backendu?
« Odpověď #5 kdy: 03. 08. 2019, 13:44:42 »
Zkousim na to jit cestou long pollingu:

to není long polling, pouze polling. Long polling je zdržení odpovědi na backendu. To co děláš, je nejhorší možný způsob, jak z uživatelského hlediska, tak z hlediska zatížení serveru.

Ale hovno, je to presne long polling.

https://en.wikipedia.org/wiki/Push_technology#Long_polling

Citace
With long polling, the client requests information from the server exactly as in normal polling, but with the expectation the server may not respond immediately.

Na long polling potřebuješ neblokující server. Až se dostanu k počítači, pošlu příklad.

Re:Jak ve frontendu okamzite reagovat na udalost na backendu?
« Odpověď #6 kdy: 03. 08. 2019, 13:50:00 »

Na long polling nepotrebuju neblokujici server, na long polling potrebuju, aby server vratil response az v momente, kdy je k dispozici nova informace. To je long polling, a to presne dela moje implementace.

https://stackoverflow.com/questions/11077857/what-are-long-polling-websockets-server-sent-events-sse-and-comet
Citace
Ajax Long-Polling:
A client requests a webpage from a server using regular HTTP (see HTTP above).
The client receives the requested webpage and executes the JavaScript on the page which requests a file from the server.
The server does not immediately respond with the requested information but waits until there's new information available.
When there's new information available, the server responds with the new information.
The client receives the new information and immediately sends another request to the server, re-starting the process.




Ty proste jenom nevis, co dela ten blok "synchronized(lock)", protoze ses javascriptar nebo pythonak...

gill

  • ****
  • 270
    • Zobrazit profil
    • E-mail
Re:Jak ve frontendu okamzite reagovat na udalost na backendu?
« Odpověď #7 kdy: 03. 08. 2019, 13:53:44 »
To je long polling, a to presne dela moje implementace.

tak proč tam máš ten timeout? Smysl long pollingu je, že pošleš nový request okamžitě, server stále drží jeden request.
« Poslední změna: 03. 08. 2019, 13:58:56 od gill »

Re:Jak ve frontendu okamzite reagovat na udalost na backendu?
« Odpověď #8 kdy: 03. 08. 2019, 14:07:09 »
To je long polling, a to presne dela moje implementace.

tak proč tam máš ten timeout? Smysl long pollingu je, že pošleš nový request okamžitě, server stále drží jeden request.

To je ted uplne jedno ze to tam je, a to tvoje "okamzite" predstavuje taky jisty timeout. Tak si predstav ze ten timeout je ten tvuj "okamzite"-timeout a budes spokojeny.

Ja tady v Jave ted nedokazu udelat neblokujici server, na kazdy request se vyrobi 1 thread, takovy je design frameworku ktery pouzivam. Long Polling -> musim waitovat thready.

gill

  • ****
  • 270
    • Zobrazit profil
    • E-mail
Re:Jak ve frontendu okamzite reagovat na udalost na backendu?
« Odpověď #9 kdy: 03. 08. 2019, 14:21:55 »
Mezi 2 po sobe jdoucimi http requesty je slepe misto, kdy na udalost neceka zadny request - pokud by se zde vyvolala udalost, tak ji nic nezachyti. To je problem.

musíš pro každou stránku vytvořit na serveru frontu zpráv.

Potreboval bych nejakou javascriptovou knihovnu k tomuto urcenou, ktera se mi sama postara o to, aby na backendu vzdy naslouchal alespon 1 request.

na klientské straně bych začal odstraněním toho timeoutu

Re:Jak ve frontendu okamzite reagovat na udalost na backendu?
« Odpověď #10 kdy: 03. 08. 2019, 14:43:29 »
Ja se na to jak to udelat pres Websocket dival:
https://spring.io/guides/gs/messaging-stomp-websocket/

neni to zbytecne slozite? Ten polling dokaze byt docela jednoduchy, jenom k tomu mit vhodnou knihovnu. Jakou pridanou hodnotu mi daji Websockety?
Přidaná hodnota je ta, že už to máte ve Springu implementované (včetně volitelného fallbacku na long-polling), je to vyzkoušené a funguje to. Včetně těch klientských knihoven.

Když si to budete implementovat sám, po nasazení do produkce zjistíte, že v reálném světě jde komunikace často přes nějaký NAT nebo proxyserver, které mají nějaké timeouty na dobu neaktivního spojení. Takže vám po nějakém čase spojení tiše uzavřou, a vy se to dozvíte až v okamžiku, když budete chtít ze serveru odeslat nějakou zprávu. Jenže to spojení potřebujete znovu navázat z klienta. Takže tu svou implementaci rozšíříte o nějaký mechanismus keep-alive.

Pak zjistíte, že mít na serveru jedno uspané vlákno pro každého připojeného klienta také není nejlepší nápad.

A takhle se vám ta docela jednoduchá implementace bude postupně komplikovat, a za chvíli budete litovat toho, že jste nepoužil to, co máte k dispozici „na pár kliknutí“. Zvlášť, když už teď máte aplikaci postavou na Spring MVC, takže nepotřebujete nic nového, stačí to nakonfigurovat a začít používat.

Re:Jak ve frontendu okamzite reagovat na udalost na backendu?
« Odpověď #11 kdy: 03. 08. 2019, 14:54:17 »
Ja se na to jak to udelat pres Websocket dival:
https://spring.io/guides/gs/messaging-stomp-websocket/

neni to zbytecne slozite? Ten polling dokaze byt docela jednoduchy, jenom k tomu mit vhodnou knihovnu. Jakou pridanou hodnotu mi daji Websockety?
Přidaná hodnota je ta, že už to máte ve Springu implementované (včetně volitelného fallbacku na long-polling), je to vyzkoušené a funguje to. Včetně těch klientských knihoven.

Když si to budete implementovat sám, po nasazení do produkce zjistíte, že v reálném světě jde komunikace často přes nějaký NAT nebo proxyserver, které mají nějaké timeouty na dobu neaktivního spojení. Takže vám po nějakém čase spojení tiše uzavřou, a vy se to dozvíte až v okamžiku, když budete chtít ze serveru odeslat nějakou zprávu. Jenže to spojení potřebujete znovu navázat z klienta. Takže tu svou implementaci rozšíříte o nějaký mechanismus keep-alive.

Pak zjistíte, že mít na serveru jedno uspané vlákno pro každého připojeného klienta také není nejlepší nápad.

A takhle se vám ta docela jednoduchá implementace bude postupně komplikovat, a za chvíli budete litovat toho, že jste nepoužil to, co máte k dispozici „na pár kliknutí“. Zvlášť, když už teď máte aplikaci postavou na Spring MVC, takže nepotřebujete nic nového, stačí to nakonfigurovat a začít používat.

Chapu, nicmene myslim si, ze ten long pooling nebude tak obtizny.

1. Timeoutovani nejakym proxyserverem se vyresi uz v jQuery, kde se nastavi timeout 2000ms. Tim to bude vytimeoutovano drive, nez to vytimeoutuje cokoliv jineho.
2. Jedno uspane vlakno na jeden request se da snadno vyresit tak, ze se ve springu pouzije jako response DefferedResult<NejakaMessage>. Chova se to jako Future. Takto i kdybych tam mel 100000 requestu na long pollingu, tak mi je dokaze vsechny obsluzit jedno jedine vlakno. Kdyz vznikne potrebna eventy, tak to jedno vlakno nastavi vsem DefferedResult status na completed.

Potrebuju opravdu jenom jednu jedinou vec: knihovnu v javascriptu, abych si to nemusel zbytecne psat sam.

Ta knihovna bude postavena rekneme nad jQuery a postara se mi o chytre redundantni naslouchani na Serveru. To je opravdu vsechno co potrebuju.
« Poslední změna: 03. 08. 2019, 14:56:06 od PetrK »

gill

  • ****
  • 270
    • Zobrazit profil
    • E-mail
Re:Jak ve frontendu okamzite reagovat na udalost na backendu?
« Odpověď #12 kdy: 03. 08. 2019, 15:06:31 »
1. Timeoutovani nejakym proxyserverem se vyresi uz v jQuery, kde se nastavi timeout 2000ms. Tim to bude vytimeoutovano drive, nez to vytimeoutuje cokoliv jineho.

timeoutování je lepší řešit na serveru. Odpadnou chybové stavy, po nějaké době čekání server odešle prázdnou odpověď.

Re:Jak ve frontendu okamzite reagovat na udalost na backendu?
« Odpověď #13 kdy: 03. 08. 2019, 15:13:53 »
Nicmene to bude aplikace kde budou tak max 3 useri najednou na intranetu, takze me to staci jednoduse:
Kód: [Vybrat]

<script>
    (function poll() {
        setTimeout(function() {
            $.ajax({
                url: "/events?timeout=5000",
                type: "GET",
                success: function(data) {
                    $("#table").html(data);
                },
                complete: poll
            })
        }, 0);
    })();
</script>


Kód: [Vybrat]
    @GetMapping("events")
    public String getEventsLongPoll(@RequestParam(name="timeout", required = false, defaultValue="0") Integer timeout, Model model) {
        if(timeout > 0) {
            events.changeListener(timeout);
        }
       
        model.addAttribute("events", events.getEventQueue());
       
        return "events";
    }

Template v Thymeleafu:
Kód: [Vybrat]
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">

<p th:text="${events}></p>

Pricemz na user si potencionalne muze tedy nastavit, jaky chce timeout, pripadne si muze dat timeout=0 aby dostal okamzitou odpoved.

Re:Jak ve frontendu okamzite reagovat na udalost na backendu?
« Odpověď #14 kdy: 03. 08. 2019, 16:13:02 »
Požadavek na straně serveru musí počkat, dokud si ho konkrétní klient nevyzvedne. Tím je problém odstraněn a hluché místo tím pádem nebude na klientovi vadit. Takže na serveru doplnit synchronizovanou frontu (jak bylo řečeno) a registrovat klienty. Timeout na klientovi se dá myslím zrušit. Pokud je potřeba pooling na klientovi v určitý okamžik přerušit, tak počkat na odpověď serveru a vykonat prázdnou akci a další pooling už neiniciovat.

Jinak existují js knihovny, které tohle celé řeší a odstiňují od implementačních rozdílů v prohlížečích (teď si nevybavuji, socket.io vyžaduje node, to asi není ono). Například pokud prohlížeč neumí websockety, použije se long pooling. Co se týče websocketů, on se ten socket taky může zavřít a je třeba to řešit, ne? Takže tam je trochu režie s tím, navíc takové mobilní připojení apod. přináší výpadky připojení apod. Nic co by chtěl člověk mermomocí řešit, pokud to není téma které ho apriori zajímá. Radši bych použil knihovnu na bezpečné asynchronní posílání zpráv mezi backendem a frontendem.

Hodně štěstí :-)
« Poslední změna: 03. 08. 2019, 16:17:35 od Ondrej Nemecek »