Mag ik een ASP.NET server in leven houden dmv heartbeat?

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Ik ben bezig met een applicatie die af en toe communiceert met een server. Het gaat om een C#/WPF applicatie die communiceert met een ASP.NET Core web api (straks draaiend op Debian, waarschijnlijk, nu nog op localhost).

99% van de communicatie met de server is zodat de gebruiker kan inloggen op zijn account en zijn licenses kan ophalen, waarmee hij bepaalde delen van de client applicatie ontgrendelt.

In principe is het dus genoeg dat de client bij het opstarten een api/account/login aanroept en daarna een api/licenses/get aanroept of iets dergelijks.

Voor logging en om 'fraude' (delen van accounts/licenses) te voorkomen wil ik graag de sessies van gebruikers bijhouden. Zodra ze inloggen krijgen ze een sessie (gewoon een record in de database). In principe wil ik per gebruiker maar een sessie tegelijk toestaan. Momenteel stuurt de client een "close_session" berichtje als hij afsluit waarmee de sessie gesloten wordt en er weer een nieuwe gemaakt mag worden.

In de praktijk werkt dit echter natuurlijk niet; de applicatie zou kunnen crashen zonder de sessie te eindigen, de internet connectie zou tijdelijk weg kunnen zijn, de server zou tijdelijk down of net in maintenance kunnen zijn, etc.

Het idee is nu om een soort van 'heartbeat' te gebruiken om te bepalen hoe lang de gebruiker verbonden is. In principe zou de client dan bijvoorbeeld elke minuut even de server benaderen, en de tijd van die heartbeat wordt in de sessie opgeslagen. Bij het verzoek voor een nieuwe sessie kan ik dan kijken of de huidige sessie nog bezig is (minder dan een minuut geleden gebruikt) of verlopen is (meer dan een minuut geleden verbruikt en door toeval niet gesloten).


Hoewel ik dit allemaal wel ingebouwd krijg vraag ik me af of er geen nadelen aan verbonden zijn. Ik ben lang geen expert in ASP.NET maar ik weet wel dat het in principe gebruikelijk is dat een website of api zichzelf "afsluit" als hij niet in gebruik is, en weer opstart wanneer nodig. Als er nu tig users continu elke minuut een heartbeat lopen sturen zal de server daar dus nooit de kans voor krijgen. Ik kan me voorstellen dat dit (misschien op lange termijn) niet de bedoeling is?

Is er een betere oplossing? Of is dit helemaal geen probleem en werkt mijn idee prima?

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • Xepos
  • Registratie: September 2009
  • Laatst online: 16:03
NickThissen schreef op dinsdag 21 februari 2017 @ 23:40:
In de praktijk werkt dit echter natuurlijk niet; de applicatie zou kunnen crashen zonder de sessie te eindigen, de internet connectie zou tijdelijk weg kunnen zijn, de server zou tijdelijk down of net in maintenance kunnen zijn, etc.
Dit kun je overigens opvangen door de sessie automatisch af te sluiten na x minuten. Je moet dan je heartbeat net wat korter instellen, zodat je niet steeds sessies zit af te sluiten en daarna weer een sessie opstart.
Het idee is nu om een soort van 'heartbeat' te gebruiken om te bepalen hoe lang de gebruiker verbonden is. In principe zou de client dan bijvoorbeeld elke minuut even de server benaderen, en de tijd van die heartbeat wordt in de sessie opgeslagen. Bij het verzoek voor een nieuwe sessie kan ik dan kijken of de huidige sessie nog bezig is (minder dan een minuut geleden gebruikt) of verlopen is (meer dan een minuut geleden verbruikt en door toeval niet gesloten).
Qua performance, als je echt veel users hebt dan krijg je een ddos attack op je server.
Hoewel ik dit allemaal wel ingebouwd krijg vraag ik me af of er geen nadelen aan verbonden zijn. Ik ben lang geen expert in ASP.NET maar ik weet wel dat het in principe gebruikelijk is dat een website of api zichzelf "afsluit" als hij niet in gebruik is, en weer opstart wanneer nodig. Als er nu tig users continu elke minuut een heartbeat lopen sturen zal de server daar dus nooit de kans voor krijgen. Ik kan me voorstellen dat dit (misschien op lange termijn) niet de bedoeling is?
Dat kan je instellen zodat de serverapplicatie zichzelf niet afsluit. Volgens mij kun je dat bereiken door scheduling in IIS (application pool) al ben ik hier er niet helemaal zeker van.


