nginx与http学习笔记

基本命令
start nginx
nginx -s stop(立即停止)
nginx -s quit(平缓停止,有请求事不会意外停止)
nginx -s reload
遇到的坑:不要重复地去启动多个nginx进程,这样会导致你修改的nginx对应不上,可以通过资源管理器杀掉进程

http keepalive 长连接

短连接:TCP建立连接->请求资源->响应资源-> 关闭连接 每次请求都要经历这样的过程
长连接:建立一次连接后,每次请求都复用该连接。
并行连接:并发的短连接
http1.0中为了支持长连接必须在请求头中指定

Connection:keep-alive

在 HTTP 1.1 中 所有的连接默认都是持续连接,除非特殊声明不支持;现在大多数浏览器都默认是使用HTTP/1.1,所以keep-alive都是默认打开的

Connection:close 指定不使用长连接

怎么判断一个请求数据接收完?
在响应头中如果有Tansfer-Encoding:chunked则表示body为流式输出,会被分成多个块,每个块开始都会标识当前块的长度,此时body不需要长度;如果是非chunked则按照content-length来接收数据;否则等到服务端主动断开连接。
nginx中关于transfer-encoding的设置chunked_transfer_encoding on | off;
建立连接后服务端不是一直在等待下一次请求,通过nginx设置最大等待时间
nginx: http模块中可以设置超大等待时间
keepalive_timeout 65;

pipeline流水线作业
http1.1新特性,一个连接做多次请求;
在keepaiive中第二个请求要等第一个请求响应完全接受之后才能发起。
在pipeline中不必等待第一个请求处理后就可以发起第二个请求,nginx会把读取的数据放在buffer中,在nginx处理完第一个请求时发现buffer中还有数据,就会任务剩下的数据是下一个请求的开始,然后处理下一个请求,否则设置为keepalive

扩展:TCP的keepalive:
AB在三次握手建立TCP连接之后,B突然宕机了,但是A内核还会维护着AB之间的连接,浪费系统资源。引入keepalive机制,A定时给B发送空数据包,称为心跳包,一旦发现B网络不通则关闭连接。
Nginx涉及到tcp层面的keepalive只有一个:so_keepalive

nginx请求处理流程

Http处理过程
初始化http request(读取客户端数据,生成HTTP Request对象,该对象包含了所有请求信息)
处理请求头
处理请求体
调用此请求的url或者location关联的handle
依次调用各phase handler处理(phase handler是指包含某阶段若干个handler)

一个phase handler处理的任务

  1. 获取location配置
  2. 产生适当的响应
  3. response header
  4. response body

当nginx读取到一个http Request的header时,首先寻找与请求关联的虚拟主机配置,如果找到则经历以下几个phase handler
1.server级别的url重写阶段,在读取请求头过程中nginx会根据host和端口寻找对应的虚拟主机配置:

server{
    rewrite 规则 定向路径 重写类型
    # 访问 /last.html 的时候,页面内容重写到 /index.html 中
    rewrite /last.html /index.html last;
}
    
规则:字符串或者正则来匹配目标url
定向路径:匹配到规则后要定向的url
重写类型:

 1. last 完成重写浏览器地址栏url不变
 2. break终止后面的匹配,地址栏url不变
 3. redirect 302临时重定向,地址栏显示跳转后的url
 4. permanent 301永久重定向,显示跳转后的url

2.location rewrite

  1. 执行server rewrite
  2. 执行location匹配
  3. 执行location rewrite

location 与 location rewrite的区别:

  • location是对一类资源控制访问和反向代理,proxy_pass到其他机器,location rewrite则是更改资源获取路径
rewrite regex replacement [flag];
1.重写带http://
location /{
    # 当匹配 正则表达式 /test/(.*)时 请求将被临时重定向到 http://www.$1.com
    rewrite /test/(.*) http://www.$1.com
    return 200 'ok'
}
# 在浏览器中输入 127.0.0.1:8080/test1/baidu 
# 则临时重定向到 www.baidu.com
# 后面的 return 指令将没有机会执行了
2.重写不带http://
location / {
    rewrite /test/(.*) www.$1.com;
    return 200 "ok";
}
# 发送请求如下
# curl 127.0.0.1:8080/test/baidu
# ok

# 此处没有带http:// 所以只是简单的重写。请求的 uri 由 /test/baidu 重写为 www.baidu.com
# 因为会顺序执行 rewrite 指令 所以 下一步执行 return 指令 响应了 ok

TCP优化

