标签:erlang
Figuring out OTP applications is usually rather simple. They usually all share a directory structure that looks like:
?搞清楚 OTP applications通常都非常简单,他们通常在同一个目录下, 目标结构如下:doc/;
ebin/
src/
test/
LICENSE.txt
README.md
rebar.config
{application, useragent, [
{description, "Identify browsers & OSes from useragent strings"},
{vsn, "0.1.2"},
{registered, []},
{applications, [kernel, stdlib]},
{modules, [useragent]}
]}.
{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, many src/ [注2]:最终会用构建系统生成.app文件放到ebin/下,而且大部分的src/
1.1
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通常会有一些叫appname_XXX和一个叫appname的模块名组成,他们通常是连接libary核心的接口模块,或提供搞定大部分功能的便捷函数。
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.
?通过看这些模块的源代码,你会明白:它是如何与behaviour(gen_server,gen_fsm等)相互协调工作的。你遇到的情况最有可能是:在自己的监控进程(supervisors)创建一个普通的工作进程。
If no behaviour is included, then you probably have a functional, stateless library on your hands. For this case, the module’s exported functions should give you a quick way to understand its purpose.
如果没有包括behaviour, 这些代码很可能是一个没有状态信息(stateless)的函数工具库,对于这种情况,这个模块导出的函数是为了让你快速地理解它们。
1.2
For a regular OTP application, there are two potential modules that act as the entry point:
?对于一个regular OTP application,有2个可能的模块作为入口:
?1. appname
?2. appname _app
The first file should 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用法和library application中的相似,而appname_app文件会实现application 的具体行为(behaviour),它处于Applicaiton进程结构中的最顶层,在某些情况下,appname文件同时担当这两个角色。
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.
?如果你只是打算简单地将应用程序作为依赖项添加自已的app里面,你只需要看懂appname里面的信息和细节; 如果你需要操作或修复这个Application的问题,你就应该去看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 survival of the application.
?Application 会启动最高层的监控进程(supervisor)并返回他的pid. 这个监控进程会含用自己启动的所有子进程的详细信息3。
?进程所处树的位置越高,对application的生存影响越大。
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.
?你也要通过进程的重要性来决定进程启动顺序(所有的在supervisor下的子进程都会顺序启动)。如果进程E启动在进程[ABCD...]后启动,E就很有可能在启动时要依赖于[ABCD...]中的某些进程。
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 corrupts its state.
?此外,在同一个application中相互依赖的工作进程(例如:一个缓存socket信息并把socket交给一个负责解析协议的有效状态机的进程)会被分给同一个监控进程,这样可以其中一个出错后,2个进程同时失败(fail)掉。这是一个非常好的做法,因为重启它们就是小菜一碟,重启2个进程比试图恢复其中的错误进程简单得多。
The supervisor restart strategy reflects the relationship between processes under a supervisor:
? one_for_one and simple_one_for_one are 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_one will be used to represent processes that depend on each other in a linear manner.
? one_for_all is 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 exploring supervision subtrees.
监控进程的重启策略由子进程间的相互关系决定:
? one_for_one 和 simple_one_for_one
?用于:子进程间没有直接相互依赖关系,但是他们失败会集中记录到application关闭4。
? rest_for_one 会用于进程间有线性关系(linear manner)的场合。
? one_for_all 用于一个进程异常,其它全部也会影响的场合。
从这些结构可以看出:通过监控树(supervisior)的方式自上而下地驾驭OTP application是一件非常容易的事。
For each worker process supervised, the behaviour it implements will give a good clue about its purpose:
? a gen_server holds resources and tends to follow client/server patterns (or more generally, request/response patterns)
? a gen_fsm will 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.
? a gen_event will 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.
每一个工作进程都应当被监控,工作进程的behaviour会很好地表明其真正的目的:
? gen_server 储存资源(holds resources),倾向于遵循客户/服务器模式(client/server patterns),或更确切地说是,请求/应答模式(request/response patterns).
? gen_fsm 会处理一系列的事件或输入,或输入带来的一系列反应,就像一个有限状态机(Finite State Machine).常用于实现协议(implement protocols).
? gen_event 作为一个事件回调中心,或用来处理某些形式的通知。
?以上所有的模块都有着相同的结构:导出面向使用者的接口,导出回调接口,私有函数。通常在模块中的顺序也是如此。
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 should reveal a lot of information about the program you’re diving into.
?基于他们的监控关系和典型的behaviour角色,其它模块调用这些接口就可以给你提供很多信息,帮助你深入理解代码。
[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.
[注3]:在某些情况下,监控进程不会有这些详细信息,他们会被用API动态的创建出来;或者从OTP的环境变量中获得app file加载。
[注4]:有些开发者在使用one_for_one的策略,但是实际上rest_for_one更合适。他们在进程死亡或重启时都需要遵守一定严格顺序。
1.3
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 diagram that can be generated from looking at app files to help understand the structure of OTP applications.
?所有的applications都有依赖性(dependencies),这些被依赖的application也还有自己的依赖application5。OTP applications之间通常是不共享状态的,所以你可以只通过app文件就知道这个app大概如何使用,当然是假定这个application是开发者是用正确的方式写出来的前提下。
Figure 1.1 能过app文件生成的图表来理解OTP applications结构.
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 diagram, find recon’s script directory and call escript script/app_deps.erl6. Similar
?使用这种层次结构和查看每个applications的简明描述,就可以画一个粗略,但包含所有关键元素所在的地图来.你也可以使用recon脚本:调用escript script/app_deps.erl 来生成类似的图表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的依赖图示,Boaho开源云代码库,本图忽略了像kernel和stdlib类常用的依赖applications, 长方形表示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.
?层次关系还可以使用observer 7 application来查看,但只是针对个别supervisior树。把所有的汇总,你就可以轻松地深入代码。
[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
[注5]:至少会依赖kernel和stdlib.
[注6]:此脚本依赖于画图工具(graphviz).
[注7]:http://www.erlang.org/doc/apps/observer/observer_ug.html
标签:erlang
原文地址:http://blog.csdn.net/erlib/article/details/40866551