Ik kan overigens ook een scenario bedenken waarbij je de API post uitbreid met een unieke device id en dat opslaat bij je sessie data.

Wanneer een gebruiker inlogt dan controleer je of er een sessie is met die gebruiker
Nee -> Maak sessie aan
Ja -> Controleer device id bestaat die al?

Nee -> Laat toegang niet toe, sluit oude sessie af (gebruiker logt opnieuw in)
Ja -> Niks aan de hand gebruiker krijgt zijn/haar informatie te zien

Op deze manier zijn er nooit 2 sessie tegelijk bij 1 gebruiker. Enige nadeel is als een sessie incorrect is afgesloten dan moet de gebruiker 2 keer inloggen, het is niet zo gebruiksvriendelijk.

[ Voor 44% gewijzigd door Xepos op 22-02-2017 00:29 ]


Acties:
  • 0 Henk 'm!

  • defiant
  • Registratie: Juli 2000
  • Nu online

defiant

Moderator General Chat
Misschien is het sowieso handiger om de situatie om te draaien. Laat bij het opvragen van de license door de user de vorige sessie invalideren. De gebruiker kan hierdoor direct verder en eventuele ghost clients worden vanzelf geïnvalideerd.

"When I am weaker than you I ask you for freedom because that is according to your principles; when I am stronger than you I take away your freedom because that is according to my principles"- Frank Herbert


Acties:
  • +1 Henk 'm!

  • Brainstorm
  • Registratie: November 2000
  • Laatst online: 08-10 21:29
NickThissen schreef op dinsdag 21 februari 2017 @ 23:40:
... maar ik weet wel dat het in principe gebruikelijk is dat een website of api zichzelf "afsluit" als hij niet in gebruik is, en weer opstart wanneer nodig. Als er nu tig users continu elke minuut een heartbeat lopen sturen zal de server daar dus nooit de kans voor krijgen. Ik kan me voorstellen dat dit (misschien op lange termijn) niet de bedoeling is?
Het 'afsluiten' wat je beschrijft komt doordat de IIS worker pools standaard ingesteld zijn om zichzelf af te sluiten als er geen requests binnenkomen. Op zich wordt dit niet direct veroorzaakt door ASP.NET, maar is simpelweg hoe IIS werkt. Als je constant requests binnen krijgt op een worker pool, zal IIS inderdaad het achterliggende worker process niet afsluiten. In principe kan dit geen kwaad voor een ASP.NET applicatie, mits de applicatie geen defecten heeft zoals memory leaks. Daarnaast is IIS standaard ook ingesteld om eens in de x uur een worker pool te recyclen, wat wil zeggen dat een nieuw worker proces opgestart wordt en het oude afgesloten wordt.

Met .NET Core is de situatie zowel anders, als identiek :)

Jouw .NET Core applicatie ga je straks publishen zodat je hem kunt runnen. Core applicaties gebruiken in dit geval Kestrel, wat een webserver is. Dat betekent dat zodra het proces gestart is, je HTTP requests naar de applicatie kunt afvuren. Aangenomen dat je wederom geen problemen zoals leaks en crashes hebt, zal Kestrel blijven draaien en actief blijven.

Echter, Microsoft raadt het niet aan om Kestrel direct over het internet te exposen, maar in plaats daarvan gebruik te maken van een reverse HTTP proxy. De reden daarvoor is dat hoewel Kestrel geschreven is voor performance, het juist een aantal features expliciet mist die je van een webserver gewend bent. Een praktisch voorbeeld is port sharing: je kunt niet meerdere Kestrel applicaties poort 80 laten delen. In plaats daarvan geef je iedere Kestrel applicatie een eigen poort (bijvoorbeeld 8081, 8082, 8083, etc) en gebruik je de reverse proxy op inkomend verkeer op poort 80 te splitsen naar de juiste applicatie. Een andere veel gebruikte feature is bijvoorbeeld SSL termination.

Er zijn verschillende reverse proxies die je kunt gebruiken. Op Windows is IIS nog steeds een keuze. Wat dit betekent is dat je net zoals normaal IIS draait op poort 80 (en/of 443 voor SSL) en net zoals normaal pools en sites aanmaakt. In tegenstelling tot klassiek ASP.NET zal IIS echter niet jouw code binnen het worker proces van IIS hosten, maar in plaats daarvan wordt het verkeer doorgelust naar de Kestrel webserver die jouw .NET Core applicatie bevat. Zie bijvoorbeeld deze link voor meer details. De worker pools zullen zich net zoals normaal gedragen, inclusief de standaard recycle instellingen. Als een worker proces dus niet actief is en er een request binnenkomt zal er nog steeds eerst een worker moeten starten.

