Real-Time Revolution: SignalR in Action - Kevin Griffin - NDC London 2022

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
oh hello everyone welcome to real time revolution uh this is signalr in action um before we get started i always like to tell a little bit of a story i'm an independent consultant in the united states i work with a variety of companies all around the world um various countries and i get the most like basic questions that come across my email i always try to do like a 15-minute call just get an idea where people are and i had a client's come last year give or take and they said kevin we have an application not an actual screenshot i swear i i'm not very good with css i could probably create something a little bit prettier in css if i tried but their app looked like this and you probably have an app as well they look similar to this it's some icons up in the top right corner there's a menu or toolbar and it does some stuff and they say kevin we have this application and we have two very important icons on this page we have a little envelope that tells people when they're getting new messages that come into the system and then we also have this global alerts notifier well what happens is when stuff happens in the app these little icons pop up and say you have two alerts that you need to know about or you have five new messages that you need to be aware of and this is different for every user and is up to date no matter how many tabs i have open at any one time and you probably have seen this dynamic before if you've used facebook or any similar applications you understand what the badges do if you don't open them they go away stuff gets synced back to the server the the way they accomplished this was they had their app so we talked to their web server and every now and then it would go and say i need to know the number of unread messages that i currently have or it would go say well what are the number of alert notifications that i need to be aware of it's the client specifically going to the server asking it for information it's doing a pull request technically and would do this both calls every five seconds every five seconds okay not a big deal except you make this assumption as just one person using your app it's not really one person using your app it's multiple people using your apps and if your users are anything like mine they have multiple tabs open multiple windows maybe on multiple devices they're all hitting the same server and they're all getting the same information over and over again and they took the process of trying to optimize in different ways they couldn't necessarily cache that information long term because it's always changing stuff is always happening on the server those statistics are always changing so they try to do funny things with like tab caching so one tab might go grab the information and share it with their other tabs and they try to over architect what could be a simpler solution and they went to their telemetry and their telemetry said that 90 of their calls were those two specific endpoints because every client every five seconds was hitting them trying to get new information that wasn't necessarily changing all the time so we asked ourselves and they asked me and they paid me kevin is there a better way that we can accomplish this well you already know there is because you wouldn't be here if there wasn't a bear way hi everyone i'm kevin griffin from the united states uh microsoft mvp independent consultant my primary focus in life and career is building web applications on asp.net core and deploying solution to microsoft azure so i'm happy to be here with you all today if you ever like to reach out to me afterwards you can go to my website consultwithgriff.com or hit me up on twitter at one kev griff i would love to chat with you i also have a best-selling course on udemy uh on signalr which i went and grabbed the screenshot in pounds today because dollars wouldn't have made any sense to anyone uh so it's really cheap at the moment you should go grab it but i'm not going to push my course anymore than this let's talk about a better way to solve that problem i have my app i have my server and the ideal solution you probably already know the ideal solution is just set up a websocket between the clients and the server because there's this great thing that happens when the server gets a message it knows everything that's happening it is the ultimate state of truth for our entire ecosystem if an email comes in the server knows about it if a direct message comes from another client it knows about it if the databases needs to be updated the server knows about it because the server is the one doing the work so the server is aware of everything that's happening in our app so why couldn't the server just send that message down to the client to tell it that something occurred email comes in the server sends the message down to the client the client increments decrements changes its notification to reflect what the current state of the server is and this works really well at scale so instead of all these various clients making calls multiple calls every five seconds to get various pieces of information they just establish a single connection a websocket and when something happens the server will notify whomever needs to know about it now it might not be every client that needs to get a message it might be uh specific clients it might just be your one tab or your one tab or your one tab everyone gets the message that they need at the time that they need it even better what if we could have a system where i write code one way but that code supports not just web pages it could also support multiple devices windows apps windows services mobile apps you name it what if i could write one server implementation that could feed all these different types of applications that i'm building so you probably already guessed it that solution is signalr which has been in the asp.net framework since 2013. i believe that was the first course i created on signalr it was added as this way to really make websockets more approachable to net developers because if you ever had a program a raw websocket it's easier now than it was 10 years ago but it's still kind of a pain in the butt it's a lot to manage especially across numerous users and signalr just abstracts all that away from you even better if you have a case where websockets isn't supported you don't have to change your code in any way signalr is automatically going to find the best way for your clients and your server to communicate with each other so okay i want to jump out of slides for a minute i want to start just running through some basic demos and when i say basic i mean basic i have this philosophy when it comes to signal r that the simpler the demo the better there's so many folks that want to show you a chat demo there's an unwritten rule in the signalr community you do not write chat demos if someone writes a chat demo in single r there's any microsoft folks in here good microsoft vote yes all right don't tell anyone i said this microsoft folks break this all the time like chat chat chat chat chat don't write chat demos uh instead i'm just gonna show you the world's ugliest demo uh let's set it up real fast i have this starter this is all on github so you can see it after the fact if you want nope that's not it i want to set up what our intention is with this demo you see this is the world's ugliest demo it just says hi uh i have a text box i have a check box i also have a couple buttons and there's a couple phases to this demo our ultimate goal and this is 90 of what i do in signalr is when i type something into that first text box i want it to also display on that second text box signal r the majority of the work you're going to do in cinema is state transfer something happens in the system and i need to notify the clients about it i need the transfer state when state changes now it doesn't do anything at the moment because we haven't wired up any signal r so if i just type kevin nothing happens so let's start fixing that bits by bit by bit i'm going to shut down my application let's walk through the code just a little bit so you see where we're starting with i try to keep everything simple again our html input box check box a couple buttons that we'll get to later i have my scripts at the very end for including signalr plus some other fancy tools when you primarily use signalr in a real application you're probably using something like webpack or some sort of build system so you're going to npm install your signalr dependency and you'll just include it wherever necessary for this demo i'm just using signalr kind of raw raw javascript but the syntax that i'm using is fairly similar to what you would write in typescript or just raw javascript the javascript itself nothing up my sleeve i have a couple placeholders just for event handlers but there's no signalr in this application yet well let's start at the very beginning in our startup cs if you're working with new minimal apis it's just program cs but we have two things that we need to do here the first is we need to configure services for signalr and that's fairly straightforward say services.add cigar sigma r is already a part of the box when you create a new asp.net core application no special nuget packages that you need to install the second thing we have to do is create a place for our clients to connect and traditionally in signalr this is called a hub if you're familiar with mvc model view controller you understand the concept of controllers and controller is a place that connections go to perform work in signalr a hub is a place that web sockets go signal our connections go to basically be managed by signalr so all of our connections will go through what we call the hub i'm going to create one called a sync up i'm doing a lot of this by hand and it implements the class or the type hub which is part of signalr now this is all i really need to do for now my hubs don't have to have any methods in them they can just simply exist for managing connections if i go to my startup i have to register that hub with asp.net core and that's done through the use endpoints middleware we'll say configure map hub i need to tell asp.net core what type of hub i want to implement so i'm going to say sync hub and i have to give it a path and that path is going to be hubs slash sync of course there's no formatted for c sharp all right so we're going to save that we're going to go back to our app and make sure this compiles nope because i don't have the namespace so this is using microsoft's asp.net core signal r there we go all right that's step one step two let's go back to our javascript file we have a hub on our asp.net core app it's exposed an endpoint we need to set up a connection between our clients and our server so that's done with what's called the connection hub builder so we'll create a new connection which will create a new signalr hub connection builder and i have to tell this hub connection builder where to go to connect uh so there's a couple options on here we'll say with url we'll tell it hubs just sync and then i'm going to tell it to build itself so our creates the connection but it hasn't really done anything yet the connection is created but we have to go tell it to start the connection which is done down here at the bottom and in your application the connection can start whenever you want to typically i try to start the connection as soon as possible when a page renders just because it's nicer to have that socket open and ready to go for when you need it then wait until a later time we're going to say net run we'll open this in a browser again i'm going to bring up the dev tools this time let's do a refresh come on signalr is not defined yes it is signal r capital r there we go so a couple things happens here our page renders you see the first line we're normalizing our url so it's fully qualified uh you can also use signalr across domains uh there's core support uh we don't need that in the moment but then my second line which was this dot start signalr does a couple things you can see we have websocket connected at our endpoint but the process to get there let's find our network tab let's go back to all let's bring this up a little bit more a couple things happened uh first was this negotiation call uh the clients and the server have a little chat and they ask what's the best way for us to talk to each other because they really want to be on the same page ideally they want to use web sockets but sometimes there are cases where websockets can't apply we'll talk about that in a moment if websockets works they say websocket is the way we're going to go the server assigns a connection id which is unique to this tab this instance and then the next call to sync is an actual websocket that gets opened back to the server with the with the connection token and such so now the client and the server can talk over this websocket as much as they want to we can watch the messages here as they come these are just uh heartbeats coming in every couple seconds so we've set up the scaffold for now actually being able to send information up and down the pipe let's talk about protocols for a moment so ideally you would use a websocket which simply is an end-to-end connection between all your clients and your server so the server can send information up to the clients the clients can send information down to the server but there are cases where websockets cannot be used not so much today but maybe five six years ago this was the case every now and then you get a proxy or some sort of vpn it doesn't allow certain websocket connections sometimes the server won't allow it or it's disabled if there's a case where a websocket isn't supported there are a couple fallbacks there's one called server sentiments which has been around since the netscape days and the way this works is that the client makes an initial request to the server saying i would like to start service and events connection with you the server returns what's called an event source back to the client and this is a pipe for the server to send messages down to the client whenever it wants to and this is fantastic however it doesn't work as well when the client wants to send messages to the server the fallback is that the server just makes traditional ajax requests or post requests to the server whenever there's a message that needs to get made so this pipe is dedicated just for the server to go to the client and it will work in 95 of cases where websockets isn't supported for all the other cases there's the final final fallback which is long polling uh long polling is just simply opening the request to a server the server maintains that connection until it has something to say so if there's no messages that need to come down to the client immediately that request is just going to stay open as soon as the server has a message to send sends back the response the connection closes we repeat the pattern the only time that the request would get terminated prematurely is if the timeout expires on the client side this could typically be about two minutes but it's customizable based off the browser this is the final final fallback just because this will work all the way back to ie6 hopefully no one's supporting ie6 if you are raise your hands or we can laugh at you excellent uh so just quick comparison normally you're going to use websockets it's the de facto standard nowadays it's always available connection between a client and a server uh there is a con because it's always open connection that's you could potentially exhaust your socket pool in your application don't ask me how i found this out the hard way if we have time i will tell that story i have learned that you can ddos yourself accidentally with websockets it's a good story if websockets aren't available for any reason proxies vpns say not we fall back to server sent events which are great but the only issue is that the client can't use the server the event source to send messages to the server so it has to fall back and then finally long polling which works across all browsers it's just horrible it's actually the problem that we had at the very beginning of the talk where we're constantly making requests to the server over and over and over again uh so that's the worst case scenario now you might be thinking kevin do i have to write the code to handle all these different cases well this demo right here will handle all three of these without any code changes on your side the only thing you could do or might want to do is you might want to tell your app don't use long polling or don't use server sent events only use web sockets that's just a quick configuration option and you can do that i don't recommend it unless you have a particular use case but that's all you need to know about the transport protocols so let's get into some meat let's send some messages let's make this boring demo actually do something i'm going to close all this stuff shut down my server let's go back into our app all right come on vs good there you go all right uh no real good place to start we're going to start in the hub and we're going to worry about that text box because that's what we want to do first uh we have to create a method on the server that the clients can call when it wants to update the state on everyone's text box so we'll create a new public async method because the any signalr method inside of a hub will return a task it's all asynchronous my number one consulting gig is awaiting people's signalr calls because they don't realize that every single hour method that you create is asynchronous it's literally the easiest money i've ever made hey oh wait oh wait oh wait oh wait wait pay me please uh so just call this sync text box and sync text box is going to take a string and we'll just say text box and when this method gets called which it will be called by a client we want to turn around take the contents of text box and send it to all our connected clients that aren't the person making the call so how do we get that connection list well because we're implementing hub we have this collection called clients and this is a data structure that knows about every single connection to our server at this time i can say clients not all and it will send a message to all the connected clients including myself so myself being if i'm the tab that's making the request but that's not not necessarily what i want i want to send this to all the others that are connected to this server we'll say send async and i'm going to pass in a magic string there's other ways to do this i'm just using magic strings for demo purposes i need to tell my clients what method they need to execute on their side and again we're going to call this sync text box just to keep everything the same and we're going to pass in our string and we're going to oh wait on this call now kevin what happens if i don't wait on this call the message may or may not go through it all depends on how your server is feeling at that time uh because what happens is if you don't await threads might terminate prematurely it's a whole mess and then you call me and i charge you a lot of money to fix it uh so we'll wait on our call seeing text box go over to the client so let's go ahead and set up the first event so on our connection we can say on sync text box let me pass in a function so this is the local event sync text box which correlates to the call that we're making on the server so technically when the server calls the the clients to say hey sync text box here's the text this is the method that gets called we are going to take our text box and set the value on it to text yay now what if i'm the one changing the text box all right i have the event handler set up for the text box all the fancy html stuff we are going to go to our connection and we are going to ask it to invoke a method on the server called sync text box uh casing doesn't technically matter here and i'm going to pass in the value of target the value type something in text box hit tab enter whatever calls a change event assuming we have an existing connection we're going to send a message to the server telling the server invoke sync text box with a given value it's going to execute that method sync text box passing in the parameter which will turn around and send the message to everyone else now that all sounds good let's make sure i actually coded this correctly nope oh oh hey uh remind me what namespace task is in hey who said that there you go it's a task or tap just system that's ready here i'm going to owe you a cookie nope never i don't owe you a cookie hey there you go all right thank you sir all right let's assume everything connected there's no console errors oops all right everything's good in the console now i'm going to create two instances of this app from side by side and i want to type kevin and hit enter when i hit enter we got our local event handler it took this stuff out of the text box send it to the server saying i want to call the sync text box method with the term kevin turns around sends that to all the other connected clients one and it gets the value kevin drops it into text box and we're all good we put these side by side we can show you it goes either way yay now i know that is not the most impressive demo but that is 90 of signal artwork is just handling messages uh between the clients and the server in this form now those who don't like magic strings there's a way to create what i i call them strongly typed hubs where you can create an interface and everything can be strongly typed so you don't have to say send async sync text box um that takes about 15 minutes to get into so i'm not doing it in this talk but i happen to have a course that talks about all that stuff you can get it for 13 pounds all right so for the sake of time i'm not going to wire up the checkbox the checkbox is basically the same thing with a different data type so you notice i'm passing it a string but i could very well pass in a boolean i could pass in a full object what i want to get into now is actually sending more complex objects but selectively sending messages to only people that want to get them that's where these buttons come into play start notifications and end notifications so one of the things that are that's been happening in the background is i've been running this user generator every 10 minutes we go and generate a new user and we don't do anything with it what would be nice is selectively sending that message to uh to our page only for the people that want to receive those messages so let's go into our code let's shut this down real fast and i'm going to add two more methods start notify and then end notify so start notify is going to get called whenever i press the start notifications button and notify when i press end notification you all can figure that out when i press start notify i want to add myself my connection to what we call a group and a group is a logical way to organize connections in your applications so maybe uh when i log in uh i'm an administrator so we might have a special group for just administrators or administrative connections um i might i might be looking at a widget if i have a widget page well a specific widget i might have a group for just updates to a specific widget when we're done i'm going to show you an actual clients application i wrote where we use groups uh religiously but just think of the idea of a group as a logical grouping of of connections so we're going to say groups oh i don't have my intellisense that's all right that's why i have notes i'm going to add to group async and this takes two parameters the first is my connection id now because i'm implementing hub i get clients i get groups i also get this context context represents the current connection for the current tab the current window that's looking at this app and it has a connection id then i need to tell it which group i want to uh add this connection to and we'll just call it notify me all right if i don't want to be in the group anymore or programmatically something happens and i want to be removed from the group i can say remove from group and same thing context connection id and i'll remove myself from notify me excellent let's go back to the javascript all right i have my two buttons now you might already be able to figure out how we call these i can say invoke start notify and then invoke and notify this adds me to the group or it takes me out of the group let's wire up a case where we actually want to do the notification and for this i'm going to bring up my sample code just to copy and paste here we go so on the connection event it will eventually get called new user i'm going to get a user object from the server and all i'm going to do with that user object is display some toast with the person's name and picture and all that good stuff over on the server where am i going to get the notification from i'm taking advantage of asp.net core background services runs automatically runs forever until you shut it down well i have a case here where i go to the server go to a server get a user and i have a to-do and to-do's are fun aren't they i mean it's work you have to do in the future let's go ahead and all right i'll do this part by hand i need to get a reference to all my clients that are currently connected well i can't do that easily because i'm not in the hub i'm outside of the hub and this is another common question how do i send messages to users when i'm not inside of a hub i might be in a controller i might be in another service or somewhere else in the app well everything in signalr depends on asp.net dependency injection so i can inject an ihub context of sync hub and this gives me basically a reference to all my connected clients on this server so now i can come down where i say to do signal r i can send i can say hubcontextclients.all nope group i can pass in the name of the group notify me and tell them to send async nope yep no then i'm right sorry and what's the name of the method i chose new user so on the message new user i'm going to pass in uh was it r dot first dots results dot first all right let's assume again that i did everything correctly this is build the only unit test that matters i failed oh because i was dominant make it private and then down here we'll say oops and that's not right okay let's try it again oh i have a call that's not a way did i bring my own rule yes i did where were you all folks i could have given you money for that but oh well yep let's restart the app because we definitely don't want to test that theory right now we'll restart again let's go ahead and open up a new tab make sure everything connects correctly all right i'm going to say start notifications so what happened underneath the scene oh there it comes right away all right so welcome grayson and we stay here and let it run for a moment every 10 seconds every day hey we have no new person that pops up that's because i subscribe to the notifications for this particular app if i open up another tab and they're both connected all right we get the notification on one side so i'll start notifications on the right side oh wait a moment they should both get the same notification levi cool so we're selectively sending messages only to clients that want to receive them if i hit endnotify on the right side it should not get the next notification because i've removed myself from that group there we go let's talk a little bit deeper about connections and groups now i want to show you more fancy demo stuff all right so there's a variety of ways to filter clients uh the one that we saw earlier so think about our infrastructure like this we have our server we could technically have two or three or 15 or 50 000 servers and all of our connections are connecting to this server if i want to send a message to all my clients no matter what i can filter my clients with the uh the all collection so clients.all will send a message to all the connected clients that want to receive it sometimes i only want to send a message back to the person that made the original call so here when i press send or start notifications i am technically the caller the person making the call to the server now that was client a the server might say well let's send a message back to only the person that made the original call so only i gets that message we saw earlier if i want to send the message to everyone other than the caller i can say clients.others a sends a message in everyone else gets a response this is really useful when you know a already knows the new state so something changed you don't need to confuse client a by sending the information back to it it really depends on the setup of your app now this is where it starts to get really weird you can filter connections uh by connection id so i could say send to all clients except connections c and e so everyone else gets it uh these are filters i've never used in a production application i think they exist because somewhere there was a design meeting where they said oh this would be a good idea to have i've never seen it used clients.client you can send to a specific client so if you know the connection id of a specific client you can send a message directly to them i don't recommend this uh there's a i have a very long article on why you shouldn't do this has to do with scaling and stuff like that but i don't use this method but you could if you wanted to you can specify multiple clients d and f if you and this is uh just a list of parameters so you could stack as many as you wanted to into that list uh if you're using identity asp.net core identity and you have an eye user principle you can use that user principle to identify specific connections if i told signalr to send a message to kevin signalr is going to know based off user principle which connections are kevin and it will send messages directly to those this is hit or miss in my experience on in scaled environments locally it works great all the time scaled environments it can be hit or miss you can specify multiple users so if you know a variety of user principal names you can send to everyone that you want to kevin and sally then we can get into groups this is where i i think the real power single r is i use groups all the time specifically as a solution to managing connection ids because it's so much easier to put connections into a group and send to a group instead of trying to manage connection ids directly or even trying to manage things like user principle i will in every application automatically put an authenticated user into their own group like user kevin that way anywhere in the application if i know i need to send a message to kevin i can send say group dot user kevin i don't care how many connections kevin has kevin will get the message on all his connections and this syntax is the same way clients.group send async call a particular method on the client uh oh so here i have the server and then i have the connections broken up into well it's supposed to be orange orange and yellow so only the orange group gets gets a message you can filter this a little bit more i don't do this either everyone in the group accept a particular connection or particular connections you could do this again i don't think it's worth your time you can send them multiple groups i've done this a handful of times as well so everyone in yellow everyone in orange they all get the message and if you're a caller in a group you could say others in the group with me so i'm connection a i say send to all the others in the group yellow only b and c will receive the message all right now i've talked a lot about groups and all this stuff i've shown you probably the worst demo ever um don't give me yellow on that please or red just give me green i'm going to show you an application i worked with this is a non-profit based in the united states called the national institute of public safety technology and i'm going to bump this font up a little bit for y'all let me explain a little bit how it works in the united states because so over here was in 999 emergency services in the united states we have 9-1-1 and when you live in a populated city like like a new york city or i'm from a place called norfolk virginia we have dedicated emergency services all over the place in some rural parts of the united states you you don't have emergency services right down the street from you uh you might have emergency services in a county over or if they're in your county they're they're miles and miles away uh so the solution to the problem of how do we get emergency services to people as quickly as possible we i worked with this team to work on this system called livecat that aggregates emergency service data from a variety of uh call centers so if you call into our 9-1-1 emergency services and say uh i'm sick or elevator emergency that sounds horrible right now because it is seven in the morning on the west coast on midwest i'm trying to think of the time there yeah it's early and they're stuck in an elevator that sounds bad uh so we're getting information from all these emergency services you can actually tell we have uh different colors for the emergencies uh the services so there's west lc rcc and fair uh they're sending information in our system we're quickly aggregating it and we build these dashboards for different firehouses emergency services and whatnot so the way this works in in the firehouses is someone might be in the firehouse looking at the the screen it's up on a big tv and if something really bad happens that i need to be aware of this screen flashes bright red just over and over again so thankfully that's not happening right now but when that happens it's our cue that we need to suit up get into the truck and head out to to a service call when i first came on to this project if the the request wasn't like the the beginning of the talk hey we're hitting this end point too many times it was worse like way worse uh they had a dashboard it looked very similar to this we didn't change the look too much but it was uh asp.net web forms who's done that all right who remembers update panel all right this was in an update panel the whole thing was an update panel not like little update panels here there the whole thing was an update panel when that update panel rendered it would go to the database the sql server database on the same box as the iis server and it would ask the database all right give me the the trucks the resources we call them give me the list of incidents that i need to know about for a configuration and uh hey by the way also tell me what color everything should be why are you asking the database what color stuff should be so it's doing all this work on the database it comes back builds the html delivers the update panel re-renders the screen and it does that every one second across thousands of clients and they said kevin our cpu is like at 99 all the time and we're maxing out all the memory and i went yeah i can see what we can fix that so we refactored we actually rewrote the entire back end in that it's like dot-net 4.7 it was a newer.net framework it was before core uh and we had used signal r for net framework and it has since been uh refactored again it's now currently running.net 6 it's all up to speed so we went through and we built it with this concept of we're sending data into the server the server is going to aggregate it and it's going to selectively send updates to clients based off of their configuration information so if i look at this list we have all these different configurations of how data can be viewed and they all have their own unique id now i have a signalr group for all these different ids when i go to let me just pick something uh no active incidents let's go back to mecklenburg that's a is it better because there's actually data in here when i load this view behind the scenes we make a call hey add me to the group oh 25 mech 3. so now when the server gets data that comes in and realizes hey oh 25 meg 3 needs to know about this i get a message telling me that something has changed whether it's a resource or an incident but a lot of time like right now it's just sitting here not doing anything because there's nothing to be done we deployed this now we deployed it to an azure app service so we made a change we took them off the box under the table and uh we loaded up and i'm watching things like cpu and memory utilization it was like one percent and people were hitting the server i'm like oh crap i broke it i broke something i i whatever i pushed in production is not working um i'm like oh crap they're gonna sue me this is gonna be horrible and turns out it's working just fine it was just we had taken all the load off the server all the load off the database and we were using a more intelligent way to send data down to different clients so everything was push notifications instead of pull notifications and this has been running in production for years and we've just made updates to things like the.net framework and to.net core and keeping the libraries up to date so the story that happened after we pushed this out they got a write-up in the industry magazine that's uh uh there was some folks in a firehouse they were doing their normal night shift stuff the screen started flashing red and yep that's something we got to do they get suited up they get in the truck doors open truck is out the door the moment the truck is out the door a dispatcher comes over the intercom says there's medical emergency here's the address whatever folks are already out the door they're heading towards the emergency we have designed a system that is faster than a person coming over a an intercom and what's important about that is maybe someone in here you have a loved one or even a person you don't like has had a medical emergency like a stroke heart attack something that requires life-saving attention as soon as possible you know that every second counts and we used a silly thing like signalr to build a system that gets emergency personnel to life-saving resources faster than a human can get life-saving resources to um to a person so that's why i tell everyone i save lives with websockets uh i actually have an entire talk just on that on this app um that i i'm not doing here but this is kind of the culmination of all the ideas and putting into real practice not just theoretical like dumb dumb demo that i had before but i have about nine minutes left i am happy to answer any or all questions that you have thank you all so much for hanging out with me here at ndc i hope you've enjoyed yourself maybe learned something yes sir what point does signal our service come into play without it i've struggled i've got similar work on my app yep functionality put it into search but then i thought well okay i can't see why it's there the all right uh so the question is about the azure signalr service um we're getting into a scaling discussion here uh so i get to tell my story how i ddos myself how about that when if i go back a whole bunch of slides i really should put a slide in here for this because it's super useful uh let's go to this slide this is a good slide i had an app we built on signalr dot net framework uh so old signalr had this philosophy where i could have multiple hubs and only one websocket so uh the the concept was like mvc controllers you'd have a hub for different different ideas so we would create multiple hubs for for accounts for users for this for that and they all used one websocket when we made the refactoring to.net core at the time and they rewrote signalr they changed the philosophy and said well it's a websocket per hub so i have five hubs so imagine five low lines going to each client each client 5 10 15 20 25 so on so on now uh our users would instinctively always have 10 to 15 tabs open just by the because of how the application works multiply that by thousands of users at any one time we pushed the changes live and went oh this is working great until we hit our peak time at 10 am east coast time that first monday and i keep getting emails from the clients that say this server's really slow all right let's figure out what's happening server is restarting itself i had three servers running in a load balance set it kept restarting itself every every couple minutes because it was running out of memory it was exhausting all the sockets on that server because not only was it web sockets html requests javascript request css so on so on just regular api calls so i was killing these servers all right like have you ever had to do a refactoring on the fly under stress like that's that was me for the next 12 hours was refactoring this application to get it down to two hubs while maintaining all the functionality not breaking anything so the the lesson learned there was you only want to have one websocket per app really to bring that to azure single r service uh azure signalr service solves two problems one it takes the socket connections off your server so let's imagine azure's over here your app doesn't connect to your server it connects the azure signalr service and then proxy's request to your server so automatically you're cutting every websocket connection from your server you don't have to manage it that's that's great uh that's great if you have stable predictable load it's not great if you have peaks and valleys um i actually have a whole section on this in the course i'm not trying to push the course but i have a whole section on it uh plus like math of the pricing because it's the pricing is the reason we didn't use it in this scenario uh because our peak was however many thousand connections and they do daily billing and not hourly billing so we couldn't scale just for the four-hour period that we needed it we'd have to scale it for the entire day and wasn't cost effective the other thing it does is it acts as a back plane so if i have multiple servers in a load balance set there's a problem if i have multiple servers load balanced if i say clients.all well that's really just all the clients on that server doesn't necessarily include this all the clients on the other servers so if i'm connected to server a and someone else is on server c then they're not going to get my message we have a way of solving that it's called a backplane you can use redis which is amazing back plane that you can host yourself or you can use azure signalr service that takes care of that for you automatically um so i'm not sure if that answers your question it's a very specific scaling um case where you know if you don't have that many users you don't have that much load azure signal or service is not necessarily for you i wouldn't spend the months 50 us dollars for one can up to a thousand connections um which is actually a good deal but you don't need it oh live cad uses azure signal or service because we have predictable load all the time we know exactly what we're paying for we pay for two units all the time so 100 us dollars to basically take all the stress off the server for socket management the biggest reason we did that was not only our users firehouses and emergency services we also have people that like to listen to police scanners and just are really nosy on what's going on they pay us uh donations so they can see all that data um i'm not sure if that's a is that a thing over here people just like to listen to police scanners and no you're all not nosy like that it's just an american thing okay cool uh i would totally believe that if you said yes kevin that's totally an american thing um but that's that's the big use case for azure signal or service you could technically write your own if you wanted to i don't remember it might have been date of david fowler the gentleman who wrote signalr he does a demo which he might have done at ndc 2020 where he writes basically writes his own signalr service there's not a lot of documentation on that and i think there should be because you could technically write your own if you wanted to but that's an excellent question thank you sir yes uh you would um what i was just talking about having backplane so it's a the servers connect to uh something that lets them communicate with each other whether that's azure signal or service that you have to pay for or you can use your own with redis server or redis um azure redis cache works fine we use that you can set up your own reddit server talk to that man about redis that's that's the cheaper alternative and works great we use it for hundreds of users an hour without any issue and i've other apps where we've done thousands of users but that's the way you solve it is you tell all your servers connect to this redis server and they will pub sub messages between each other so uh the server in new york just sent a message the server in london hears about it picks it up and sends it out to all of its users yes sir i'm sorry say that a little ladder that's a great question the signalr guarantee message deliverability no actually have a whole article on it on my site um there's not a mechanism in signalr to that can acknowledge messages coming back and forth between the servers and the clients the best you can do is the clients can can get an acknowledgement from the server that it successfully sent a message upstream but there's no acknowledgement of the server sending messages down to clients that's because connections are finicky if i'm a websocket and a websocket gets disconnected i kind i know about that immediately but if i'm using server sent events or long polling i might not know about that disconnection for up to 15 minutes after it's occurred and how many thousands of messages could have potentially been sent in that time so signalr doesn't guarantee it their documentation says they don't guarantee it it's more it needs to be used as a pub sub so you're just yelling out into the out into the world that something's happened and whoever needs to listen for that message listens for it but not necessarily there's no acknowledgement model yep the the big thing we do is that we uh we look at connection events so i get an event on both the client and the server when a connection gets cut the client's most important because the client's going to know if it's disconnected before the server does when the client gets connect disconnected gets severed it's immediately going to attempt to reconnect there's automatic reconnection built into well not built in you have to turn it on so automatically try to reconnect when but as soon as it's disconnected we put up a message that says you're disconnected we don't know what the current data is it's it's a big warning as soon as a reconnection occurs which usually it does it reconnects right away as soon as a reconnection occurs we assume all of our local state is bad and we immediately go to the server and says i need fresh state pretend i just loaded i need everything i might need to know and we invalidate everything on the client the cert in the server because the server might still be trying to send me messages and those messages may or may not be getting through so we put it on the client to reestablish the state of the page as it needs to be yeah definitely oh so um uh so the question is that then first that i'm maintaining state on the clients if i have to go back and ask the server for for new snapshot yes yeah so if i go back to livecad i'll just use livecat as an example and i'm a moment over my time so we are maintaining all this local state at the moment uh so i'm using vue.js underneath the scenes with vuex for save management oh see it just refreshed um when i get disconnected for any way any reason as soon as i reconnect i do throw all this away and i go to the server and we have a process we call it a resync i'll say i am looking at this configuration please tell me everything i need to know like don't assume i have any information as soon as i get that resync i start listening for updates um and we just go back into the cycle we had before now for mission critical stuff like this we also have a sub process every couple minutes we'll just do a check in to make sure that we have the current state of the application um we we just assume we just assume local states gonna go might get disrupted for any reason that never happens unless you have a disconnection event and then you know about it um we just we do it as a sanity check just in case uh but you don't it's not necessary um yep stuff's updating oh it's going flash right on it so this is what happens in a firehouse during an emergency um so some trucks get dispatched and hopefully they go put that fire out did that answer your question yes sir yes theoretically the the roslin analyzers for async await should catch it because it's returning a task so it should identify hey we're returning a task obviously you'll want to wait on this uh in that particular case i don't think they had the analyzers turned on or they were ignoring that one yes steve you could just return a task yeah you did yeah you actually don't have to do the await yourself you can just return the task so yeah one more time i don't remember off top of my head all right friends well i'll stick around for a moment if you have any other questions but thank you all for coming out
Info
Channel: NDC Conferences
Views: 10,847
Rating: undefined out of 5
Keywords: Kevin Griffin, .NET, Web, SignalR, Browser, NDC, Conferences, 2022, Live, London
Id: wOq9meKcdlw
Channel Id: undefined
Length: 66min 4sec (3964 seconds)
Published: Tue Aug 02 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.