Building Micro-services with GO | Part2
In case you are landing here directly, it’s strongly suggested that you go and read through this for fundamentals.
In this blog, we shall be looking at following concepts :-
- Demonstrating the usage of HttpHandler Interface’s ServeHttp method.
- Launching multiple Handlers within same Server.
- Handling of Timeouts.
Question :- Demonstrate exposing APIs in GO using HttpHandler Interface ?
Step #1.) Here is straight about HttpHandler from documentation :-
Step #2.) Now, we shall separate out the our handler to the separate file.
- Line #18, is what we require to satisfy the HtttpHandler Interface’s requirements.
- Line #14, is how we are defining the Idiomatic way of defining the way to initialise the object.
Step #3.) Now, we shall separate out the our handler to the separate file.
- As a general principle, we should avoid creating direct objects inside our handlers. The reason for that is : when we start looking at our testability, we should be able to replace those aspects and use something called as Dependency-Injection.
- At line #20, we have precisely used the injected object of logger, to print something to the logger. This is something known as Dependency-Injection and we shall be using it heavily, when we interact with the databases.
NOTE.) Revisiting back the earlier way of server exposing in GO
With HandleFunc() function, we are converting the function into an handler type and we are registering it on a thing called as DefaultServeMux. The DefaultServeMux is a server multiplexer and is able to invoke the correct handler basis of the path.
Step #4.) Let’s code our main function, in order to invoke the aforesaid (Step #2) written handler :-
- Whenever a request comes into your server, the server has a default handler. That DefaultHandler is HttpServeMux. The server is going to then call http.serveMux.serveHttp() function.
- At line #20, The ListenAndServe() method also takes in two parameters. The first one is port where server shall be listening. Second one is the handler. If we don’t specify anything, it shall be DefaultServeMux. Recall that, DefaultServeMux also implements the Handler Interface. In our case, we shall be defining the CustomServeMux for us.
Step #5.) Let’s no execute the aforesaid code and observe behaviour :-
Question :- Demonstrate another handler into the same Server now ?
Step #1.) Here is another handler that we defined in a separate file. Note that, name of the file is not so crucial, but it should be readable enough. The package name is very important. Again, we are using the ServeHttp() method from HttpHandler interface.
Step #2.) Next, At line #15 below, we declare a new object of this handler. Also, we then bind it to the CustomBuiltServeMux. We can see that, another endpoint has been exposed and is working fine in the output section in console.
Question :- Why Timeout is important ?
Answer :- Timeouts are important because :-
- Resources are finite and there are only a limited connections that a particular server can handle. Example → Say a client connects to server and starts doing something and it just pauses, that would be considered as a BlockedConnection.
- When there are such multiple blocked/paused connections, our server would eventually fail to serve fresh requests.
It’s kind of a very basic DOS (Denial of Service) Attack.
Question :- Let’s demonstrate the Timeouts into our Codebase ?
At Line #23, we are creating our own CustomHttpServer with following parameters configured into it :-
ReadTimeOut → The maximum amount of time, that our server shall be reading from the client. Example → Say we are reading a relatively larger file, then we need to have a larger ReadTimeOut.
WriteTimeOut → The maximum amount of time, that our server shall be writing back to the client. Say our server is writing some tiny blocks of JSON, then we can effectively have small value for this.
IdleTimeOut → This is all about the connection-pooling. When a connection is made to your server, establishing that connection, tls handshake, etc. is not free. This is a costly operation.
- Example → Say we have lot of requests from a single client, we can optimise the same, by keeping our connection open. The Client could potentially use same connection over and over and over again. This is particularly useful in situations where we have multiple micro-services talking to each other and we can be performant by sharing the connections i.e. by creating the persistent connections. As we would want to keep those connections alive, we can keep a Higher value for IdleTimeOut.
- On the other hand, if we are getting requests from various random clients, then we can keep the value for IdleTimeOut to be quite Low.
Question :- What’s the importance of Gracefully Shutting Down the connections ?
Answer:- This is directly related to the RELIABILITY pattern. Say, we wanted to upgrade the version of our Micro-service and we decide to shut down our Server.
- If we are dis-connecting directly, we stand at a 100% risk of dis-connecting the clients dis-gracefully. Basically, we are saying that : Client is not allowed to finish it’s work-in-progress and this is quite worry-some.
- Say we are uploading a large file to client OR we are finishing off the database work, we would get enough time, if we have shut down the server gracefully.
Question :- How do we actually gracefully Shut Down the Server ?
Answer:- We use something known as ShutDown() hook, which would be waiting for all the connections to complete their work gracefully. As we immediately call Shutdown() function, the Server would no longer accept the new requests, but it would wait for all current requests (In Transit) to be completed and then server shall be gracefully shut-down.
Question :- Explain and demonstrate ShutDown() function practically ?
Answer:- This function receives Context Object and there are 2 parameters to it. See line #46 below :-
- timeout → This is the time, that we want to allow to the server to gracefully shut down all connections / requests. If there is any work, which any of the handlers are still performing after this timeout, would be forcefully shut-down.
Following are code level changes that we did here :-
- Following are set of dependencies :-
- Following is how our handlers are tied to the server :-
- We are starting the server via the Go-Routine, so that it doesn’t blocks the main function flow.
- Whenever Interrupt or Kill Signal is being received received on system, it’s going to send a message onto the channel. We are then reading the message from this afore-created channel and we know that, we shall be blocked until we receive any message onto this channel.
- Say we receive either Kill or Interrupt signal, then same shall be consumed onto the channel, then we are going to shut everything down.
- Here is actual code of our handler, which would serve the Http Requests. Any Go based code, which makes uses of httpHandlers would have to mandatorily provide implementation for the below method :ServeHttp() :-
- Finally here is how output looks like :-
That’s all in this section. If you liked reading this blog, kindly do press on clap button multiple times, to indicate your appreciation. We would see you in next series.