Je noemde dat je straks op Debian wil draaien en waarschijnlijk wil je dus geen IIS gebruiken. Het is dan aan te raden om een andere reverse proxy te kiezen, zoals NGINX of Apache. NGINX is vrij populair, zie bijvoorbeeld deze link om een indruk te krijgen.

Qua applicatie architectuur kun je hier ook rekening mee houden en juist tot je voordeel gebruiken. Voorbeeld: stel dat je een nieuwe versie van je applicatie wilt releasen. In feite stop je dan het bestaande Kestrel proces, kopieert de nieuwe files en start het nieuwe proces. In de tussentijd zal je applicatie offline zijn en zullen de heartbeats die je beschrijft, niet verwerkt worden.
Een verbetering is bijvoorbeeld dit: Kopieer de nieuwe versie van je Kestrel applicatie en start deze op een tweede poort. In je reverse proxy schakel je vervolgens het verkeer over van de oude poort naar de nieuwe poort. Op die manier heb je geen downtime en je kunt ook makkelijk terugschakelen naar de oude versie.

Programmer's Drinking Song: 99 little bugs in the code, 99 bugs in the code, Fix one bug, compile it again, 100 little bugs in the code. (go to start if bugs>0)


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Xepos schreef op woensdag 22 februari 2017 @ 00:23:
[...]


Dit kun je overigens opvangen door de sessie automatisch af te sluiten na x minuten. Je moet dan je heartbeat net wat korter instellen, zodat je niet steeds sessies zit af te sluiten en daarna weer een sessie opstart.
Ik zie geen praktisch verschil met deze oplossing. Op het moment heeft de sessie database entry een 'last heartbeat' datum/tijd, welke dus elke minuut geupdate wordt. Zodra een 'last heartbeat' meer dan een minuut geleden is (zeg, 70s bijv), dan weet ik dus dat de laatste heartbeat gemist is en dat de sessie in principe expired is.

Zodra er nu een nieuwe login komt met een nieuw device, dan weet ik dat het ok is want de laatste sessie is afgesloten.

Zodra er een nieuwe login komt met een nieuw device terwijl de 'last heartbeat' nog binnen de minuut is weet ik dat er dus (waarschijnlijk) een tweede login geprobeerd wordt vanaf een ander device, en kan ik dat blokkeren.
Xepos schreef op woensdag 22 februari 2017 @ 00:23:
[...]
Qua performance, als je echt veel users hebt dan krijg je een ddos attack op je server.
Zoveel users verwacht ik niet dus dat zal wel meevallen.
Xepos schreef op woensdag 22 februari 2017 @ 00:23:
[...]
Dat kan je instellen zodat de serverapplicatie zichzelf niet afsluit. Volgens mij kun je dat bereiken door scheduling in IIS (application pool) al ben ik hier er niet helemaal zeker van.
Ja, maar dat wil ik juist niet, en ik vraag me af het niet nadelig is om de applicatie continu "wakker" te houden, aangezien dit voor zover ik begrijp niet standaard is.
Xepos schreef op woensdag 22 februari 2017 @ 00:23:
[...]
Ik kan overigens ook een scenario bedenken waarbij je de API post uitbreid met een unieke device id en dat opslaat bij je sessie data.

Wanneer een gebruiker inlogt dan controleer je of er een sessie is met die gebruiker
Nee -> Maak sessie aan
Ja -> Controleer device id bestaat die al?

Nee -> Laat toegang niet toe, sluit oude sessie af (gebruiker logt opnieuw in)
Ja -> Niks aan de hand gebruiker krijgt zijn/haar informatie te zien

