网络

HTTP 缓存介绍

和缓存相关的 header 共有如下几种

  • Pragma
  • Cache-Control
  • Expires
  • Last-Modified
  • Etag
  • If-Modified-Since
  • If-Non-Match

Expires 由服务器返回,用于指定当前页面过期时间,使用绝对时间表示。

Cache-Control 指定了相对过期的时间,由当前时间多久后过期的秒数表示。

Cache-Control: max-age=86400

Last-Modified 是由服务器给出了文档的过期时间,当第二次请求该文档的时候,浏览器可以使用 If-Modified-Since 头部指定该过期时间,如果文档还没有过期,那么服务器应该返回 304,否则返回 200 和新文档。

Etag 是由服务器给出的文档的哈希值,当第二次请求该文档的时候,浏览器可以使用 If-None-Match 头部指定该哈希值,如果文档没有变动,那么服务器应该返回 304,如果有变动,那么哈希值也变了,应该返回 200 和新文档。

可以看出 Etag 相比 Last-Modified 更准确一些,所以两个头部都有的前提下,应该是 Etag 优先。

实际使用中,为了兼容性考虑,应该把这几种头部都结合起来使用。

参考

  1. https://www.mnot.net/cache_docs/
  2. http://stackoverflow.com/questions/499966/etag-vs-header-expires

TCP 握手常见考点

连接建立

图挂了

上面的图说的已经很好了,不再赘述。

为什么需要三次握手

这主要是为了防止已失效的连接请求报文段突然又传送到了服务端,服务端建立一个新的连接,因而产生错误。

所谓已失效的连接请求报文段是这样产生的。A 发送连接请求,但因连接请求报文丢失而未收到确认,于是 A 重发一次连接请求,成功后建立了连接。数据传输完毕后就释放了连接。

现在假定 A 发出的第一个请求报文段并未丢失,而是在某个网络节点长时间滞留了,以致延误到连接释放以后的某个时间才到达 B。本来这是一个早已失效的报文段。但 B 收到此失效的连接请求报文段后,就误以为 A 又发了一次新的连接请求,于是向 A 发出确认报文段,同意建立连接。假如不采用三次握手,那么只要 B 发出确认,新的连接就建立了。

由于 A 并未发出建立连接的请求,因此不会理睬 B 的确认,也不会向 B 发送数据。但 B 却以为新的运输连接已经建立了,并一直等待 A 发来数据,因此白白浪费了许多资源。

采用 TCP 三次握手的方法可以防止上述现象发生。例如在刚才的情况下,由于 A 不会向 B 的确认发出确认,连接就不会建立。

如果在 TCP 第三次握手中的报文段丢失了会发生什么情况?

Client 认为这个连接已经建立,如果 Client 端向 Server 写数据,Server 端将以 RST 包响应,方能感知到 Server 的错误。

链接释放

图挂了

需要注意的是,TCP 是全双工的协议,因此链接建立之后就没有客户端服务器的概念了,两边是对等的,都可以释放链接

MSS(最长报文长度), 由两端的较短值决定,在以太网中的典型值为 1460, 是以太网的 MTU(1500) 减去 IP 的头部 40B 得到的。

图挂了

为什么需要 TIME_WAIT 状态

首先是另一个概念 MSL, 最长报文生命周期,在 BSD 系统上一般设定为 30s, 不过可以长到 2min。TIME_WAIT 状态的时间设定为 2MSL。

TIMEWAIT 是主动关闭端进入的状态,发送完最后一个 ACK 之后进入 TIMEWAIT 状态等待 2MSL 才进入 CLOSED 状态

TIMEWAIT 状态存在的两个理由:
1. 可靠地实现 TCP 全双工链接的终止;假如对方没能收到最后一个 ACK, 那么他将会重新发送 FIN, 这时候如果客户端已经关闭了显然不能再次回复 ACK 了。
2. 保证上一个相同连接的数据包已经在网络上消失;如果一个新的链接建立在了同一个端口上,那么他将可能收到上一个进程的数据包,这是我们为老连接保留了 2MSL 的 TIME
WAIT 值,那么就可以保证原有的链接都不存在网络上了。

如果服务端主动关闭链接,也需要等待两个 MSL,那么重启服务器怎么绑定原有端口?

