//这部分是有关后端查询的部分。假设后端用的是mysql,可以看到如果没有缓存的话那就是直接对数据库的查询
class gMySQLFactory : public BackendFactory
{
DNSBackend *make(const string &suffix="")
{
return new gMySQLBackend(d_mode,suffix);
}
//构造函数
gMySQLBackend::gMySQLBackend
{
reconnect();
{
setDB(new SMySQL())
}
}
}
class SMySQL()
{
SMySQL::SMySQL()
{
connect();
{
//注意它在连接的时候会有retry机制,这个就是和真正的mysql建立连接
mysql_real_connect()
}
}
//所有的query操作都会转化为mysql的execute操作
execute()
{
调用mysql_query
}
}
class UDPNameserver
{
//构造函数,构造的时候监听好ipv4或者ipv6的端口
UDPNameserver(true)
{
//依据监听的套接字去调用不同的函数,ipv4或者ipv6
if(!::arg()["local-address"].empty())
bindIPv4()
{
//这里解析它要监听的地址(包括IP和port)
vector<string>locals;
stringtok(locals,::arg()["local-address"]," ,");
for(;i=locals.begin();i!=locals.end();++i)
{
s=socket(AF_INET,SOCK_DGRAM,0);
//如果可重用的话,会把每一个socket设置成SO_REUSEPORT属性,这样的话,多个线程可以监听同一个端口了,内核帮你做选择,你不需要锁了, 对于udp来说,这个时候的端口就是可以接收数据的
#ifdef SO_REUSEPORT
if( d_can_reuseport )
if( setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)))
d_can_reuseport = false;
#endif
bind(s, (sockaddr*)&locala, locala.getSocklen())
d_sockets.push_back(s);
struct pollfd pfd;
pfd.fd = s;
pfd.events = POLLIN;
pfd.revents = 0;
d_rfds.push_back(pfd);
}
}
if(!::arg()["local-ipv6"].empty())
bindIPv6();
}
}
class TCPNameserver
{
//获取很多配置文件的时间
for(vector<string>::const_iteratorladdr=locals.begin();laddr!=locals.end();++laddr) {
//通过local-address获取到它要监听的地址,注意这里是写的几个就监听几个端口,当然如果写的是一样的话,那就可能会去监听同一个端口了
vector<string>locals;
stringtok(locals,::arg()["local-address"]," ,");
for(;laddr=locals.begin();laddr!=locals.end();++laddr)
{
int s=socket(AF_INET,SOCK_STREAM,0);
//为监听socket设置SO_REUSEPORT选项
bind()
listen(s,128);
d_sockets.push_back(s);
struct pollfd pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = s;
pfd.events = POLLIN;
d_prfds.push_back(pfd);
}
}
class UeberBackend
{
//它的构造函数比较特殊
UeberBackend::UeberBackend(const string &pname)
{
backends=BackendMakers().all(pname=="key-only");
}
void go()
{
//唤醒条件变量d_cond,通知睡眠在上面的线程
pthread_mutex_lock(&d_mut);
d_go=true;
pthread_cond_broadcast(&d_cond);
pthread_mutex_unlock(&d_mut);
}
loadmodule(const string &name)
{
//打开动态连接库
void *dlib=dlopen(name.c_str(), RTLD_NOW);
}
}
class BackendMakers
{
all()
{
if(metadataOnly)
//这个就是加载的模块,make就是每个模块自定义的函数,这里假定用了mysql作为后端,那就去看gmysql的make函数
made = d_repository[i->first]->makeMetadataOnly(i->second);
{
return this->make(suffix);
}
}
launch
{
for(vector<string>::const_iterator i=parts.begin();i!=parts.end();++i)
{
const string &part=*i;
加载到d_repository中
load(module);
d_repository[module]->declareArguments(name);
d_instances.push_back(make_pair(module,name));
}
}
load
{
//继续调用它
UeberBackend::loadmodule
}
}
class Distributor
{
//如果是一个线程的话就调用single这个
void create(int n)
{
if( n == 1 )
return new SingleThreadDistributor<Answer,Question,Backend>();
else
return new MultiThreadDistributor<Answer,Question,Backend>( n );
}
}
class PacketHandler
{
void question()
{
//这个是lua的逻辑,也就是说如果指定lua作为查询逻辑的话,那就直接用lua查询就可以了
if(d_pdl)
{
ret=d_pdl->prequery(p);
if(ret)
return ret;
}
return doQuestion(p);
}
//否则才去后端查询
void doQuestion
{
//这个是真正的去查询后端了
}
}
//这个是distributor创造出来的单实例,这种情况
class SingleThreadDistributor
{
//只是new一个后端实例
SingleThreadDistributor()
{
//对于dns权威来说,它的Backend就是PacketHandler
b=new Backend;
}
//callback就是那个sendout函数
void question(Question* q, callback_t callback)
{
Answer *a;
bool allowRetry=true;
retry:
try
{
if (!b)
{
allowRetry=false;
b=new Backend;
}
//调用上面实例化出来的b去查询question,然后把结果复制给answer a
a=b->question(q); // a can be NULL!
}
紧接着俩次catch
{
看情况要么返回要么retry
}
//相当于调用sendout函数发送a,每个线程自己去发送数据
callback(a);
}
}
class MultiThreadDistributo(n)
{
//构造函数
MultiThreadDistributo(int n)
{
//获取queue的大小
d_num_threads=n;
d_overloadQueueLength=::arg().asNum("overload-queue-length");
d_maxQueueLength=::arg().asNum("max-queue-length");
//创建管道
for(int i=0; i < n; ++i) {
int fds[2];
if(pipe(fds) < 0)
unixDie("Creating pipe");
d_pipes.push_back({fds[0],fds[1]});
}
//创建线程,
for(int i=0;i<n;i++) {
pthread_create(&tid,0,&makeThread,static_cast<void *>(this));
Utility::usleep(50000); // we've overloaded mysql in the past :-)
}
}
//
makeThread()
{
MultiThreadDistributor *us=static_cast<MultiThreadDistributor *>(p);
int ournum=us->d_running++;
//相当于每一个分发线程实例化一个packethandler出来
Backend *b=new Backend();
int queuetimeout=::arg().asNum("queue-limit");
//这里for死循环,分发问题给后端,同时
for(;;)
{
//它的内部封装的是一个question和一个callback,从 pipe的一个端口读放到QD中
QuestionData* QD;
if(read(us->d_pipes[ournum].first, &QD, sizeof(QD)) != sizeof(QD))
//如果包太多的时候对于这种超时很多的包就不处理了
if(queuetimeout && QD->Q->d_dt.udiff()>queuetimeout*1000)
{
delete;
continue
}
--us->d_queued;
Answer *a;
//下面这个是和后端交互的唯一的地方,这里有一个retry,如果失败了就会反复的去请求
retry:
try
{
if (!b) {
allowRetry=false;
b=new Backend();
}
//去调用b的question的方法,
a=b->question(QD->Q);
delete QD->Q;
}
catch(const PDNSException &e)
{
delete b;
b=NULL;
if (!allowRetry) {
L<<Logger::Error<<"Backend error: "<<e.reason<<endl;
//如果失败了而且不许重试,那就直接返回错误了
a=QD->Q->replyPacket();
a->setRcode(RCode::ServFail);
S.inc("servfail-packets");
S.ringAccount("servfail-queries",QD->Q->qdomain.toLogString());
delete QD->Q;
}
else
{//这里允许重试,就是再问一次后端
L<<Logger::Notice<<"Backend error (retry once): "<<e.reason<<endl;
goto retry;
}
}
// 调用callback,就是那个sendout
QD->callback(a);
}
}
//多线程的question函数
question()
{
q=new Question(*q);
//该结构里封装了question和sendout函数
auto QD=new QuestionData();
QD->Q=q;
QD->callback=callback;
auto ret = QD->id = nextid++
//这里把问题通过pipe发给那些线程
write(d_pipes[QD->id % d_pipes.size()].second, &QD, sizeof(QD)) != sizeof(QD)
d_queued++
if(d_queued > d_maxQueueLength)
{
throw DistributorFatal();
}
}
}
// 解析从load-modules参数出来的module
loadModules()
{
vector<string>modules;
stringtok(modules,::arg()["load-modules"],", ");
for(vector<string>::const_iterator i=modules.begin();i!=modules.end();++i)
{
const string &module=*I;
//进行模块的加载
res=UeberBackend::loadmodule(module);
}
}
int receive(DNSPacket *prefilled)
{
vector<struct pollfd> rfds= d_rfds;
for(auto &pfd : rfds) {
pfd.events = POLLIN;
pfd.revents = 0;
}
//这里遍历所有的rfds,反复调用直到本次数据全部接收完整
err = poll(&rfds[0], rfds.size(), -1);
//调用recvmsg接收数据
for(auto &pfd : rfds)
{
if(pfd.revents & POLLIN)
{
sock=pfd.fd;
if((len=recvmsg(sock, &msgh, 0)) < 0 )
Utility::sock_t sock=-1;
//注意它这里处理完一个fd以后就break了,也就是说一个线程只处理一个fd,而不管是哪个fd
break
}
}
//设置socket的一些信息,本地和远端的地址信息
DNSPacket *packet;
packet->setSocket(sock);
packet->setRemote(&remote);
//parse包
if(packet->parse(mesg, (size_t) len)<0)
{
//如果支持edns协议的话就获取相关的信息
//复制要查询的域名
//填充dns的一些报文,比如qtype,qclass
}
最终如果解析正确的话就把这个packet返回去
}
//接收来自Nameserver的questions,同时hands them to the Distributor for further processing
qthread()
{
DNSPacket *P;
//分发器,那个数字是分发线程的个数
//注意有这么一个typedef,所以DNSDistributor中的bankend就是PacketHandler,那对于别的分发器可能会有别的bankend
typedef Distributor<DNSPacket,DNSPacket,PacketHandler> DNSDistributor;
DNSDistributor *distributor = DNSDistributor::Create(::arg().asNum("distributor-threads", 1)); // the big dispatcher!,这里会依据单线程还是多线程会去创造不同的distributor实例,如果是单线程的话只是开了一个后端实例,如果是多线程的话,则直接开了n个线程,每个线程开一个Backend后端实例,
//注意这里是有多少个receive线程,就有多少个distributor实例
g_distributors[num] = distributor;
//声明dnspacket实例
DNSPacket question(true);
DNSPacket cached(false);
//读取udp配置参数
//声明一个server
shared_ptr<UDPNameserver> NS;
//从g_udpReceivers获取到对应的udpserver
if( number != NULL && N->canReusePort() )
{
NS = g_udpReceivers[num];
}
else
{
NS = N;//N就是创建的唯一的那个监听地址的socket,如果设置了SO_REUSEPORT那就创建多个线程
}
//该线程的作用就是循环接收包
for(;;) {
if(!(P=NS->receive(&question))) { // receive a packet inline
//这里调用receive函数
continue; // packet was broken, try again
// 依据返回的p开始统计信息
numreceived++;
//中间会有一个cache的判断,如果命中了则直接返回
//如果后端负载过重,那么直接丢弃
//if(distributor->isOverloaded())
{
overloadDrops++;
continue;
}
//否则的话则把包丢给distributor去查询,注意单distributor和多的有不同的处理方法
distributor->question(P, &sendout);
//sendout是一个函数
void sendout(DNSPacket* a)
{
if(!a)
return;
//就是上面的那个N,第一个udpserver来发送数据
N->send(a);
int diff=a->d_dt.udiff();
avg_latency=(int)(0.999*avg_latency+0.001*diff);
delete a;
}
}
}
AuthPacketCache PC;//!< This is the main PacketCache, shared across all threads
AuthQueryCache QC;
mainthread()
{
//设置一些用户的属性,dnspacket的属性
//设置DNSPacket的一些属性,比如dnspacket的包的大小
//设置cache的一些属性
PC.setTTL(::arg().asNum("cache-ttl"));
PC.setMaxEntries(::arg().asNum("max-packet-cache-entries"));
QC.setMaxEntries(::arg().asNum("max-cache-entries"));
//设置webserver的一些属性
AuthWebServer webserver;
//设置解析器的属性
DynListener *dl;
//创建线程,
dl->go();
pthread_t qtid;
//启动webserver
if(::arg().mustDo("webserver") || ::arg().mustDo("api"))
webserver.go();
if(::arg().mustDo("slave") || ::arg().mustDo("master") || !::arg()["forward-notify"].empty())
Communicator.go();
//依据receiver-threads创建线程,这几个线程就负责接收数据包
unsigned int max_rthreads= ::arg().asNum("receiver-threads", 1);
g_distributors.resize(max_rthreads);
for(unsigned int n=0; n < max_rthreads; ++n)
pthread_create(&qtid,0,qthread, reinterpret_cast<void *>(n)); // receives packets
//主线程
for(;;) {
sleep(1800);
try {
doSecPoll(false);
}
catch(...){}
}
}
在main函数中
main():
//读取配置文件,设置日志等相关的信息
//通过这个配置可以在一个单机上加载不同的pdns实例,也就是不同的pdns指定不同的配置文件
if(::arg()["config-name"]!="")
s_programname+="-"+::arg()["config-name"];
//保护模式下创建某个实例
// 这个函数是加载各个指定的模块,解析从load-modules参数出来的module
loadModules()
//选择某个后端模块启动,这里选择某个后端登陆的时候,保证只选择一个,就是加载动态库文件而已
BackendMakers().launch(::arg()["launch"])
//监听控制台socket
//唤醒所有沉睡在某个条件变量上的线程
UeberBackend::go();
//声明一个UDPNameserver,注意在这里第一次创建的时候,d_additional_socket是false
N=std::make_shared<UDPNameserver>()
g_udpReceivers.push_back(N);
// 获取该指令配置的线程的个数,创建多个UDPNameserver,即内部创建多个socket监听同一个地址+端口
//这里还没有创建线程来着
size_t rthreads = ::arg().asNum("receiver-threads", 1);
for (size_t idx = 1; idx < rthreads; idx++) {
try {
// 为实例申请空间
g_udpReceivers[idx] = std::make_shared<UDPNameserver>(true);
}
catch(const PDNSException& e) {
L<<Logger::Error<<"Unable to reuse port, falling back to original bind"<<endl;
break;
}
}
//如果没有禁用tcp的话,那就创建 tcp nameserver,注意对于该监听socket来说没有对socket设置SO_REUSEPORT属性,也就是不可以有多个线程监听同一个地址
if(!::arg().mustDo("disable-tcp"))
TN=new TCPNameserver;
}
mainthread()