使用 libevent 和 libev 提高網路應用效能的方法_Linux

來源:互聯網
上載者:User
有許多解決方案,但是 libevent 庫和 libev 庫能夠大大提高效能和事件處理能力。在本文中,我們要討論在 UNIX® 應用程式中使用和部署這些解決方案所用的基本結構和方法。libev 和 libevent 都可以在高效能應用程式中使用,包括部署在 IBM Cloud 或 Amazon EC2 環境中的應用程式,這些應用程式需要支援大量並發用戶端或操作。
簡介
許多伺服器部署(尤其是 網頁伺服器部署)面對的最大問題之一是必須能夠處理大量串連。無論是通過構建雲端式的服務來處理網路通訊流,還是把應用程式分布在 IBM Amazon EC 執行個體上,還是為網站提供高效能組件,都需要能夠處理大量並發串連。
一個好例子是,web 應用程式最近越來越動態了,尤其是使用 AJAX 技術的應用程式。如果要部署的系統允許數千用戶端直接在網頁中更新資訊,比如提供事件或問題即時受監視的系統,那麼提供資訊的速度就非常重要了。在網格或雲環境中,可能有來自數千用戶端的持久串連同時開啟著,必須能夠處理每個用戶端的請求並做出響應。
在討論 libevent 和 libev 如何處理多個網路連接之前,我們先簡要回顧一下處理這類串連的傳統解決方案。

處理多個用戶端

處理多個串連有許多不同的傳統方法,但是在處理大量串連時它們往往會產生問題,因為它們使用的記憶體或 CPU 太多,或者達到了某個作業系統限制。

使用的主要方法如下:

迴圈:早期系統使用簡單的迴圈選擇解決方案,即迴圈遍曆開啟的網路連接的列表,判斷是否有要讀取的資料。這種方法既緩慢(尤其是隨著串連數量增加越來越慢),又低效(因為在處理當前串連時其他串連可能正在發送請求並等待響應)。在系統迴圈遍曆每個串連時,其他串連不得不等待。如果有 100 個串連,其中只有一個有資料,那麼仍然必須處理其他 99 個串連,才能輪到真正需要處理的串連。
poll、epoll 和變體:這是對迴圈方法的改進,它用一個結構儲存要監視的每個串連的數組,當在網路通訊端上探索資料時,通過回調機制調用處理函數。poll 的問題是這個結構會非常大,在列表中添加新的網路連接時,修改結構會增加負載並影響效能。
選擇:select() 函數調用使用一個靜態結構,它事先被寫入程式碼為相當小的數量(1024 個串連),因此不適用於非常大的部署。
在各種平台上還有其他實現(比如 Solaris 上的 /dev/poll 或 FreeBSD/NetBSD 上的 kqueue),它們在各自的 OS 上效能可能更好,但是無法移植,也不一定能夠解決處理請求的高層問題。
上面的所有解決方案都用簡單的迴圈等待並處理請求,然後把請求指派給另一個函數以處理實際的網路互動。關鍵在於迴圈和網路通訊端需要大量管理代碼,這樣才能監聽、更新和控制不同的串連和介面。
處理許多串連的另一種方法是,利用現代核心中的多線程支援監聽和處理串連,為每個串連啟動一個新線程。這把責任直接交給作業系統,但是會在 RAM 和 CPU 方面增加相當大的開銷,因為每個線程都需要自己的執行空間。另外,如果每個線程都忙於處理網路連接,線程之間的環境切換會很頻繁。最後,許多核心並不適於處理如此大量的活躍線程。

libevent 方法

libevent 庫實際上沒有更換 select()、poll() 或其他機制的基礎。而是使用對於每個平台最高效的高效能解決方案在實現外加上一個封裝器。
為了實際處理每個請求,libevent 庫提供一種事件機制,它作為底層網路後端的封裝器。事件系統讓為串連添加處理函數變得非常簡便,同時降低了底層 I/O 複雜性。這是 libevent 系統的核心。
libevent 庫的其他組件提供其他功能,包括緩衝的事件系統(用於緩衝發送到用戶端/從用戶端接收的資料)以及 HTTP、DNS 和 RPC 系統的核心實現。
建立 libevent 伺服器的基本方法是,註冊當發生某一操作(比如接受來自用戶端的串連)時應該執行的函數,然後調用主事件迴圈 event_dispatch()。執行過程的控制現在由 libevent 系統處理。註冊事件和將調用的函數之後,事件系統開始自治;在應用程式運行時,可以在事件隊列中添加(註冊)或刪除(取消註冊)事件。事件註冊非常方便,可以通過它添加新事件以處理新開啟的串連,從而構建靈活的網路處理系統。
例如,可以開啟一個監聽通訊端,然後註冊一個回呼函數,每當需要調用 accept() 函數以開啟新串連時調用這個回呼函數,這樣就建立了一個網路伺服器。清單 1 所示的程式碼片段說明基本過程:

