Skip to content

Latest commit

 

History

History
346 lines (272 loc) · 8.66 KB

File metadata and controls

346 lines (272 loc) · 8.66 KB

workflow源码解析10 : dns 01

项目源码 : https://github.com/sogou/workflow

更加详细的源码注释可看 : https://github.com/chanchann/workflow_annotation

协议rfc : https://datatracker.ietf.org/doc/html/rfc1035

我们使用getaddrinfo函数时,可以使用域名,内部自动处理了域名的请求,这个函数虽然方便,但它是阻塞的,不大适用自己写的高并发服务器。

我们可以先看create_dns_task

perl calltree.pl "create_dns_task" "" 1 1 3

create_dns_task
├── WFResolverTask::dispatch  [vim src/nameservice/WFDnsResolver.cc +271]
│   ├── start [vim _include/workflow/Workflow.h +68]
│   ├── Workflow::start_series_work   [vim _include/workflow/Workflow.h +169]
│   ├── Workflow::start_series_work   [vim _include/workflow/Workflow.h +185]
│   ├── SubTask::subtask_done [vim src/kernel/SubTask.cc +21]
│   ├── ParallelTask::dispatch        [vim src/kernel/SubTask.cc +54]
│   ├── __WFConditional::dispatch     [vim src/factory/WFResourcePool.cc +44]
│   ├── start [vim src/factory/Workflow.h +68]
│   ├── Workflow::start_series_work   [vim src/factory/Workflow.h +172]
│   └── Workflow::start_series_work   [vim src/factory/Workflow.h +188]
├── WFDnsClient::create_dns_task      [vim src/client/WFDnsClient.cc +238]
│   ├── WFResolverTask::dispatch      [vim src/nameservice/WFDnsResolver.cc +271]
│   ├── WFDnsClient::create_dns_task  [vim src/client/WFDnsClient.cc +238]
│   └── WFTaskFactory::create_dns_task        [vim src/factory/DnsTaskImpl.cc +163]
└── WFTaskFactory::create_dns_task    [vim src/factory/DnsTaskImpl.cc +163]
    ├── WFResolverTask::dispatch      [vim src/nameservice/WFDnsResolver.cc +271]
    ├── WFDnsClient::create_dns_task  [vim src/client/WFDnsClient.cc +238]
    └── WFTaskFactory::create_dns_task        [vim src/factory/DnsTaskImpl.cc +163]

大概可以看出,主要是两个来源,一个是WFDnsClient,一个是WFResolverTask

我们先看看WFDnsClient

WFDnsClient

// src/manager/WFGlobal.h
class WFGlobal
{
  ...
	static class WFDnsClient *get_dns_client();
  ...
};

然后可以看到他有个dns manager单例

// src/manager/WFGlobal.cc
WFDnsClient *WFGlobal::get_dns_client()
{
	return __DnsClientManager::get_instance()->get_dns_client();
}

可以看出在这个地方构造的时候,生成了dnsClient

__DnsClientManager

// src/manager/WFGlobal.cc
class __DnsClientManager
{
private:
	__DnsClientManager()
	{
		const char *path = WFGlobal::get_global_settings()->resolv_conf_path;

		client = NULL;
		if (path && path[0])
		{
			int ndots = 1;
			int attempts = 2;
			bool rotate = false;  
			std::string url;
			std::string search; 

			__parse_resolv_conf(path, url, search, &ndots, &attempts, &rotate);
			if (url.size() == 0)
				url = "8.8.8.8";

			client = new WFDnsClient;
			if (client->init(url, search, ndots, attempts, rotate) >= 0)
				return;

			delete client;
			client = NULL;
		}
	}

	WFDnsClient *client;
};

__parse_resolv_conf 这里主要就是处理 /etc/resolv.conf 中的内容了

__parse_resolv_conf

// src/manager/WFGlobal.cc
static int __parse_resolv_conf(const char *path,
							   std::string& url, std::string& search_list,
							   int *ndots, int *attempts, bool *rotate)
{
	size_t bufsize = 0;
	char *line = NULL;
	FILE *fp;
	int ret;

	fp = fopen(path, "r");
	if (!fp)
		return -1;

	while ((ret = getline(&line, &bufsize, fp)) > 0)
	{
		if (strncmp(line, "nameserver", 10) == 0)
			__split_merge_str(line + 10, true, url);
		else if (strncmp(line, "search", 6) == 0)
			__split_merge_str(line + 6, false, search_list);
		else if (strncmp(line, "options", 7) == 0)
			__set_options(line + 7, ndots, attempts, rotate);
	}

	ret = ferror(fp) ? -1 : 0;
	free(line);
	fclose(fp);
	return ret;
}

然后 __split_merge_str__set_options 细节可以看源码里面的注释,把传入的这几个参数都填上conf里读出的值

然后我们最终是把conf文件的这些参数读出来初始化WFDnsClient

那么我们再回过头看一下WFDnsClient

WFDnsClient

/src/client/WFDnsClient.h
class WFDnsClient
{
public:
	WFDnsClient() : params(NULL) { }
	virtual ~WFDnsClient() { }