Op deze manier zijn er nooit 2 sessie tegelijk bij 1 gebruiker. Enige nadeel is als een sessie incorrect is afgesloten dan moet de gebruiker 2 keer inloggen, het is niet zo gebruiksvriendelijk.
Dit is ongeveer wat ik al doe, bij de login gebruik ik een device id waarmee ik bepaal of dit nog steeds dezelfde gebruiker op hetzelfde device is. Als het device anders is, dan wil ik dat alleen toestaan als de huidige sessie afgesloten of verlopen is (meer dan een minuut geleden geupdate met de heartbeat).
defiant schreef op woensdag 22 februari 2017 @ 00:54:
Misschien is het sowieso handiger om de situatie om te draaien. Laat bij het opvragen van de license door de user de vorige sessie invalideren. De gebruiker kan hierdoor direct verder en eventuele ghost clients worden vanzelf geïnvalideerd.
Het probleem met "de vorige sessie invalideren" is dat de gebruiker typisch maar 1 keer communiceert met de server - alleen bij het inloggen en het ophalen van de licenses. Als hij de applicatie daarna gewoon laat draaien kan ik zijn login invalideren maar kan hij natuurlijk wel gewoon de applicatie blijven gebruiken (tot hij weer moet inloggen). Ik kan niet vanuit de server de client stoppen, alleen andersom.

De applicatie zal soms lange tijd gebruikt worden, typisch geval kan wel eens 2 uur lang zijn en soms zelfs wel 24 uur lang achter elkaar door. Op deze manier kunnen mensen dus een account gebruiken en gewoon achter elkaar inloggen, zolang ze daarna niet meer de server benaderen kunnen ze toch de 24 uur gebruik maken van de applicatie.

Ik wil juist voorkomen dat er een tweede login gedaan wordt. De eerste login mag gewoon geldig blijven, maar de tweede moet geblokkeerd worden.

Nu kan ik natuurlijk elke minuut checken of de licenses nog geldig zijn of iets dergelijks, maar dat komt op hetzelfde neer: daarmee hou ik dus ook de server wakker... is dat geen probleem?


@Brainstorm
Bedankt voor de uitleg, echter is het me nog steeds niet helemaal duidelijk of dit nu een probleem is of niet. Je eerste paragraaf lijkt uit te leggen dat het niet veel uitmaakt dat de server wakker blijft (tenzij er memory leaks zijn), maar daarna zeg je dat het met Core misschien anders is?

Het hele publish / hosting gebeuren doe ik niet zelf, daar heb ik geen verstand van, maar ik zal het doorgeven.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • Brainstorm
  • Registratie: November 2000
  • Laatst online: 08-10 21:29
Nee, een constante stroom van requests is geen probleem voor de server. De applicatie blijft inderdaad actief, maar dit is juist wat je wilt.

De standaard instellingen rondom recyclen van de pool in IIS is met name om twee redenen:
* Als je een applicatie met heel weinig requests hebt, kan het lonen om de applicatie te stoppen en het geheugen vrij te geven, zodat het voor andere doeleinden gebruikt kan worden.
* Preventief, om fouten in de applicatie (zoals leaks) "op te lossen" door af en toe de applicatie te herstarten.
Als je IIS in combinatie met .NET Core gebruikt, dan zal IIS nog steeds dezelfde recycling doen en dus wordt de applicatie op identieke wijze gestart/herstart zoals met klassiek ASP.NET. Dit komt omdat IIS ook de controle neemt over wanneer jouw applicatie (en dus Kestrel) gestart/gestopt wordt (link)

Stel dat je je .NET Core applicatie zonder reverse proxy gebruikt (geen IIS, geen NGINX, etc). In feite is het dan niets meer dan een normaal proces dat je runt. Het starten en stoppen zul je dan zelf moeten regelen. Eenmaal gestart, zijn de redenen om het proces te herstarten niet heel bijzonder:
* Het proces crasht, of gedraagt zich abnormaal
* Preventieve herstart om fouten "op te lossen"
IIS is in dit scenario niet aanwezig en kan dit dus niet voor je doen.

Programmer's Drinking Song: 99 little bugs in the code, 99 bugs in the code, Fix one bug, compile it again, 100 little bugs in the code. (go to start if bugs>0)


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Bedankt, dan ga ik er van uit dat het wel goed komt. Ik heb dit systeem inmiddels op deze manier draaien op localhost, en ik zal het goed testen als het naar de server deployed is.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • Mercatres
  • Registratie: September 2009
  • Laatst online: 12:25
Nu is het ook wel zo dat Microsoft Azure zelf ook een "Always On"-functionaliteit aanbied. En dat doen ze ook door elke 5 minuten een requestje te sturen naar je server. Dit voorkomt dat IIS Workers gaan recyclen, maar zorgt er ook voor dat je "opspinning" cycle ook niet meer bestaat.
Pagina: 1