清單 1. 開啟監聽通訊端,註冊一個回呼函數(每當需要調用 accept() 函數以開啟新串連時調用它),由此建立網路伺服器
複製代碼 代碼如下:

int main(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 loop. */
event_dispatch();
}

event_set() 函數建立新的事件結構,event_add() 在事件隊列機制中添加事件。然後,event_dispatch() 啟動事件隊列系統,開始監聽(並接受)請求。

清單 2 給出一個更完整的樣本,它構建一個非常簡單的回顯伺服器:
清單 2. 構建簡單的回顯伺服器
複製代碼 代碼如下:

#include <event.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#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, flags);
}

void buf_read_callback(struct bufferevent *incoming,
void *arg)
{
struct evbuffer *evreturn;
char *req;

req = evbuffer_readline(incoming->input);
if (req == NULL)
return;

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

void buf_write_callback(struct bufferevent *bev,
void *arg)
{
}

void buf_error_callback(struct bufferevent *bev,
short what,
void *arg)
{
struct client *client = (struct client *)arg;
bufferevent_free(client->buf_ev);
close(client->fd);
free(client);
}

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

client_fd = accept(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,
client);

bufferevent_enable(client->buf_ev, EV_READ);
}

int main(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 localhost.
Escape character is '^]'.
Hello!
You said Hello!

這樣的網路應用程式非常適合需要處理多個串連的大規模分布式部署,比如 IBM Cloud 系統。

很難通過簡單的解決方案觀察處理大量並發串連的情況和效能改進。可以使用嵌入的 HTTP 實現協助瞭解延展性。

使用內建的 HTTP 伺服器

如果希望構建本機應用程式,可以使用一般的基於網路的 libevent 介面;但是,越來越常見的情境是開發基於 HTTP 協議的應用程式,以及裝載或動態地重新裝載資訊的網頁。如果使用任何 AJAX 庫,用戶端就需要 HTTP,即使您返回的資訊是 XML 或 JSON。

libevent 中的 HTTP 實現並不是 Apache HTTP 伺服器的替代品,而是適用於與雲和 web 環境相關聯的大規模動態內容的實用解決方案。例如,可以在 IBM Cloud 或其他解決方案中部署基於 libevent 的介面。因為可以使用 HTTP 進行通訊,伺服器可以與其他組件整合。

要想使用 libevent 服務,需要使用與主要網路事件模型相同的基本結構,但是還必須處理網路介面,HTTP 封裝器會替您處理。這使整個過程變成四個函數調用(初始化、啟動 HTTP 伺服器、設定 HTTP 回呼函數和進入事件迴圈),再加上發送回資料的回呼函數。清單 4 給出一個非常簡單的樣本:


清單 4. 使用 libevent 服務的簡單樣本
複製代碼 代碼如下:

#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <event.h>
#include <evhttp.h>

void generic_request_handler(struct evhttp_request *req, void *arg)
{
struct evbuffer *returnbuffer = evbuffer_new();

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

int main(int argc, char **argv)
{
short http_port = 8081;
char *http_addr = "192.168.0.22";
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();

return(0);
}

應該可以通過前面的樣本看出代碼的基本結構,不需要解釋。主要元素是 evhttp_set_gencb() 函數(它設定當收到 HTTP 要求時要使用的回呼函數)和 generic_request_handler() 回呼函數本身(它用一個表示成功的簡單訊息填充響應緩衝區)。

HTTP 封裝器提供許多其他功能。例如,有一個請求解析器,它會從典型的請求中提取出查詢參數(就像處理 CGI 請求一樣)。還可以設定在不同的請求路徑中要觸發的處理函數。通過設定不同的回呼函數和處理函數,可以使用路徑 '/db/' 提供到資料庫的介面,或使用 '/memc' 提供到 memcached 的介面。

libevent 工具包的另一個特性是支援通用計時器。可以在指定的時間段之後觸發事件。可以通過結合使用計時器和 HTTP 實現提供輕量的服務,從而自動地提供檔案內容,在修改檔案內容時更新返回的資料。例如,以前要想在新聞頻發的活動期間提供即時更新服務,前端 web 應用程式就需要定期重新裝載新聞稿,而現在可以輕鬆地提供內容。整個應用程式(和 web 服務)都在記憶體中,因此響應非常快。

這就是 清單 5 中的樣本的主要用途:


清單 5. 使用計時器在新聞頻發的活動期間提供即時更新服務
複製代碼 代碼如下:

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <event.h>
#include <evhttp.h>

#define RELOAD_TIMEOUT 5
#define DEFAULT_FILE "sample.html"

char *filedata;
time_t lasttime = 0;
char filename[80];
int counter = 0;

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

stat(filename,&buf);

if (buf.st_mtime > lasttime)
{
if (counter++)
fprintf(stderr,"Reloading file: %s",filename);
else
fprintf(stderr,"Loading file: %s",filename);

FILE *f = fopen(filename, "rb");
if (f == NULL)
{
fprintf(stderr,"Couldn't open file\n");
exit(1);
}

fseek(f, 0, SEEK_END);
size = ftell(f);
fseek(f, 0, SEEK_SET);
data = (char *)malloc(size+1);
fread(data, sizeof(char), size, f);
filedata = (char *)malloc(size+1);
strcpy(filedata,data);
fclose(f);


fprintf(stderr," (%d bytes)\n",size);
lasttime = buf.st_mtime;
}
}

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 event));

