changeset 2410:392e11db3260

Development guide: added the "Common Pitfalls" section.
author Vladimir Homutov <vl@nginx.com>
date Tue, 23 Jul 2019 19:07:55 +0300
parents 3bc28d88f34e
children 7202f078bfa7
files xml/en/docs/dev/development_guide.xml
diffstat 1 files changed, 179 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/xml/en/docs/dev/development_guide.xml	Tue Jul 23 15:16:41 2019 +0300
+++ b/xml/en/docs/dev/development_guide.xml	Tue Jul 23 19:07:55 2019 +0300
@@ -9,7 +9,7 @@
 <article name="Development guide"
          link="/en/docs/dev/development_guide.html"
          lang="en"
-         rev="7">
+         rev="8">
 
 <section name="Introduction" id="introduction">
 
@@ -258,7 +258,7 @@
 <section name="Strings" id="strings">
 
 
-<section name="Overview" id="overview">
+<section name="Overview" id="string_overview">
 
 <para>
 For C strings, nginx uses the unsigned character type pointer
@@ -7264,6 +7264,183 @@
 </programlisting>
 </para>
 </section>
+</section>
+
+<section name="Common Pitfalls" id="common_pitfals">
+
+<section name="Writing a C module" id="module_pitfall">
+
+<para>
+The most common pitfall is an attempt to write a full-fledged C module.
+In most cases your task can be accomplished by creating a proper configuration.
+If writing a module is inevitable, try to make it
+as small and simple as possible.
+For example, a module can only export some
+<link id="http_variables">variables</link>.
+</para>
+
+<para>
+Before starting a module, consider the following questions:
+
+<list type="bullet">
+
+<listitem>
+Is it possible to implement a desired feature using already
+<link doc="../../docs/index.xml">available modules</link>?
+</listitem>
+
+<listitem>
+Is it possible to solve an issue using built-in scripting languages,
+such as <link doc="../http/ngx_http_perl_module.xml">Perl</link>
+or <link doc="../njs/index.xml">njs</link>?
+</listitem>
+
+</list>
+
+</para>
+
+</section>
+
+<section name="C Strings" id="c_strings">
+
+<para>
+The most used string type in nginx,
+<link id="string_overview">ngx_str_t</link> is not a C-Style
+zero-terminated string.
+You cannot pass the data to standard C library functions
+such as <c-func>strlen</c-func> or <c-func>strstr</c-func>.
+Instead, nginx <link id="string_overview">counterparts</link>
+should be used that accept either <literal>ngx_str_t</literal>
+or pointer to data and a length.
+However, there is a case when <literal>ngx_str_t</literal> holds
+a pointer to a zero-terminated string: strings that come as a result of
+configuration file parsing are zero-terminated.
+</para>
+
+</section>
+
+<section name="Global Variables" id="global_variables">
+
+<para>
+Avoid using global variables in your modules.
+Most likely this is an error to have a global variable.
+Any global data should be tied to a <link id="cycle">configuration cycle</link>
+and be allocated from the corresponding <link id="pool">memory pool</link>.
+This allows nginx to perform graceful configuration reloads.
+An attempt to use global variables will likely break this feature,
+because it will be impossible to have two configurations at
+the same time and abandon of them.
+Sometimes global variables are required.
+In this case, special attention is needed to manage reconfiguration
+properly.
+Also, check if libraries used by your code have implicit
+global state that may be broken on reload.
+</para>
+
+</section>
+
+<section name="Manual Memory Management" id="manual_memory_management">
+
+<para>
+Instead of dealing with malloc/free approach which is error prone,
+learn how to use nginx <link id="pool">pools</link>.
+A pool is created and tied to some object -
+<link id="http_conf">configuration</link>,
+<link id="cycle">cycle</link>,
+<link id="connection">connection</link>,
+or <link id="http_request">HTTP request</link>.
+When an object is destroyed, the associated pool is destroyed too.
+So when working with an object, it is possible to allocate as much as
+needed from the corresponding pool and don't care about freeing memory,
+even in case of errors.
+</para>
+
+</section>
+
+<section name="Threads" id="threads_pitfalls">
+
+<para>
+It is recommended to avoid using threads in nginx because it will
+definitely break things: most nginx functions are not thread-safe.
+It is expected that a thread will be executing only system calls and
+thread-safe library functions.
+If you need to run some code that is not related to client request processing,
+the proper way is to schedule a timer in the <literal>init_process</literal>
+module handler and perform required actions in timer handler.
+Internally nginx makes use of <link id="threads">threads</link> to
+boost IO-related operations, but this is a special case with a lot
+of limitations.
+</para>
+
+</section>
+
+<section name="Blocking Libraries" id="libraries">
+
+<para>
+A common mistake is to use libraries that are blocking internally.
+Most libraries out there are synchronous and blocking by nature.
+In other words, they perform one operation at a time and waste
+time waiting response from other peer.
+As a result, when a request is processed with such library, whole
+nginx worker is blocked, thus destroying performance.
+Use only libraries that provide asynchronous interface and don't
+block whole process.
+</para>
+
+</section>
+
+
+<section name="HTTP Requests to External Services" id="http_requests_to_ext">
+
+<para>
+Often modules need to perform an HTTP call to some external service.
+A common mistake is to use some external library, such as libcurl,
+to perform HTTP request.
+It is absolutely unnecessary to bring a huge amount of external
+(probably <link id="using_libraries">blocking</link>!) code
+for the task which can be accomplished by nginx itself.
+</para>
+
+<para>
+There are two basic usage scenarios when an external request is needed:
+
+<list type="bullet">
+
+<listitem>
+in the context of processing a client request (for example, in content handler)
+</listitem>
+
+<listitem>
+in the context of a worker process (for example, timer handler)
+</listitem>
+
+</list>
+
+</para>
+
+<para>
+In the first case, the best is to use
+<link id="http_subrequests">subrequests API</link>.
+Instead of directly accessing external service, you declare a location
+in nginx configuration and direct your subrequest to this location.
+This location is not limited to
+<link doc="../http/ngx_http_proxy_module.xml" id="proxy_pass">proxying</link>
+requests, but may contain other nginx directives.
+An example of such approach is the
+<link doc="../http/ngx_http_auth_request_module.xml" id="auth_request"/>
+directive implemented in
+<link url="http://hg.nginx.org/nginx/file/tip/src/http/modules/ngx_http_auth_request_module.c">ngx_http_auth_request module</link>.
+</para>
+
+<para>
+For the second case, it is possible to use basic HTTP client functionality
+available in nginx.
+For example,
+<link url="http://hg.nginx.org/nginx/file/tip/src/event/ngx_event_openssl_stapling.c">OCSP module</link>
+implements simple HTTP client.
+</para>
+
+</section>
 
 </section>