Professional Documents
Culture Documents
Home (/en/)
About you
IassumethatyouarefamiliarwithErlangssyntax,thatyouknowwhatErlangmodulesareandhowtocompilethem
andthatyouunderstandfunctionalprogrammingconceptssuchasrecursionandsingleassignmentvariables.Youdo?
Great!LetslearnsomeOTP.Itisnotdifficulttogetstarted,asyouwillfindoutshortly,butfirstletmetellyoubriefly
aboutmyownErlangexperience.
About me
Iamprettymuchonthesamepageasyouexceptperhaps,thatIvestartedtoreadManningsErlang&OTPin
action.IamnotexaggeratingwhenIsaythatOTPinactionisoneofmyfavouritetechnicalbooks.Itcontainsawealth
ofeasytoabsorbinformation.Buyacopy(http://www.manning.com/logan)ifyoucan.
Scaffolding,Ithink,iswhattheycalleditinpsychologyclasswhenpeersteachpeers.Iamwritingthisasmuchto
cementmyownundertstandingofOTPastohelpkickstartyours.Letsstudytogether.Pleaseleaveacomment
wheneveryouthinkIamwrong.
About OTP
OTPstandsforOpenTelecomPlatformbutinspiteofitsnameitisnottiedtotelecomapplicationswhatsoever.Think
ofOTPasframeworktohelpyouwriterobustandhighlyavailableapplicationsontheErlangplatform.
Specifially,youmightliketothinkofOTPintermsofthefollowingconcepts:
aspecification(calledabehaviour)thatdictateswhichfunctionsyourmoduleshouldexport
aconcretemodulethatdeclaresthatitconformstoabehaviourandimplementsthesefunctions
aruntimecomponentthatcanexecutebehaviourcompliantmoduleswithintheOTPcontext
IfyouarecomingfromonObjectOrientedworldsuchasJavetheseconceptsroughlytranslateto:
aninterfaceorabstractclassthatdefinesabehaviour
aconcretederivedimplementationofthatinterfaceorabstractclass
acontainerthatknowshowtoinstrumentinstancesofsuchconcreteimplementations
Pleaseunderstandthattheseare,ofcourse,onlysuperficialsimilarities.Theymighthelpusunderstandnewconcepts
intermsofoldknowledge,but,makenomistake,wearecomparingapplesandoranges.
?
%%%
%%%@docAsimpleserverthatdoesnotuseOTP.
%%%@authorHansChristianv.Stockhausen<hcatvst.io>
%%%@end
%%%
module(no_otp).%modulename(sameasourerlfilename,
%i.e.no_otp.erl)
%%API
export([%thefunctionsweexportourAPI
start/0,%startnewserverprocess
stop/0,%stopserver
say_hello/0,%print"Hello"tostdout
get_count/0%replywithnumberoftimesthemain
]).%loopwasexecuted
%%Callbacks
export([init/0]).%exportedsowecanspawnitinstart/0
%listedasaseparateexporthereasa
%hinttoclientsnottouseit
define(SERVER,?MODULE).%SERVERmacroisanaliasforMODULE,
%andexpandsto'no_otp'
record(state,{count}).%recordforourserverstate.Rather
%arbitrarilywetrackhowoftenthe
%mainloopisrunseeloop/1
%=============================================================================
%APIfunctionsthatourclientsusetointeractwiththeserver
%=============================================================================
start()>%spawnnewprocessthatcallsinit/0
spawn(?MODULE,init,[]).%thenewprocess'PIDisreturned
stop()>%sendtheatomstoptotheprocess
?SERVER!stop,%toinstructourservertoshutdown
ok.%andreturnoktocaller
say_hello()>%sendtheatomsay_hellototheprocess
?SERVER!say_hello,%toprint"Hello"tosdout
ok.
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
get_count()>%sendcallersPidandtheatomget_count
?SERVER!{self(),get_count},%torequestcountervalue
receive%waitformatchingresponseandreturn
{count,Value}>Value%Valuetothecaller
end.
%=============================================================================
%callbackfunctionsnottobeusedbyclientsdirectly
%=============================================================================
init()>%invokedbystart/0
register(?SERVER,self()),%registernewprocessPIDunderno_otp
loop(#state{count=0}).%startmainserverloop
%=============================================================================
%internalfunctionsnote,thesefunctionsarenotexported
%=============================================================================
loop(#state{count=Count})>%themainserverloop
receiveMsg>%whenAPIfunctionssendamessage
caseMsgof%checktheatomcontained
stop>%ifatomis'stop'
exit(normal)%exittheprocess
say_hello>%ifatomis'say_hello'
io:format("Hello~n")%write"Hello"tostdout
{From,get_count}>%ifMsgistuple{Pid(),get_count}
From!{count,Count}%replywithcurrentcountervalue
end%intaggedtupple{count,Value}
end,
loop(#state{count=Count+1}).%recursewithupdatedstate
EshellV5.8.5(abortwith^G)
1>c(no_otp).
{ok,no_otp}
2>no_otp:start().
<0.39.0>
3>no_otp:get_count().
0
4>no_otp:say_hello().
Hello
ok
5>no_otp:get_count().
2
6>
Iinitiallyfoundthetermserverinthiscontextalittleconfusing.Howisthisaserver?Onsecondthought,itisnotatall
unliketheHTTPserverthatservedthispagetoyourbrowser.Itrunscontinouslyuntilyoustopit,itspeaksaprotocol
(notHTTPbutErlangsinternalmessagingprotocol)andrespondstoinputswithsideeffectsorresponsemessages.
Itsaserver,alright.
Iamnotgoingtodiscussthecodehereindetail,sincethecommentsalreadydothat,Ihope.However,Iwouldliketo
brieflyoutlinethestructure:
ImplementationdetailsarehiddenfromclientsbyathinAPIlayer.Thefunctionsstart/0,stop/0,etc..decouple
clientsfromtheinternalprotocol(i.e.theatomsstart,stop,thatthesefunctionssend)aswellasfrominternal
functions.Asaresultwecouldmodifyeitherorbothwithoutbreakingclientcode.
Thereexistsafunctioninit/0thatsetsuptheinitialstateandstartsthemainserverloop.Italsoregisteresour
server,byconvention,underthemodulenameandcouldperformadditionalinitialisationstepssuchasopeninga
databaseconnection.Notethatthisfunctionisexportedbyourmoduleeventhoughitisnotintendedtobecalled
byclientcode.Otherwiseourcalltospawn/3instart/0doesnotwork.
Thereisamainloopthatwaitsformessages,performssomeactionsinresponse,optionallyupdatestheserver
stateandfinallycallsitself.
bugsinyourcode,itwillcrashandyoucanfixit.Ifitcrashes,becauseofbugsinyourclientscode,letitcrashand
restartinaknowngoodstate,ratherthantryingtokeepitaliveatallcosts.Dontcomplicatethecodebytryingtosort
outyourclientsmess.Itistheirstofix.Gotit?
Supervision
Fine,Illletitcrash,buthowcanIautomaticallyrestarttheserverwhenthathappens?Theansweristocodeanother
process,thatmonitorsourserverandrestartsitwhenitdetectsthatitdied.Suchaprocessisasupervisorandwhat
followsisthecodeforournonOTPversion.
A non-OTP supervisor
BeforeIshowyouthesupervisorcodehavealookattheannotatedErlangshellsessionbelowtoseehowtoexerciseit.
Thefunctionwhereis/0returnsthePidoftheprocessthatisregisteredunderaparticularname.Wecanseethat
sup:start_server/0mustinfacthavestartedourno_otpserver,sinceitshowsupunderprocessID<0.40.0>.Automatic
restartalsoseemstoworkasitshould.
EshellV5.8.5(abortwith^G)
1>c(sup).%compileoursupervisormodule
{ok,sup}
2>sup:start_server().%startthesupervisor
<0.39.0>%itgottheprocessID..39..
3>whereis(no_otp).%let'sfindourno_otpprocess
<0.40.0>%itgot..40..
4>no_otp:say_hello().%let'strytousetheAPI
Hello%works
ok
5>no_otp:get_count().%workstoo
1
6>no_otp!crash.%timetocrashit
crash
=ERRORREPORT===
Errorinprocess<0.40.0>withexitvalue:
{{case_clause,crash},[{no_otp,loop,1}]}%asexpectedacase_clauseerror
7>no_otp:get_count().%butanewprocesswasstarted
0%aswecanseehere
8>no_otp:stop().%nowlet'sstopit'normally'
ok
9>whereis(no_otp).%andcheckforanewinstance
undefined%butthereisnone.Correct!
10>
Now,forthesupervisorcode:
%%%
%%%@docAnonOTPsupervisor
%%%@authorHansChristianv.Stockhausen<hcatvst.io>
%%%@end
%%%
module(sup).
%%API
export([start_server/0]).
%%Callbacks
export([supervise/0]).
%%===========================================================================
==
%%API
%%===========================================================================
==
start_server()>
spawn(?MODULE,supervise,[]).
%%===========================================================================
==
%%Callbacks
%%===========================================================================
==
supervise()>
process_flag(trap_exit,true),%don'tcrashusbutrathersendusa
%messagewhenalinkedprocessdies
Pid=no_otp:start(),%startourserver
link(Pid),%andlinktoit
receive{'EXIT',Pid,Reason}>%waitforamessagethatinformsus
caseReasonof%thatourporcessshutdown
normal>ok%ifthereasonis'normal'donothing
_Other>start_server()%otherwisestartallover
end
end.
AsIwroteintheintroduction,IamanErlangnovicemyself.Ihavelittleconfidenceinthesupervisorcodeabove,and
thatsthepoint.IfwewanttowriteapplicationstheErlangway,ifwewanttoembracethephilosophyofletit
crash,wehavetostructureourcodeintermsofservers,supervisorsandsupervisorssupervisors.Thatsalotof
structuralcodeandalotofrepetitiveboilerplatewithambleopportunitytointroducebugs.Theconceptofserversand
supervisionhierarchiesiseasyenoughtounderstand.However,isitalsoeasytoget100%right?
Inspectthecompileroutput
pressC0toclosethecompilerwindow
pressCxCctoexitemacs
Powerfullstuffandwevebarelyscratchedthesurface,Iamsure,butfeelfreetofollowalongwithyourfavourite
editor.
BeforewefinallyimplementourOTPbehaviours,wehavetolookatOTPfolderlayout.
OTP folders
TocreatetheOTPfolderstructureusethefollowingcommandfrombash,alternativelyrebarcancreatethemforyou.
Onlysrcandebinarerequired.
mkdirpmy_app/{doc,ebin,priv,include,src,test}
docfordocumentationwhichtypicallyisgeneratedautomatically
ebinyourcompiledcode(.beam)
privresourcefilessuchashtml,imagesetc.
includepublicheaderfilesthatclientcodemayuse(.hrl)
srcsourcecodeandprivateheaderfiles(.erl,.hrl)
testtests(_test.erl)
OTP gen_server
Asthenameimplies,thegen_serverbehaviourhelpsuswritegenericservers.HeresourserverrewrittenasOTP
behaviour.
%%%
%%%@docAnOTPgen_serverexample
%%%@authorHansChristianv.Stockhausen<hcatvst.io>
%%%@end
%%%
module(hello_server).%Nothingnewinthissectionexceptforthe
%nextlinewherewetellthecompilerthat
behaviour(gen_server).%thismoduleimplementsthegen_server
%behaviour.Thecompilerwillwarnusif
define(SERVER,?MODULE).%wedonotprovideallcallbackfunctions
%thebehaviourannounces.Itknowswhat
record(state,{count}).%functionstoexpectbycalling
%gen_server:behaviour_info(callbacks).Tryi
t.
%%
%%APIFunctionExports
%%
export([%HerewedefineourAPIfunctionsasbefore
start_link/0,%startsandlinkstheprocessinonestep
stop/0,%stopsit
say_hello/0,%prints"Hello"tostdout
get_count/0]).%returnsthecountstate
%%
%%gen_serverFunctionExports
%%
export([%Thebehaviourcallbacks
init/1,%initializesourprocess
handle_call/3,%handlessynchronouscalls(withresponse)
handle_cast/2,%handlesasynchronouscalls(noresponse)
handle_info/2,%handlesoutofbandmessages(sentwith!)
terminate/2,%iscalledonshutdown
code_change/3]).%calledtohandlecodechanges
%%
%%APIFunctionDefinitions
%%
start_link()>%start_linkspawnsandlinkstoanew
gen_server:start_link(%processinoneatomicstep.Theparameters:
{local,?SERVER},%nametoregistertheprocessunderlocall
y
?MODULE,%themoduletofindtheinit/1callbackin
[],%whatparameterstopasstoinit/1
[]).%additionaloptionstostart_link
stop()>%Notethatwedonotuse!anymore.Instead
gen_server:cast(%weusecasttosendamessageasynch.to
?SERVER,%theregisteredname.Itisasynchronous
stop).%becausewedonotexpectaresponse.
say_hello()>%Prettymuchthesameasstopaboveexcept
gen_server:cast(%thatwesendtheatomsay_helloinstead.
?SERVER,%Againwedonotexpectaresponsebut
say_hello).%areonlyinterestedinthesideeffect.
get_count()>%Here,ontheotherhand,wedoexpecta
gen_server:call(%response,whichiswhyweusecallto
?SERVER,%synchronouslyinvokeourserver.Thecall
get_count).%blocksuntilwegettheresponse.Notehow
%gen_server:call/2hidesthesend/receive
%logicfromus.Nice.
%%
%%gen_serverFunctionDefinitions
%%
init([])>%thesearethebehaviourcallbacks.init/1is
{ok,#state{count=0}}.%calledinresponsetogen_server:start_lin
k/4
%andweareexpectedtoinitializestate.
handle_call(get_count,_From,#state{count=Count})>
{reply,
Count,%herewesynchronouslyrespondwithCount
#state{count=Count+1}%andalsoupdatestate
}.
handle_cast(stop,State)>%thisisthefirsthandle_caseclausethat
{stop,%dealswiththestopatom.Weinstructthe
normal,%gen_servertostopnormallyandreturn
State%thecurrentStateunchanged.
}%Note:thesemicolonhere....
handle_cast(say_hello,State)>%...becuasethesecondclauseishereto
io:format("Hello~n"),%handlethesay_helloatombyprinting"Hell
o"
{noreply,%again,thisisasynch,sonoreplyandweals
o
#state{count=
State#state.count+1}
}.%updateourstatehere
handle_info(Info,State)>%handle_infodealswithoutofbandmsgs,i
e
error_logger:info_msg("~p~n",[Info]),%msgsthatweren'tsentviacast
{noreply,State}.%orcall.Herewesimplylogsuchmessages.
terminate(_Reason,_State)>%terminateisinvokedbythegen_server
error_logger:info_msg("terminating~n"),%containeronshutdown.
ok.%welogitandacknowledgewithok.
code_change(_OldVsn,State,_Extra)>%calledduringreleaseup/down
{ok,State}.%gradetoupdateinternalstate.
%%
%%InternalFunctionDefinitions
%%
%wedon'thaveany.
TocompileourOTPserver,changeintotheapplicationdirectory(i.e.theoneabovesrc)andrun./rebarcompileor,if
youdecidednottouserebar,runerlcoebinsrc/*.erlinstead.rebarautomaticallycreatestheebindirectory,soifyou
useerlc,youneedtocreateitmanuallyfirst.
LetstakeourOTPserverforatestride.Heresmyoutput:
$>erlpaebin#starterlandpa(pathadd)ourebin
1>hello_server:start_link().%startourserver.
{ok,<0.34.0>}
2>hello_server:say_hello().%trythesay_hello/0APIfunction
Hello
ok
3>hello_server:get_count().%observehowtheserverkeepstrack
1
4>hello_server:get_count().%ofstate
2
5>hello_server!stop.%let'ssendanoutofbandmessage
=INFOREPORT===%here'stheoutputfromerror_logger
stop
stop
6>hello_server:stop().%nowlet'scallourstop/0APIfunction
=INFOREPORT===%thisistheoutputfromterminate
terminating
ok
7>whereis(hello_server).%andaswecanseetheprocessnolonger
undefined%exists.
8>
So,whathavewegainedbyturningournonOTPversionintoanOTPgen_server?
Firstofall,anyonefamiliarwithgen_servercanquicklyunderstandhowourcodeisorganized,whatAPIwe
exposeandwheretolookforimplementationdetails.Thisisthepowerofpatterns.
Threedistinctmessagingmechanisms:synchronous,asynchronousandoutofband.
Buttheresmore.If,asthefollowingsessionshows,westarttheSASLapplication(SystemApplicationSupport
Libraries)andthenforceacrash,wegetavaluableerrorreport,somethingournonOTPversiondoesnotbenefitfrom.
1>application:start(sasl).%let'sstartSASL
ok
2>
=PROGRESSREPORT====%I'vedeletedallstartupmes
sages
application:sasl%exceptforthelastonehere
started_at:nonode@nohost%forbrevity.SASLisup.
2>hello_server:start_link().%let'sstartourserver
{ok,<0.44.0>}
3>whereis(hello_server).%andcheckthatitwasregist
ered
<0.44.0>
4>gen_server:cast(hello_server,crash).%nowlet'scrashit
=INFOREPORT====17Dec2012::11:00:56===%excellent,gen_servercalled
terminate
terminating%sowecouldcleanup,close
sockets,etc.
=ERRORREPORT====17Dec2012::11:00:56===%whatfollowsisaSASLrepor
tthatwe
**Genericserverhello_serverterminating%forfree,simplybyusingOT
P.
**Lastmessageinwas{'$gen_cast',crash}
**WhenServerstate=={state,0}
**Reasonfortermination==%vahahere'swhatwent
wrong
**{function_clause,[{hello_server,handle_cast,[crash,{state,0}]},
{gen_server,handle_msg,5},
{proc_lib,init_p_do_apply,3}]}
=CRASHREPORT====17Dec2012::11:00:56===%andhere'sevenmoreinfoon
theenvironent
crasher:
initialcall:hello_server:init/1
pid:<0.44.0>
registered_name:hello_server
exceptionexit:{function_clause,
[{hello_server,handle_cast,[crash,{state,0}]},
{gen_server,handle_msg,5},
{proc_lib,init_p_do_apply,3}]}
infunctiongen_server:terminate/6
ancestors:[<0.32.0>]
messages:[]
links:[<0.32.0>]
dictionary:[]
trap_exit:false
status:running
heap_size:377
stack_size:24
reductions:142
neighbours:
neighbour:[{pid,<0.32.0>},
{registered_name,[]},
{initial_call,{erlang,apply,2}},
{current_function,{io,wait_io_mon_reply,2}},
{ancestors,[]},
{messages,[]},
{links,[<0.26.0>,<0.44.0>]},
{dictionary,[]},
{trap_exit,false},
{status,waiting},
{heap_size,2584},
{stack_size,30},
{reductions,6595}]
**exceptionexit:function_clause
infunctionhello_server:handle_cast/2
calledashello_server:handle_cast(crash,{state,0})
incallfromgen_server:handle_msg/5
incallfromproc_lib:init_p_do_apply/3
5>whereis(hello_server).%ourprocesscrashed,butat
leastthe
undefined%reportabovegivesusanide
awhy.
OTP supervisor
Heresthecodethatrebargeneratedforus.Itisasupervisortemplateandnotyetcustomized.
module(hello_sup).
behaviour(supervisor).
%%API
export([start_link/0]).
%%Supervisorcallbacks
export([init/1]).
%%Helpermacrofordeclaringchildrenofsupervisor
define(CHILD(I,Type),{I,{I,start_link,[]},permanent,5000,Type,
[I]}).
%%===================================================================
%%APIfunctions
%%===================================================================
start_link()>
supervisor:start_link({local,?MODULE},?MODULE,[]).
%%===================================================================
%%Supervisorcallbacks
%%===================================================================
init([])>
{ok,{{one_for_one,5,10},[]}}.
Wow,thatsshort!Wow,whatonearthdoesitmean?Weregoingtodissectitinamomentandthenfillinthegapto
makethistemplateworkwithourgen_server,butevenifwedontunderstandthedetailsyettheresalotwe
understandalready.
ThismoduleimplementstheOTPsupervisorbehaviour
IthasoneAPIfunctionstart_link/0andonebehaviourcallbackfunctioninit/1.
Thereisonlyonesinglefunctioncallinthisentirelisting(supervisor:start_link/3)
init/1,whereweexpectthemagictohappen,onlyreturnsatuple.
Well,init/1iswherethemagichappens.ButincontrasttoournonOTPsupervisor,wherewehadtocodethelogicto
monitorandrestartoursever,weareusingapurelydeclarative,albeitstillcrypticapproach,informofthetuple,to
configurethesupervisorcontainer.
Whatsgoingonwiththatdefine(CHILD(I,Type),...)?Itisamacro,justlikethefamilardefine(SERVER,?
MODULE).macro,exceptthatthisonetakestwoarguments.Youusethismacrobyprefixingitwith?andby
providingvaluesforIandType.So,forexample,?CHILD(hello_server,worker)wouldbeexpandedto{hello_server,
{hello_server,start_link,[]},permanent,5000,worker,[hello_server]}atcompiletime.Butthemacroisntused
anywhereinourcodeyet.Wherearewesupposedtouseit?
Letstakealookatthesupervisortemplateemacsgeneratesforus.Weareonlyinterestedintheinit/1function,which
iswhyIomittedtherest.Buttrytouseemacstogenerateasupervisortemplateyourself.Todiscoverwhatgoodness
Imhidinghit:F10,e,S,0,whereestandsforErlang,SforSkeletonsand0selectsthesupervisortemplate.Heres
whatIgot:
init([])>
RestartStrategy=one_for_one,
MaxRestarts=1000,
MaxSecondsBetweenRestarts=3600,
SupFlags={RestartStrategy,MaxRestarts,MaxSecondsBetweenRestarts},
Restart=permanent,
Shutdown=2000,
Type=worker,
AChild={'AName',{'AModule',start_link,[]},
Restart,Shutdown,Type,['AModule']},
{ok,{SupFlags,[AChild]}}.
Thisisprobablyeasiertounderstand,thanwhatrebargeneratedforus.Althoughitshould,ofcourse,atleast
stucturally,boildowntothesameconfigurationtuple.LetssubstitutebothatomsANameandAModulewith
hello_serverandresolvethelasttuplefully.Iamgoingtoinsertsomelinebreaksandcommentstomakeiteasierto
seewhatgoeswhere.
{ok,%ok,supervisorhere'swhatwewantyoutodo
{
{%Globalsupervisoroptions
one_for_one,%usetheoneforonerestartstrategy
1000,%andallowamaximumof1000restarts
3600%perhourforeachchildprocess
},
[%Thelistofchildprocessesyoushouldsupervise
{%Weonlyhaveone
hello_server,%Registeritunderthenamehello_server
{%Here'showtofindandstartthischild'scode
hello_server,%*themoduleiscalledhello_server
start_link,%*thefunctiontoinvokeiscalledstart_link
[]%*andhere'sthelistofdefaultparametersto
use
},
permanent,%childshouldrunpermantenly,restartoncrash
2000,%givechild2sectocleanuponsystemstop,th
enkill
worker%FYI,thischildisaworker,notasupervisor
[hello_server]%thesearethemodulestheprocessuses
}
]
}
}.
Ileaveituptoyoutodecideontheformatyouprefer.Youcanuserebarscompactnotation,emacsverbosetemplate
oreventheresolvedsnippetaboveinyoursupervisorsinit/1.Asanexercise,pleaseimplementyoursupervisornow
andcompileit.However,beforewetestit,letslookattheparametersweusedinthesnippetabove.
one_for_one:Thisrestartstrategyinstructsthesupervisortorestartthecrashedchildprocessonly.Inourcase
thereisonlyone,butifthereweremoresupervisedchildprocesses,theywouldnotbeaffected.Incontrast,a
restartstrategyofone_for_allautomaticallyrestartsallchildrenifonecrashes.Forautonomouschildprocesses
useone_for_one.If,ontheotherhand,childprocessesformatightlycoupledsubsystemitmaybebettertorestart
theentiresubsystemwithone_for_all.Consultman3erlsupervisororthispage
(http://www.erlang.org/doc/man/supervisor.html)foradditionalinformation.Inparticular,lookatthe
simple_one_for_onestrategywhichallowsyoutouseasupervisorasaprocessfactory.
permanent:Thistellsthesupervisorthatthechildshouldalwaysberestarted(subjecttotheglobalrestartrules).
OtherRestartvaluesaretemporaryandtransient.temporaryprocessesareneverrestartedbuttransientchild
processesareiftheyterminateabnormally(i.e.theycrashed).Forourusecasepermanentmakessense,butinthe
caseofaprocessfactory,transientortemporarymaybemoreapplicable.
worker:Herewewanttosuperviseagen_serveraworkerbutitiscommontobuildsupervisorhierarchies.
Settingthisvaluetosupervisorinsteadinformsthesupervisorthatthechildisalsoasupervisor.Thisistohelpit
managechildprocessesoptimally.
LetstryourOTPsupervisor.
1>hello_sup:start_link().%let'sstartourrootsupervisor
{ok,<0.34.0>}
2>whereis(hello_server).%andensurethatitstartedoutch
ildprocess
<0.35.0>
3>hello_server:say_hello().%nowlet'scallthechildAPI
Hello
ok
4>hello_server:get_count().%andverifythatallworksasbefo
re
1
5>gen_server:cast(hello_server,crash).%timetocrashtheworkerprocess
=INFOREPORT====%good,terminateisstillbeingca
lled
terminating
=ERRORREPORT====17Dec2012::13:55:51===
**Genericserverhello_serverterminating
**Lastmessageinwas{'$gen_cast',crash}
**WhenServerstate=={state,2}
**Reasonfortermination==%andvhereweseewhyitcrash
ed
**{function_clause,[{hello_server,handle_cast,[crash,{state,2}]},
{gen_server,handle_msg,5},
{proc_lib,init_p_do_apply,3}]}
ok
6>whereis(hello_server).%butlook,anewprocesswasalread
ystarted
<0.40.0>
7>hello_server:get_count().%obviously,withanewstate
0
8>hello_server:stop().%let'scallourAPIstopfunction
=INFOREPORT===
terminating%lookinggood
ok
9>whereis(hello_server).%butoursupervisorstartedanother
server.
<0.44.0>
10>
Itworksasdesigned,although,perhaps,notasexpected.Thecalltostop_server/0stopsourprocess,butthesupervisor
immediatelystartsanewinstance.WecouldchangetheRestartparameterinoursupervisorsChildspecificationto
transient,butthenweareleftwithachildlesssupervisor.Idontthinkthisisaproblemthough,butrathertheresultof
oursomewhatarbitraryHelloscenario.LetsturnourattentionthereforetoOTPapplicationstolearnhowtostart
andstopapplicationsasawhole.
OTP application
Wehavealreadyseenanexampleofanapplicationinthispost:SASL,whichwelaunchedwithapplication:start(sasl).
Nowitistimetofindouthowwecanbundleourrootsupervisorandourserverinanapplicationsothatwecanstartit
inthesamemanner.
Unlikeourgen_serverandsupervisorimplementation,theOTPapplicationbehaviourrequirestwofiles.A
configurationfile,conventiallynamed,appname.appandanErlangsourcefilecalledappname_app.erl.Inourcase
theseare:
ebin/hello.app
src/hello_app.erl
Notethatthehello.appfileisplacedintotheebindirectory.Thismakessense.The.appfiletellsErlanghowtolaunch
ourapplication.Sincewemightonlyshipourappscompiled.beamfiles,notitssource,thisiswherethe.appfile
naturallybelongs.
Asarebarusersyouwillnoticethatthereexistsalsoasrc/hello.app.srcfile,asmentionedearlier.Thisfileservesasa
templateforyourebin/hello.appfilewhenyourun./rebarcompile.
Hereismy.erland.appfileasgeneratedbyrebar.
module(hello_app).
behaviour(application).
%%Applicationcallbacks
export([start/2,stop/1]).
%%===================================================================
%%Applicationcallbacks
%%===================================================================
start(_StartType,_StartArgs)>
hello_sup:start_link().
stop(_State)>
ok.
Thisisthehello_app.erlfilethatimplementstheOTPapplicationbehaviour.NeedIsaymore?
{application,hello,
[{description,[]},
{vsn,"1"},
{registered,[]},
{applications,[kernel,stdlib]},
{mod,{hello_app,[]}},
{env,[]},
{modules,[hello_app,hello_server,hello_sup]}]}.
ThisistheconfigurationfilethattellsErlanghowtostartourapplication.Itsjustatuple.Thefirstelementistheatom
application,thesecondtheapplicationnameandthethridalistofkeyvaluetuples.Ifyouarenotusingrebardont
forgetthefullstopattheend.Letsbrieflylookatthekeyvaluepairs.
description:WeshouldchangethistoThehelloOTPtutorialapp
vsn:Ourapplicationsversion.Itscommontousex.y.z,butfornow,rebarsdefaultisfine.
registered:Isalistofnamesourappregisteresitselfunder.Thisistypicallyonlytherootsupervisorsname.In
ourcaseweshouldaddhello.suphere,butitsnotrequired.
applications:Liststheapplicationsourappdependson.Everyapprequireskernelandsdlibtobeupandrunning.
Trytoaddsaslifyoulikeandseewhathappenswhenyoutrytostartyourapplication.
mod:TellsErlangwhichmodulecontainsourapplicationbehaviour.
env:Isalistofenviromentalvariablesyoumaywishtopasstoyourapplication.
modules:Isthelistofmodulesourapplicationconsistsof.
ItistimetostartourapplicationfromtheErlangshell.
1>application:start(hello).
ok
2>whereis(hello_sup).
<0.37.0>
3>whereis(hello_server).
<0.38.0>
4>hello_server:say_hello().
Hello
ok
5>application:stop(hello).
=INFOREPORT====17Dec2012::15:37:47===
application:hello
exited:stopped
type:temporary
ok
6>whereis(hello_sup).
undefined
7>whereis(hello_server).
undefined
Great,nowwecanstartandstopourapplicationsimplybyname.Buttheresmore.DoyourememberhowSASLs
extendedreportingbecameautomaticallyavailablewhenweturnedournonOTPserverintoagen_server?Trytorun
theshellsessionaboveanothertime,butfirststarttheApplicationMonitorusingappmon:start(),then,onceyouve
startedthehelloapp,clickonthehellobuttontodisplaytheprocesshierarchy.Prettynifty,dontyouthink?
Up next?
InterestedinapplyingyourOTPskills?Havealookaterlangdnsatmygithub
repositoryhttps://github.com/hcvst/erlangdns(https://github.com/hcvst/erlangdns).
(http://www.patternmatched.com/about/pmtcareers/)
(http://www.patternmatched.com/about/pmtcareers/)
(http://www.patternmatched.com/about/pmtcareers/)
Like
Tweet(http://twitter.com/share)
16Comments
Bot.co.zaBlog
Share
Recommend 4
showerst 3yearsago
Ithinkyouhaveaslighttypotheline
Savethecodeabovetoafilenamedno_top.erl.
shouldprobablyread:
Savethecodeabovetoafilenamedno_otp.erl.
Reply Share
hcvst
Mod >showerst
3yearsago
Thankyou.Ifixedit.
Reply Share
LeeWeiYeong>showerst 3yearsago
bump
Login
SortbyBest
Jointhediscussion
346
Reply Share
Colorado 3monthsago
Reallyappreciatethispostthankyou!
Reply Share
Stephan 3monthsago
Greatpost(still).Ifyouareon17insteadofcalling``appmon:start().``youcanuse
``observer:start().``OnethingthatI'venoticedisthatwhencrashingtheserverbye.g.callinga
functionthatisnotimplemented/exported,e.g.``hello_server:get_foo().``thenitremainsdownand
thesupervisorseemsnottobeabletorestartit.Isthisthesupposedbehaviour?
Reply Share
Asgeir 2yearsago
Hi,
inno_otp,you'renotobligedtoexportinit/0,itworkswithoutexportingit.Ibelievethereisno
obligationtoexportfunctionsyouwanttospawn.
Note:I've"forgotten"toexportinit/0andthecodestillworked.
Reply Share
JuliusBeckmann 2yearsago
Hifolks,
iranintoaproblemandfoundasolutionthatmighthelpsome:
Thislinedoesnotworkasexpected,becausethesupervisorcrashesalso.
"5>gen_server:cast(hello_server,crash).%timetocrashtheworkerprocess"
Itseemstodoso,becausetheprocessesarelinkedtotheerlangshell.Whatworkedwas:
"catchgen_server:cast(hello_server,crash)."
Myerlangversion:"ErlangR15B01(erts5.9.1)[source][smp:4:4][asyncthreads:0][kernel
poll:false]".
Thanksforthisveryinformativehowto!
Reply Share
2yearsago
Greatpost!Thankyou.
Reply Share
nikhilmarathe 3yearsago
Ihaven'tgotintoErlangyet,butIreallylikedyourwritingstyle!
Reply Share
kblake 3yearsago
Thisisgreat!
Reply Share
hcvst
Mod >kblake
3yearsago
Thanksalot.Gladyoufoundithelpful.IprettymuchwrotewhatIthinkwouldhavehelped
meinitially,butcreditgoestotheOTPbooktoo,ofcourse.Erlang'sgreatandjustalotof
fun.
Reply Share
LeeWeiYeong 3yearsago
Inyour'LetstakeourOTPserverforatestride.Heresmyoutput:'section,youmissedout
`c(hello_server).`tofirstloadyourhello_servercodebeforeusage.
Reply Share
hcvst
Mod >LeeWeiYeong
3yearsago
Hi,thanksforyourcomment.Sincewecompiledtheservermoduleintheprevious
paragraphit'senoughtoinvoke"erlpaebin".Tabcompletiondoesnotworksoyouhaveto
typehello_server:start_link()thefirsttimebutthenerlshouldfindthe.beamfileinebin.
Regards,HC
1
Reply Share
LeeWeiYeong>hcvst 3yearsago
Thanksfortheclarification.
Reply Share
KuldeepSinhChauhan 3yearsago
Thankyou.Veryhandywriteupwithpracticalsuggestionsaboutusing"rebar"and"emacs".Forme
it'saverytimelyread,IhavejustcompletedreadingfirsthalfofErlangOTPbook(yoursuggestion
onusingrebarandemacswillmakedifferencefurther).
Reply Share
hcvst
Mod >KuldeepSinhChauhan
3yearsago
Thankyouforyourcomment.Yes,rebarandemacsaregreat.WhatIfoundpleasantly
surprisingisthataddingemacstomytoolboxdidn'tconfusemy"vifingers"much.Ihad
expectedtostrugglewiththekeybindingswhenchangingbackandforthbetweenviand
emacsbutthatisnotthecase.
Reply Share
WHAT'STHIS?
ALSOONBOT.CO.ZABLOG
Sourcecodeeditorwithsyntax
highlightingforZotonicblogtinyMCE
MakeZotonicyoursHowtoinstalla
customtheme
2comments3yearsago
2comments3yearsago
AquaponicsFirststeps
AZotonictutorialoncustomtypes
10October199010:11:1210October1990
(/en/page/354/otphierarchy)
WEBSITEPOWEREDBYZOTONIC(HTTP://ZOTONIC.COM)0.9.0.