使用 libevent 和 libev 提高网络应用性能

办理多个 UNIX 广播网衔接

Martin C. Brown
2011 年 2 月 09 日宣布

出发灵:

这是出发 # 节的偏爱的 # 比例:

请怀胎本出发的后续灵。。

这是出发的偏爱的:

请怀胎本出发的后续灵。。

简介

大方的满足必要摆设(显著地地 web 满足必要摆设交谈的最大成绩经过是它是电话制造联络的。。无论是经过构造由于云的上菜用具来处置广播网一致流,或散布勤勉 IBM Amazon EC 状况上,为网站企图高机能集会,您必要可以处置大方的接着产生衔接。。

独一晴朗的的例是,web 勤勉再度设法对付全部事件动态。,显著地地在运用 AJAX 技术的勤勉。设想零碎被摆设,不计其数的客户将被容许修正,比方,独一事情或成绩企图实时测量零碎,因而企图通知的摧毁是绝重要的。。在网格或云经济状况中,可能性有是人数千个客户机的耐久衔接,而且它会翻开。,只好可以处置每个客户端的召唤并做出呼应。

在议论 libevent 和 libev 若何处置多个广播网衔接,让咱们先简明的回头一看一下处置机灵的国际公约方法。。

处置多个客户

处置多一独一衔接有大方的不相像的人于的国际公约方法。,但在处置大方的衔接时,它们常常有成绩。,由于他们运用的追忆 CPU 过于,或对绝的采取军事行动零碎的限度局限。

首要采取的方法列举如下:

  • 圈出:前段零碎运用简略的圈出选择receive 接纳。,经过圈出遍历翻开的广播网衔接列表。,决定假设有要读取的记载。。就是很方法很慢(显著地跟随衔接数的补充部分m。,白白(由于等等衔接可能性发送召唤和等候)。遍历零碎圈出打中每一独一衔接,等等衔接只好等候。设想有 100 一独一衔接,他们中仅独一有记载。,因而你否则要和等等人结成一队 99 一独一衔接,必要处置的衔接。。
  • poll、epoll 和异型:这是对圈出方法的改善。,它用独一机构饲料要监督的每一独一衔接的数字组,当记载在广播网套接字上找届时,经过回调机制必要处置作用。poll 成绩是机构将绝大。,当独一新的广播网衔接添加到列表中时,修正机构会补充部分负担并冲击机能。
  • 选择select() 作用必要运用动态机构。,它预先准备好的编码为独一相当小的数(1024)。 一一独一衔接),因而它不快用于绝大的摆设。。

在杂多的平台上静静地等等如愿以偿(如 Solaris 上的 /dev/poll 或 FreeBSD/NetBSD 上的 kqueue),他们是属于本人的。 OS 优胜的机能可能性较好的。,但它不克不及嫁接法。,也批评一定可以处理处置召唤的高层成绩。

下面尽量的些人receive 接纳都运用独一简略的圈出来等候并处置召唤。,与将召唤分合理的服装另独一作用来处置实践的广播网。。关键在于圈出和广播网套接字必要大方的办理密码,这可以经过这种方法监督。、修正和把持不相像的人于的衔接和管嘴。

处置大方的衔接的另类的方法是,采取多线索的近代内核忍受听力折术,为每一独一衔接启动独一新线。这是采取军事行动零碎的责。,但它将在 RAM 和 CPU 开销实质上补充部分,由于每个线都必要本人的担当管理人投宿。。旁白,设想每个线都再处置广播网衔接,线经过的语境切换将绝频繁。。上个,大方的拉岩芯不快合处置多少使有生气的T。。

libevent 方法

libevent 藏书实践上无被移走。 select()poll() 或等等机制的根底。而批评运用独一最高效和高机能的receive 接纳。

为了实践处置每个召唤,libevent 该库企图了独一事情机制。,它充任粗涂广播网后端的包装器。。事情零碎使得为衔接添加处置效能绝轻易。,同时失效臀部 I/O 不均一。这是 libevent 零碎的拉岩芯。

libevent 该库的等等集会企图等等效能,一种容纳缓冲器的事情零碎(用于缓冲发送到的记载)。 HTTP、DNS 和 RPC 零碎的拉岩芯如愿以偿。

使译成 libevent 满足必要的根本方法是,独一采取军事行动时应当流露的作用(比方增加独一,与必要主事情圈出。 event_dispatch()。担当管理人折术的把持如今由 libevent 零碎处置。在流露事情和要必要的作用随后,事情零碎开端是或集团等的。;勤勉运转时,您可以添加(流露)或迅速离开(约去流露)事情。事情流露绝便宜。,您可以经过它添加新事情来处置新翻开的衔接。,构造松紧带的广播网处置零碎。

比方,你可以翻开独一监听插座。,与流露回调作用,每回你必要说某种说的 accept() 作用在新衔接翻开时必要就是很回调作用。,这将使译成独一广播网满足必要。。清单 1 密码使分开显示了根本折术。:

清单 1. 翻开监督器插座,流露独一回调作用(每回你必要说某种说的 accept() 作用在翻开新衔接时必要它。,从此使译成独一广播网满足必要

int 首要(int argc, char **argv)
{
...
    ev_init();

    /* Setup listening socket */

    event_set(&ev_accept, listen_fd, EV_READ|EV_PERSIST, on_accept, null)
    event_add(&ev_accept, null)

    /* Start the event 环。 */
    event_dispatch();
}

event_set() 作用使译成独一新的事情机构。,event_add() 向事情队列机制添加事情。与,event_dispatch() 启动事情队列零碎,开端监听(并增加)召唤。

清单 2 举独一更十分的例,它构造了独一绝简略的底色显示满足必要。:

清单 2. 构造简略的底色显示满足必要

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SERVER_PORT 8080
int debug = 0;

struct client {
  int fd;
  struct bufferevent *buf_ev;
};

int setnonblock(int FD)
{
  int flags;

  flags = fcntl(fd, F_GETFL);
  flags |= O_NONBLOCK;
  fcntl(fd, F_SETFL, 标示)
}

void buf_read_callback(struct bufferevent *incoming,
                       void 精氨酸)
{
  struct evbuffer *evreturn;
  char *req;

  req = evbuffer_readline(incoming->input);
  if (规定 == null)
    return;

  evreturn = evbuffer_new();
  evbuffer_add_printf(evreturn,"You said %s\n",规定)
  bufferevent_write_buffer(incoming,evreturn);
  evbuffer_free(evreturn);
  free(规定);
}

void buf_write_callback(struct bufferevent *bev,
                        void 精氨酸)
{
}

void buf_error_callback(struct bufferevent *bev,
                        short what,
                        void 精氨酸)
{
  struct client *client = (struct client *)arg;
  bufferevent_free(client->buf_ev);
  close(client->FD);
  收费(客户端)
}

void accept_callback(int fd,
                     short ev,
                     void 精氨酸)
{
  int client_fd;
  struct sockaddr_in client_addr;
  socklen_t client_len = sizeof(client_addr);
  struct client *client;

  client_fd = 增加(FD,
                     (struct sockaddr *)&client_addr,
                     &client_len);
  if (client_fd < 0)
    {
      warn("Client: accept() failed");
      return;
    }

  setnonblock(client_FD);

  client = calloc(1, sizeof(*client));
  if (client == null)
    err(1, "malloc failed");
  client->fd = client_fd;

  client->buf_ev = bufferevent_new(client_fd,
                                   buf_read_callback,
                                   buf_write_callback,
                                   buf_error_callback,
                                   客户端)

  bufferevent_enable(client->buf_ev, EV_READ);
}

int 首要(int argc,
         char **argv)
{
  int socketlisten;
  struct sockaddr_in addresslisten;
  struct event accept_event;
  int reuse = 1;

  event_init();

  socketlisten = socket(AF_INET, SOCK_STREAM, 0);

  if (socketlisten < 0)
    {
      fprintf(stderr,"Failed to create listen socket");
      return 1;
    }

  memset(&addresslisten, 0, sizeof(addresslisten));

  addresslisten.sin_family = AF_INET;
  addresslisten.sin_addr.s_addr = INADDR_ANY;
  addresslisten.sin_port = htons(SERVER_PORT);

  if (bind(socketlisten,
           (struct sockaddr *)&addresslisten,
           sizeof(addresslisten)) < 0)
    {
      fprintf(stderr,"Failed to bind");
      return 1;
    }

  if (listen(socketlisten, 5) < 0)
    {
      fprintf(stderr,"Failed to listen to socket");
      return 1;
    }

  setsockopt(socketlisten,
             SOL_SOCKET,
             SO_REUSEADDR,
             &reuse,
             sizeof(reuse));

  setnonblock(socketlisten);

  event_set(&accept_event,
            socketlisten,
            EV_READ|EV_PERSIST,
            accept_callback,
            null)

  event_add(&accept_event,
            null)

  event_dispatch();

  close(socketlisten);

  return 0;
}

议论了以下作用及其采取军事行动:

  • main():使译成的首要效能是监听衔接。,与使译成 accept() 的回调作用其目的是为了经过事情处置作用处置每一独一衔接。
  • accept_callback():当增加衔接,事情零碎必要就是很作用。。此作用增加与客户机的衔接。;添加客户端套接字通知和独一 bufferevent 机构;添加回调到客户端套接字打中读/写/失当事情;将客户端机构作为限制因素使铭记(并嵌入) eventbuffer 和客户端套接字。每回对应的客户端套接字都容纳读、著作或失当采取军事行动时,必要呼应的回调作用。
  • buf_read_callback():当客户端套接字有要读取的记载时必要它。用作显示上菜用具,就是很作用 "you said..." 给客户回信。套接字仍产生翻开事件。,增加新的召唤。
  • buf_write_callback():当必要作曲记载时必要它。在这简略的上菜用具中,不必要此效能。,因而界限是空的。
  • buf_error_callback():产生失当时必要它。这包孕客户端挂衔接。。在尽量的失当的壮观中,停产客户端套接字,从事情列表中从客户端套接字迅速离开事情章,放开客户机机构的内存。
  • setnonblock():设置广播网套接字以翻开 I/O。

当客户端衔接,向事情队列添加新事情以处置客户端衔接;当客户端挂衔接时迅速离开事情。在在后台,libevent 处置广播网插座,引人注目必要上菜用具的客户机,区别必要呼应的作用。

要构造就是很勤勉,必要编辑 C 源密码和添加 libevent 库:$ gcc -o basic basic.c -levent

从客户的角度,此满足必要只发送发送它的一点点版本(会诊 清单 3)。

清单 3. 满足必要发回发送它的版本。

$ telnet localhost 8080
Trying 127.0.0.1...
Connected to 住处附近的当地酒店熟练。
Escape character is ''^]''.
Hello!
You said Hello!

很的广播网勤勉绝廉正必要处置多一独一衔接的大规模分配摆设,比方 IBM Cloud 零碎。

这是很难观察到大方的的接着产生衔接和机能。你可以用嵌入式 HTTP 有助于逮捕可伸缩性。

运用内置的 HTTP 满足必要

设中间构造住处附近的当地酒店勤勉,可以运用普通的由于广播网的广播网。 libevent 管嘴;可是,越来越通俗的的壮观是开拓由于 HTTP 科学实验报告的服用,和网页装满或动态重行装满通知。设想就中一点点独一被运用 AJAX 库,客户的盘问 HTTP,假设你隐现的通知是 XML 或 JSON。

libevent 打中 HTTP 执行批评 Apache HTTP 满足必要的替代者,它依从的空气和空气。 web 与经济状况关心的大规模动态灵的效用receive 接纳。比方,可以在 IBM Cloud 摆设或等等receive 接纳由于 libevent 的管嘴。由于它可以用 HTTP 举行一致,满足必要可以与等等集会集成。。

想用它吗? libevent 上菜用具,必要运用与首要广播网事情图案比得上的根本机构,可是只好处置广播网管嘴。,HTTP 包装工会处置你的。。这使得总计的折术译成第四作用必要(设定初值)。、启动 HTTP 满足必要、设置 HTTP 回调作用和事情圈出的章,添加隐现到记载的回调作用。。清单 4 举个简略的例:

清单 4. 运用 libevent 上菜用具的简略示例

#include 

#include 
#include 
#include 

#include 
#include 

void generic_request_handler(struct evhttp_request *req, void 精氨酸)
{
  struct evbuffer *returnbuffer = evbuffer_new();

  evbuffer_add_printf(returnbuffer, "Thanks for the request!");
  evhttp_send_reply(规定, HTTP_OK, "Client", returnbuffer);
  evbuffer_free(returnbuffer);
  return;
}

int 首要(int argc, char **argv)
{
  short          http_port = 8081;
  char          *http_addr = "";
  struct evhttp *http_server = NULL;

  event_init();
  http_server = evhttp_start(http_addr, http_port);
  evhttp_set_gencb(http_server, generic_request_handler, null)

  fprintf(stderr, "Server started on port %d\n", http_port);
  event_dispatch();

  隐现(0)
}

你应当可以经过发生看密码的根本机构,不必要解说。首要元素是 evhttp_set_gencb() 作用(在接纳时设置) HTTP 召唤时要运用的回调作用)和 generic_request_handler() 回调作用它自己(它用独一简略的MES配药呼应缓冲器)。

HTTP 包装器企图大方的等等效能。。比方,有独一召唤解析器,它从典型召唤(如处置)中拉查询限制因素。 CGI 比得上的召唤)。您还可以设置处置作用在不相像的人于的召唤引发。经过设置不相像的人于的回调作用和处置作用,您可以运用路程 ''/db/'' 记载库的管嘴,或运用 / MEMC 企图 memcached 的管嘴。