http {
    sendfile           on;
    tcp_nopush         on;
    tcp_nodelay        on;

    keepalive_timeout  60;
    ... ...

senfile on可以提高静态资源托管效率,是一个系统调用,不需要先read再write,没有上下文切换开销
tcp_nopush 在sendfile开启后才生效,启用后数据包累计一定的大小才会发送,提高了网络效率,减少了开销
tcp_nodelay 数据包累计到一定大小后尽快发送,nginx只会针对处于keepalive的TCP连接启用tcp_nodelay
keepalive_timeout 表示每个tcp连接可以保持多少秒,默认是75秒,有些浏览器最多是保持60秒,所以设置为60秒

TFO(TCP Fast open)优化策略

nginx与http学习笔记

客户端第一次建立连接还是要三次握手,不同的是客户端会在第一个SYN设置一个fast-open的标志,服务端会生成fast-open cookie并放在SYN-ACK中,客户端就可以把cookie存起来以后syn用;
在这之后用户再建立TCP连接,发送syn包的同时会把cookie带上,然后可以直接带上http数据。换句话说在发送SYN包的时候把应用层数据发送出来,减少了一个RTT对性能的影响。
但是这种优化成本非常高需要操作系统、内核的支持(服务端和用户端都要支持)。

开启gzip

http {
    gzip               on;
    gzip_vary          on;

    gzip_comp_level    6;
    gzip_buffers       16 8k;

    gzip_min_length    1000;
    gzip_proxied       any;
    gzip_disable       "msie6";

    gzip_http_version  1.0;

    gzip_types         text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;
    ... ...
}

gzip_vary 用来输出vary响应头解决某些服务缓存问题

  • http内容协商机制,同一个url可能对应多份不同文档,要求服务端和客服端有一个选择最合适版本的机制。例如服务端可以将静态文件输出为压缩和未压缩两个版本。
  • 一般使用的方案是服务端根据客户端请求头的某些字段发送最合适的版本。分为内容专用字段(Accept字段)、其他字段
//请求头
Accept:*/* 接受任何MIME类型资源
Accept-Encoding:gzip,deflate,sdch 接受gzip,deflate,sdch压缩过的资源
Accept-Language:zh-CN,en-US;q=0.8,en;q=0.6 可以接受zh-ch,en-us,en;其中zh-cn的优先级最高(q 取值 0 - 1,最高为 1,最低为 0,默认为 1),服务端应优先返回zh-cn

//响应头
Content-Type: text/javascript 表示文档确切的类型是text/javascript
Content-Encoding: gzip 文档使用了gzip压缩
//没有content-language通常表示返回的是Accept-language权重最高的那种语言

Accept字段并不够用,如果要针对特定浏览器,如ie6就要使用到user-agent;cookie也可能作为服务器输出内容差异的依据。
如果服务器和客户端之间存在有缓存服务器,而服务器根据不同的user-agent返回不同的内容,缓存服务器却把针对特定浏览器的内容缓存下来统一返回,就会有问题。
所以http协议规定,如果服务器提供的内容是取决于user-agent(Accept之外的字段)请求头字段,响应头中必须要包含vary字段,而且vary字段必须包含user-agent。

//当内容取决于User-Agent和cookie时,vary字段应该类似于这样,也就是列出一个响应字段列表,告诉缓存服务器遇到同一个url的时候如何缓存和筛选合适的版本。
Vary:User-Agent, Cookie;

Content-Encoding在缓存服务器的问题
缓存服务器应该根据不同的Content-Encoding缓存不同的内容,再根据请求头的Accept-Encoding来返回合适的版本。为了避免给客户端返回不合适的版本:
1.将请求头的Cache-control改为private
2.增加vary:Accept-Encoding明确告诉缓存服务器按照Accept-Encoding字段内容缓存不同的版本。

以上的工作nginx都以gzip_vary: on搞定,相当于在启用了gzip的响应上加vary:Accept-Encoding

gzip_disable
接受一个正则匹配,请求的User-Agent匹配到后响应不会启用gzip。是为了解决某些浏览器启用gzip带来的问题。

gip_http_version
nginx默认启用http版本是Http/1.1,因为早期Http/1.0启用gzip有bug,现在基本忽略,所有可以指定gzip_http_version: 1.0;

开启缓存
优化代码逻辑的极限是移除所有极限;优化请求的极限是不发送任何请求。

http{
    proxy_cache_path /home/nginx/proxy_cache_path levels=1:2 keys_zone=pnc:300m inactive=7d max-size=10g;
}

/home/nginx/proxy_cache_path: 本地路径,缓存文件存放的路径
levels:默认所有文件放在/home/nginx/proxy_cache_path下,影响缓存性能,大部分场景是推荐使用2级目录来缓存文件。

post请求提交数据的四种格式
http协议规定post方法提交的数据主体必须方式消息主体中(entity-body),但是协议并没有规定数据必须使用什么编码格式。开发者可以自己决定主体内容。服务器都内置了解析常见数据格式的功能,根据请求头的content-type来获取消息中的请求消息主体是以何种方式编码,再对主体进行解析。
Content-Type

  1. application/x-www-form-urlencoded

    • 原生的form提交不设置enctype属性时默认使用这种格式提交,提交格式按照val1=key1&val2=key2格式进行编码,key和val都进行了url转码。

      POST http://www.example.com HTTP/1.1
      Content-Type: application/x-www-form-urlencoded;charset=utf-8
      title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3
  2. multipart/form-data

    • 在form表单上传文件的时候必须设置enctype为这个格式。首先生成boundary用于分割不同字段。Content-type中指明了数据是以multipart/form-data来编码,boundary是什么。
    • 消息主体中按照字段的个数又分为多个类似结构的部分,每部分都是以--boundaey开头,紧接着内容描述信息,然后回车最后是字段具体内容(文本或者二进制)。
    • 如果传输的是文件,还要包括文件名和文件类型信息。消息主体最后以--boundary--结束。

      POST http://www.example.com HTTP/1.1
      Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA
      
      ------WebKitFormBoundaryrGKCBY7qhFd3TrwA
      Content-Disposition: form-data; name="text"
      
      title
      ------WebKitFormBoundaryrGKCBY7qhFd3TrwA
      Content-Disposition: form-data; name="file"; filename="chrome.png"
      Content-Type: image/png
      
      PNG ... content of chrome.png ...
      ------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
  3. application/json

    • 告诉服务器消息主体是序列化后的json字符串
  4. text/html

相关推荐