evtimer_set(loadfile_event,
load_file,
loadfile_event);

evtimer_add(loadfile_event,
&tv);
}

void generic_request_handler(struct evhttp_request *req, void *arg)
{
struct evbuffer *evb = evbuffer_new();

evbuffer_add_printf(evb, "%s",filedata);
evhttp_send_reply(req, HTTP_OK, "Client", evb);
evbuffer_free(evb);
}

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

if (argc > 1)
{
strcpy(filename,argv[1]);
printf("Using %s\n",filename);
}
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。

現在,建立作為資料使用的靜態檔案;預設檔案是 sample.html,但是可以通過命令列上的第一個參數指定任何檔案(見 清單 6)。


清單 6. 建立作為資料使用的靜態檔案
複製代碼 代碼如下:

$ ./basichttpfile
Loading file: sample.html (8046 bytes)
Server started on port 8081


現在,程式可以接受請求了,重新裝載計時器也啟動了。如果修改 sample.html 的內容,應該會重新裝載此檔案並在日誌中記錄一個訊息。例如,清單 7 中的輸出顯示初始裝載和兩次重新裝載:
清單 7. 輸出顯示初始裝載和兩次重新裝載
複製代碼 代碼如下:

$ ./basichttpfile
Loading file: sample.html (8046 bytes)
Server started on port 8081
Reloading file: sample.html (8047 bytes)
Reloading file: sample.html (8048 bytes)

注意,要想獲得最大的收益,必須確保環境沒有限制開啟的檔案描述符數量。可以使用 ulimit 命令修改限制(需要適當的許可權或根訪問)。具體的設定取決與您的 OS,但是在 Linux® 上可以用 -n 選項設定開啟的檔案描述符(和網路通訊端)的數量:


清單 8. 用 -n 選項設定開啟的檔案描述符數量

$ ulimit -n
1024



通過指定數字提高限制:$ ulimit -n 20000。

可以使用 Apache Bench 2 (ab2) 等效能基準測試應用程式檢查伺服器的效能。可以指定並發查詢的數量以及請求的總數。例如,使用 100,000 個請求運行基準測試,並發請求數量為 1000 個:$ ab2 -n 100000 -c 1000 http://192.168.0.22:8081/。

使用伺服器樣本中所示的 8K 檔案運行這個樣本系統,獲得的結果為大約每秒處理 11,000 個請求。請記住,這個 libevent 伺服器在單一線程中運行,而且單一用戶端不太可能給伺服器造成壓力,因為它還受到開啟請求的方法的限制。儘管如此,在交換的文檔大小適中的情況下,這樣的處理速率對於單線程應用程式來說仍然令人吃驚。


--------------------------------------------------------------------------------
回頁首
使用其他語言的實現

儘管 C 語言很適合許多系統應用程式,但是在現代環境中不經常使用 C 語言,指令碼語言更靈活、更實用。幸運的是,Perl 和 PHP 等大多數指令碼語言是用 C 編寫的,所以可以通過擴充模組使用 libevent 等 C 庫。

例如,清單 9 給出 Perl 網路伺服器指令碼的基本結構。accept_callback() 函數與 清單 1 所示核心 libevent 樣本中的 accept 函數相同。


清單 9. Perl 網路伺服器指令碼的基本結構
複製代碼 代碼如下:

