a microservices system is dynamic and we could have many services being created or destroyed over time microservices also need to talk with each other so managing the addresses of all the services that you need to communicate with is a tough problem luckily we can easily solve this with service Discovery service Discovery in a microservices system allows us to introduce a central service registry that's going to manage the addresses of the microservices that are part of our system what this allows us to do is to assign a lot logical name to a micros service and then reach out to the service registry to determine what is the physical address of that service so that you can send a network request to that service let's illustrate this with an example where we have a client that's trying to communicate with free microservices now a client could also be another microservice inside of our system this isn't too relevant for the example let's say we have a product service a search service and an ordering service our client wants to communicate with all of these Services individually in order to achieve this the client needs to know the physical address where this service is deployed one way to solve this is to hard code the physical addresses of all of the downstream services and then we would be able to call them and execute the behavior that we want to implement where this becomes problematic is when we have a large number of services that we need to integrate with managing all of those physical addresses is a problem on its own and it becomes even more interesting when we scale out our services and each Downstream service could have multiple physical addresses how do we figure out which one we want to call so let's define our problem as service URLs can change over time let's pick one service for example the product service from the previous example and let's say that we scaled it out to 10 instances these are 10 physical addresses that we need to know how to reach we could solve this by introducing a load balancer in front that's going to Route requests to the available services but then the load balancer also needs to know the physical addresses of the downstream Services what could also happen is one of the is going down and being replaced with a different one with a new physical address a very interesting way to solve this problem that I want to explore in this video is service Discovery there are a few popular solutions for implementing service Discovery out there and the two most famous ones are hash Corp console and Netflix Eureka I'm going to be using hash Corp console for the remainder of this video but the general idea behind service Discovery Remains the Same irrelevant of which service registry we are using so let's go back to our previous example with the product service search service and the ordering service now we introduced another component into our system which is the service registry and let's say that the actual service that we are running is hashicorp console what's going to happen next is when we start our services they're going to register their physical address with the service registry in this case that's going to be the console instance that we are going to be running this also changes how the overall flow of sending requests to the downstream Services looks we already discussed the first step which which is the downstream service let's say the ordering service is going to register with the service registry which is a console instance the next step when our client wants to send a request to the downstream service the client is going to perform a lookup with the service registry which is going to give it back a URL that it can use to call the downstream service what's very interesting here is that we can assign a logical address for example just ordering service for the actual Downstream service and we are going to use this logical address for the lookup with the service registry this is going to give us back a physical address that we can then use to call the actual service if we now look at the entire example we have our fre microservices that are registering their physical addresses with our console instance which is the service registry the client is going to use logical addresses for example the ordering service the search service and the product service which aren't actual physical addresses and is going to use these to reference the downstream services at runtime since we can send a request to a logical address right we're going to perform a service lookup with our service registry which is console and this is going to give us a physical address which we can send a network request to and process back the response now let's jump into the code and see how we can implement this in asp.net core this is the system that we're going to use for the example we have two API Services a newsletter API and a newsletter reporting API right now these services are communicating through messaging with a rabid mq message broker but we also o want to introduce request response communication between these services using HTTP requests so what I'm going to do is to copy the get article feature and this is a vertical slice from the reporting API and I'm going to create a slight variation of that feature in the newsletter API I'm going to call this the get reporting article use case this is going to be the get reporting article vertical slice what I'm going to do here is to define a typed HTTP client that I'm going to use to send the request to the downstream service which is going to be a reporting API so let's create a class called client and I'm going to inject an HTTP client instance that's going to be configured with the Base address of the reporting API to start out we won't be using service Discovery now I'm going to expose just one method on this strongly type client that's going to return my response this is going to be a nullable reference type and I'm just going to call this method get async it's going to have one argument which is going to be the article ID and then how we get the response is by saying await HTTP client get from Json async I'm going to specify the response type and the downstream route is going to be API articles and then ID remember that I'm going to configure The Base address to point to my reporting API and finally let's just return this response I'm also going to expose a minimal API endpoint let's call this the get reporting article endpoint I'm using Carter in this example so I'm going to implement an i Carter module and this allows me to map my endpoint by calling map get so let's say that the route on the newsletter API is going to be API SL articles the ID of the article that I want to fetch and then reporting to represent that I'm going to send this to the downstream service inside of my request delegate I'm going to accept the identifier and the client remember that this is going to be my strongly typed HTTP client my implementation is just going to be using the client to call the one method that I have which is get async and then if the article is null meaning it doesn't exist with the downstream service I'm going to return results not found otherwise we can just return results. okay and specify the article instance and the one thing remaining before we can test this out is for me to register my strongly typed HTTP client so I'm going to go into my program file and let's say right here before we build the web application I will say Builder services at HTTP client and I'm going to specify the get reporting article vertical slice and the client instance now what I actually want to do here is to configure the HTTP client and I'm going to set the Base address to a new URI and we could be fetching this value from our application settings but it doesn't really change anything because it's still hardcoded in the application settings so I'm just going to hardcode it here and the URL is going to be HTTP and then reporting API which is the name of my container in the docker compos file I'm going to show you that in just a moment the port is going to be 8080 and this is pretty much our Downstream address if you are confusing why I'm specifying reporting API here it's because this is the name of my service inside of my Docker container and I'm also specifying my internal Port that's Exposed on the container instance which is 8080 now let's do something simple and let's say we update the URL over Downstream service to reporting API 1 however our hardcoded value here is still reporting API so this is going to be a mismatch and you can imagine that this becomes even more difficult to manage when we scale out the reporting API and we have many instances coming and going now a better way to solve this instead of hardcoding the URI is to introduce service discovery which I'm going to show you in just a moment but let's actually test out our implementation and confirm that it's working of course I'm going to fix the URI here so that we go back to the URI that I specified here so now I'm in the Swagger UI of the newsletter API and I'm just going to create a dami article this is going to give me back the article ID which I'm going to fetch through the API endpoint exposed by this API now this is going to send some messages to our reporting service which is going to cause an update to the article instance in the reporting API database and now if I try to fetch the article through the Articles SL id/ reporting endpoint and I go ahead and send it this request we're going to hit the breakpoint in our typed client instance if I open up the HTTP client and we observe the Base address you can see that this is going to be the hardcoded address pointing to our reporting API now if I go and send this request you can see that we get back a response which means that our HTTP request is working so I'm going to hit continue and this is the response that we get back in the Swagger UI so the article is present and it contains some records in the events array so let's go back to our solution and see how we can refactor the to use service Discovery the first thing we are going to need is our service registry which is going to be console and I'm going to run the console instance inside of a Docker container so let me Define this container in my Docker compost setup let's create a console service I need to specify the image that I want to run for this container and this is going to be hashy Corp console and then let's use the latest version I also like giving my containers a descriptive name so let's call this newsletter cont console just following the naming convention that I already have in place and I want to expose some ports for my console instance and the default Port is 8500 so I'm just going to expose that one now if you're entirely new to this concept just understand that console is going to allow us to maintain a database of physical addresses of the downstream services that we want to call our services are going to register their addresses with console and console is going to expose these addresses through a logical key lookup if you want to learn learn more about the console service and what you can do with it I'm going to leave a link to the documentation in the description of this video where you can check out the many interesting things that you can do with console now let's go back to our example this is all we need for the console setup and the next thing we need is a way to connect to the console instance so I'm going to use an open source Library called Steelo service Discovery and I'm going to install the steel toe Discovery console nugget package let's go ahead and install it in both of our services because both of them will need to talk to the console instance so I'm going to install the nuget package and then let me show you what we need to do to configure it if I open up the program file of the newsletter API all I need to introduce is say Builder services and then call the add service Discovery method which is introduced by the library that we just installed and I'm also going to provide a delegate to say that I want to be using the console Service as my service Reg let's also apply the same line of code in the newsletter reporting API right over here and then the next thing we need to do is to provide some application settings to connect to the console instance so let me open up my app settings development Json file in the newsletter API and I'm going to Define another section here that's called console inside of it I'm going to specify two values one is going to be the host which I'm going to just assign the value of console and this is the service name of the console instance in my Docker compos setup so this is how my service is going to reach the console instance the next setting I want to provide is related to service Discovery so I'm going to define a discovery section and I'm going to set the register flag to false because I don't want the newsletter API to register with my console instance I only want to set this up for the newsletter reporting API because that's what I'm going to be calling as my Downstream service so let's add the console section here and in this case I'm going to configure some different settings in the discovery section so first of all let's give our service a logical name this is going to be the reporting service and this is how our other services in the system are going to be able to reach this API I also need to assign a host name and this is what we're going to send to console so that it knows how to resolve the reporting service as a logical name and convert that into a physical address and I'm also going to specify the port where you can reach this instance this is going to be the 880 Port which is exposed on my container instance now if you're wondering where I'm getting all of these values I'm going to refer you to the documentation if you head over to the Steelo website you can find the documentation for the service Discovery Library the section that we want to check out is the Hashi Corp console section which contains the actual configuration settings that we need to provide in order to connect to the console instance and this is how you can figure out what you need to set in order to register with your console instance we're not going to be going through everything everything just know that I'm not making up these application settings all of this is well documented for the service Discovery library now let me show you how we are going to use the service name that I specified here which is The Logical name for the newsletter reporting API I will go back to the program file of the newsletter API and here instead of hardcoding the downstream address and the port I'm just going to specify HTTP and reporting service and then the magic happens when I say add service Discovery and this is a method that's exposed by the Steelo service Discovery library and what this is going to do is reach out to console and perform a service lookup with this logical name console is going to respond with a physical address of the respective Downstream service and if there are multiple Downstream Services we can introduce load balancing for example I can introduce a round robin load balancer and then the client Side Library is going to apply the round robin algorithm when choosing between the available Downstream uis now let's go ahead and start the application and I'm going to show you how all of this works with service Discovery implemented if I open up Docker desktop you can see that our console instance is running and you can go into the console instance and observe some logs the more important logs are going to come from our two apis and here you can see the log from the steel tow Library saying that the newsletter reporting API is registering with the console service using this logical name our other API isn't registered in with console so we won't find any logs there I also want to show you what happens when we navigate to Local Host 8500 where we can find the console dashboard this is what you will find if you navigate to the console dashboard and it contains the servers that are part of this console cluster we only have one server instance so if I navigate into this server instance I can see some health check statuses for the noes that we have if I go over to Services I can see that we have two services in total one is the console service service and we also have our reporting service which is the newsletter reporting API here you can see what is the downstream address which is the physical address of the service and this is what's going to be resolved when we try to call the service using service Discovery if you go into the actual service instance you can look at some health checks and other data so you can see that our reporting service is healthy now let's try to send a downstream request and see if service Discovery is working as expected I'm going to send a request using the Swagger user interface and we hit the breakpoint inside of our typed client now if I open up the HTTP client instance you can see that we are using the logical address of the reporting service now if I actually send this request you can see that I get back the same response so service disc Discovery seems to be working if I open up Docker desktop and go into the newsletter API you can see some interesting logs here and what I want to highlight is this log here that we are starting to process an HTTP request for the HTTP reporting service and then this is the rest of our route now notice that we are using the logical name of our route which is reporting service and then we are actually sending the request to the reporting API 880 Port so this is our service Discovery in action which is performing a service lookup for this logical name and then Translating that into a physical address where we can actually send an HTTP request I'm going to press continue here and we are going to get the response back in our Swagger user interface since I also I have distributed tracing configured I'm going to open up the Jagger user interface which is my distributed tracing platform and I will look for traces for the newsletter API service and then I'm going to specify the operation for the endpoint that we just called and you can see that I have one Trace here so let's open up and see what we have inside so first we are sending a request to our API which is this operation here and then we have another span which I want to expand and this is our actual call to the console service so you can see this is an API request to our console instance and then specifying the reporting service in the route which is The Logical name of the downstream service that we want to call then you're going to see another request to console using the physical address of the reporting API and finally since we resolved the downstream address we can send a request to the reporting API on the respective Port which is going to process the request and give us back to response so the following two spans are in the scope of the reporting API service which is going to reach out to the database and give us back to response if you want to play around with this example of implementing service Discovery with console you can find the source code for this video on my patreon Community page and if you're just getting started with distributed systems then you should watch this video next where I'm building a microservices system from scratch check out my clean architecture and modular monolith courses and until next time stay awesome