目錄
晚上8點騰訊課堂直播地址:https://ke.qq.com/course/2705727?tuin=137bb271
- Linux高性能網(wǎng)絡(luò):協(xié)程系列01-前言
- Linux高性能網(wǎng)絡(luò):協(xié)程系列02-協(xié)程的起源
- Linux高性能網(wǎng)絡(luò):協(xié)程系列03-協(xié)程的案例
- Linux高性能網(wǎng)絡(luò):協(xié)程系列04-協(xié)程實現(xiàn)之工作原理
- Linux高性能網(wǎng)絡(luò):協(xié)程系列05-協(xié)程實現(xiàn)之原語操作
- Linux高性能網(wǎng)絡(luò):協(xié)程系列06-協(xié)程實現(xiàn)之切換
- Linux高性能網(wǎng)絡(luò):協(xié)程系列07-協(xié)程實現(xiàn)之定義
- Linux高性能網(wǎng)絡(luò):協(xié)程系列08-協(xié)程實現(xiàn)之調(diào)度器
- Linux高性能網(wǎng)絡(luò):協(xié)程系列09-協(xié)程性能測試
- Linux高性能網(wǎng)絡(luò):協(xié)程系列10 待續(xù)
2.協(xié)程的起源
??問題:協(xié)程存在的原因?協(xié)程能夠解決哪些問題?
??在我們現(xiàn)在CS,BS開發(fā)模式下,服務(wù)器的吞吐量是一個很重要的參數(shù)。其實吞吐量是IO處理時間加上業(yè)務(wù)處理。為了簡單起見,比如,客戶端與服務(wù)器之間是長連接的,客戶端定期給服務(wù)器發(fā)送心跳包數(shù)據(jù)。客戶端發(fā)送一次心跳包到服務(wù)器,服務(wù)器更新該新客戶端狀態(tài)的。心跳包發(fā)送的過程,業(yè)務(wù)處理時長等于IO讀?。≧ECV系統(tǒng)調(diào)用)加上業(yè)務(wù)處理(更新客戶狀態(tài))。吞吐量等于1s業(yè)務(wù)處理次數(shù)。

??業(yè)務(wù)處理(更新客戶端狀態(tài))時間,業(yè)務(wù)不一樣的,處理時間不一樣,我們就不做討論。
??那如何提升recv的性能。若只有一個客戶端,recv的性能也沒有必要提升,也不能提升。若在有百萬計的客戶端長連接的情況,我們該如何提升。以Linux為例,在這里需要介紹一個“網(wǎng)紅”就是epoll。服務(wù)器使用epoll管理百萬計的客戶端長連接,代碼框架如下:
while (1) {
int nready = epoll_wait(epfd, events, EVENT_SIZE, -1);
for (i = 0;i < nready;i ++) {
int sockfd = events[i].data.fd;
if (sockfd == listenfd) {
int connfd = accept(listenfd, xxx, xxxx);
setnonblock(connfd);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = connfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
} else {
handle(sockfd);
}
}
}
??對于響應(yīng)式服務(wù)器,所有的客戶端的操作驅(qū)動都是來源于這個大循環(huán)。來源于epoll_wait的反饋結(jié)果。
對于服務(wù)器處理百萬計的IO。Handle(sockfd)實現(xiàn)方式有兩種。
- handle(sockfd)函數(shù)內(nèi)部對sockfd進行讀寫動作
int handle(int sockfd) {
recv(sockfd, rbuffer, length, 0);
parser_proto(rbuffer, length);
send(sockfd, sbuffer, length, 0);
}
handle的io操作(send,recv)與epoll_wait是在同一個處理流程里面的。這就是IO同步操作。
優(yōu)點:
1.sockfd管理方便。
2.操作邏輯清晰。
缺點:
1.服務(wù)器程序依賴epoll_wait的循環(huán)響應(yīng)速度慢。
2.程序性能差handle(sockfd)函數(shù)內(nèi)部將sockfd的操作,push到線程池中:
int thread_cb(int sockfd) {
// 此函數(shù)是在線程池創(chuàng)建的線程中運行。
// 與handle不在一個線程上下文中運行
recv(sockfd, rbuffer, length, 0);
parser_proto(rbuffer, length);
send(sockfd, sbuffer, length, 0);
}
int handle(int sockfd) {
//此函數(shù)在主線程 main_thread 中運行
//在此處之前,確保線程池已經(jīng)啟動。
push_thread(sockfd, thread_cb); //將sockfd放到其他線程中運行。
}
??Handle函數(shù)是將sockfd處理方式放到另一個已經(jīng)其他的線程中運行,如此做法,將io操作(recv,send)與epoll_wait 不在一個處理流程里面,使得io操作(recv,send)與epoll_wait實現(xiàn)解耦。這就叫做IO異步操作。
優(yōu)點:
1.子模塊好規(guī)劃。
2.程序性能高。
缺點:
正因為子模塊好規(guī)劃,使得模塊之間的sockfd的管理異常麻煩。每一個子線程都需要管理好sockfd,避免在IO操作的時候,sockfd出現(xiàn)關(guān)閉或其他異常。
??上文有提到IO同步操作,程序響應(yīng)慢,IO異步操作,程序響應(yīng)快。
??下面來對比一下IO同步操作與IO異步操作,代碼如下:
server_mulport_epool.c
??在這份代碼的main函數(shù)內(nèi),#if 1, 打開的時候,為IO異步操作。關(guān)閉的時候,為IO同步操作。
#if 1
if (nRun) {
printf(" New Data is Comming\n");
client_data_process(clientfd);
} else {
client_t *rClient = (client_t*)malloc(sizeof(client_t));
memset(rClient, 0, sizeof(client_t));
rClient->fd = clientfd;
job_t *job = malloc(sizeof(job_t));
job->job_function = client_job;
job->user_data = rClient;
workqueue_add_job(&workqueue, job);
}
#else
client_data_process(clientfd);
#endif
??接下來把我測試接入量的結(jié)果粘貼出來。
-
IO異步操作
IO異步操作,每1000個連接接入的服務(wù)器響應(yīng)時間(900ms左右)。
IO異步操作 -
IO同步操作
IO同步操作,每1000個連接接入的服務(wù)器響應(yīng)時間(6500ms左右)。
IO同步操作
epoll的IO異步操作與IO同步操作比較如下:
| 對比項 | IO同步操作 | IO異步操作 |
|---|---|---|
| Socket管理 | 管理方便 | 多個線程共同管理 |
| 代碼邏輯 | 程序整體邏輯清晰 | 子模塊邏輯清晰 |
| IO性能 | 響應(yīng)時間長,性能差 | 響應(yīng)時間短,性能好 |
??有沒有一種方式,有異步性能,同步的代碼邏輯。來方便編程人員對IO操作的組件呢? 有,采用一種輕量級的協(xié)程來實現(xiàn)。在每次send或者recv之前進行切換,再由調(diào)度器來處理epoll_wait的流程。
就是采用了基于這樣的思考,寫了NtyCo,實現(xiàn)了一個IO異步操作與協(xié)程結(jié)合的組件。
更多分享
email: 1989wangbojing@163.com
email: lizhiyong4360@gmail.com
email: 592407834@qq.com
協(xié)程技術(shù)交流群:829348971

