成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何基于Reactor網絡模型實現業務并測試性能

網絡 網絡管理
這篇文章我們通過實現了一個簡單的HTTP服務來說明如何將Reactor網絡模型應用到業務中去,這是在學校和網上10小時入門C語言里不會講的,但它又非常重要。

對于實現一個http服務來講,一個http請求正常情況下可以分為request和response兩部分,我們可以隨便打開一個網頁,比如:

圖片圖片

我們可以看到,一個http請求,包含Request Header和Response Header,針對HTTP協議的這個特點,我們就可以抽象出http_request和http_response兩個方法,它們分別用于處理HTTP的請求和響應。

那么,最終的流程是從recv_callback接收到數據包,然后調用http_request方法解析HTTP協議,處理相應的業務邏輯,處理完之后將響應結果通過http_response寫入到wbuf中,然后觸發EPOLLOUT事件,最終調用send_callback將wbuf中的數據發送出去,流程大致如下:

圖片圖片

下面我們就來實現一下http_request和http_response,代碼如下:

int http_request(connection_t *conn) {
    // TODO parse http headr and body 
}


int http_response(connection_t *conn) {
    conn->wlen = sprintf(conn->wbuf, 
    "HTTP/1.1 200 OK\\r\\n"
    "Accept-Ranges: bytes\\r\\n"
    "Content-Length: 47\\r\\n"
    "Content-Type: text/html\\r\\n"
    "Date: Sta, 06 Aug 2023 13:16:46 GMT\\r\\n\\r\\n"
    "<html><body><h1>Hello Server</h1></body></html>");


    return conn->wlen;
}

這里省略了http_request中的代碼,本文要關注的重點是如何在基于事件的網絡模型中插入業務代碼,你可能會覺得迷糊,HTTP服務不是也是基礎服務嗎?怎么能叫業務代碼呢?這是相對而言的,對于TCP協議來說,HTTP實際上只是TCP的一個應用,當然算得上是業務代碼了。

上面代碼中定義了一個connection_t類型的參數,實際上這個參數的原型就是conn_channel,其定義如下:

typedef struct conn_channel connection_t;

到這里,不知道你有沒有一種感覺,對于上層的業務代碼來講,它并不關心下層的網絡IO,我們在http_request和http_reponse中并沒有看到直接操作網絡IO的地方,它們只管從對應的rbuf和wbuf中讀和寫,至于數據是怎么收進來的,又是怎么發送出去的并不關心。上一篇文章我們講到網絡IO的分離,通過這一篇文章相信你會有一個更直觀的理解。

我將完整的代碼直接貼在下面了

#include <sys/socket.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <error.h>


#define BUFFER_LEN 1024


int epfd = 0;


typedef int(*callback)(int);


struct conn_channel {
    int fd;


    char wbuf[BUFFER_LEN];
    int wlen;
    char rbuf[BUFFER_LEN];
    int rlen;


    union {
        callback recv_call;
        callback accept_call;
    } call_t;


    callback send_call;
};


struct conn_channel conn_map[1024] = {0};


typedef struct conn_channel connection_t;


int create_serv(int port) {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);


    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(struct sockaddr_in));


    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port);


    int on = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));


    int ret = bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
    if (ret == -1) {
        perror("socket-bind-fail");
        return -1;
    } 


    listen(sockfd, 1024);


    return sockfd;
}


void make_nonblocking(int fd) {
    fcntl(fd, F_SETFL, O_NONBLOCK);
}


int http_request(connection_t *conn) {
    // TODO parse http headr and body 
}


int http_response(connection_t *conn) {
    conn->wlen = sprintf(conn->wbuf, 
    "HTTP/1.1 200 OK\\r\\n"
    "Accept-Ranges: bytes\\r\\n"
    "Content-Length: 47\\r\\n"
    "Content-Type: text/html\\r\\n"
    "Date: Sta, 06 Aug 2023 13:16:46 GMT\\r\\n\\r\\n"
    "<html><body><h1>Hello Server</h1></body></html>");


    return conn->wlen;
}


