前端开发者必备的Nginx知识
<div><p><strong>摘要:</strong> 最常用的Web服务器 -- Nginx</p>
<ul>
<li>原文:前端开发者必备的Nginx知识</li>
<li>作者:ConardLi</li>
</ul>
<p><strong>Fundebug经授权转载,版权归原作者所有。</strong></p>
<h3>Nginx在应用程序中的作用</h3>
<ul>
<li>解决跨域</li>
<li>请求过滤</li>
<li>配置gzip</li>
<li>负载均衡</li>
<li>静态资源服务器</li>
</ul>
<p>nginx是一个高性能的HTTP和反向代理服务器,也是一个通用的TCP/UDP代理服务器,最初由俄罗斯人Igor Sysoev编写。</p>
<p>nginx现在几乎是众多大型网站的必用技术,大多数情况下,我们不需要亲自去配置它,但是了解它在应用程序中所担任的角色,以及如何解决这些问题是非常必要的。</p>
<p>下面我将从nginx在企业中的真实应用来解释nginx在应用程序中起到的作用。</p>
<p>为了便于理解,首先先来了解一下一些基础知识,<code>nginx是一个高性能的反向代理服务器</code>那么什么是反向代理呢?</p>
<h3>正向代理与反向代理</h3>
<p><strong>代理</strong>是在服务器和客户端之间假设的一层服务器,<strong>代理</strong>将接收客户端的请求并将它转发给服务器,然后将服务端的响应转发给客户端。</p>
<p>不管是正向代理还是反向代理,实现的都是上面的功能。</p>
<h4>正向代理</h4>
<p><strong>正向代理</strong>,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。</p>
<p><strong>正向代理</strong>是为我们服务的,即为客户端服务的,客户端可以根据正向代理访问到它本身无法访问到的服务器资源。</p>
<p><strong>正向代理</strong>对我们是透明的,对服务端是非透明的,即服务端并不知道自己收到的是来自代理的访问还是来自真实客户端的访问。</p>
<h4>反向代理</h4>
<p><strong>反向代理</strong>(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。</p>
<p><strong>反向代理</strong>是为服务端服务的,反向代理可以帮助服务器接收来自客户端的请求,帮助服务器做请求转发,负载均衡等。</p>
<p><strong>反向代理</strong>对服务端是透明的,对我们是非透明的,即我们并不知道自己访问的是代理服务器,而服务器知道反向代理在为他服务。<br>
图片描述</p>
<h2>基本配置</h2>
<h4>配置结构</h4>
<p>下面是一个nginx配置文件的基本结构:</p>
<pre class="hljs nginx"><code class="nginx"><span class="hljs-section">events {
}
<span class="hljs-section">http
{
<span class="hljs-section">server
{
<span class="hljs-attribute">location path
{
...
}
<span class="hljs-attribute">location path
{
...
}
}
<span class="hljs-section">server
{
...
}
}
</span></span></span></span></span></span></code></pre>
<ul>
<li><code>main</code>:nginx的全局配置,对全局生效。</li>
<li><code>events</code>:配置影响nginx服务器或与用户的网络连接。</li>
<li><code>http</code>:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。</li>
<li><code>server</code>:配置虚拟主机的相关参数,一个http中可以有多个server。</li>
<li><code>location</code>:配置请求的路由,以及各种页面的处理情况。</li>
<li><code>upstream</code>:配置后端服务器具体地址,负载均衡配置不可或缺的部分。</li>
</ul>
<h4>内置变量</h4>
<p>下面是<code>nginx</code>一些配置中常用的内置全局变量,你可以在配置的任何位置使用它们。</p>
<table>
<thead>
<tr><th>变量名</th><th>功能</th></tr>
</thead>
<tbody>
<tr>
<td><code>$host</code></td>
<td>请求信息中的<code>Host</code>,如果请求中没有<code>Host</code>行,则等于设置的服务器名</td>
</tr>
<tr>
<td><code>$request_method</code></td>
<td>客户端请求类型,如<code>GET</code>、<code>POST</code></td>
</tr>
<tr>
<td><code>$remote_addr</code></td>
<td>客户端的<code>IP</code>地址</td>
</tr>
<tr>
<td><code>$args</code></td>
<td>请求中的参数</td>
</tr>
<tr>
<td><code>$content_length</code></td>
<td>请求头中的<code>Content-length</code>字段</td>
</tr>
<tr>
<td><code>$http_user_agent</code></td>
<td>客户端agent信息</td>
</tr>
<tr>
<td><code>$http_cookie</code></td>
<td>客户端cookie信息</td>
</tr>
<tr>
<td><code>$remote_addr</code></td>
<td>客户端的IP地址</td>
</tr>
<tr>
<td><code>$remote_port</code></td>
<td>客户端的端口</td>
</tr>
<tr>
<td><code>$server_protocol</code></td>
<td>请求使用的协议,如<code>HTTP/1.0</code>、·HTTP/1.1`</td>
</tr>
<tr>
<td><code>$服务器地址server_addr</code></td>
<td>服务器地址</td>
</tr>
<tr>
<td><code>$server_name</code></td>
<td>服务器名称</td>
</tr>
<tr>
<td><code>$server_port</code></td>
<td>服务器的端口号</td>
</tr>
</tbody>
</table>
<h3>解决跨域</h3>
<p>先追本溯源以下,跨域究竟是怎么回事。</p>
<h4>跨域的定义</h4>
<p>同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。通常不允许不同源间的读操作。</p>
<h4>同源的定义</h4>
<p>如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。</p>
<div class="image-package">
<div class="image-container">
<div class="image-container-fill"> </div>
<div class="image-view" data-width="800" data-height="249"><img src="https://img2018.cnblogs.com/blog/48590/201907/48590-20190708094534344-2138208771.jpg" alt=""></div>
</div>
<div class="image-caption"> </div>
</div>
<h4>nginx解决跨域的原理</h4>
<p>例如:</p>
<ul>
<li>前端server的域名为:<code>fe.server.com</code></li>
<li>后端服务的域名为:<code>dev.server.com</code></li>
</ul>
<p>现在我在<code>fe.server.com</code>对<code>dev.server.com</code>发起请求一定会出现跨域。</p>
<p>现在我们只需要启动一个nginx服务器,将<code>server_name</code>设置为<code>fe.server.com</code>,然后设置相应的location以拦截前端需要跨域的请求,最后将请求代理回<code>dev.server.com</code>。如下面的配置:</p>
<pre class="hljs nginx"><code class="nginx"><span class="hljs-section">server {
<span class="hljs-attribute">listen <span class="hljs-number">80;
<span class="hljs-attribute">server_namefe.server.com;
<span class="hljs-attribute">location / {
<span class="hljs-attribute">proxy_pass dev.server.com;
}
}
</span></span></span></span></span></span></code></pre>
<p>这样可以完美绕过浏览器的同源策略:<code>fe.server.com</code>访问<code>nginx</code>的<code>fe.server.com</code>属于同源访问,而<code>nginx</code>对服务端转发的请求不会触发浏览器的同源策略。</p>
<h3>请求过滤</h3>
<p>根据状态码过滤</p>
<pre class="hljs nginx"><code class="nginx"><span class="hljs-attribute">error_page <span class="hljs-number">500 <span class="hljs-number">501 <span class="hljs-number">502 <span class="hljs-number">503 <span class="hljs-number">504 <span class="hljs-number">506 /50x.html;
<span class="hljs-attribute">location = /50x.html {
<span class="hljs-comment">#将跟路径改编为存放html的路径。
<span class="hljs-attribute">root /root/static/html;
}
</span></span></span></span></span></span></span></span></span></span></code></pre>
<p>根据URL名称过滤,精准匹配URL,不匹配的URL全部重定向到主页。</p>
<pre class="hljs nginx"><code class="nginx"><span class="hljs-attribute">location / {
<span class="hljs-attribute">rewrite <span class="hljs-regexp"> ^.*$ /index.html<span class="hljs-literal">redirect;
}
</span></span></span></span></code></pre>
<p>根据请求类型过滤。</p>
<pre class="hljs nginx"><code class="nginx"><span class="hljs-attribute">if ( <span class="hljs-variable">$request_method !<span class="hljs-regexp">~ ^(GET|POST|HEAD)$ ) {
<span class="hljs-attribute">return <span class="hljs-number">403;
}
</span></span></span></span></span></code></pre>
<p>推荐大家使用Fundebug,一款很好用的BUG监控工具~</p>
<h3>配置gzip</h3>
<p><code>GZIP</code>是规定的三种标准HTTP压缩格式之一。目前绝大多数的网站都在使用<code>GZIP</code>传输 <code>HTML</code>、<code>CSS</code>、<code>JavaScript</code> 等资源文件。</p>
<p>对于文本文件,<code>GZip</code> 的效果非常明显,开启后传输所需流量大约会降至 <code>1/4 ~ 1/3</code>。</p>
<p>并不是每个浏览器都支持<code>gzip</code>的,如何知道客户端是否支持<code>gzip</code>呢,请求头中的<code>Accept-Encoding</code>来标识对压缩的支持。</p>
<div class="image-package">
<div class="image-container">
<div class="image-container-fill"> </div>
<div class="image-view" data-width="396" data-height="160"><img src="https://img2018.cnblogs.com/blog/48590/201907/48590-20190708094546481-1636245962.jpg" alt=""></div>
</div>
<div class="image-caption"> </div>
</div>
<p>启用<code>gzip</code>同时需要客户端和服务端的支持,如果客户端支持<code>gzip</code>的解析,那么只要服务端能够返回<code>gzip</code>的文件就可以启用<code>gzip</code>了,我们可以通过<code>nginx</code>的配置来让服务端支持<code>gzip</code>。下面的<code>respone</code>中<code>content-encoding:gzip</code>,指服务端开启了<code>gzip</code>的压缩方式。</p>
<div class="image-package">
<div class="image-container">
<div class="image-container-fill"> </div>
<div class="image-view" data-width="415" data-height="183"><img src="https://img2018.cnblogs.com/blog/48590/201907/48590-20190708094557531-1055028065.jpg" alt=""></div>
</div>
<div class="image-caption"> </div>
</div>
<pre class="hljs nginx"><code class="nginx"> <span class="hljs-attribute">gzip <span class="hljs-literal">on;
<span class="hljs-attribute">gzip_http_version <span class="hljs-number">1.<span class="hljs-number">1;
<span class="hljs-attribute">gzip_comp_level <span class="hljs-number">5;
<span class="hljs-attribute">gzip_min_length <span class="hljs-number">1000;
<span class="hljs-attribute">gzip_types text/csv text/xml text/css text/plain text/javascript application/javascript application/x-javascript application/json application/xml;
</span></span></span></span></span></span></span></span></span></span></code></pre>
<h4>gzip</h4>
<ul>
<li>开启或者关闭<code>gzip</code>模块</li>
<li>默认值为<code>off</code></li>
<li>可配置为<code>on / off</code></li>
</ul>
<h4>gzip_http_version</h4>
<ul>
<li>启用 <code>GZip</code> 所需的<code>HTTP</code> 最低版本</li>
<li>默认值为<code>HTTP/1.1</code></li>
</ul>
<p>这里为什么默认版本不是<code>1.0</code>呢?</p>
<p><code>HTTP</code> 运行在<code>TCP</code> 连接之上,自然也有着跟<code>TCP</code> 一样的三次握手、慢启动等特性。</p>
<p>启用持久连接情况下,服务器发出响应后让<code>TCP</code>连接继续打开着。同一对客户/服务器之间的后续请求和响应可以通过这个连接发送。</p>
<div class="image-package">
<div class="image-container">
<div class="image-container-fill"> </div>
<div class="image-view" data-width="742" data-height="210"><img src="https://img2018.cnblogs.com/blog/48590/201907/48590-20190708094614507-1993308298.jpg" alt=""></div>
</div>
<div class="image-caption"> </div>
</div>
<p>为了尽可能的提高 <code>HTTP</code> 性能,使用持久连接就显得尤为重要了。</p>
<p><code>HTTP/1.1</code>默认支持<code>TCP</code>持久连接,<code>HTTP/1.0</code> 也可以通过显式指定 <code>Connection: keep-alive</code> 来启用持久连接。对于<code>TCP</code>持久连接上的<code>HTTP</code> 报文,客户端需要一种机制来准确判断结束位置,而在 <code>HTTP/1.0</code>中,这种机制只有<code>Content-Length</code>。而在<code>HTTP/1.1</code>中新增的 <code>Transfer-Encoding: chunked</code> 所对应的分块传输机制可以完美解决这类问题。</p>
<p><code>nginx</code>同样有着配置<code>chunked的</code>属性<code>chunked_transfer_encoding</code>,这个属性是默认开启的。</p>
<p><code>Nginx</code>在启用了<code>GZip</code>的情况下,不会等文件 <code>GZip</code> 完成再返回响应,而是边压缩边响应,这样可以显著提高 <code>TTFB</code>(<code>Time To First Byte</code>,首字节时间,WEB 性能优化重要指标)。这样唯一的问题是,<code>Nginx</code> 开始返回响应时,它无法知道将要传输的文件最终有多大,也就是无法给出<code>Content-Length</code>这个响应头部。</p>
<p>所以,在<code>HTTP1.0</code>中如果利用<code>Nginx</code>启用了<code>GZip</code>,是无法获得<code>Content-Length</code>的,这导致HTTP1.0中开启持久链接和使用<code>GZip</code>只能二选一,所以在这里<code>gzip_http_version</code>默认设置为<code>1.1</code>。</p>
<h4>gzip_comp_level</h4>
<ul>
<li>压缩级别,级别越高压缩率越大,当然压缩时间也就越长(传输快但比较消耗cpu)。</li>
<li>默认值为 <code>1</code></li>
<li>压缩级别取值为<code>1-9</code></li>
</ul>
<h4>gzip_min_length</h4>
<ul>
<li>设置允许压缩的页面最小字节数,<code>Content-Length</code>小于该值的请求将不会被压缩</li>
<li>默认值:<code>0</code></li>
<li>当设置的值较小时,压缩后的长度可能比原文件大,建议设置<code>1000</code>以上</li>
</ul>
<h4>gzip_types</h4>
<ul>
<li>要采用gzip压缩的文件类型(<code>MIME</code>类型)</li>
<li>默认值:<code>text/html</code>(默认不压缩<code>js</code>/<code>css</code>)</li>
</ul>
<h3>负载均衡</h3>
<h4>什么是负载均衡</h4>
<div class="image-package">
<div class="image-container">
<div class="image-container-fill"> </div>
<div class="image-view" data-width="717" data-height="475"><img src="https://img2018.cnblogs.com/blog/48590/201907/48590-20190708094628124-1612951845.jpg" alt=""></div>
</div>
<div class="image-caption"> </div>
</div>
<p>如上面的图,前面是众多的服务窗口,下面有很多用户需要服务,我们需要一个工具或策略来帮助我们将如此多的用户分配到每个窗口,来达到资源的充分利用以及更少的排队时间。</p>
<p>把前面的服务窗口想像成我们的后端服务器,而后面终端的人则是无数个客户端正在发起请求。负载均衡就是用来帮助我们将众多的客户端请求合理的分配到各个服务器,以达到服务端资源的充分利用和更少的请求时间。</p>
<h4>nginx如何实现负载均衡</h4>
<p>Upstream指定后端服务器地址列表</p>
<pre class="hljs nginx"><code class="nginx"><span class="hljs-attribute">upstream balanceServer {
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.33:12345;
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.34:12345;
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.35:12345;
}
</span></span></span></span></span></span></span></code></pre>
<p>在server中拦截响应请求,并将请求转发到Upstream中配置的服务器列表。</p>
<pre class="hljs nginx"><code class="nginx"> <span class="hljs-section">server {
<span class="hljs-attribute">server_namefe.server.com;
<span class="hljs-attribute">listen <span class="hljs-number">80;
<span class="hljs-attribute">location /api {
<span class="hljs-attribute">proxy_pass http://balanceServer;
}
}
</span></span></span></span></span></span></code></pre>
<p>上面的配置只是指定了nginx需要转发的服务端列表,并没有指定分配策略。</p>
<h4>nginx实现负载均衡的策略</h4>
<p><strong>轮询策略</strong></p>
<div class="image-package">
<div class="image-container">
<div class="image-container-fill"> </div>
<div class="image-view" data-width="717" data-height="539"><img src="https://img2018.cnblogs.com/blog/48590/201907/48590-20190708094643826-1700546265.jpg" alt=""></div>
</div>
<div class="image-caption"> </div>
</div>
<p>默认情况下采用的策略,将所有客户端请求轮询分配给服务端。这种策略是可以正常工作的,但是如果其中某一台服务器压力太大,出现延迟,会影响所有分配在这台服务器下的用户。</p>
<pre class="hljs nginx"><code class="nginx"><span class="hljs-attribute">upstream balanceServer {
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.33:12345;
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.34:12345;
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.35:12345;
}
</span></span></span></span></span></span></span></code></pre>
<p><strong>最小连接数策略</strong></p>
<div class="image-package">
<div class="image-container">
<div class="image-container-fill"> </div>
<div class="image-view" data-width="717" data-height="539"><img src="https://img2018.cnblogs.com/blog/48590/201907/48590-20190708094655455-1170558787.jpg" alt=""></div>
</div>
<div class="image-caption"> </div>
</div>
<p>将请求优先分配给压力较小的服务器,它可以平衡每个队列的长度,并避免向压力大的服务器添加更多的请求。</p>
<pre class="hljs nginx"><code class="nginx"><span class="hljs-attribute">upstream balanceServer {
least_conn;
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.33:12345;
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.34:12345;
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.35:12345;
}
</span></span></span></span></span></span></span></code></pre>
<p><strong>最快响应时间策略</strong></p>
<div class="image-package">
<div class="image-container">
<div class="image-container-fill"> </div>
<div class="image-view" data-width="717" data-height="539"><img src="https://img2018.cnblogs.com/blog/48590/201907/48590-20190708094708300-1509210421.jpg" alt=""></div>
</div>
<div class="image-caption"> </div>
</div>
<p>依赖于NGINX Plus,优先分配给响应时间最短的服务器。</p>
<pre class="hljs nginx"><code class="nginx"><span class="hljs-attribute">upstream balanceServer {
fair;
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.33:12345;
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.34:12345;
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.35:12345;
}
</span></span></span></span></span></span></span></code></pre>
<p><strong>客户端ip绑定</strong></p>
<p>来自同一个ip的请求永远只分配一台服务器,有效解决了动态网页存在的session共享问题。</p>
<pre class="hljs nginx"><code class="nginx"><span class="hljs-attribute">upstream balanceServer {
ip_hash;
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.33:12345;
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.34:12345;
<span class="hljs-attribute">server <span class="hljs-number">10.1.22.35:12345;
}
</span></span></span></span></span></span></span></code></pre>
<h3>静态资源服务器</h3>
<pre class="hljs nginx"><code class="nginx"><span class="hljs-attribute">location <span class="hljs-regexp">~* \.(png|gif|jpg|jpeg)$ {
<span class="hljs-attribute">root /root/static/;
<span class="hljs-attribute">autoindex <span class="hljs-literal">on;
<span class="hljs-attribute">access_log<span class="hljs-literal">off;
<span class="hljs-attribute">expires <span class="hljs-number">10h;<span class="hljs-comment"># 设置过期时间为10小时
}
</span></span></span></span></span></span></span></span></span></span></code></pre>
<p>匹配以<code>png|gif|jpg|jpeg</code>为结尾的请求,并将请求转发到本地路径,<code>root</code>中指定的路径即nginx本地路径。同时也可以进行一些缓存的设置。</p>
<h3>小结</h3>
<p>nginx的功能非常强大,还有很多需要探索,上面的一些配置都是公司配置的真实应用(精简过了)。</p>
</div>
<p><br><br>作者:Fundebug<br>链接:https://www.jianshu.com/p/bd652a2a8bea<br>来源:简书<br>简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。</p><br><br>
来源:https://www.cnblogs.com/telwanggs/p/11149393.html
頁:
[1]