Give your Go app superpowers with an Embedded NATS Server

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
so net servers are pretty neat you can Lego brick them together into any sort of arbitrary topology and we've covered a lot of that on this channel from Leaf nodes to clusters and super clusters but did you know that you could actually if you using the go programming language embed aat server directly into your application and this yields some pretty interesting benefits one you won't have to distribute gats alongside your application it's just in there and two we can actually bypass the network interface all together and directly talk to Nats in process and so today I'm going to go over how to write a go program that embeds the natat server directly and we'll explore some of the options available to us when we do so so let's get straight into [Applause] [Music] it but before you do be sure to hit that like And subscribe button if you like content like this I'd like to produce more of it so getting more feedback from y'all is going to be really really helpful to keep working on this channel let's move on to the video so this feature is not really welln but we're starting to see more and more folks use it because I think there's a really interesting Niche use case around it um or a couple interesting Niche use cases around it and that's taking the natat server and embedding it directly into your application now just as a little uh primer uh a typical Nats architecture involves you know a client server architecture where you might have a client inside of your application a client Library that's making a network connection to the n server um and these n servers could be it could be a single server or a cluster of servers um and they're all coordinating between one another um but typically you're going to have latency over the network that's going to be affecting your performance um in this case and this makes a lot of sense if you're building web applications and you're using Nat's server as kind of your middleman messaging layer um to to be able to have that network connection and have Nat's kind of broker all the information between these clients but what if you're building something like a modular monolith or a offline first desktop application and this is your kind of back end that that also kind of runs in the background um you might want to utilize n server in a slightly different way there's an option if you're using the go programming language to actually take the natat server and put it directly inside of your app um which is great because it means the natat server runs you know uh alongside your code um in the same life cycle when your application shuts down Nat server shuts down etc etc and you still get all the same benefits of you know um the natat server in general being able to have multiple things you know talk to each other um using Nat's jet stream um even using things like Leaf nodes which we'll get into uh in just a second here but there is already a lot of benefit to just being able to put Nat server inside of your application um for instance if you're Distributing a desktop application you might not want to distribute your binary and say you have a dependency of natat server hey go install it or here you know we'll run it for you um you might not want to worry about you know making sure Nat server is still you know always running alongside your other code um so it might be more beneficial to just bake it all into one binary so when your code's running the NAT server is running and vice versa um now one of the issues here is even though you're embedding Nat server you're still connecting over the The Local Host loop back interface which means you're still incurring some Network latency and you're still going through the network stack and this might not be a problem performance-wise for many many many applications but we've also offered some other options when you're embedding a at server that I think are really cool and can um lead to some really interesting use cases and that's using our inprocess connection layer so not only are you embedding natat server inside of your application but you're also creating a direct connection between the client and the natat server so we're not going through the network layer we're literally just pushing bites around inside of the application um to service that and again you still get all of the cool features of gats from multi-tenancy to jet stream you know key Value Store Object Store and that lives real close to your code um and this could be really beneficial for a lot of reasons maybe you want to have a unified RPC layer that no matter where your client lives they can still talk to each other over the same kind of gats protocol interface um and and I think this makes for a lot of really interesting architectures especially if you're creating things like modular monoliths where you want to package your entire application together but you want still a separation of concerns and Nats can be that protocol that provides that separation of concerns another interesting reason why you might want an at server is if you're creating a uh application that really kind of is represented as infrastructure and you might need these things to Auto mesh together or to Leaf node into each other automatically and you can embed an at server have it preconfigured you know for what you need and basically make all these applications easy to set up um and connect to each other we've also seen use cases like that and so um this leads to some really really cool stuff um especially when you start combining lot of the other gats features that are available to us and so let's say we embed This natat Server directly in our application and we're using the you know direct inprocess connection to communicate between our clients and our Nat server inside the application so it's super super fast um but then we might want to take our data you know that's in Jetstream and we might want to um actually sync that over to the cloud on a remote n server over here and we can actually do that with Leaf nodes we have lots of options in terms of hey do we want to use the network do we want to not use the network um and we can have a lot of control over you know how we decide to do that so what are some other use cases for being able to build applications like this well some of the things that I think about you know that come to mind first is offline first applications maybe you're building a desktop app and you want to use Nats to kind of you know or Nats Jetstream to store your data Maybe key value things like that and you want to sync it with the cloud well you can do all of that um I already spoke a little bit about modular monoliths and how you know gats can be that layer of uh you know separation of concerns and that way it can be very very easy to um break these things out into microservices later you keep them as microservices the entire time but for performance reasons or for um you know deployment ease reasons you might want to bundle it all together into a single application um I've also used this um quite a lot for you know platforms with no network or in my case I'm using it for wasum um where I might not have a network layer defined in the case of wasm you really and so you kind of have to shim out a network layer and um I actually do this in the browser where you can actually run an at server in the browser and you could talk to it with an ad's client um and that's all happening over um you know kind of a shim that's happening in the browser which is really really neat because it opens up a lot of other possibilities um I think some other benefits is like you get this unified RPC layer no matter if you're in process or running locally on a box or trying to talk to something across the world you have this like um you know this single protocol that you use to be able to communicate with things um and then like I said projects that may be using gats under the hood where the end user has no idea about gats but these things are connecting using gats as a backbone to be able to like connect uh to each other and mesh and things like that and so um that's just a quick overview uh but I wanted to spend more time actually you know diving in and writing the code for this and kind of exploring what it looks like and how easy it is to embed nats inside of your application so so I've created a little go project um with really nothing in it um but I've imported the uh the go client Library as well as the natat server and so we can actually run both of these things inside of our go application so um now we're going to want to do a little bit of benchmarking at some point here so I'm going to actually extract uh running all of this server and client stuff um into a function so I can kind of Benchmark it later so um I'm going to just create a function called run embedded server and this is going to return uh three results it's going to have a Nats connection that is going to return uh which we'll import right here and then we're going to have a Nats server that we're going to also return so I'll just say server server and an error um and now we need to import This natat Server code um which it's Nats IO um Nats server V2 and this is what you'll be importing with go mod and then just the server package um and that's what gives us access to our NS server so let's go ahead and I'm going to fix this run embedded server we're missing a d there we go um so now we can start writing this code to uh instantiate our server instantiate our connection to that server and then we can kind of play with the different performance differences um and options that we have available to us so let's first uh create a set of options for This Server we just simply go server. Ops and we're going to leave the defaults for now but um sorry it's options um we're going the defaults for now but uh this is what we're going to pass into the server and you can kind of think about this as like your config file um you can Define all this stuff you know as you're instantiating your server which is nice um Now to create a server we're just going to say uh not server and error is server. new server and we're going to pass in our options which is just going to be the uh the default let's go ahead and um if our error is not nil we'll return nil nil and error okay um now we got to use our natat server and so uh uh We've created our natat server and now we need to fire it up so the first thing we're going to do is um we're going to say ns. start and this is going to start up our server but we want to run this in a go routine because we want to do other things alongside This Server and one of the things we need to do um before we connect to this server with our Nat's client is we actually need to wait um for it to be ready for connections so aat server has kind of a startup phase where it's going to you know spin up all its go routines and do all of its checks and things like that and that kind of happens asynchronously especially when I'm running this inside of a go routine so um we need to have a way to kind of check uh whether or not go is up and ready to run and the way we do that is with the ready for connections method so we're we can say ns. ready for connections and this returns a Boolean and we just give it a timeout um so it's going to you know uh block until it's ready for connections it's going to time out with a false um if we uh if you know it's it's not able to do it in that time so we can say uh like five seconds um I think that's probably good for us um and we want to check this we want to say if um we're not ready for connections after 5 seconds then we can of course just you know turn nil nil and we'll say uh errors. new and we'll just say uh you know Nats server timeout and I think that's fine for now Okay so we've started our server we're checking for connections and now we can simply create a uh a new client and connect to it and we'll test this out so um we'll say Nats connection and error equals ns. connect and since we started this with really no um options all the defaults this is going to actually connect over the default URL so we could just say ns. default URL to access our server another way we can do this is we could actually just say uh Nats server. client URL and that's going to give us uh the URL that we've instantiated the server on which is great um we'll check for errors and finally if that um was okay we'll just kind of return our uh Nat's client our Nat's server and um no for our error okay great so we've written this function uh now we got to use it inside of our main function so we'll just say uh Nas client or Nas connection Nat server and error is uh run embedded server we'll check if error is not nil we'll log. fatal with the air and then we can simply say natat server um. wait for shutdown and this is going to essentially listen for any sort of like termination signals or anything like that the N server automatically listens for um which is great because it handles all that life cycle for us um now before we actually run this um let's actually have something to test with we're going to use our Nat's client and just create a subscription that we'll respond on so we'll just say Nats connection do um subscribe we'll say hello world and we'll respond with a simple response we'll just say m. respond hello there okay so we've responded to this message um so let's go ahead and run this I'm going to say uh go run this and you could see that we have no output here um and the reason is that we can actually have configurable logging for this natat server but trust me silence is golden in this case since it didn't crash we should be able to connect to this I'm going to go ahead and say Nats context select default to just point the natat CLI to my default server and I can say natat request hello. World high high and you can see that I get a response which is great so I've essentially just created an application that runs the natat server but also alongside it has kind of a little microservice or uh request reply responder um so that's awesome um I'm already getting a lot of benefit out of embedding an at server but I think we could take it a little bit further um so let's go ahead and explore a couple things first we'll kind of configure logging so we can actually see the natat server uh out log output here um we'll also turn on the ability to have inprocess connections and we'll actually Benchmark this and see kind of how fast these inprocess connections are versus going over the network um and then lastly let's turn on jet stream and maybe even do a little bit of leaf node Shenanigans here so going back to our code we're going to modify this run embedded server uh signature to have a couple uh booleans we want to have an inprocess Boolean um and a enable logging Boolean and let's start with enable logging cuz that one's the simplest um to enable logging we first need a reference to our n server and we just say if uh enable logging we can say Nat server. configure configure logger um and that's simply going to turn the NAT server logging on um so if we can actually test this we can say in process is set to false for now and uh enable logging is set to True let's go ahead and run this and you can see we now have output about the N server which is great and it also tells us that we're listening on Port 4222 so we are indeed going over the network um okay great so now let's uh Implement our second piece which is the inprocess connection so there's a couple pieces to this um but basically that both the client and the server have the ability to kind of um shim out the network um or provide a different interface for that network connection um so we don't we can bypass the network all together and really just use kind of an inmemory way of passing data around so um so the first thing we want to do is and this is completely optional but we actually have an option in the the natat server options to say don't listen and what this is going to do is it's going to um basically if we set in process to true it's going to tell the NAT server to not listen on any port so you're not actually listening over any sort of network interface altogether now you don't have to do this you can actually have an impress connection and um a network available to you but just to kind of illustrate what this looks like if I set don't listen to um true let's go ahead and set this to True over here and I go ahead and run this um we're actually going to close and the reason for that is that Nat's client tried to connect ran into an error and we did a log. fatal so we actually crashed the program um but the interesting thing is is the server was ready and it didn't actually have that log line saying that it's going over the network um which is great um so you can actually create this like really closed um application and use the N server without actually exposing any sort of you know network interface to it which I think is really cool um but let's actually uh we'll keep this around for now but uh what we'll actually do is let's set up that in process connection so that client can actually successfully connect to the natat server and so the way that we do this is we need to pass in a particular client operation or a client option um into our connection logic so that uh the client can know that it needs to connect directly to the snat server using an inprocess connection so let's go ahead and uh right above our connect call we're going to say uh we're going to Define some client options and this is just going to be array of ns. option s all right and we'll keep it blank for now um but what we want to do is if we set in process to true then we want to append uh new client options uh client Ops ns. inprocess server um and the Nats server now the cool thing about this is this inprocess server is just a option that you pass into the the Nats client um but it actually takes an interface called The inprocess Connection provider and so um while this is being provided directly by the natat server you can actually create you know whatever interface you want for this uh which is really nice because you can go you know shuffle this data via memory or you can go you know I guess over a different like Network protocol um besides TCP if you wanted um there's some really interesting things that you could do here so uh I think this is pretty cool um let's pass this option into um our connection and let's test this out okay so now we've successfully connected because we don't have an error um but we obviously can't talk to this uh to this directly because that don't listen is not set if I go ahead and just comment this out for a bit we can kind of test out what this looks like um so that client that's listening on hello world is actually talking directly to the natat server instead of going over the loot back interface so if I say uh natat request Hello World um I still get a response here which is nice and we actually have like much better performance because we you don't have to go over the network interface uh now how much better performance let's actually find that out back to our code I'm just going to go turn don't listen back to on um and we'll close out this server and I've actually set up a benchmark um this Benchmark is going to test the inprocess connection as well as the loop back interface and kind of see like how much faster the uh inprocess connection is so uh let's go ahead and run this Benchmark and um see what we get okay so now that we ran the Benchmark you can kind of see that we have um you know a pretty significant you know 4X um even when I was uh benchmarking here without running my uh my video recording I was getting like a 5x Improvement um over you know over a loop back when you're using impr process and so you might not even need this performance because this is like measured in you know 16,000 nanocs um and this is kind of like a this is a full synchronous request reply that's happening by the way um but as you could see like we can eek out a lot more performance by doing in process connections which is really cool um so you don't have to worry about like incurring any overhead about go uh around going through the um the the network interface you could actually just like disable it alt together and run a true modular monolith that has a lot of really cool performance with it um okay what's next well let's go ahead and add Jetstream into the mix because you know it wouldn't be fun if it was all just cornets we want to be able to use Jetstream create streams create KVs and let's actually set up a leaf node connection to um to Cadia cloud so we can see how that all can kind of work where we have an embedded natat server inside of our application but we're still syncing data to Cadia cloud and so first and foremost let's add to our configuration here we're going to say server name is uh embedded server and this is what's going to show up when we connect as a leaf node um let's go ahead and say jet stream is true to enable Jetstream and um we're going to also set a jetstream domain and this is important because we are uh what we're going to want to do is we're going to want to connect to Cadia cloud and we want to be able to administer our you know jet stream maybe create a stream fire off some events stuff like that um but we need a jet stream domain to be able to identify um all of these stream assets like streams and KVs and and which kind of bucket or uh server not system they belong to um so I'm going to call this embedded as my jet stream domain and we'll use that a little bit later and then we're going to actually fire up a leaf node um configuration ation block and I'm just going to copy and paste over here um what this is going to be doing is it's going to create a uh a leaf node remote connection um to a URL called Leaf URL which I'll just go create over here um and we'll say that leaf URL is uh it's it's a URL type and so we'll just uh over here and say url. parse and we'll say we want to connect to Nats Leaf connect. ns. Global which is our Cadia Cloud connection URL and if error is not M we'll go ahead and return that perfect so we've parsed our URL for connecting to uh to NGS or Cadia cloud and that's truly all we need so now we have an embedded server that's using Jetstream connecting to Cadia Cloud um let's go ahead and run this and see if uh this is all going to be working correctly go run main.go okay so I have some warnings here um and that was are just from some previous examples that has nothing to do with uh any of this but you could see that we are um running the in the embedded domain that we have jet stream enabled and we also are getting messages that we have a successful Leaf node connection um that uh has has made it over to Cadia Cloud so let's go over to Cadia Cloud for a minute and uh take a look at this so there we go so if I look at my connections graph you can see I have that uh embedded server Leaf node um you can see it it has kind Leaf node over here and if I click into it and scroll down you could see that I actually have a bunch of subscriptions that were made for me and this is subscriptions that are going over the leaf node connection so things like JS embedded API consumer you can see the whole jet uh Jetstream API has been um you know subscribed to uh under the embedded domain and this is how we're able to talk to this server over jet stream um we also see our hello world that's showing up right here which is great because this is our little subscriber that shows up um over the inprocess connection um a couple other you know nice neat little things that were subscribed to but you can kind of see the subscriptions that all exist right here so let's go ahead and go into our CLI and I'm going to switch my Nats context um to this office hour CLI which is on the same account that uh that this Leaf node just connected to and I'm going to actually say Nats request hello. world um hi hi and we're getting that hello there um as a response so I'm basically talking to Sadia Cloud but I'm reaching in to that um Leaf node uh and and that that inprocess connection is responding and then being forwarded up to Cadia Cloud so lots of really cool moving pieces going on here um but you could see that even though you're using inprocess connections you could still you know reach in and all the natat stuff kind of works as as you would expect um which is great not only that but if I do natat stream list these are going to be the streams that are right now inside of my Sadia cloud account um but I can say uh JS domain embedded and then it's going to list out the streams for this embedded context or domain um and there's no streams defined here but I can easily add one I can say Nats stream add and let's call this um I don't know events and let's use all the defaults and it's gonna ask me for subjects I'm just going to say events. greater than and there we go so now if I run Nat stream list we now get um events and this is all you know inside of my embedded natat server um the other thing I can do is start sending uh events to this so I can actually say Nats you know Pub um events. one23 you know hello and let's just give them 100 so this is again sending it to Cadia Cloud but this is actually being picked up by the uh by the embedded Nat server because that um subscription you know has crossed over the leaf node connection so if I go ahead and say Nat stream list again we have 100 messages in that event so lots of power here being able to kind of reach into an embedded natat server um but what if I wanted to take these uh these events on this natat server and I wanted to kind of back them up onto Cadia Cloud well I can easily do that by creating a mirror so I'm going to say uh natat stream ad and and I'm going to say um let's call this events mirror and I'm going to say use all the defaults and I also want to say I want to mirror the events um stream okay so it's going to ask me a couple other questions do I want to adjust the start time for the mirror no do I want to filter or transform it no U do I want to import it from a different jet stream domain yes I do um what's our foreign Jetstream domain it's embedded and so what this is going to do is it's going to create a mirror of that embedded events stream um let's go ahead and check it out Nat's stream list so we're on Cadia Cloud again and we can see that events mirror with 100 messages in it now the really cool part about this is we just created essentially an offline first application where they can use the Nats protocol to interact with jet stream create events key value stores things like that but we can easily back all of these things up to the cloud and Nats kind of forwards all that data once you know we get connected again to that cloud and so lots of really really cool patterns you can express in um in a feature like this and I'm really curious to know how you guys as the community want to use a feature like this and and maybe what other kind of like capabilities you'd like to mix and match um while embedding an at server but um that kind of brings our demonstration here to a close um uh you know go ahead and jump into the comments below and let me know like what kind of ideas you have or where you'd like to kind of utilize this idea of an embedded Nat server in one of your projects I'd love to chat more about it but um this is all for our episode um if you do like you know seeing more of these uh be sure to again give us a like And subscribe and I'll see youall in the next episode [Music]
Info
Channel: Synadia
Views: 2,037
Rating: undefined out of 5
Keywords:
Id: cdTrl8UfcBo
Channel Id: undefined
Length: 27min 23sec (1643 seconds)
Published: Wed Jul 10 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.