您的位置:首页 >资讯列表 > 正文
发布时间:2020-04-10 16:46
Linux内核态高效HTTP代理的设计与实现

  Linux系统下用作HTTP代理的软件有很多,比如Tiny Proxy、Squid等,它们大多是在Linux系统的用户态下实现,与操作系统内核分离。这种网络应用与系统内核分离的软件体系结构,严重制约了网络应用的性能。


  随着通过HTTP代理上网的用户不断增加,对HTTP代理的网络性能以及吞吐量要求不断提高,传统的Linux用户态HTTP代理难以并发处理上千个Web用户的HTTP请求。为此,本文提出了一种HTTP代理与Linux核心结合的体系结构,将HTTP代理移入内核实现,提高HTTP代理的并发处理能力,并通过网络应用测试软件Load Runner测试了代理的性能,同用户态下有名的Squid代理作了性能的比较。


  Linux系统中有两种系统状态,一种是用户态,一种是内核态。用户态下的HTTP代理软件一般都使用Linux C标准库的BSD Socket接口实现,然而,用户态BSD socket接口频繁的系统调用,系统状态切换以及内核与用户空间的内存操作,严重制约了代理软件的网络性能。本文提出的基于Linux内


  核的HTTP代理设计与实现方案,不使用C标2准库2的BSD


  socket接口,而是直接使用内核下原始的BSD socket接口,它直接调用下层的INET接口。由于代理服务器的主要目的是数据转发和数据过滤,在Linux内核下,直接实现用户态下代理的处理程序,就可以避开用户态BSD socket频繁系统调用和地址空间转换的瓶颈,从而提高代理的网络性能。


  1总体设计


  HTTP代理的主要任务是代表Web请求客户与Web服务器交互,既是Web客户端也是服务器端,它的主要工作过程如下:


  1)在代理服务器的某个固定端口,监听客户端的连接,并接收客户端的HTTP请求。


  2)对客户请求的HTTP包,进行解析和处理,并生产新的请求包。根据请求中的域名(或者IP地址)和Web服务器端口号,建立与Web服务器的连接,将新的请求发送给Web服务器。


  3)等待并接收Web服务器返回的HTTP响应。


  4)将HTTP响应转发给客户端Web浏览器。


  图1显示了HTTP代理服务器的工作的一般过程。


  图1 HTTP代理工作过程


  根据HTTP代理的工作过程模型,将代理服务软件分成如下几个模块实现:


  接收请求包头模块:负责接收客户发送到代理服务器的


  包头解析和检查模2块:负责提取πHTTP请求的Web服务


  HTTP请求包头,并检查它的完整性。


  器的域名(或者IP地址)以及端口号,并检查客户发送的HTTP请求的正确性,以及通过鉴权模块,鉴定客户端IP地址以及请求中URL的合法性。


  鉴权模块:负责维护URL访问规则,根据客户端的IP地址,鉴定客户的访问权限。


  DNS解析模块:负责域名解析,把请求的域名换成Web服务器的IP地址。


  数据转发模块:负责连接Web服务器,并转发服务器返回的HTTP响应数据。


  日志管理模块:负责记录代理服务器的各种操作。


  收稿日期:2005-03-13;修订日期:2005-05-14基金项目:国家863计划资助项目(2004AA414034)


  作者简介:梁达明(1980-),男,广东深圳人,硕士研究生,主要研究方向:Linux内核、Linux网络、嵌入式系统;陈德人(1951-),男,浙江杭州人,教授,博士生导师,主要研究方向:电子商务;郑小林(1977-),男,讲师,博士,主要研究方向:电子商务.


  19 24计算机应用2005年


  参数配置以及显示模块:负责配置代理软件的初始参数,以及显示运行时的统计数据。


  除了DNS解析模块使用了用户态的DNS解析接口函数外,其他所有的模块都是基于Linux内核设计开发的。这样做的目的是使得所有的网络数据处理都在Linux内核态下进行,网络数据无需再往系统用户态发送,因而避免了频繁的系统调用和地址空间、内存操作(如copy_from_user和copy_to_ user等),提高代理系统的网络效率。


  模块间的关系见图2,箭头表示数据流的方向。


  图2代理模块关系


  2若干关键技术


  2.1内核模块编程


  由于代理在Linux内核下运行,所以我们使用Linux系统提供的模块编程方法设计和实现。内核模块是一些让操作系统内核在需要时载入和执行,不需要时卸载的代码。它们扩展了操作系统内核的功能却不需要重新启动系统。将网络应用写成内核模块,可以轻松的实现网络应用的安装和卸载,无需重新编译Linux内核。通过insmod和rmmod命令,可以方便的插入和删除内核模块,通过lsmod命令,可以查看内核已经加载了哪些模块。一个内核模块应该至少包含两个函数。一个开始(初始化)的函数被称为init_module(),当内核模块被insmod加载进入内核时被执行。还有一个结束(干一些收尾清理的工作)的函数被称为cleanup_module(),当内核模块被rmmod卸载时被执行。还有,任一个内核模块需要包含<linux/module.h>和<lniux/kernel.h>文件,否则编译将失败。


  2.2在Linux内核态下进行Socket编程


  Socket是网络服务程序的主体,所以如何在Linux内核下使用Socket是实现HTTP代理系统的关键。Linux的内核下的主要Socket函数包含在<net/sock.h>和<linux/net.h>两个头文件中,所以内核HTTP代理模块需要包含这两个头文件。在<linux/net.h>中有socket结构的定义。其中,最常用到的字段有sk和ops,sk字段是一个sock(INET套接字)结构指针,可以利用sk中的state字段来判断当前socket的状态。指针ops是特定协议族的socket操作集的指针。在TCP中,它指向操作inet_stream_ops,在UDP中,它指向inet_dgram _ops。在该结构中定义了许多socket的基本操作,包括bind,listen,accept,connect等。内核初始化时,它们被置入内核并将注册到socket结构中。在内核中使用socket的这些操作时,都要引用proto_ops结构。


  在建立新的socke连接(connect)服务器时,内核使用


  sock_create函数,而在服务器端,当服务器接收到一个新的连接请求(accept)时,内核使用sock _alloc来分配一个新的socket与客户端通讯。当socket不再使用的时候,使用sock_


  release函数可以将socket释放,清除它在内核中的内存空间。在socket接收数据的时候,内核使用sock_recvmsg函数,在发送数据的时候,使用sock_sendmsg函数。这两个函数调用时,都要用到msghdr结构,将要接收或者发送数据的缓冲区指针,付给msghdr.msg_iov.iov_base。我们举接收数据为例:


  int recv(struct sockebuffer,int len)


  {struct msghdr ms struct iovec iov;mm_segment_t f int result;


  if(skb_queue_e>sk->receive_queue)))re fs=


  set_ msg.msg.msg.msg.msg.msg.


  iov.iov.


  resusg,len,


  MSG set_ retu


  }


  这段代受数据的。代码中,首先使用skb_queue_empty函数来判断当前套接字的接受队列中是否有数据,如果没有直接返回0,这样可以提高接收数据的效率。如果队列非空,则填充msghdr结构体,把要接收数据的缓冲区指针buffer付给iov.iov_base,再调用sock_recvmsg就可完成接收数据的操作。当然由于sock_recvmsg在内核中运行,在调用sock_recvmsg前要获取内核空间范围,我们通过


  函数set_fs(get_ds())实现。


  2.3包头完整性检查


  当代理接收到客户发过来的HTTP请求后,代理不能够马上处理客户的请求,而是首先要检查包头是否完整。一般的发向代理的HTTP请求的包头的格式是这样的:


  操作http://域名/文件路径HTTP/协议版本\r\n字段名:字段内容\r\n


  ∗\r\n


  [实体内容\r\n]


  其中,[]表示可选择的部分。操作有Get和Post等。Get操作表示向Web服务器获取请求中URL指定的资源。Post操作表示向服务器提交用户数据或表单。HTTP协议版本有1.0和1.1两种,HTTP-NG(Next Generation of HTTP)的建议已经提出。字段名有:Connection,Host,Proxy-Connection,Content-Length等。包头完整性检查的过程是这样的。首先扫描整个包头,看是否包含两个连续的\r\n。若没有,则表示包头不完整,代理要继续等待数据包。如果已经包含两个连续的\r\n,则要进一步检查,包头中是否包含Content-Length字段,如果包含了该字段,就检查代理收到的两个连续\r\n后的内容大小是否与Content-Length字段的一致。如果一致,则包头是完整的,如果不一致,则包头不完整,代理要继续等待数据。


  2.4包解析和生成


  第8期梁达明等:Linux内核态高效HTTP代理的设计与实现1 92 5


  包头解析的主要任务是提取请求数据包中的Web服务器的域名(或者IP地址)和端口号。在包头解析模块中,代理模块主要用到了字符串操作函数。它们在<linux/string.h>中定义。比较常用的两个函数有strstr以及strchr:strstr函数可在一个字符串中查找子串,strchr函数可在一个字符串中查找传入的第一个字符的位置。另外,还有strcat函数,用于合并两个字符串。


  2.5 DNS域名解析


  HTTP请求的Web服务器地址在请求包的Host字段中,该字段可以是一个域名或者是一个IP地址。如果是域名,代理软件必须把域名解析成IP地址,只有获得请求的Web服务器的IP地址后,代理才能进行连接服务器操作。Linux内核没有提供DNS解析的函数接口,Linux只是在用户态的C库中提供了gethostbyname()函数。因此,代理模块要在内核态中解析域名,必须通过用户态的gethostbyname函数。我们在用户态编写了一个简单的域名解析助手,它与代理同时运行,内核态下的代理通过socket与域名助手通讯,代理把要解析的域名字符串发给助手,助手收到域名字符串后,调用gethostbyname解析,将结果通过socket发回给代理。其原理如图3所示。


  图3 DNS域名解析


  2.6非阻塞队列轮询算法


  为了提高代理的效率和吞吐量,内核代理模块的网络函数都使用非阻塞的方式进行的。通过查询socket的状态,可以避免由于网络原因造成的不必要的等待,把CPU资源分给下一个服务请求,这样就大大提高了代理的性能。代理首先通过accept函数检查是否有新的连接请求,把accept的第三个参数设置成O_NONBLOCK,让它以非阻塞方式工作,如果accept的返回值为0,则表示有新的连接请求进来。如果有新的请求,代理就建立一个HTTP请求数据结构(我们自己定义的HTTP请求的数据结构,里面包含接收的数据包头,IP,Port等字段),并把它加入到服务队列中。代理对服务队列中的请求进行轮询服务,若一个请求遇到网络的等待,如连接过程中的等待,接收服务器数据时的等待,就继续轮询下一个请求。等把服务队列遍历了若干次(由的参数设定)后,再重新进行accept操作,将新的请求抓到请求队列中,一直accept直到没有新的请求进来。


  2.7 SMP多线程以及线程休眠


  Linux是一个支持SMP多个CPU的优秀操作系统。为了进一步提高代理的吞吐量,使用了多线程技术,为每个CPU分配一个线程,使得代理程序在繁忙时,尽可能的使用多个CPU的资源。代理的多个线程都共用一个在主服务器端口监听的socket,还共用一组HTTP请求结构,由空闲HTTP请求结构队列(HTTP_Req_Free_List)提供,由我们的程序事先分配好。每一个线程有一个自己独立的HTTP请求服务队列。每个线程从主socket中进行accept操作,将请求拿到自己的队列中,然后轮询自己的队列,进行代理服务。


  另外,轮询算法造成代理进程的CPU占用率很高,为了


  减少CPU占用率,代理使用了等待队列,让线程在空闲时睡


  眠,主要使用interruptible_sleep_on_timeout函数。这个函数让线程休眠一定时间,超过时间就唤醒,它的第一个参数是等待队列,第二个参数是超时的时间。


  3性能分析


  我们使用行业网络应用测试软件Load Runner来测试代理的性能。在同一的网络环境下,测试了我们实现的代理的性能和用户态下有名的Squid代理的性能。实验是在百兆网卡上,局域网内部进行,实验用了三台机器,一台Web服务器,一台代理服务器,一台Load Runner测试客户端。各台机器的配置如下:服务器


  CPU:2G(双CPU);内存:1G;网卡:全双工百兆;操作系统:RedHat9.0;内核:2.4.20 8;代理


  CPU:3G;内存:512M;网卡:全双工百兆;操作系统:


  RedHat9.0;内核:2.4.20 8;客户端


  CPU:3G;内存:256M;网卡:全双工百兆;操作系统:


  W indows XP;


  测试结果如图4所示。从测试结果可以看出,在内核态下的HTTP代理比用户态下的Squid代理网络性能有大幅度的提高,性能差不多翻了一倍。再看看CPU占用率,并不是很高。这说明了设计以及算法的正确性。


  图4测试结果


  4结语本文详细介绍了内核下一个高吞吐量的HTTP代理的设


  计以及实现,分析了实现的2若干关键技术。并与Squid对比,


  给出了性能测试结果。测试结果说明,本文提出的HTTP代理与Linux系统内核结合的体系结构,比传统的两者分离的体系结构优越


上一篇 支持HTTP多路复用的代理服务器的设计与实现 下一篇 基于HTTP协议的安全代理研究