?? 1. OTP Applications
Figuring out OTP applications is usually rather simple. They usually all share a directory structure that looks like:
? It is usually very simple to understand OTP applications. They are usually in the same directory and the target structure is as follows:
Doc /;
Ebin/
Src/
Test/
LICENSE.txt
README. md
Rebar. config
There might be slight differences, but the general structure will be the same. each OTP application shoshould contain an app file, either ebin /. app or more often, src /. app. src 2. there are two main varieties of app files:
The specific project may be slightly different, but the general structure is the same. Each OTP application contains an app file, which is stored in ebin/. app or src/. app. src 2. The structure of the two types of app files is as follows:
Library application:
{Application, useragent ,[
{Description, "Identify browsers & OSes from useragent strings "},
{Vsn, "0.1.2 "},
{Registered, []},
{Applications, [kernel, stdlib]},
{Modules, [useragent]}
]}.
Regular application:
{Application, dispcount ,[
{Description, "A dispatching library for resources and task"
"Limiting based on shared counters "},
{Applications, [kernel, stdlib]},
{Registered, []},
{Mod, {dispcount, []},
{Modules, [dispcount, dispcount_serv, dispcount_sup,
Dispcount_supersup, dispcount_watcher, watchers_sup]}
]}.
[2] A build system generates the final file that goes in ebin. note that in these cases, using src /. app. src files do not specify modules and let the build system take care of it.
[Note 2]: it will be generated by the build system. put the app file under ebin/, and most of the src /. app. src does not list modules, and the build system automatically adds them.
1.1
Library Applications
Library applications will usually have modules named appname _ something, and one module named appname. this will usually be the interface module that's central to the library and contains a quick way into most of the functionality provided.
? Library application usually has some modules named appname_XXX and appname. They are usually interface modules connected to the core of libary, or provide convenient functions to handle most of the functions.
By looking at the source of the module, you can figure out how it works with little effort: If the module adheres to any given behaviour (gen_server, gen_fsm, etc .), you're most likely expected to start a process under one of your own supervisors and call it that way.
? By looking at the source code of these modules, you will understand how it coordinates with behaviour (gen_server, gen_fsm, etc. The most likely situation you encounter is: create a common working process in your own monitoring process (supervisors.
If no behaviour is supported, then you probably have a functional, stateless library on your hands. For this case, the module's exported functions shoshould give you a quick way to understand its purpose.
If behaviour is not included, the code is probably a function Tool Library without stateless. In this case, the function exported by this module is to help you quickly understand them.
1.2
Regular Applications
For a regular OTP application, there are two potential modules that act as the entry point:
? For a regular OTP application, there are two possible modules as the portal:
? 1. appname
? 2. appname _ app
The first file shoshould be similar in use to what we had in a library application (an entry point), while the second one will implement the application behaviour, and will represent the top of the application's process hierarchy. in some cases the first file will play both roles at once.
? Appname usage is similar to that in the library application, while the appname_app file implements the specific behavior of the application (behaviour), which is at the top of the Applicaiton process structure. In some cases, the appname file also plays these two roles.
If you plan on simply adding the application as a dependency to your own app, then look inside appname for details and information. if you need to maintain and/or fix the application, go for appname _ app instead.
? If you want to simply add your Application as a dependency to your app, you only need to understand the information and details in the appname. If you need to operate or fix this Application, you should check appname_app.
The application will start a top-level supervisor and return its pid. this top-level supervisor will then contain the specifications of all the child processes it will start on its own 3. the higher a process resides in the tree, the more likely it is to be vital to the prior Val of the application.
? Application starts the top-level monitoring process (supervisor) and returns its pid. This monitoring process contains details about all sub-processes started by itself.
? The higher the position of the tree where the process is located, the greater the impact on application survival.
You can also estimate how important a process is by the order it is started (all children in the supervision tree are started in order, depth-first ). if a process is started later in the supervision tree, it probably depends on processes that were started earlier.
? You also need to determine the process startup sequence through the process importance (all sub-processes under the supervisor will start in sequence ). If process E is started after process [ABCD...], it is likely that E depends on some processes in [ABCD...] at startup.
Moreover, worker processes that depend on each other within the same application (say, a process that buffers socket communications and relays them to a finite-state machine in charge of understanding the protocol) are likely to be regrouped under the same supervisor and to fail together when something goes wrong. this is a deliberate choice, as it is usually simpler to start from a blank slate, restarting both processes, rather than trying to figure out how to recuperate when one or the other loses or reset UPTs its state.
? In addition, work processes that depend on each other in the same application (for example, a process that caches socket information and submits the socket to a valid state machine responsible for parsing the protocol) will be assigned to the same monitoring process, so that two processes will fail (fail) at the same time after one of the errors occurs. This is a good practice, because restarting them is a piece of cake. Restarting two processes is much easier than trying to recover the wrong process.
The supervisor restart strategy reflects the relationship between processes under a supervisor:
? One_for_oneAndSimple_one_for_oneAre used for processes that are not dependent upon each other directly, although their failures will collectively be counted towards total application shutdown 4.
? Rest_for_oneWill be used to represent processes that depend on each other in a linear manner.
? One_for_allIs used for processes that entirely depend on each other.
This structure means it is easiest to navigate OTP applications in a top-down manner by grouping supervision subtrees.
The restart policy of the monitoring process is determined by the relationship between sub-processes:
?One_for_oneAndSimple_one_for_one
? Used to: subprocesses do not directly depend on each other. However, if they fail, application is disabled in a centralized manner.
? Rest_for_oneIt is used in the case of linear manner between processes.
? One_for_allIt is used when a process is abnormal and all other processes will be affected.
From these structures, we can see that it is very easy to master the OTP application from top to bottom through the monitoring tree (supervisior.
For each worker process supervised, the behaviour it implements will give a good clue about its purpose:
? AGen_serverHolds resources and tends to follow client/server patterns (or more generally, request/response patterns)
? AGen_fsmWill deal with a sequence of events or inputs and react depending on them, as a Finite State Machine. It will often be used to implement protocols.
? AGen_eventWill act as an event hub for callbacks, or as a way to deal with notifications of some sort.
All of these modules will contain the same kind of structure: exported functions that represent the user-facing interface, exported functions for the callback module, and private functions, usually in that order.
Every working process should be monitored, and the behaviour of the working process will demonstrate its real purpose well:
? Gen_serverThe storage resources tend to follow the client/server patterns or, more specifically, request/response patterns ).
? Gen_fsmIt processes a series of events, inputs, or responses, like a Finite State Machine (Finite State Machine). It is often used to implement the protocol (implement protocols ).
? Gen_eventAs an event callback center, or used to process some forms of notifications.
? All the above modules share the same structure: user-oriented export interfaces, export callback interfaces, and private functions. The order in the module is usually the same.
Based on their supervision relationship and the typical role of each behaviour, looking at the interface to be used by other modules and the behaviours implemented shoshould reveal a of information about the program you're diving.
? Based on their monitoring relationship and typical behaviour roles, other modules can call these interfaces to provide you with a lot of information and help you understand the code in depth.
[3] In some cases, the supervisor specifies no children: they will either be started dynamically by some function of the API or in a start phase of the application, or the supervisor is only there to allow OTP environment variables (in the env tuple of the app file) to be loaded.
[4] Some developers will use one_for_one supervisors when rest_for_one is more appropriate. They require strict ordering to boot correctly, but forget about said order when restarting or if a predecessor dies.
[Note 3]: in some cases, the monitoring process does not have such details, and they are dynamically created using the API; or the app file is obtained from the OTP environment variable for loading.
[Note 4]: some developers are using the one_for_one policy, but rest_for_one is more suitable. They must follow a strict sequence when the process dies or restarts.
1.3
Dependencies
All applications have dependencies5, and these dependencies will have their own dependencies. OTP applications usually share no state between them, so it's possible to know what bits of code depend on what other bits of code by looking at the app file only, assuming the developer wrote them in a mostly correct manner.
Figure 1.1 shows a divisor that can be generated from looking at app files to help understand the structure of OTP applications.
? All applications have dependencies, and these applications depend on application5. OTP applications are usually not shared, so you can only know how to use this app through the app file, assume that the application is written by the developer in the correct way.
Figure 1.1 can understand the structure of OTP applications through charts generated by app files.
Using such a hierarchy and looking at each application's short description might be helpful to draw a rough, general map of where everything is located. to generate a similar digoal, find recon's script directory and call escript script/app_deps.erl6. similar
? By using this hierarchy and viewing the concise description of each application, you can draw a rough map that contains all the key elements. you can also use the recon script: call escript script/app_deps.erl to generate similar Chart 6.
Figure 1.1: Dependency graph of riak_cs, Basho's open source cloud library. The graph ignores dependencies on common applications like kernel and stdlib. Ovals are applications, rectangles are library applications.
Figure1.1: riak_cs dependency graph. The Boaho open-source cloud code library ignores commonly used dependent applications such as kernel and stdlib, and rectangular representation of library applications.
Hierarchies can be found using the observer 7 application, but for individual supervision trees. Put together, you may get an easy way to find out what does what in the code base.
? You can also use observer 7 application to view hierarchies, but only for individual supervisior trees. With all the summary, you can easily dive into the code.
[5] At the very least on the kernel and stdlib applications
[6] This script depends on graphviz
[7] http://www.erlang.org/doc/apps/observer/observer_ug.html
[Note 5]: it depends on at least the kernel and stdlib.
[Note 6]: this script depends on the drawing tool (graphviz ).
[Note 7]: http://www.erlang.org/doc/apps/observer/observer_ug.html
[Erlang Crisis] (1.3) OTP applications