int recv_callback(int fd) {
    char *buffer = conn_map[fd].rbuf;
    int idx = conn_map[fd].rlen;


    int count = recv(fd, buffer+idx, BUFFER_LEN - idx, 0);
    if (count == 0) {
        printf("discounnect: %d\\n", fd);
        return -1;
    }


    conn_map[fd].rlen = count;


    // do http request and response
    http_request(&conn_map[fd]);


    http_response(&conn_map[fd]);


    struct epoll_event ev;
    ev.events = EPOLLOUT;
    ev.data.fd = fd;


    epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);


    return count;
}


int send_callback(int fd) {
   int count = send(fd, conn_map[fd].wbuf, conn_map[fd].wlen, 0);
   printf("send-count:%d\\n", count);


   struct epoll_event ev;
   ev.events = EPOLLIN;
   ev.data.fd = fd;


   epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);


   return count; 
}


int accept_callback(int fd) {
    struct sockaddr_in clientaddr;
    socklen_t len = sizeof(clientaddr);


    int clientfd = accept(fd, (struct sockaddr*)&clientaddr, &len);


    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = clientfd;


    conn_map[clientfd].wlen = 0;
    conn_map[clientfd].rlen = 0;
    conn_map[clientfd].call_t.recv_call = recv_callback;
    conn_map[clientfd].send_call = send_callback;


    memset(conn_map[clientfd].wbuf, 0, BUFFER_LEN);
    memset(conn_map[clientfd].rbuf, 0, BUFFER_LEN);


    epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev);


    return clientfd;
}


int main() {
    int sockfd = create_serv(2048);
    if (sockfd == -1) {
        perror("sockfd-create-fail");
        return -1;
    }


    make_nonblocking(sockfd);


    epfd = epoll_create1(0);


    printf("epfd:%d, sockfd: %d\\n", epfd, sockfd);


    conn_map[sockfd].wlen = 0;
    conn_map[sockfd].rlen = 0;
    conn_map[sockfd].call_t.recv_call = accept_callback;
    conn_map[sockfd].send_call = send_callback;


    memset(conn_map[sockfd].wbuf, 0, BUFFER_LEN);
    memset(conn_map[sockfd].rbuf, 0, BUFFER_LEN);


    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = sockfd;


    epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);


    struct epoll_event events[1024] = {0};


    while(1) {
        int nready = epoll_wait(epfd, events, 1024, -1);


        int i = 0;
        for (i = 0; i < nready; i++) {
            int connfd = events[i].data.fd;


            if (events[i].events & EPOLLIN) {
                int ret = conn_map[connfd].call_t.recv_call(connfd);            
                printf("epollin-ret: %d\\n", ret); 
            } else if (events[i].events & EPOLLOUT) {
                int count = conn_map[connfd].send_call(connfd);
                printf("send-count: %d\\n", count);
            }
        }
    }
}

上面的代碼200行不到,確包含了Reactor網絡模型的核心思想,以及實現方式,如果有興趣你可以基于此進一步擴展,你也可以參考我們前面網絡編程系列文章,有更加完整的實現。

代碼寫完了,性能究竟如何我們需要進一步驗證,這里推薦一個性能測試工具wrk,wrk是一款開源的性能測試工具,使用C實現,地址:https://github.com/wg/wrk,我準備了兩臺機器,配置如下:

ubuntu20.4      8C8G       192.168.56.2

ubuntu20.4-1   4C4G       192.168.56.3

我們的服務跑在8C8G這臺機器上,另外一臺4C4G的機器用于運行wrk工具進行性能測試。

為了有一個對比,我們首先在8C8G的機器上安裝一個Nginx用來作對比,版本是1.25.4

圖片圖片

