🔗 内部子系统交互的愿望清单
这是对squid各部分在多种用例中**应**如何交互的尝试性文档。
当前为草稿,请谨慎阅读
🔗 指导原则
- 需要对其他对象进行任意调用的对象应持有
RefCountReferences - 需要通知事件完成的对象应持有
CallbackReferences - 一个类,一个使命
- 清晰的所有者 - 尽量只为对象指定清晰的所有者
🔗 通用所有权说明(仍为草稿)
- 在squid的客户端,有监听套接字和单个客户端套接字。squid配置拥有监听客户端侧套接字 - 它决定了其生命周期。操作系统也拥有这些套接字,但在正常运行情况下,它不会产生任何导致关闭的事件,所以有两个所有者:squid配置和操作系统。对于单个客户端套接字,只有一个所有者 - 远程客户端,它在我们的进程中由操作系统表示。
- 客户端连接上的请求由连接拥有。
- 用于满足连接的来自存储或上游的数据源由请求拥有。
- 上游请求由请求者拥有 - 当请求未被重定向到存储时由客户端请求拥有,当请求被复制到缓存时由存储拥有。
- 服务器端连接由操作系统拥有 - 在文件描述符打开期间,它必须保持为一个有效对象。它也由该协议的协议分发器拥有 - 该分发器将一系列请求放到它上面。
- 协议分发器是一个全局资源,由配置和所有当前通过它的请求拥有。
- 存储是一个全局资源,由配置拥有。它拥有代表客户端发起的请求。
- 存储客户端由从中读取数据的客户端拥有。
🔗 用例
🔗 无法解析的请求
在此示例中,Socket是指代表单个操作系统Socket的对象 - 在unix上是fd,在windows上是HANDLE。
HttpClientConnection 指的是单个 HttpClientConnection 对象
- 它代表了单个套接字用于HTTP。
- 操作系统报告新的套接字可用。
- 通信层构建Socket对象。
- 通信层持有Socket的
RefCountReference(通信层在此代表操作系统) - 在通知操作系统等之前,它不能被释放。 - Socket持有通信层的
CallbackReference以通知其关闭。
- 新的Socket被传递给接收到它的端口的监听工厂。
- 工厂构建
HttpClientConnection以在协议层表示Socket。 - 工厂调用
Socket.setClient(HttpClientConnection) - Socket持有
HttpClientConnection(它继承自SocketClient)的RefCountReference。 -
HttpClientConnection持有Socket的CallbackReference。
- 工厂构建
-
HttpClientConnection对Socket调用read()。- 对于某些系统,读取现在被安排在套接字上。对于其他系统,当下一个事件循环发生时,读取将完成。
- Socket获得了分发器的
RefCount引用。
- Socket请求从操作系统读取(如果尚未安排)。
- 读取完成。
- Socket将
HttpClientConnection和读取结果交给分发器。 - 分发器持有
HttpClientConnection的CallbackReference。
- Socket将
- 分发器回调
HttpClientConnection。-
HttpClientConnection无法解析请求。
-
-
HttpClientConnection调用Socket的write()以发送错误页面。- 根据套接字逻辑,写入可能立即发出,或者可能等待下一个事件循环。
- Socket获得了分发器的
RefCountReference。
- Socket向操作系统发出写入请求(如果未立即发出)。
- 写入完成。
- Socket将
HttpClientConnection和写入结果交给分发器。 - 分发器持有
HttpClientConnection的CallbackReference。
- Socket将
- 分发器以写入状态回调
HttpClientConnection。- 分发器放弃其对
HttpClientConnection的CallbackReference。
- 分发器放弃其对
-
HttpClientConnection调用Socket的clean_close()。- Socket检查是否有待处理的读取或写入。
- Socket调用shutdown(SD_SEND)到操作系统。
- Socket调用
HttpClientConnection的 `socket_detached`,通知它已被释放。 - Socket放弃其对
HttpClientConnection的CallbackReference。
- Socket调用
-
HttpClientConnection没有持有对其的RefCountReferences,因此被释放。 - Socket调用setClient,使用
LingerCloseSocketClient来设置自己。- Socket持有
LingerCloseSocketClient的RefCountReference。
- Socket持有
-
LingerCloseSocketClient调用socket的read()以检测EOF。- socket现在向操作系统安排读取。
-
LingerCloseSocketClient注册了一个回调,时间为现在 + LINGERDELAY。-
EventScheduler持有LingerCloseSocketClient和分发器的CallbackReference。
-
- 或者Socket现在可以安排读取到操作系统,在下一个事件循环中。
情况1:先读取到EOF (远端已确认关闭)
- 读取完成。
- Socket标记其读取通道为已关闭。
- Socket将
LingerCloseSocketClient和读取结果交给分发器。 - 分发器持有
LingerSocketClient的CallbackReference。
- 分发器将读取结果交给
LingerSocketClient。-
LingerSocketClient看到已达到EOF。
-
-
LingerSocket调用Socket的close()。- Socket执行sd_shutdown(SD_BOTH)和close(fd)。
- Socket回调通信层回调,通知其已完成。
- 通信层放弃其对socket的
RefCountReference。
- 通信层放弃其对socket的
- Socket因没有引用而释放。
- Socket调用
LingerSocketClient的 `socket_detached`。
- Socket调用
-
LingerSocketClient因没有引用而释放。
情况2:Linger超时发生。
EventScheduler将LingerSocketClient放入分发队列。- 分发器持有
LingerSocketClient的CallbackReference。 -
EventScheduler放弃其对LingerSocketClient的CallbackReference。
- 分发器持有
- 分发器向
LingerSocketClient触发事件。- 分发器放弃对
LingerSocketClient的CallbackReference。
- 分发器放弃对
-
LingerSocketClient调用socket.force_close()。- Socket执行sd_shutdown(SD_BOTH)和close(fd)。
- Socket回调通信层回调,通知其已完成。
- 通信层放弃其对socket的
RefCountReference。
- 通信层放弃其对socket的
- Socket因没有引用而释放。
- Socket调用
LingerSocketClient的 `socket_detached`。
- Socket调用
-
LingerSocketClient因没有引用而释放。
🔗 内部请求
- 监听套接字工厂为已打开的套接字创建一个
SocketClient对象。- Socket通过
RefCount拥有SocketClient。 - Socket由通信层拥有。如果是基于FD的,它在一个表中。如果是基于HANDLE的,它被放入一个已打开套接字集合中。
-
SocketClient对Socket有一个弱引用:新的Client拥有socket。没有人拥有Client。Socket对Client有回调,用于通知事件:ReadPossible(数据已到达),Close (因请求或外部事件)。其他事件在排队时得到回调 - 要求socket读取并将回调传递给它。这可以是“this”,如果我们的API结构良好,也可以是其他东西。
需要更多细节/仔细处理。
- Socket通过
- Client使用其原生协议将URL解析为一个规范化的请求:
HTTPClient将使用HTTP规则解析URL,FTP客户端将执行FTP代理操作以获取目标服务器等。这会创建一个新对象来处理该单个请求 -ClientRequest。SocketClient在ClientRequest中注册自身,此时ClientRequest可能会从核心启动其请求:Socket有对SocketClient的回调,SocketClient拥有Socket,并拥有其创建的ClientRequest。 -
SocketClient调用ClientRequest.atReadFront() 来指示ClientRequest现在位于套接字的队列前端,并且如果它想读取正文数据,就可以开始读取。Socket有对SocketClient的回调,SocketClient拥有Socket,并拥有其创建的ClientRequest。ClientRequest有一个对SocketClient的回调句柄。 -
ClientRequest调用SocketClient.finishedReadingRequest() 来指示它将不再从SocketClient读取任何数据,并且下一个请求可以被解析。 -
SocketClient调用ClientRequest.atWriteFront() 来指示ClientRequest现在位于套接字的队列前端。ClientRequest对SocketClient有回调,用于处理事件:WillNotReadAnyMore,SocketMustBeClosed,SocketMustBeReset。Socket有对SocketClient的回调,SocketClient拥有Socket,并拥有其创建的ClientRequest。ClientRequest有对SocketClient的回调,用于处理事件:WillNotReadAnyMore,SocketMustBeClosed,SocketMustBeReset,并且 -
ClientRequest请求squid核心的URL映射器对该规范化请求的响应。Socket有对SocketClient的回调,SocketClient拥有Socket,并拥有其创建的ClientRequest。ClientRequest有对SocketClient的回调,用于处理事件:WillNotReadAnyMore,SocketMustBeClosed,SocketMustBeReset。 - URL映射器根据协议或URL路径确定请求是针对内部资源的。
- 请求被转发到内部资源以满足。一个对象被提供给Client,该对象代表数据“源” - 它上面有方法允许请求响应头,拉取数据流,以及取消客户端的请求。
- 内部资源对象被客户端调用以启动传输,然后它交付内部头信息和内部生成的数据。
- 内部资源在最后一次请求读取数据时向客户端发出文件结束信号。
- 客户端。