主动关闭链接端相应的端口会在 2 MSL 内处于 TIME-WAIT 状态而不能用。如果是客户端,那么问题不大,客户端一般会重新使用一个新的端口。如果是在服务端,因为服务端使用的都是常用端口,不能改变,也就是需要等待两个 MSL 才能使用刚拿的端口,比如 Nginx 重启,内核会显示当前端口是 busy 的,不能使用,等待 4min 显然是不现实的。为了解决这个问题,可以强制复用端口,在创建 socket 的时候使用 SOREUSEADDR 就可以了。不过需要注意的是,SOREUSEADDR 仅对 TIMEWAIT 状态有用,如果 socket 在其他状态,是不能复用的。注意不是 SOREUSEPORT。

其他

多个进程绑定同一个端口

我们知道一个同一个端口只有一个进程可以 bind 成功,那么 web 服务器是如何做到多进程呢?传统方法是在主进程使用 listen 然后在其他进程使用 accept 接受链接。不过这样会导致当有链接来的时候,所有的进程都会被唤醒,影响性能。在 3.9 内核之后,添加了 SOREUSEPORT 属性,可以使用 SOREUSEPORT 让每一个进程有当单独的监听队列,这样当有链接来的时候,内核只会唤醒一个进程。

当网络断开时,TCP 链接会断开吗?

TCP 链接有 keepalive 的功能,需要手动打开,但是一般来说没有人用。所以可以认为一般情况下,当物理网络断开时的时候,如果仅考虑 TCP 层,那么这个链接永远不会断开。不过一般来说,通过其他层面会知道这个链接断开了。比如说一般的应用层协议都会有心跳包的机制,这样就可以知道链接断开了。

参考

  1. https://serverfault.com/questions/329845/how-to-forcibly-close-a-socket-in-time-wait
  2. http://www.unixguide.net/network/socketfaq/4.5.shtml
  3. https://blog.csdn.net/Yaokai_AssultMaster/article/details/68951150
  4. https://www.zhihu.com/question/53672815
  5. https://blog.qiusuo.im/blog/2014/09/14/linux-so-reuseport/
  6. https://blog.csdn.net/xy010902100449/article/details/48274635
  7. https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/

关于 CDATA

CDATA stands for Character Data and it means that the data in between these strings includes data that could be interpreted as XML markup, but should not be.

So we could use CDATA to smuggle some HTML into the XML document, so that the HTML doesn’t confuse the XML document structure, and then use XSLT later to pull it out and spit it into a HTML document that is being output.
 
In short, you don’t have to escape all the < and & in CDATA section
 

RSS 2.0

<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
    <channel>
        <title>Example Feed</title>
        <description>Insert witty or insightful remark here</description>
        <link>http://example.org/</link>
        <lastBuildDate>Sat, 13 Dec 2003 18:30:02 GMT</lastBuildDate>
        <managingEditor>johndoe@example.com (John Doe)</managingEditor>
        <item>
            <title>Atom-Powered Robots Run Amok</title>
            <link>http://example.org/2003/12/13/atom03</link>
            <pubDate>Sat, 13 Dec 2003 18:30:02 GMT</pubDate>
            <description>Some text.</description>
            <source>Shit News</source>
        </item>
        <item>...</item>
    </channel>
</rss>

RSS 协议的一些不足和改进方向

  1. 没有标识文章重要度的字段
  2. 没有途径把订阅数量等信息反馈给 RSS 提供方
  3. 没有品牌特性
  4. 没有机器推荐
  5. 如果能够把 RSS 包装成像是 Amazon Prime 那样的服务,用户可能会很愿意付钱

实际上文章的增删改查是一套组合操作,而只使用一个 RSS 作为列表显然是不够的,必然要拓展。

现代的 RSS 阅读器需要做三个方面

  1. 一个社区
  2. 能够把所有服务都提供 RSS,包括不提供 RSS 的站点
  3. 评论服务
  4. 转码。有的 RSS 只提供了文章的摘要,有的 RSS 有实效性,有的 RSS 有自己的字体

     

    Atom 1.0

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Example Feed</title>
    <subtitle>Insert witty or insightful remark here</subtitle>
    <link href="http://example.org/"/>
    <updated>2003-12-13T18:30:02Z</updated>
    <author>
        <name>John Doe</name>
        <email>johndoe@example.com</email>
    </author>
    <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
 
    <entry>
        <title>Atom-Powered Robots Run Amok</title>
        <link href="http://example.org/2003/12/13/atom03"/>
        <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
        <updated>2003-12-13T18:30:02Z</updated>
        <summary>Some text.</summary>
    </entry>
 
</feed>

 

reference

 
[1] http://www.intertwingly.net/wiki/pie/Rss20AndAtom10Compared