libevent 成套用品的另独一特点是忍受普通点火调节装置。。您可以在指定的的工夫量子引发事情。。它可以使化合时限装置的运用和 HTTP 如愿以偿轻量级上菜用具,原来如此无意识或下意识行为企图用纸覆盖的灵。,修正用纸覆盖灵时修正隐现的记载。比方,中间在频繁的出版物事情中企图即时修正,前端 web 勤勉必要活期重行装满出版物稿。,如今很轻易企图灵。总计的勤勉(和 web 上菜用具)内存中,因而反映很快。。

结果到了! 清单 5 在示例首要用途:

清单 5. 运用点火调节装置在频繁的出版物事情中企图即时修正

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define RELOAD_TIMEOUT 5
#define DEFAULT_FILE ""

char *filedata;
time_t lasttime = 0;
char 用纸覆盖名[ 80 ]
int counter = 0;

void read_file()
{
  int size = 0;
  char *data;
  struct stat buf;

  STAT(用纸覆盖名,buf)

  if (buf.st_mtime > lasttime)
    {
      if (反驳
        fprintf(stderr,"Reloading file: %s",用纸覆盖名)
      else
        fprintf(stderr,"Loading file: %s",用纸覆盖名)

      FILE *f = fopen(用纸覆盖名, RB
      if (f == null)
        {
          fprintf(stderr,"Couldn''t open file\n");
          输入(1)
        }

      fseek(f, 0, SEEK_END);
      size = ftell(f);
      fseek(f, 0, SEEK_SET);
      data = (char *)malloc(size+1);
      fread(记载, sizeof(char), size, f);
      filedata = (char *)malloc(size+1);
      strcpy(filedata,记载)
      停产用纸覆盖(F)


      fprintf(stderr," (%d 八位字节n,大块)
      lasttime = 
    }
}

void load_file()
{
  struct event *loadfile_event;
  struct timeval tv;

  read_file();

  tv.tv_sec = RELOAD_TIMEOUT;
  tv.tv_usec = 0;

  loadfile_event = malloc(sizeof(struct 事情)

  evtimer_set(loadfile_event,
              load_file,
              loadfile_event);

  evtimer_add(loadfile_event,
              电视节目)
}

void generic_request_handler(struct evhttp_request *req, void 精氨酸)
{
  struct evbuffer *evb = evbuffer_new();

  evbuffer_add_printf(evb, "%s",file记载)
  evhttp_send_reply(规定, HTTP_OK, "Client", EVB)
  evbuffer_free(EVB)
}

int 首要(int argc, char *argv[])
{
  short          http_port = 8081;
  char          *http_addr = "";
  struct evhttp *http_server = NULL;

  if (argc > 1)
    {
      strcpy(filename,argv[1]);
      printf(运用 %s\n",用纸覆盖名)
    }
  else
    {
      strcpy(filename,DEFAULT_FILE);
    }

  event_init();

  load_file();

  http_server = evhttp_start(http_addr, http_port);
  evhttp_set_gencb(http_server, generic_request_handler, null)

  fprintf(stderr, "Server started on port %d\n", http_port);
  event_dispatch();
}

此满足必要的根本原理与后面的示例比得上。。率先,本子设置了独一 HTTP 满足必要,它只对根本灵作出呼应。 URL 熟练/左转舵结成召唤(非处置召唤) URI)。第一步是装满用纸覆盖。 (read_file())。在装满原始用纸覆盖和时限装置引发时运用此作用。

read_file() 作用的运用 stat() 作用必要反省用纸覆盖的更改工夫,仅当用纸覆盖在上个一次装满随后被修正时,它不管到什么程度读用纸覆盖的灵。就是很作用经过必要来必要。 fread() 装满用纸覆盖记载,将记载繁殖到另独一机构中,与用它 strcpy() 将记载从装满字母行转变到大局字母行。

load_file() 作用是当时限装置被引发时必要的作用。。它是由电话制造呼叫的。 read_file() 装满灵,与用它 RELOAD_TIMEOUT 值设定时限装置,进攻装满用纸覆盖在前的秒数。。libevent 时限装置的运用 timeval 机构,容许在秒和手写本中拇指定的点火调节装置。。点火调节装置批评运行性的。;当引发点火调节装置事情时设置它。,与迅速离开事情队列打中事情。。

以与后面示例比得上的体式编辑密码:$ gcc -o basichttpfile basichttpfile.c -levent

如今,使译成独一用作记载的动态用纸覆盖;默许用纸覆盖是 ,可是您可以经过命令行打中第独一限制因素指定的一点点用纸覆盖(请参阅 清单 6)。

清单 6. 使译成独一用作记载的动态用纸覆盖

$ ./basichttpfile
Loading file:  (8046 八位字节)
Server started on port 8081

如今,顺序可以增加召唤。,重行装满点火调节装置也开端了。。设想修正 的灵,您应当重行装满该用纸覆盖,并在日记中记载条音讯。。比方,清单 7 输入显示初始装满和两倍装满。:

清单 7. 输入显示初始装满和两倍装满

$ ./basichttpfile
Loading file:  (8046 八位字节)
Server started on port 8081
Reloading file:  (8047 八位字节)
Reloading file:  (8048 八位字节)

留意,增加最大得益,只好确保经济状况不限度局限。可以运用 ulimit 命令修正限度局限(必要有关性的靠动力行进或根)。详细的设置静止你。 OS,可是在 Linux® 上可以用 -n 调动球员设置翻开的用纸覆盖周转符(和广播网套接字)的总计:

清单 8. 用 -n 由调动球员翻开的用纸覆盖周转符的数量。

$ ulimit -n
1024

经过指定的数字来增殖限度局限:$ ulimit -n 20000

可以运用 Apache Bench 2 (ab2) 机能参照试验勤勉反省SER的机能。。接着产生查询的总计和召唤总额可以是SP。。比方,运用 100,000 召唤运转参照试验,接着产生召唤的总计是 1000 个:$ ab2 -n 100000 -c 1000 http://:8081/

如在运用满足必要示例中所示 8K 该用纸覆盖运转就是很示例零碎。,赢得的发生大概是每秒处置。 11,000 个召唤。请记取,就是很 libevent 满足必要运转在独一线中。,多于对方的一次击球客户机不太可能性在满足必要上形成压力。,由于它也受到召唤翻开的方法的限度局限。。不管如此,在被掉换者用纸覆盖中庸大块的事件下,很的处置速率为了单线勤勉来说依然参加胡乱干的工作。

等等说的如愿以偿

不管 C 说绝廉正于大方的零碎勤勉。,但它在近代经济状况中没有的常常运用。 C 说,本子说更松紧带。、更效用。侥幸的是,,Perl 和 PHP 形成大块本子说被运用。 C 调解的,因而你可以运用拉长说模块 libevent 等 C 库。

比方,清单 9 让步 Perl 广播网满足必要本子的根本机构。accept_callback() 效能和 清单 1 所示拉岩芯 libevent 在示例 accept 比得上的效能。

清单 9. Perl 广播网满足必要本子的根本机构

my $server = ::::INET Socket IO ->新
    LocalAddr       => localhost,
    LocalPort       => 8081,
    Proto           => 科学实验报告,
    ReuseAddr       => SO_REUSEADDR,
    Listen          => 1,
    Blocking        => 0,
    ) or die $@;

my $accept = event_new($server, EV_READ|EV_PERSIST, \&accept_callback);

$main->add;

event_mainloop();

用这些说写的 libevent 通常忍受如愿以偿。 libevent 零碎的拉岩芯,但它不必要忍受。 HTTP 包装器。所以,运用这些本子制作节目将全部事件复杂。。有两种方法:左右本子说嵌入在基中。 C 的 libevent 在独一勤勉,或许运用大方的由于本子的说经济状况 HTTP 执行经过。比方,Python 容纳有效地效能的 HTTP 满足必要类 (httplib/httplib2)。

应当转位短时间。:本子说中无是什么不会有的性运用的。 C 重行如愿以偿。可是,然而开拓工夫的边界,与持续存在密码集成可能性更为重要。。

libev 库

与 libevent 相像的人于,libev 该零碎也由于事情的零碎。,它在 poll()select() 在住处附近的当地酒店如愿以偿的根底上企图了由于事情的圈出。。等我写冠词的时分,libev 执行本钱较低。,可以增加较好的的参照试验发生。libev API 相比原始,无 HTTP 包装器,可是 libev 忍受在如愿以偿中使译成更多事情典型。比方,一种 evstat 可以监督多个用纸覆盖的属性更改的如愿以偿,可以在 清单 4 所示的 HTTP 在用纸覆盖receive 接纳中运用它。

可是,libevent 和 libev 根本折术是相像的人于的。。使译成所需的广播网监督器套接字,在担当管理人折术中要必要的事情的流露,与启动主事情运行,让 libev 其他的折术。

比方,可以运用 Ruby 管嘴根底和列表 1 企图底色显示满足必要的相像的人方法,见 清单 10。

清单 10. 运用 Ruby 管嘴企图底色显示满足必要。

require ''rubygems''
require 启

PORT = 8081

class EchoServerConnection < Rev::TCPSocket
  def on_read(data)
    write ''You said: '' + data
  end
end

server = Rev::('''', PORT, EchoServerConnection)
(Rev::)

puts "Listening on localhost:#{PORT}"
Rev::.run

Ruby 如愿以偿显著地好。,由于它为大方的公共广播网receive 接纳企图了包装器。,包孕 HTTP 客户端、OpenSSL 和 DNS。等等本子说如愿以偿包孕十分的效能。 Perl 和 Python 如愿以偿,你可以试试看。。

加标签于

libevent 和 libev 企图松紧带和有效地的经济状况,忍受担当管理人高机能广播网(和等等广播网) 输入输入管嘴。咱们的目的是增殖效率(CPU / RAM) 运用量低)的方法忍受数千甚至数万一独一衔接。在本贴纸,你理解一点点例,包孕 libevent 中内置的 HTTP 上菜用具,这些技术可以用来忍受。 IBM Cloud、EC2 或 AJAX 的 web 勤勉。



发表评论

电子邮件地址不会被公开。 必填项已用*标注