Eric 发布的文章

HTTP 2 的特性

从 HTTP 2 in action 这本书看的, 发现这个网站总结的不错: https://tools.keycdn.com/http2-test, 就摘抄过来了. 顺便测试了一下 (20191104), taobao, ebay 支持 Http2, jd 和 baidu 主页都不支持 HTTP2

  1. HTTP/2 is binary, instead of textual.
  2. It is fully multiplexed, sending multiple requests in parallel over a single TCP connection.
  3. It uses header compression HPACK to reduce overhead.
  4. It allows servers to “push” responses proactively into client caches instead of waiting for a new request for each resource
  5. It uses the new ALPN extension which allows for faster-encrypted connections since the application protocol is determined during the initial connection.
  6. It reduces additional round trip times (RTT), making your website load faster without any optimization.
  7. Domain sharding and asset concatenation is no longer needed with HTTP/2.
  8. Flow control
  9. Stream prioritization

如何判断一个网站是不是支持 HTTP2? 使用最新的 curl 命令:

LM-SHC-16507776:tmp xiatian$ curl -vvv -s  --http2 https://www.jd.com:443/ 2>&1 | grep ALPN
* ALPN, offering h2
* ALPN, offering http/1.1
* ALPN, server accepted to use http/1.1
LM-SHC-16507776:tmp xiatian$ curl -vvv -s  --http2 https://www.ebay.com:443/ 2>&1 | grep ALPN
* ALPN, offering h2
* ALPN, offering http/1.1
* ALPN, server accepted to use h2

上面的输出中 baidu 首页不支持, ebay 首页支持.


从下面这个问答看:
在 Chrome 里面, 对于 HTTP 2 同一个 origin 不同的 tab 之间共享一个 tcp 连接.
对于 HTTP 1.1:

  1. HTTP/1.1 sockets are stored in a pool when they are not active. A request from any tab may end up using any socket in the pool;
  2. Incognito tabs use their own independent socket pool (All incognito tabs for a profile share just one socket pool, though);
  3. Isolated apps use their own socket pools.
  4. Different profiles and guest mode use their own socket pools.
  5. Global requests not tied to a profile use their own socket pool;

https://stackoverflow.com/questions/51535819/in-http-1-1-are-connections-shared-across-multiple-chrome-tabs-to-the-same-origi
https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/S3TEvmRWcNE

netcat(nc) 的各种操作

  1. 扫描端口
    nc -vzu 192.168.0.1 80-90
    nc -v -z www.tianxiaohui.com 90-100
    nc -v -n -z -w 1 192.168.1.2 1-1000

  2. 从客户单传文件到服务端
    服务端先起来
    nc -l 8080 > file.txt
    nc -v remote_server 8080 > localfile.txt

  3. 从服务端传文件到客户端
    nc -4 -v -l 7070 < myfile.txt
    客户端可用浏览器或者 nc 命令
    nc -v www.tianxiaohui.com 7070

  4. 作为代理

    单向代理: nc -l 12345 | nc www.google.com 80
    双向:
    mkfifo backpipe
    nc -l 12345 0<backpipe | nc www.google.com 80 1>backpipe

  5. 让任何命令作为网络服务器 (网络后门)

    服务端: nc -4 -l -p 1234 -e /bin/sh
    客户端: nc remote_server 1234
    ls -lah

如果遇到:
close: Bad file descriptor
加上 -4 参数.

使用 telnet, nc, openssl 浏览网站

LM-SHC-16507776:~ xiatian$ telnet www.tianxiaohui.com 80
Trying 156.236.115.130...
Connected to www.tianxiaohui.com.
Escape character is '^]'.

GET /about.html HTTP/1.1
Host: www.tianxiaohui.com

HTTP/1.1 400 Bad Request
Date: Sat, 02 Nov 2019 03:18:30 GMT
Server: Apache
Accept-Ranges: bytes
Connection: close
Content-Type: text/html


printf "GET /index.html HTTP/1.0\r\nHost: www.tianxiaohui.com\r\n\r\n" | nc www.tianxiaohui.com 80

使用 openssl 浏览 https 的
LM-SHC-16507776:~ xiatian$ openssl

OpenSSL> s_client -crlf -connect www.bing.com:443 -quiet

depth=2 C = IE, O = Baltimore, OU = CyberTrust, CN = Baltimore CyberTrust Root
verify return:1
depth=1 C = US, ST = Washington, L = Redmond, O = Microsoft Corporation, OU = Microsoft IT, CN = Microsoft IT TLS CA 2
verify return:1
depth=0 CN = www.bing.com
verify return:1

GET / HTTP/1.1
Host:www.bing.com

HTTP/1.1 302
Cache-Control: private
Content-Length: 137
Content-Type: text/html; charset=utf-8
Location: https://cn.bing.com/
Date: Sat, 02 Nov 2019 03:11:59 GMT

