原创内容,谢绝转载!
前言
前段时间研究了一些 java 代码实现的 http 请求框架的源码,发现之前开发的 jni 采集数据逻辑有一些可以改进的地方,所以现在将这些记录下来,方便以后自己查阅。
改良后的设计思路
connect, write, read,handshake, sslwrite, sslread, 这几个方法在同一个请求中,调用顺序是固定的,所以采集数据的时候应该做好状态转换,不是这个顺序的数据应该抛弃。
职责
Trace:
一个 socket 连接代表一个 trace;
一个 socket 连接可能有多个 http 请求;所以 trace 应该有个 httptrace 集合,httptrace 同一时刻可以由 fd, 和 threadid 唯一标示。
map:
以 fd 为 key, 全局 Trace 字典。
QA
1、 为什么要改良,之前的不好么?
其一之前设计的时候考虑的问题特别多,比如同一个 http 请求,发送请求和接受响应可能不在同一个线程; 其二之前设计的时候考虑的不是很全比如不同的 http 请求可能共用一个 socket 连接的方式,这其中的细节操作之前是比较粗暴的;其三之前 https 的具体实现没有考虑到 在之前会发送一个 http connect 请求,改良后考虑了
2、为什么要用 fd 和 线程 id 作为键?
同一个请求过程,不管是 http 还是 https fd 和 线程是相同的, 这里是指用 fd,和 id 标示 同一个 http 请求(httpTrace)
3、socket 连接复用问题怎么解决?
这个问题在上面的时序图中并没有说明白,这里我在详细的文字描述下处理: 首先所谓的 socket 复用,就是对于相同的 ip 和 端口,http 请求复用已经连接过的 socket,但是线程可能不相同,也就是说, write 操作后,从 map 里面根据 fd get 获取 trace 实例,如果获取不到 trace,或者 trace 存在超过 10s 那么应该抛弃本次收集,如果获取到 fd,但是 threadId 不相同的话,并且接下来 write 的是 http 报文的话,那么本次应该是一次 连接复用,所以应该生成一个 httpTrace 添加到 trace 中,并更新修改时间。
4、https 是怎么处理的呢?
注意连接复用要求协议一样,也就是说如果 socket 发送了 https,那么以后复用的也是 https,而且发送 https 之前会发送 http CONNECT 请求,根据这个就可以认为这是 https 了 。按照上面的序列图,获取到各状态的数据后,统一进行处理。
5、超时这里没看懂?
其一注册的socket连接不能超过一定时间(防止 oom 或者 fd 相同导致的错误);
其二请求发送,请求接受是一次连续的操作,自然两次发送不会超过太长时间,所以需要超时控制,比如 write,发送请求时可能会调用多次,这几次的间隔时间不应该超过一定时间(比如我定义的500ms)。 同样的发送完 request 后,最后一个 write 和第一个read 的时间不应该超过一定时间(比如10s), 读取响应的时候 read 可能会调用多次,每次的时间不应该超过 500ms( 这里指读取数据直到响应头结束)。依此其他几个方法一样,需要控制超时时间
6、ip 和 host 怎么关联?
一个 host 可能对应多个 ip, 可以用一个 LRU 算法, 关联 host 到 ip 集合的映射,在 getaddrinfo 方法中添加映射