my $server = IO::Socket::INET->new(
LocalAddr => 'localhost',
LocalPort => 8081,
Proto => 'tcp',
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 檔案解決方案中使用它。

但是,libevent 和 libev 的基本過程是相同的。建立所需的網路監聽通訊端,註冊在執行期間要調用的事件,然後啟動主事件迴圈,讓 libev 處理過程的其餘部分。

例如,可以使用 Ruby 介面按照與清單 1 相似的方式提供回顯伺服器,見 清單 10。


清單 10. 使用 Ruby 介面提供回顯伺服器
複製代碼 代碼如下:

require 'rubygems'
require 'rev'

PORT = 8081

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

server = Rev::TCPServer.new('192.168.0.22', PORT, EchoServerConnection)
server.attach(Rev::Loop.default)

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



Ruby 實現尤其出色,因為它為許多常用的網路解決方案提供了封裝器,包括 HTTP 用戶端、OpenSSL 和 DNS。其他指令碼語言實現包括功能全面的 Perl 和 Python 實現,您可以試一試。

結束語

libevent 和 libev 都提供靈活且強大的環境,支援為處理伺服器端或用戶端請求實現高效能網路(和其他 I/O)介面。目標是以高效(CPU/RAM 使用量低)的方式支援數千甚至數萬個串連。在本文中,您看到了一些樣本,包括 libevent 中內建的 HTTP 服務,可以使用這些支援人員基於 IBM Cloud、EC2 或 AJAX 的 web 應用程式。

參考資料

學習

  • C10K problem 對處理 10,000 個串連的問題做了精彩的概述。

  • IBM Cloud Computing 網站提供不同雲實現的相關資訊。

  • 閱讀 系統管理工具包: 標準化您的 UNIX 命令列工具(Martin Brown,developerWorks,2006 年 5 月),學習如何跨多台機器使用相同的命令。

  • 讓 UNIX 和 Linux 一起工作(Martin Brown,developerWorks,2006 年 4 月)講解如何讓傳統的 UNIX 發行版和 Linux 一起工作。

  • 揭秘雲端運算(Brett McLaughlin,developerWorks,2009 年 3 月):協助您根據自己的應用程式需求選擇最好的雲端運算平台。

  • 閱讀 用 Amazon Web Services 進行雲端運算(Prabhakar Chaganti,developerWorks,2008 年 7 月):詳細講解如何使用 Amazon Web Services。

  • 可以通過 developerWorks Cloud Computing Resource Center 使用適用於 Amazon EC2 平台的 IBM 產品。

  • developerWorks Cloud Computing Resource Center 使用適用於 Amazon EC2 平台的 IBM 產品。

  • 在 developerWorks 的 雲開發人員資源 中,發現和共用應用程式和服務開發人員構建其雲部署項目的知識和經驗。

  • AIX and UNIX 專區:developerWorks 的“AIX and UNIX 專區”提供了大量與 AIX 系統管理的所有方面相關的資訊,您可以利用它們來擴充自己的 UNIX 技能。

  • AIX and UNIX 新手入門:訪問“AIX and UNIX 新手入門”頁面可瞭解更多關於 AIX 和 UNIX 的內容。

  • AIX and UNIX 專題匯總:AIX and UNIX 專區已經為您推出了很多的技術專題,為您總結了很多熱門的知識點。我們在後面還會繼續推出很多相關的熱門專題給您,為了方便您的訪問,我們在這裡為您把本專區的所有專題進行匯總,讓您更方便的找到您需要的內容。

  • AIX and UNIX 下載中心:在這裡你可以下載到可以運行在 AIX 或者是 UNIX 系統上的 IBM 伺服器軟體以及工具,讓您可以提前免費試用他們的強大功能。

  • IBM Systems Magazine for AIX 中文版:本雜誌的內容更加關注於趨勢和企業級架構應用方面的內容,同時對於新興的技術、產品、應用方式等也有很深入的探討。IBM Systems Magazine 的內容都是由十分資深的業內人士撰寫的,包括 IBM 的夥伴、IBM 的主機工程師以及進階管理員。所以,從這些內容中,您可以瞭解到更高層次的應用理念,讓您在選擇和應用 IBM 系統時有一個更好的認識。

  • 在 developerWorks 播客 上收聽面向軟體開發人員的有趣訪談和討論。

  • developerWorks 技術活動和網路廣播:隨時關注 developerWorks 技術活動和網路廣播。

獲得產品和技術

  • 擷取 libev 庫,包括下載和文檔。

  • 擷取 libevent 庫。

  • ruby libev (rev) 庫和文檔。

  • Memcached 是用於儲存和處理資料的 RAM 緩衝(其核心使用 libevent,也可以使用其他 libevent 伺服器)。

  • 使用 IBM 試用軟體 改進您的下一個開放源碼開發項目,這些軟體可以通過下載或從 DVD 獲得。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.