[产品]开源实践 详解dnspod-sr架构

关于dnspod-sr

dnspod-sr是中国最大域名解析服务商DNSPod官方于2012年6月1日开源的一款递归DNS服务器软件。最新的一次更新是在2014年5月16日,修复了已发现的bug、重写了部分功能模块和优化了部分解析性能。当然现在我们对dnspod-sr的修改尚未完成,之后还会有较大的改动,持续维护下去。

开源协议: BSD协议

Github地址: https://github.com/DNSPod/dnspod-sr

53b3cf62c0122_middle

 

 

 

 

 

 

 

 

 

dnspod-sr作为一个运行在Linux平台上的高性能递归DNS服务器软件,具备高性能、高负载、易扩展的优势,相比 BIND等优势明显,上图就是针对BIND、dnspod-sr进行的性能测试对比数据。

测试环境:千兆网卡、4核CPU、4G内存、Linux 64位系统

测试结果:

特点介绍

  1. CNAME解析加速:在解析过程中,dnspod-sr会首先循环查找本地缓存的CNAME记录,如果解析到CNAME记录,再去解析最后一级CNAME记录值的指定记录类型,在解析请求存在CNAME时可以省却递归解析过程,大大加快解析速度。详见 author.c 的  find_record_from_mem() 接口。
  2. A记录组包缓存:根据我们的解析日志统计,DNS解析请求超过90%都是请求的A记录(即IPv4地址),所以dnspod-sr针对此进行了一项优化,如果授权返回了A记录,则会将该A记录提前组好应答包存储到本地缓存中,在客户端下次请求该记录时省去了组包的过程,加速解析过程。但该功能在前一版本中有时候会出现解析错误的问题,在新版本中暂时移除了该功能,待修正解析错误的问题后会再下一版本重新增加该功能。
  3. 请求转发功能:通过配置 sr.conf 文件的 xfer: 模块,可以设置在解析指定的域名时直接将该解析请求发送到指定的解析服务器(可以是递归服务器也可以是授权服务器)进行解析。详见 dns.c 的pre_find() 接口。
  4. 缓存刷新功能:该功能主要用于手动强制刷新dnspod-sr本地缓存中的记录值,主要用于刷新一些被污染的记录或者TTL时间很长但又更新了记录需要快速更新生效时。需要配合 tool 目录下的客户端工具使用。
  5. HASH表缓存:dnspod-sr的本地缓存方式使用了比较流行的HASH表方式,所有数据缓存在内存中,不进行数据库的相关操作,另外quizzer列表也使用HASH表方式。详见 storage.c 中的相关接口。
  6. 内存池:内存池是新版新加的功能,主要是减少在缓存查找和递归解析整个过程中频繁的内存分配和释放操作,提高性能。详见 memory.c 中的相关接口。
  7. 系统函数重写:因为系统函数的性能问题,所以对部分系统函数进行了自行实现,如大小写转换和域名有效性检查改为使用查表法进行;字符串比较和ip地址转换也进行了重新实现等。

总体架构

53b4d1d55502f

 

 

 

 

 

 

 

 

如图所示,dnspod-sr总体结构相对比较简单,其中sentinel、fetcher、quizzer和control是主要的逻辑处理模块,event和dns是功能模块,net、storage、memory、io、datas和utils为其上的逻辑和功能模块提供支撑。

下面,我们对各个处理模块详细的介绍。

1.   逻辑处理模块

dnspod-sr的主要工作线程,功能完成整个DNS的递归解析过程。

  • sentinel:使用EPOLL模型处理UDP和TCP的DNS查询请求,接收数据并将数据地址存放到msgcache中。
  • fetcher:从msgcache中获取DNS查询请求数据,解包后先查询内存缓存(CNAME和请求类型),如果查询成功则返回解析结果,如果查询失败则将查询请求加入到quizzer列表中。
  • quizzer:循环遍历quizzer列表中的查询请求,对每一个请求进行递归查询,逐级向各级授权DNS服务器发送查询请求,并解析各级授权服务器返回的解析结果,直到解析出结果后返回给客户端并将中间各级解析结果插入本地缓存。特别的,如果尝试解析次数在将要达到最大尝试次数时还未解析出结果,将会向GOOGLE的公共DNS服务器转发该解析请求;如果解析超时或超过最大尝试解析次数后则停止解析该请求。
  • control:该模块主要是配合客户端工具(tool目录)手动刷新指定的域名的缓存,可以用于某些TTL较长的记录修改后或者被污染记录修复后的强制刷新。

2.   功能模块

处理网络包和dns数据包的相关操作。

  • event:主要功能是处理EPOLL和UDP/TCP查询数据接收的相关操作。
  • dns:主要完成DNS数据包的解包、组包等功能操作。

3.   支撑模块

为各功能提供支撑的基础模块。

  • net:网络的各种操作接口,完成socket的相关操作和数据收发。
  • storage:内存存储模块,为解析记录缓存和quizzer列表提供增、删、改、查功能支持。
  • memory:内存池的相关操作接口,用于内存池的创建、分配和释放。
  • io:物理读写模块,包括加载配置文件和记录日志等功能。
  • datas:红黑树的相关实现接口,用于自动刷新即将过期的记录。
  • utils:一些杂项操作接口都丢在这里了,如获取随机数据、大小写转换等操作

解析过程

53b3caa7a902c

 

 

 

 

 

 

 

 

 

 

 

上图是一次典型的域名解析过程,如果dnspod-sr已经在本地缓存了解析结果,则会直接返回解析结果,如果没有缓存结果,则会从根开始逐级递归进行解析,递归解析过程可以参考 dig +trace <domain> 的解析结果,当然如果中间某一级的结果已经在本地缓存中存在,则会直接从该级进行递归解析。

在典型的解析过程之外,尚存在一些其他一些非典型操作,如在第一个quizzer线程中,每次循环都会检查用于自动刷新的记录的TTL时间,如果TTL剩余时间小于3秒,就该将记录添加到quizzer列表中,在下次解析时进行刷新。但是目前自动刷新功能尚存在问题,暂时关闭了所有解析记录的自动刷新功能,只对root.z文件中的记录进行刷新。

尚存在的问题

目前dnspod-sr尚在修改维护中,还存在一些如下问题,将会在之后的版本更新中进行解决,如:

  1. 不支持PTR反解析;
  2. 增加配置参数文件,解决现在需要通过修改代码修改配置的问题,如fetcher和quizzer线程数等,并可以通过动态加载部分参数实现不停机修改部分配置;
  3. 在最近更新中因存在问题而去掉的A记录提前组包功能;
  4. 红黑树如果自动刷新所有记录尚存在问题;
  5. 代码整理、包括风格、函数调用和无用代码清理等;
  6. 增加在程序崩溃时将缓存记录转储到磁盘的功能;
  7. 较老版本内核和32位系统的兼容性测试等。