HttpSM.cc
DNS的查询(包括hostdb的查询)从以下函数开始
void
HttpSM::set_next_state()
{
......
HTTP_SM_SET_DEFAULT_HANDLER(&HttpSM::state_hostdb_lookup);
do_hostdb_lookup();
......
}
HttpSM::set_next_state()--->HttpSM::do_hostdb_lookup()
void
HttpSM::do_hostdb_lookup()
{
ink_assert(t_state.dns_info.lookup_name != NULL);
ink_assert(pending_action == NULL);
milestones.dns_lookup_begin = ink_get_hrtime();
bool use_srv_records = t_state.srv_lookup;
//在配置proxy.config.srv_enabled情况下的处理
if (use_srv_records) {
char d[MAXDNAME];
memcpy(d, "_http._tcp.", 11); // don't copy '\0'
ink_strlcpy(d + 11, t_state.server_info.name, sizeof(d) - 11 ); // all in the name of performance!
DebugSM("dns_srv", "Beginning lookup of SRV records for origin %s", d);
HostDBProcessor::Options opt;
if (t_state.api_txn_dns_timeout_value != -1)
opt.timeout = t_state.api_txn_dns_timeout_value;
Action *srv_lookup_action_handle =
hostDBProcessor.getSRVbyname_imm(this, (process_srv_info_pfn) & HttpSM::process_srv_info, d, 0, opt);
if (srv_lookup_action_handle != ACTION_RESULT_DONE) {
ink_assert(!pending_action);
pending_action = srv_lookup_action_handle;
historical_action = pending_action;
} else {
char *host_name = t_state.dns_info.srv_lookup_success ? t_state.dns_info.srv_hostname : t_state.dns_info.lookup_name;
opt.port = t_state.dns_info.srv_lookup_success ? t_state.dns_info.srv_port : t_state.server_info.port;
opt.flags = (t_state.cache_info.directives.does_client_permit_dns_storing)
? HostDBProcessor::HOSTDB_DO_NOT_FORCE_DNS
: HostDBProcessor::HOSTDB_FORCE_DNS_RELOAD
;
opt.timeout = (t_state.api_txn_dns_timeout_value != -1) ? t_state.api_txn_dns_timeout_value : 0;
opt.host_res_style = ua_session->host_res_style;
Action *dns_lookup_action_handle = hostDBProcessor.getbyname_imm(this,
(process_hostdb_info_pfn) & HttpSM::
process_hostdb_info,
host_name, 0,
opt);
if (dns_lookup_action_handle != ACTION_RESULT_DONE) {
ink_assert(!pending_action);
pending_action = dns_lookup_action_handle;
historical_action = pending_action;
} else {
call_transact_and_set_next_state(NULL);
}
}
return;
} else { /* we arent using SRV stuff... */
DebugSM("http_seq", "[HttpSM::do_hostdb_lookup] Doing DNS Lookup");
int server_port = t_state.current.server ? t_state.current.server->port : t_state.server_info.port;
if (t_state.api_txn_dns_timeout_value != -1) {
DebugSM("http_timeout", "beginning DNS lookup. allowing %d mseconds for DNS lookup",
t_state.api_txn_dns_timeout_value);
}
HostDBProcessor::Options opt;
opt.port = server_port;//DNS server端口
//一些标志为位
opt.flags = (t_state.cache_info.directives.does_client_permit_dns_storing)
? HostDBProcessor::HOSTDB_DO_NOT_FORCE_DNS
: HostDBProcessor::HOSTDB_FORCE_DNS_RELOAD
;
opt.timeout = (t_state.api_txn_dns_timeout_value != -1) ? t_state.api_txn_dns_timeout_value : 0;//超时时间
opt.host_res_style = ua_session->host_res_style;//类型:ipv4、ipv6
//调用getbyname_imm进行DNS查询
Action *dns_lookup_action_handle = hostDBProcessor.getbyname_imm(this, (process_hostdb_info_pfn) & HttpSM::process_hostdb_info, t_state.dns_info.lookup_name, 0, opt);
if (dns_lookup_action_handle != ACTION_RESULT_DONE) {
ink_assert(!pending_action);
pending_action = dns_lookup_action_handle;
historical_action = pending_action;
} else {
call_transact_and_set_next_state(NULL);
}
return;
}
ink_assert(!"not reached");
return;
}
HttpSM::set_next_state()--->HttpSM::do_hostdb_lookup()--->HostDBProcessor::getbyname_imm()
Action *
HostDBProcessor::getbyname_imm(Continuation * cont, process_hostdb_info_pfn process_hostdb_info,
const char *hostname, int len, Options const& opt)
{
ink_assert(cont->mutex->thread_holding == this_ethread());
bool force_dns = false;
EThread *thread = cont->mutex->thread_holding;
ProxyMutex *mutex = thread->mutex;
HostDBMD5 md5;
//对标志位进行处理
//是否强制进行DNS查询(不查hostdb)
if (opt.flags & HOSTDB_FORCE_DNS_ALWAYS)
force_dns = true;
else if (opt.flags & HOSTDB_FORCE_DNS_RELOAD) {
force_dns = (hostdb_re_dns_on_reload ? true : false);
if (force_dns)
HOSTDB_INCREMENT_DYN_STAT(hostdb_re_dns_on_reload_stat);
}
HOSTDB_INCREMENT_DYN_STAT(hostdb_total_lookups_stat);
if (!hostdb_enable || !*hostname) {
(cont->*process_hostdb_info) (NULL);
return ACTION_RESULT_DONE;
}
//创建待查询域名的md5结构
md5.host_name = hostname;
md5.host_len = hostname ? (len ? len : strlen(hostname)) : 0;
md5.port = opt.port;
md5.db_mark = db_mark_for(opt.host_res_style);
#ifdef SPLIT_DNS
//如果开启splitDNS功能,则查找对应的DNS server
if (SplitDNSConfig::isSplitDNSEnabled()) {
const char *scan = hostname;
for (; *scan != '\0' && (ParseRules::is_digit(*scan) || '.' == *scan); scan++);
if ('\0' != *scan) {
SplitDNS* pSD = SplitDNSConfig::acquire();
if (0 != pSD)
md5.dns_server = static_cast<DNSServer*>(pSD->getDNSRecord(md5.host_name));
SplitDNSConfig::release(pSD);
}
}
#endif // SPLIT_DNS
md5.refresh();
//先查hostdb,查不到再进行DNS查询
if (!force_dns) {
bool loop;
do {
loop = false;
ProxyMutex *bucket_mutex = hostDB.lock_for_bucket((int) (fold_md5(md5.hash) % hostDB.buckets));
MUTEX_LOCK(lock, bucket_mutex, thread);
if (lock) {
HostDBInfo *r = probe(bucket_mutex, md5, false);
if (r) {
if (r->failed())
loop = check_for_retry(md5.db_mark, opt.host_res_style);
if (!loop) {
Debug("hostdb", "immediate answer for %.*s", md5.host_len, md5.host_name);
HOSTDB_INCREMENT_DYN_STAT(hostdb_total_hits_stat);
(cont->*process_hostdb_info) (r);
return ACTION_RESULT_DONE;
}
md5.refresh();
}
}
} while (loop);
}
Debug("hostdb", "delaying force %d answer for %.*s [timeout %d]", force_dns, md5.host_len, md5.host_name, opt.timeout);
//hostdb查询失败,进行DNS查询,遵循ATS的continuation模式(异步),先创建一个HostDBContinuation,再进行下面的DNS查询操作
HostDBContinuation *c = hostDBContAllocator.alloc();
HostDBContinuation::Options copt;
copt.cont = cont;//指向HttpSM的实例
copt.force_dns = force_dns;
copt.timeout = opt.timeout;
copt.host_res_style = opt.host_res_style;
c->init(md5, copt);
SET_CONTINUATION_HANDLER(c, (HostDBContHandler) & HostDBContinuation::probeEvent);
thread->schedule_in(c, HOST_DB_RETRY_PERIOD);
return &c->action;
}
HttpSM::set_next_state()--->HttpSM::do_hostdb_lookup()--->HostDBProcessor::getbyname_imm()--->HostDBContinuation::probeEvent()
int
HostDBContinuation::probeEvent(int /* event ATS_UNUSED */, Event * e)
{
ink_assert(!link.prev && !link.next);
EThread *t = e ? e->ethread : this_ethread();
MUTEX_TRY_LOCK_FOR(lock, action.mutex, t, action.continuation);
if (!lock) {
mutex->thread_holding->schedule_in(this, HOST_DB_RETRY_PERIOD);
return EVENT_CONT;
}
if (action.cancelled) {
hostdb_cont_free(this);
return EVENT_DONE;
}
//检查一下主机名
if (!hostdb_enable || (!*md5.host_name && !md5.ip.isValid())) {
if (action.continuation)
action.continuation->handleEvent(EVENT_HOST_DB_LOOKUP, NULL);
#ifdef NON_MODULAR
if (from)
do_put_response(from, 0, from_cont);
#endif
hostdb_cont_free(this);
return EVENT_DONE;
}
//如果没有强制DNS,先再查一下hostdb
if (!force_dns) {
HostDBInfo *r = probe(mutex, md5, false);
if (r)
HOSTDB_INCREMENT_DYN_STAT(hostdb_total_hits_stat);
#ifdef NON_MODULAR
if (action.continuation && r)
reply_to_cont(action.continuation, r);
//cluster模式,等到分析cluster源码时再说
if (from)
do_put_response(from, r, from_cont);
#endif
if (r || from) {
hostdb_cont_free(this);
return EVENT_DONE;
}
#ifdef NON_MODULAR
if (do_get_response(e))
return EVENT_CONT;
#endif
}
//进行DNS查询吧
do_dns();
return EVENT_DONE;
}
HttpSM::set_next_state()--->HttpSM::do_hostdb_lookup()--->HostDBProcessor::getbyname_imm()--->HostDBContinuation::probeEvent()--->HostDBContinuation::do_dns()
这个函数的功能主要是设置超时处理函数,把请求加入到等待队列和调用 dnsProcessor的相应函数进行DNS查询
void
HostDBContinuation::do_dns()
{
ink_assert(!action.cancelled);
if (is_byname()) {
Debug("hostdb", "DNS %s", md5.host_name);
IpAddr tip;
//如果待查询的域名是ip地址了,直接通知HttpSM吧,不用往下查询了
if (0 == tip.load(md5.host_name)) {
// check 127.0.0.1 format // What the heck does that mean? - AMC
if (action.continuation) {
HostDBInfo *r = lookup_done(tip, md5.host_name, false, HOST_DB_MAX_TTL, NULL);
reply_to_cont(action.continuation, r);
}
hostdb_cont_free(this);
return;
}
}
//超时调用HostDBContinuation::dnsEvent处理
if (hostdb_lookup_timeout)
timeout = mutex->thread_holding->schedule_in(this, HRTIME_SECONDS(hostdb_lookup_timeout));
else
timeout = NULL;
if (set_check_pending_dns()) {//加入等待队列
DNSProcessor::Options opt;//初始化一些选项
opt.timeout = dns_lookup_timeout;
opt.host_res_style = host_res_style_for(md5.db_mark);
SET_HANDLER((HostDBContHandler) & HostDBContinuation::dnsEvent);
if (is_byname()) {
if (md5.dns_server)
opt.handler = md5.dns_server->x_dnsH;
//进行真正的查询处理
pending_action = dnsProcessor.gethostbyname(this, md5.host_name, opt);
} else if (is_srv()) {
Debug("dns_srv", "SRV lookup of %s", md5.host_name);
pending_action = dnsProcessor.getSRVbyname(this, md5.host_name, opt);
} else {
ip_text_buffer ipb;
Debug("hostdb", "DNS IP %s", md5.ip.toString(ipb, sizeof ipb));
pending_action = dnsProcessor.gethostbyaddr(this, &md5.ip, opt);
}
} else {
SET_HANDLER((HostDBContHandler) & HostDBContinuation::dnsPendingEvent);
}
}
HttpSM::set_next_state()--->HttpSM::do_hostdb_lookup()--->HostDBProcessor::getbyname_imm()--->HostDBContinuation::probeEvent()--->HostDBContinuation::do_dns()--->DNSProcessor::gethostbyname()--->DNSProcessor::getby()
这个函数的功能是创建并初始化查询表项,这个表项要放到一个表中,然后由专门的线程从这个表中取出该表项进行DNS查询(异步嘛)
Action *
DNSProcessor::getby(const char *x, int len, int type, Continuation *cont, Options const& opt)
{
Debug("dns", "received query %s type = %d, timeout = %d", x, type, opt.timeout);
if (type == T_SRV) {
Debug("dns_srv", "DNSProcessor::getby attempting an SRV lookup for %s, timeout = %d", x, opt.timeout);
}
//创建查询表项
DNSEntry *e = dnsEntryAllocator.alloc();
e->retries = dns_retries;
//初始化查询表项
e->init(x, len, type, cont, opt);
MUTEX_TRY_LOCK(lock, e->mutex, this_ethread());
if (!lock)
thread->schedule_imm(e);
else
//调度DNSEntry::mainEvent执行
e->handleEvent(EVENT_IMMEDIATE, 0);
return &e->action;
}
HttpSM::set_next_state()--->HttpSM::do_hostdb_lookup()--->HostDBProcessor::getbyname_imm()--->HostDBContinuation::probeEvent()--->HostDBContinuation::do_dns()--->DNSProcessor::gethostbyname()--->DNSProcessor::getby()--->DNSEntry::init()
这个函数的功能是初始化DNS查询表项
void
DNSEntry::init(const char *x, int len, int qtype_arg, Continuation* acont,
DNSProcessor::Options const& opt)
{
qtype = qtype_arg;//查询类型:T_A、T_NS、T_CNAME...
host_res_style = opt.host_res_style;//ip类型:ipv4、ipv6
if (is_addr_query(qtype)) {
// adjust things based on family preference.
if (HOST_RES_IPV4 == host_res_style ||
HOST_RES_IPV4_ONLY == host_res_style) {
qtype = T_A;
} else if (HOST_RES_IPV6 == host_res_style ||
HOST_RES_IPV6_ONLY == host_res_style) {
qtype = T_AAAA;
}
}
submit_time = ink_get_hrtime();//提交时间
action = acont;//指向HostDBContinuation
submit_thread = acont->mutex->thread_holding;
#ifdef SPLIT_DNS
//如果开启splitDNS功能,那使用的DNSHandler是该DNS server对应的handler,否则使用DNS初始化时创建的吧
if (SplitDNSConfig::gsplit_dns_enabled) {
dnsH = opt.handler ? opt.handler : dnsProcessor.handler;
} else {
dnsH = dnsProcessor.handler;
}
#else
INK_NOWARN(adnsH);
dnsH = dnsProcessor.handler;
#endif // SPLIT_DNS
dnsH->txn_lookup_timeout = opt.timeout;
mutex = dnsH->mutex;
//根据DNS查询类型设置查询的主机名
if (is_addr_query(qtype) || qtype == T_SRV) {
if (len) {
len = len > (MAXDNAME - 1) ? (MAXDNAME - 1) : len;
memcpy(qname, x, len);
qname_len = len;
qname[len] = 0;
} else {
ink_strlcpy(qname, x, MAXDNAME);
qname_len = strlen(qname);
}
} else { //T_PTR
IpAddr const* ip = reinterpret_cast<IpAddr const*>(x);
if (ip->isIp6())
make_ipv6_ptr(&ip->_addr._ip6, qname);
else if (ip->isIp4())
make_ipv4_ptr(ip->_addr._ip4, qname);
else
ink_assert(!"T_PTR query to DNS must be IP address.");
}
//设置DNSEntry的handler为DNSEntry::mainEvent
SET_HANDLER((DNSEntryHandler) & DNSEntry::mainEvent);
}
HttpSM::set_next_state()--->HttpSM::do_hostdb_lookup()--->HostDBProcessor::getbyname_imm()--->HostDBContinuation::probeEvent()--->HostDBContinuation::do_dns()--->DNSProcessor::gethostbyname()--->DNSProcessor::getby()--->DNSEntry::init()--->DNSEntry::mainEvent()
这个函数的功能是把创建的DNS查询表项到DNSHandler的队列entries中,然后调用write_dns遍历该队列处理每个表项
int
DNSEntry::mainEvent(int event, Event *e)
{
switch (event) {
default:
ink_assert(!"bad case");
return EVENT_DONE;
case EVENT_IMMEDIATE:{
//到这了handler应该设置好了
if (!dnsH)
dnsH = dnsProcessor.handler;
if (!dnsH) {
Debug("dns", "handler not found, retrying...");
SET_HANDLER((DNSEntryHandler) & DNSEntry::delayEvent);
return handleEvent(event, e);
}
//看配置是否要在主机名后加上扩展后缀
if (dns_search)
domains = dnsH->m_res->dnsrch;
//如果需要在主机名后加上.org(配置文件中配的)这样的后缀
if (domains && !strnchr(qname, '.', MAXDNAME)) {
qname[qname_len] = '.';
ink_strlcpy(qname + qname_len + 1, *domains, MAXDNAME - (qname_len + 1));
qname_len = strlen(qname);
++domains;
}
Debug("dns", "enqueing query %s", qname);
//在DNSHandler的entries队列中查找该表项,如果已经有了,加到重复队列dups中
DNSEntry *dup = get_entry(dnsH, qname, qtype);
if (dup) {
Debug("dns", "collapsing NS request");
dup->dups.enqueue(this);
} else {
Debug("dns", "adding first to collapsing queue");
dnsH->entries.enqueue(this);
//遍历 entries队列处理每一个DNS查询表项
write_dns(dnsH);
}
return EVENT_DONE;
}
case EVENT_INTERVAL:
Debug("dns", "timeout for query %s", qname);
if (dnsH->txn_lookup_timeout) {
timeout = NULL;
dns_result(dnsH, this, result_ent, false); //do not retry -- we are over TXN timeout on DNS alone!
return EVENT_DONE;
}
if (written_flag) {
Debug("dns", "marking %s as not-written", qname);
written_flag = false;
--(dnsH->in_flight);
DNS_DECREMENT_DYN_STAT(dns_in_flight_stat);
}
timeout = NULL;
dns_result(dnsH, this, result_ent, true);
return EVENT_DONE;
}
}
HttpSM::set_next_state()--->HttpSM::do_hostdb_lookup()--->HostDBProcessor::getbyname_imm()--->HostDBContinuation::probeEvent()--->HostDBContinuation::do_dns()--->DNSProcessor::gethostbyname()--->DNSProcessor::getby()--->DNSEntry::init()--->DNSEntry::mainEvent()
这个函数的功能是遍历DNSHandler的队列entries处理每个表项
static void
write_dns(DNSHandler *h)
{
ProxyMutex *mutex = h->mutex;
DNS_INCREMENT_DYN_STAT(dns_total_lookups_stat);
int max_nscount = h->m_res->nscount;
if (max_nscount > MAX_NAMED)
max_nscount = MAX_NAMED;
//别的线程正在处理中
if (h->in_write_dns)
return;
h->in_write_dns = true;
if (h->in_flight < dns_max_dns_in_flight) {//dns_max_dns_in_flight控制一次最大发送的DNS请求
DNSEntry *e = h->entries.head;
while (e) {
DNSEntry *n = (DNSEntry *) e->link.next;
if (!e->written_flag) {
//如果是round_robin模式,则循环使用DNS server
if (dns_ns_rr) {
int ns_start = h->name_server;
do {
h->name_server = (h->name_server + 1) % max_nscount;
} while (h->ns_down[h->name_server] && h->name_server != ns_start);
}
//发送一个DNS查询请求,如果失败则不往下处理了
if (!write_dns_event(h, e))
break;
}
if (h->in_flight >= dns_max_dns_in_flight)
break;
e = n;
}
}
h->in_write_dns = false;
}
HttpSM::set_next_state()--->HttpSM::do_hostdb_lookup()--->HostDBProcessor::getbyname_imm()--->HostDBContinuation::probeEvent()--->HostDBContinuation::do_dns()--->DNSProcessor::gethostbyname()--->DNSProcessor::getby()--->DNSEntry::init()--->DNSEntry::mainEvent()--->write_dns_event()
这个函数的功能是构建DNS查询报文并发送一个DNS查询请求,发送DNS查询请求流程到此结束
static bool
write_dns_event(DNSHandler *h, DNSEntry *e)
{
ProxyMutex *mutex = h->mutex;
union {
HEADER _h;
char _b[MAX_DNS_PACKET_LEN];
} blob;
int r = 0;
//创建DNS查询报文,其中qname为待查询的域名
if ((r = _ink_res_mkquery(h->m_res, e->qname, e->qtype, blob._b)) <= 0) {
Debug("dns", "cannot build query: %s", e->qname);
//处理以一个表项的查询结果:重试
dns_result(h, e, NULL, false);
return true;
}
//生成该报文的id
uint16_t i = h->get_query_id();
blob._h.id = htons(i);
if (e->id[dns_retries - e->retries] >= 0) {
//clear previous id in case named was switched or domain was expanded
h->release_query_id(e->id[dns_retries - e->retries]);
}
//记录每次查询的报文id
e->id[dns_retries - e->retries] = i;
Debug("dns", "send query (qtype=%d) for %s to fd %d", e->qtype, e->qname, h->con[h->name_server].fd);
//发送DNS查询请求
int s = socketManager.send(h->con[h->name_server].fd, blob._b, r, 0);
//发送失败处理
if (s != r) {
Debug("dns", "send() failed: qname = %s, %d != %d, nameserver= %d", e->qname, s, r, h->name_server);
// changed if condition from 'r < 0' to 's < 0' - 8/2001 pas
if (s < 0) {
if (dns_ns_rr)
h->rr_failure(h->name_server);
else
h->failover();
}
return false;
}
//设置相应的标志和计数
e->written_flag = true;//已经发送
e->which_ns = h->name_server;//指向使用的DNS server
e->once_written_flag = true;
++h->in_flight;//计数
DNS_INCREMENT_DYN_STAT(dns_in_flight_stat);
e->send_time = ink_get_hrtime();//发送时间
if (e->timeout)
e->timeout->cancel();
//设置超时处理,超时后执行DNSEntry::mainEvent
if (h->txn_lookup_timeout) {
e->timeout = h->mutex->thread_holding->schedule_in(e, HRTIME_MSECONDS(h->txn_lookup_timeout));
} else {
e->timeout = h->mutex->thread_holding->schedule_in(e, HRTIME_SECONDS(dns_timeout));
}
Debug("dns", "sent qname = %s, id = %u, nameserver = %d", e->qname, e->id[dns_retries - e->retries], h->name_server);
//最后,记录失败的次数和时间,先假设失败,等响应了再置成功
h->sent_one();
return true;
}