Object moved
Object moved to https://cn.bing.com.

GET / HTTP/1.1
Host:cn.bing.com

HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Length: 114805

Tomcat 7 NIO handling request on Linux

围绕这个图, 做几点解说:
Tomcat_Request_handling.png

  1. 从操作系统层面来说, 收到 TCP 的 SYN 之后, 放到 TCP SYN backlog 队列;

  2. SYN backlog 长度由 /proc/sys/net/ipv4/tcp_max_syn_backlog 设置, 还有文章说最终值有几个因素共同决定;

  3. 如何查看一个监听端口的 SYN backlog 当前的队列长度? -> TODO ->

  4. 如果 SYN backlog 已经满了, 那么操作系统层面会自动丢弃接收到的 syn 包, 那么客户端以为对端没收到, 会继续尝试

  5. 当操作系统回复 syn+ack 之后, 对端返回 ack, 完成三次包握手, 这时, 这个连接编程 Established 的状态, 进入 listen backlog;

  6. listen backlog 的长度由 listen 系统调用的参数决定, 在 Java 里由 ServerSocket(int port, int backlog)的第一个参数决定. java 默认是 50. Tomcat 7 里面由 Connector 参数 acceptCount 决定, 默认是 100;

  7. 如果这个 listen backlog 满了, OS 收到对端为了完成握手的 ack 时, 选择 ignore 这个 ack, 那么客户端会以为丢包, 会继续尝试发送 ack;

  8. 如何查看一个监听端口的 listen backlog 队列的当前长度? -> Linux -> ss -ltn 看 Send-Q 列xiatian_lvsbastion200_____ssh_.png

  9. Tomcat 7 NIO 处理这些 socket 连接有 2 类 background 线程, 分别是 Acceptor 线程和 Poller 线程;

  10. Acceptor 线程 通过 serverSock.accept() 方法接受新连接, 就是 listen backlog 里面已经建立的连接;

  11. Acceptor 接受之后, 就把这个接受的 socket 送给 Poller 线程去处理;

  12. Poller 线程把 socket 封装之后放入 TaskQueue;

  13. Poller 线程还负责 从 socket 的 Channel 搬运数据到对应的 Buffer, 也就是负责 NIO 的 Selector 的处理任务;

  14. Acceptor 线程和 Poller 线程的数量分别由 Tomcat Connector 的 acceptorThreadCount 和 pollerThreadCount 决定;

  15. Tomcat 的 TaskQueue 是一个 LinkedBlockingQueue;

  16. TaskQueue队列里的 Task 由 Executor 里的线程读取并执行;

  17. TaskQueue队列里的 Task 由 Poller 放入;

  18. TaskQueue的长度默认是 Int 的最大值, 不过基本不会放这么多, 如果TaskQueue的任务和在处理的超过 maxConnection 的值, Poller 就会决绝新的任务;

Java NIO & NIO2 里面的 attachment 是怎么回事?

在 Java NIO 和 NIO2 里面我们经常看到attach 和 attachment 相关的 API, 如:

Objcet SelectionKey.attch(Object obj)
Object SelectionKey.attachment()
SelectionKey AbstractSelector.register(AbstractSelectableChannel ch,int ops, Object att);

这里的 Attachment 是一个 Object 对象, 也就是说它可以 attach 任何对象, 为什么要 attach 一个对象呢?
我们从 Blocking IO 说起, 当 Blocking IO 的时候, 读字节流或字符流的的线程一直 block 在读操作上, 不去干其它事情, 当来一个字节/字符的时候, 它就读一个, 直到返回 -1 代表读完(EOF). 可是当时 NIO 的时候, 读线程直接返回, 是由一个专门的 IO 线程操作 Selector 去读取的, 当有部分数据到达的时候, 它就帮你读取, 读完来的数据之后, 还没读取到 EOF, 那么要继续监听这个 socket, 再次等待它接下来的数据, 那么刚才读取的数据放哪里呢? 之后来的数据怎么拼接到原来的数据之后呢? 那么就需要有一个容器放置还没有读完的半成品, 在下次同一个 socket 来数据的时候, 再次拿出这个容器, 继续放数据, 那么这个容器怎么可以容易的每次都有数据的时候拿到呢? 那就放到 SelectionKey 上面吧, 反正每次都是通过 Selectionkey 知道有数据的, 一旦 SelectionKey 有数据, 就通过它拿到之前装有半成品的容器, 那么可以继续放了.

每次读完通过 attach(Object) 方法附在上面, 下次当有数据来的时候, 通过 attachment 方法拿到之前的容器.

参考: https://jfarcand.wordpress.com/2006/07/06/tricks-and-tips-with-nio-part-ii-why-selectionkey-attach-is-evil/