協程概念、實現與應用

2019-12-26     零聲度7

1.協程的起源

  問題:協程存在的原因?協程能夠解決哪些問題?



  在我們現在CS,BS開發模式下,伺服器的吞吐量是一個很重要的參數。其實吞吐量是IO處理時間加上業務處理。為了簡單起見,比如,客戶端與伺服器之間是長連接的,客戶端定期給伺服器發送心跳包數據。客戶端發送一次心跳包到伺服器,伺服器更新該新客戶端狀態的。心跳包發送的過程,業務處理時長等於IO讀取(RECV系統調用)加上業務處理(更新客戶狀態)。吞吐量等於1s業務處理次數。

  業務處理(更新客戶端狀態)時間,業務不一樣的,處理時間不一樣,我們就不做討論。

  那如何提升recv的性能。若只有一個客戶端,recv的性能也沒有必要提升,也不能提升。若在有百萬計的客戶端長連接的情況,我們該如何提升。以Linux為例,在這裡需要介紹一個「網紅」就是epoll。伺服器使用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);

}

}

}

  對於響應式伺服器,所有的客戶端的操作驅動都是來源於這個大循環。來源於epoll_wait的反饋結果。 對於伺服器處理百萬計的IO。Handle(sockfd)實現方式有兩種。

handle(sockfd)函數內部對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同步操作。

優點:

1.sockfd管理方便。

2.操作邏輯清晰。

缺點:

1.伺服器程序依賴epoll_wait的循環響應速度慢。

2.程序性能差

handle(sockfd)函數內部將sockfd的操作,push到線程池中:

int thread_cb(int sockfd) {

// 此函數是在線程池創建的線程中運行。

// 與handle不在一個線程上下文中運行

recv(sockfd, rbuffer, length, 0);

parser_proto(rbuffer, length);

send(sockfd, sbuffer, length, 0);

}

int handle(int sockfd) {

//此函數在主線程 main_thread 中運行

//在此處之前,確保線程池已經啟動。

push_thread(sockfd, thread_cb); //將sockfd放到其他線程中運行。

}

  Handle函數是將sockfd處理方式放到另一個已經其他的線程中運行,如此做法,將io操作(recv,send)與epoll_wait 不在一個處理流程裡面,使得io操作(recv,send)與epoll_wait實現解耦。這就叫做IO異步操作。

優點:

1.子模塊好規劃。

2.程序性能高。

缺點:

正因為子模塊好規劃,使得模塊之間的sockfd的管理異常麻煩。每一個子線程都需要管理好sockfd,避免在IO操作的時候,sockfd出現關閉或其他異常。

  上文有提到IO同步操作,程序響應慢,IO異步操作,程序響應快。

  下面來對比一下IO同步操作與IO異步操作,代碼如下: server_mulport_epool.c   在這份代碼的486行,#if 1, 打開的時候,為IO異步操作。關閉的時候,為IO同步操作。

  接下來把我測試接入量的結果粘貼出來。 IO異步操作,每1000個連接接入的伺服器響應時間(900ms左右)。 IO同步操作,每1000個連接接入的伺服器響應時間(6500ms左右)。 epoll的IO異步操作與IO同步操作比較如下:

對比項IO同步操作IO異步操作

Socket管理管理方便多個線程共同管理

代碼邏輯程序整體邏輯清晰子模塊邏輯清晰

IO性能響應時間長,性能差響應時間短,性能好

  有沒有一種方式,有異步性能,同步的代碼邏輯。來方便編程人員對IO操作的組件呢? 有,採用一種輕量級的協程來實現。在每次send或者recv之前進行切換,再由調度器來處理epoll_wait的流程。 就是採用了基於這樣的思考,寫了NtyCo,實現了一個IO異步操作與協程結合的組件。


如果你對資料感興趣,想學習後台伺服器開發這方面的技術,後台私信【架構】免費獲取小編整理的一些資料(關注才能私信)

文章來源: https://twgreatdaily.com/zh-mo/TP_dQW8BMH2_cNUgxq61.html