	int init(const std::string& url);
	int init(const std::string& url, const std::string& search_list,
			 int ndots, int attempts, bool rotate);
	void deinit();

	WFDnsTask *create_dns_task(const std::string& name,
							   dns_callback_t callback);

private:
	void *params;
	std::atomic<size_t> id;
};

其中最主要的就是init和create_dns_task

/src/client/WFDnsClient.cc
int WFDnsClient::init(const std::string& url, const std::string& search_list,
					  int ndots, int attempts, bool rotate)
{
  ...
	hosts = StringUtil::split_filter_empty(url, ',');
	for (size_t i = 0; i < hosts.size(); i++)
	{
		host = hosts[i];
		if (strncasecmp(host.c_str(), "dns://", 6) != 0 &&
			strncasecmp(host.c_str(), "dnss://", 7) != 0)
		{
			host = "dns://" + host;
		}

		if (URIParser::parse(host, uri) != 0)
			return -1;

		uris.emplace_back(std::move(uri));
	}

	if (uris.empty() || ndots < 0 || attempts < 1)
	{
		errno = EINVAL;
		return -1;
	}

	this->params = new DnsParams;
	DnsParams::dns_params *q = ((DnsParams *)(this->params))->get_params();
	q->uris = std::move(uris);
	q->search_list = StringUtil::split_filter_empty(search_list, ',');
	q->ndots = ndots > 15 ? 15 : ndots;
	q->attempts = attempts > 5 ? 5 : attempts;
	q->rotate = rotate;

	return 0;
}

这一步主要是做了两件事,解析我们的host,设置dns的 params

/src/client/WFDnsClient.cc
struct dns_params
{
  std::vector<ParsedURI> uris;
  std::vector<std::string> search_list;
  int ndots;
  int attempts;
  bool rotate;
};

第二个重要的是

/src/client/WFDnsClient.cc
WFDnsTask *WFDnsClient::create_dns_task(const std::string& name,
										dns_callback_t callback)
{
	DnsParams::dns_params *p = ((DnsParams *)(this->params))->get_params();
	struct DnsStatus status;
	size_t next_server;
	WFDnsTask *task;
	DnsRequest *req;

	next_server = p->rotate ? this->id++ % p->uris.size() : 0;

	status.origin_name = name;
	status.next_domain = 0;
	status.attempts_left = p->attempts;
	status.try_origin_state = DNS_STATUS_TRY_ORIGIN_FIRST;

	if (!name.empty() && name.back() == '.')
		status.next_domain = p->uris.size();
	else if (__get_ndots(name) < p->ndots)
		status.try_origin_state = DNS_STATUS_TRY_ORIGIN_LAST;

	__has_next_name(p, &status);

	task = WFTaskFactory::create_dns_task(p->uris[next_server],
										  0, std::move(callback));
	status.next_server = next_server;
	status.last_server = (next_server + p->uris.size() - 1) % p->uris.size();

	req = task->get_req();
	req->set_question(status.current_name.c_str(), DNS_TYPE_A, DNS_CLASS_IN);
	req->set_rd(1);

	ComplexTask *ctask = static_cast<ComplexTask *>(task);
	*ctask->get_mutable_ctx() = std::bind(__callback_internal,
										  std::placeholders::_1,
										  *(DnsParams *)params, status);

	return task;
}

可以看到其中还有个DnsStatus, 我们看看他的内部结构

/src/client/WFDnsClient.cc
struct DnsStatus
{
	std::string origin_name;
	std::string current_name;
	size_t next_server;			// next server to try
	size_t last_server;			// last server to try
	size_t next_domain;			// next search domain to try
	int attempts_left;
	int try_origin_state;
};

在它内部还是用的工厂创建的task

/src/factory/DnsTaskImpl.cc
WFDnsTask *WFTaskFactory::create_dns_task(const ParsedURI& uri,
										  int retry_max,
										  dns_callback_t callback)
{
	ComplexDnsTask *task = new ComplexDnsTask(retry_max, std::move(callback));
	const char *name;
  
	if (uri.path && uri.path[0] && uri.path[1])
		name = uri.path + 1;
	else  
		name = ".";

	DnsRequest *req = task->get_req();
	req->set_question(name, DNS_TYPE_A, DNS_CLASS_IN);

	task->init(uri);
	task->set_keep_alive(DNS_KEEPALIVE_DEFAULT);
	return task;
}

ComplexDnsTask

我们可以看到create_dns_task实际上是new了一个ComplexDnsTask

class ComplexDnsTask : public WFComplexClientTask<DnsRequest, DnsResponse,
							  std::function<void (WFDnsTask *)>>
{
	static struct addrinfo hints;

public:
	ComplexDnsTask(int retry_max, dns_callback_t&& cb):
		WFComplexClientTask(retry_max, std::move(cb))
	{
		// dns 是建立在 UDP 之上的应用层协议
		// WFComplexClientTask中默认TT_TCP
		this->set_transport_type(TT_UDP);
	}

protected:
	virtual CommMessageOut *message_out();
	virtual CommMessageIn *message_in();
	virtual bool init_success();
	virtual bool finish_once();

private:
	bool need_redirect();
};

继承自WFComplexClientTask,构造的时候就是设置TT_UDP