🔗 特性:异步调用原生支持
- 目标:简化代码,改进调试,提高 ICAP 性能,并防止单次事务错误导致崩溃。
- 版本: 3.1
- 状态:完成
- 开发者:AlexRousskov
- 更多:bug1912,bug2093,以及 email109 中的项目 1
🔗 设计草图
本节总结了异步调用接口的设计思路。此处讨论的内容只有一小部分已实现,因此预计会有许多变动。
🔗 主循环
高层概览:主循环将持续触发所有异步调用,直到没有调用为止。然后,它将从基于时间的事件队列中获取最大等待时间,并让“select”探测文件描述符。下面的代码草图概述了一次主循环迭代。
timeout = TheEvents.checkpoint();
TheCalls.fireAllCalls();
TheSelect.scan(timeout);
更现实的情况:除了上述内容,如果触发了异步调用,事件队列必须重新检查,因为调用可能耗时足够长,以至于新事件变得就绪。同样,下面的代码片段是单次主循环迭代。
do {
timeout = TheEvents.checkpoint();
something_has_changed = not TheCalls.empty();
TheCalls.fire();
} while (something_has_changed);
TheSelect.scan(timeout);
🔗 触发单次调用
在触发异步调用之前,Squid 会准备捕获调用异常。如果调用本身触发失败(例如,事件回调数据无效),Squid 将停止处理该调用。如果捕获到异常,将调用调用异常处理器。
try {
if (!call->fire())
return;
}
catch (const exception &e) {
call->handleException(e);
}
call->end();
所有异步调用的触发方式都相同。各个调用类型提供特定于调用的调试信息(格式类似),使用存储的参数调用所需代码,并定制异常处理。
实际代码也可能使用 catch(…) 子句捕获其他异常。
🔗 回调
回调是进行未来调用所需的信息。回调通常在 Squid 中用于向作业或模块注册,以接收关于重要事件的未来更新或其他异步通知。
它可以被视为一个未完成的调用:已知调用目标地址和可能的某些参数,但调用的时间和可能的其他参数未知,必须由调用者确定。
初始实现不会优化回调表示,而是仅使用调用(带有某些未设置的参数)作为回调。
🔗 调用类型
调用目标种类决定了调用类型。支持的目标包括:AsyncJob 对象方法、其他对象方法和全局静态函数。
AsyncJob 调用可能会这样实现其公共方法。
bool JobCall::fire() {
AsyncJob *job = dynamic_cast<AsyncJob*>(theObject);
if (job pointer is not valid) {
debugs(section, level, "NOT Calling " << className << "::"
<< methodName << " method ...");
return false;
}
debugs(section, level, "Entering " << className << "::" <<
methodName << " method [" >> job->status () >> "]");
(job->*)(theMethod)(...)
return true;
}
void JobCall::end()
{
if (job->done()) {
debug("Ending job [" >> job->status () >> "]");
job->swanSing();
delete job;
debug("The " << className << "::" << methodName
<< " method ended the job [" >> (void*)job >> "]");
return;
}
debug("Exited " << className << "::" << methodName <<
" method [" >> job->status () >> "]");
}
实际的 JobCall 代码将使用更多调试,并可能利用更多的 ObjectCall(即其父类)功能。
Comm 模块可能会提供自己的便捷类来处理“通知您的文件描述符已准备好”之类的调用。
🔗 是什么创建了异步调用?
在 TheCalls 队列初始化后,任何代码都可以随时排队一个异步调用。例如,以下任何调用都可能导致新的异步调用创建。
TheEvents.checkpoint(); // when the event(s) time has come
TheCalls.fireAll(); // when a call handler needs to call
TheSelect.scan(timeout); // when descriptors are ready
🔗 挑战
将会有模板、宏和/或代码生成的类来处理任意的调用目标地址以及带有零、一、二或三个参数(至少)的调用。设计一组简单而高效的包装器而不使用像 Boost 这样的基于模板的库将很棘手。
类别:功能
导航:站点搜索,站点页面,类别,🔼 向上