Created
September 3, 2019 13:30
-
-
Save kalpak92/e009322c74048ad332e890791e38ee92 to your computer and use it in GitHub Desktop.
A simple Server tutorial to understand the GenServer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule HelloServer do | |
use GenServer | |
## Server API | |
def init(initial_value) do # initailizing the state with the value passed | |
{:ok, initial_value} | |
end | |
# add the value to the state and return ok | |
def handle_call({:add, value}, _from, state) do | |
{:reply, "#{value} add", state + value} | |
end | |
# returns the state to the caller | |
def handle_call(:get,_from,state) do | |
:timer.sleep 2000 | |
{:reply,state,state} | |
end | |
# just reset the state to value 1 | |
def handle_cast(:reset,state) do | |
:timer.sleep 2000 | |
IO.puts "value has been reset " | |
{:noreply,1} | |
end | |
# This executes periodically | |
def handle_info(:work,state) do | |
IO.puts " This message prints every after 2 seconds" | |
schedule_work() | |
{:noreply,state} | |
end | |
#catch-all clause for the handle_info for handling unkown messages | |
def handle_info(_msg, state) do | |
IO.puts "unknown message" | |
{:noreply, state} | |
end | |
defp schedule_work() do | |
Process.send_after(self(), :work, 2*1000) # In 2 seconds | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
— cast
handle_cast( message, state ) :: { :noreply, state }
Handle cast is used in the situation where you don’ t care or expect reply from the server. Suppose, if you want to write a log server which will log details of the request whenever you make an unauthorised request, in that situation you cannot break your flow of execution waiting for reply whether the server has written the log or not. If you do so that will be the weirdest part of your code. This is where the cast come into play. All the log mechanism should run in backside. This is why you should use the cast to make asynchronous requests which do not block execution flow.
— call
handle_call( message, _from, state ) :: ( :reply, return_value, state )
This is just opposite to the cast which I mentioned above. It blocks the flow of execution and waits for the value to return from the caller. The best example to explain this situation is sign in and sign up mechanisms. You cannot simply move to next without getting the proper response from the server based on your sign in request with out validation. Here you supposed to wait for the authentication from the server. This is where the call come into handy. Based on the server response you can redirect the flow.
— info
handle_info(message,state) :: { :noreply, state }
handle_info/2
must be used for all other messages a server may receive that are not sent viaGenServer.call/2
orGenServer.cast/2
, including regular messages sent withKernel.send/2
. This is highly useful in the situations like monitoring the process usingProcess.monitor/1
or if you want to schedule the work to repeat after certain interval of time usingProcess.send_after/4
So, whenever you make a request like
Kernel.send(pid,message)
, thehandle_info/2
callback is triggered in the registered process ofpid
. Thishandle_info
does the asynchronous requests.Example
Sending Messages to GenServer
init
The
GenServer.start_link
will callinit
definition . Whatever you send the input to thestart_link
, it sends back to theinit
. Here we are sending initial value1
to initiate the server state.call
A
call
is synchronous. It’ll return the value upon execution.The result can be assigned to a variable since thecall
returns the value. It blocks the calling process until the value is returned.The two call functions to understand the code flow . At the initial call with message
{:add,200}
, it returns the string immediately, where we cannot observe whether the flow is blocked or not.While coming to the second call with message:get
I puttimer.sleep 2000
purposely to understand that flow is blocked.cast
The cast is an asynchronous. It won’t block the Execution flow. The
virtual-machine
says:ok
I received a message and will process that later.Once you make a call with message
:reset
immediately the virtual machine will say:ok
. Here I purposely added:timer.sleep 2000
which sleeps for 2 seconds, but it won’t block the flow unlike the previoushandle_call
one did. It will wait in background.Mean while you will get the
iex
shell back so you can fire other commands too. So after 2 seconds again it will print the string “value has been reset”.info
All messages that are sent to a process directly (instead of via call or cast) will end up here. It is asynchronous like cast and does not block the calling process.
Unlike the cast and call you have to call this with
Kernel.send
definition, which will trigger the respectivehandle_info
matching with themessage
passed. Here our message is:work
.Inside the
handle_info
we are calling a private function calledschedule_work
which executesProcess.send_after(self(),:work,2*1000)
which again fires thehandle_info
after every 2 seconds.2*1000
.So the
handle_info
callback is triggered when the external requests are made unlike calling withGenServer.call
orGenServer.cast
.Process.monitor
also triggers thehandle_info
definition.Execute
Process.exit(pid, :kill)
to get out of the print loop.