然后我們在4C4G的機器上將wrk編譯好,下載好的wrk目錄結構如下:

圖片圖片

可以看到已經有寫好的Makefile了,我們只需要make一下就可以了,make完之后會編譯出一個wrk的二進制文件

圖片圖片

接著,我們將8C8G這臺機器上的Nginx和我們剛剛寫的httpserver都啟動起來,我們的nginx運行在80端口,httpserver運行在2048端口。

接著,我們在4C4G機器上運行wrk先測試nginx,如下:

圖片圖片

接著測試我們自己寫的httpserver

圖片圖片

我們使用wrk開啟50個線程,100個并發持續10秒分別對Nginx和我們自己的httpserver進行了測試,最終的結果我們可以看到,在Nginx沒有任何優化的情況下,我們寫的httpserver明顯比Nginx性能更好。當然,Nginx實際上寫了很多的日志,我們的httpserver幾乎沒有寫什么日志,你可以自己嘗試將Nginx日志關了再對比一下看看結果。

總結

這篇文章我們通過實現了一個簡單的HTTP服務來說明如何將Reactor網絡模型應用到業務中去,這是在學校和網上10小時入門C語言里不會講的,但它又非常重要。

在學習C/C++的過程中,相信很多人都會有這么一種感覺,那就是C/C++語法說起來都會,但就是很難寫出一個完整的項目,個人覺得這是目前國內整個IT教育界的失敗,大部分的課程花了非常大的篇幅講if-else,甚至將各種變量類型都講出花來了,但就是不告訴你一個完整的項目該如何寫。

責任編輯:武曉燕 來源: 程序員班吉
相關推薦

2024-08-16 21:30:00

IO網絡網絡通信

2023-12-05 17:44:24

reactor網絡

2024-04-18 09:34:28

Reactor項目異步編程

2019-01-15 10:54:03

高性能ServerReactor

2020-12-01 07:08:23

Linux網絡I

2023-09-13 14:45:14

性能測試開發

2011-05-16 14:13:04

模型測試

2021-09-21 09:01:19

網絡安全疫情數據

2009-12-30 10:31:04

配置NAT網絡

2021-06-02 10:00:30

云網絡性能測試

2009-03-27 09:59:00

2016-11-07 18:26:39

IT可視化

2022-05-17 08:53:26

TPS性能測試

2022-01-04 11:11:32

Redis單線程Reactor

2024-04-15 13:51:03

模型LLMLLMs

2012-05-08 13:36:55

2020-06-17 16:38:22

Rust業務架構

2012-07-27 15:28:09

SingleSON華為

2022-05-26 10:12:21

前端優化測試

2024-04-16 16:14:01

人工智能LLMRAG
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日本中文字幕一区 | 国产激情网 | 亚洲国产视频一区二区 | 欧美久久国产 | 亚洲精品久久久久久久不卡四虎 | 在线视频国产一区 | 国产精品一区二区不卡 | 国产精品一区久久久 | 在线三级网址 | 亚洲精品一区二区 | 日本中文字幕日韩精品免费 | 免费一级网站 | 麻豆久久精品 | 一区二区三区免费 | 亚洲精品久久久久久一区二区 | 男人av在线| 在线一区视频 | 欧美一级网站 | 日韩三级免费观看 | 九九久久久 | 性做久久久久久免费观看欧美 | 一区二区在线观看免费视频 | 免费看的av | 久久综合九九 | 精品一区二区久久久久久久网站 | 日韩欧美一区二区三区 | 国产精品一区二区不卡 | 干干干操操操 | av中文字幕在线观看 | www.五月婷婷.com | 99久久亚洲 | 日韩色视频 | 在线观看国产精品一区二区 | 黄网免费 | 日韩中文一区 | 亚洲一区视频在线 | 日韩中文字幕网 | 日韩精品一区二区三区在线播放 | 日本黄色影片在线观看 | 一级大片网站 | 精品不卡 |