DNS的解析,gethostbyname的弊端

转自:http://blog.csdn.net/shijun_zhang/article/details/6577426

1、前言

在网络编程中,常常要使用域名转换为IP的操作,这个时候就需要用到域名解析。域名解析是一个垂直请求的过程,具体如下图。


2、gethostbyname的性能瓶颈

Unix/Linux下的gethostbyname函数常用来向DNS查询一个域名的IP地址。 由于DNS的递归查询,常常会发生gethostbyname函数在查询一个域名时严重超时。而该函数又不能像connect和read等函数那样通过setsockopt或者select函数那样设置超时时间,因此常常成为程序的瓶颈。有人提出一种解决办法是用alarm设置定时信号,如果超时就用setjmp和longjmp跳过gethostbyname函数(这种方式我没有试过,不知道具体效果如何)。
    在多线程下面,gethostbyname会一个更严重的问题,就是如果有一个线程的gethostbyname发生阻塞,其它线程都会在gethostbyname处发生阻塞。我在编写爬虫时也遇到了这个让我疑惑很久的问题,所有的爬虫线程都阻塞在gethostbyname处,导致爬虫速度非常慢。在网上google了很长时间这个问题,也没有找到解答。今天凑巧在实验室的googlegroup里面发现了一本电子书"Mining the Web - Discovering Knowledge from Hypertext Data",其中在讲解爬虫时有下面几段文字:


    Many clients for DNS resolution are coded poorly.Most UNIX systems provide an implementation of gethostbyname (the DNS client API—application program interface), which cannot concurrently handle multiple outstanding requests. Therefore, the crawler cannot issue many resolution requests together and poll at a later time for completion of individual requests, which is critical for acceptable performance. Furthermore, if the system-provided client is used, there is no way to distribute load among a number of DNS servers. For all these reasons, many crawlers choose to include their own custom client for DNS name resolution. The Mercator crawler from Compaq System Research Center reduced the time spent in DNS from as high as 87% to a modest 25% by implementing a custom client. The ADNS asynchronous DNS client library is ideal for use in crawlers.
    In spite of these optimizations, a large-scale crawler will spend a substantial fraction of its network time not waiting for Http data transfer, but for address resolution. For every hostname that has not been resolved before (which happens frequently with crawlers), the local DNS may have to go across many network hops to fill its cache for the first time. To overlap this unavoidable delay with useful work, prefetching can be used. When a page that has just been fetched is parsed, a stream of HREFs is extracted. Right at this time, that is, even before any of the corresponding URLs are fetched, hostnames are extracted from the HREF targets, and DNS resolution requests are made to the caching server. The prefetching client is usually implemented using UDP  instead of TCP, and it does not wait for resolution to be completed. The request serves only to fill the DNS cache so that resolution will be fast when the page is actually needed later on.

    大意是说unix的gethostbyname无法处理在并发程序下使用,这是先天的缺陷是无法改变的。大型爬虫往往不会使用gethostbyname,而是实现自己独立定制的DNS客户端。这样可以实现DNS的负载平衡,而且通过异步解析能够大大提高DNS解析速度。DNS客户端往往用UDP实现,可以在爬虫爬取网页前提前解析URL的IP。文章中还提到了一个开源的异步DNS库adns,主页是http://www.chiark.greenend.org.uk/~ian/adns/
    从以上可看出,gethostbyname并不适用于多线程环境以及其它对DNS解析速度要求较高的程序。


3、方法一:linux GNU gethostbyname_r

此方法支持多线程,单机测试可以达到100次/s。

参数说明:name——是网页的host名称,如百度的host名是www.baidu.com
                  ret——成功的情况下存储结果用。
                  buf——这是一个临时的缓冲区,用来存储过程中的各种信息,一般8192大小就够了,可以申请一个数组char buf[8192]
                  buflen——是buf缓冲区的大小
                  result——如果成功,则这个hostent指针指向ret,也就是正确的结果;如果失败,则result为NULL
                  h_errnop——存储错误码
该函数成功返回0,失败返回一个非0的数。

struct hostent {
         char *h_name;                     // official name of host
         char **h_aliases;                 // alias list
         int h_addrtype;                    // host address type——AF_INET || AF_INET6
         int h_length;                        // length of address
         char **h_addr_list;              // list of addresses
};
#define h_addr h_addr_list[0]      // for backward compatibility


4、方法二:自己写client端请求

一、DNS报文角度来看
主要是查看DNS报文首部中的标志字段
[QR][opcode][AA][TC][RD][RA][(zone)][rcode]
 
主要关注字段为TC字段,当TC字段为1时,表示应答总长度超过512字节,只返回前512个字节,这时DNS就需要使用TCP重发原来的查询请求。因为在UDP的应用程序中,其应用程序被限制在512个字节或更小,因此DNS报文穿数据流只能有512字节,而TCP能将用户的数据流分为一些报文段,因此TCP就能用多个报文段去传超过512字节的数据流或是任意长度的数据流。
 
大多数书只写DNS使用UDP 53端口,这并不完整,会导致别人误解,认为DNS只用UDP,不用TCP,呵呵。
 
二、应用角度来看
区域传输用TCP,其他用UDP。
什么是区域传输?
dns的规范规定了2种类型的dns服务器,一个叫主dns服务器,一个叫辅助dns服务器。在一个区中主dns服务器从自己本机的数据文件中读取该区的dns数据信息,而辅助dns服务器则从区的权威dns服务器中读取该区的dns数据信息。当一个辅助dns服务器启动时,它需要与主dns服务器通信,并加载数据信息,这就叫做区传送(zone transfer).
通俗地讲,就是DNS服务器之间传输时使用TCP,而客户端与DNS服务器之间传输时用的是UDP

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值