ZeroMQ分享-part1

weixin_33936401發表於2016-09-28

1. ZeroMQ背景知識(version-4.2.0)

ZMQ(以下ZeroMQ簡稱ZMQ)是一個簡單好用的、輕量級的訊息元件,像框架一樣的一個socket library。它的本質是個訊息處理佇列庫,在BSD socket基礎上做了一層封裝,將網路通訊、程式通訊和執行緒通訊抽象為統一的介面,可在多個執行緒、核心和主機盒之間彈性伸縮。

2. ZMQ的幾種模型

  • Client-server模型(http://rfc.zeromq.org/spec:41/CLISRV/)
    • client端可以連線一個或多個server。當連線到多個server時,傳送資料採用輪詢的方式;接收資料採用公平佇列按序接收。
    • client端在沒有可用連線時會阻塞,或者返回錯誤。(除非特殊配置)
    • server端可以連線0或多個client。它只能回覆client的請求資訊,應答資料會送到指定的client端。接收資料採用公平佇列按序接受。
    • server端應答時,若client端接收佇列已滿,會馬上返回(重試),不會阻塞。
    • 任何一對client-server都是全雙工的,每端都會維護兩個佇列用於收發資料。
    • 無論哪端,都不會因為不能入佇列丟棄訊息。
    • client和server套接字是執行緒安全的。
  • Publish-subscribe模型(http://rfc.zeromq.org/spec:29)
    • pub端向一系列sub或xsub端廣播資料,並且只發資料,會丟棄所有從sub端(一般是xsub)傳送過來的資料。
    • pub端在sub端接收佇列已滿的情況下,會丟棄傳送資料,不會堵塞。
    • pub端會將資料傳送到所有訂閱訊息的sub端。
    • xpub擴充了pub,可以從同時收發資料。在接收資料時,會將訂閱和非訂閱的sub端資料提交給應用程式。
    • sub端連線了任意多個pub或xpub端,只會接收資料(除向pub端傳送訂閱非訂閱外),採用公平佇列策略。
    • xsub擴充了sub,可以同時收發資料。在pub端接收佇列已滿的情況下,會丟棄所傳送的資料,不會阻塞。
  • Pipline模型(http://rfc.zeromq.org/spec:30)
    • push端採用輪詢的方式向任意多個pull端傳送資料。
    • push端在沒有可用連線時,傳送資料會阻塞,或者返回一個錯誤。
    • push端在無可用連線時不會接收應用程式的資料,因此不會丟棄資料。
    • pull端採用公平佇列的方式接收一系列push端的資料。
    • push端不能接收資料,同樣,pull端不能傳送資料。
  • Exclusive pair模型(http://rfc.zeromq.org/spec:31)
    • peer兩端互連,一個用於程式間通訊的模型
    • 當peer進入靜音模式時(緩衝區滿或未建立連線),任何一方的傳送資料都會被阻塞,因此不會丟棄資料。
  • Native模型
    • 用於TCP連線兩端進行非同步請求和應答的模型。
    • 在將接收到的資料傳遞給應用程式之前,socket會提前準備一個包含資料傳送端id的訊息。
    • 在傳送訊息時,會移除訊息的第一部分用於路由選擇,再將剩餘的訊息傳送到指定id的接收端。不能路由的訊息會返回錯誤資訊併傳送失敗。
// Create a new ZMQ context
void *context = zmq_ctx_new();
// For client-server pattern
void *client_receiver = zmq_socket(context, ZMQ_CLIENT)
void *server_receiver = zmq_socket(context, ZMQ_SERVER)
// For publish-subscribe pattern
void *pub_receiver = zmq_socket(context, ZMQ_PUB)
void *xpub_receiver = zmq_socket(context, ZMQ_XPUB)
void *sub_receiver = zmq_socket(context, ZMQ_SUB)
void *xsub_receiver = zmq_socket(context, ZMQ_XSUB)
// For pipeline pattern
void *push_receiver = zmq_socket(context, ZMQ_PUSH)
void *pull_receiver = zmq_socket(context, ZMQ_PULL)
// For exclusive pair pattern
void *pair_receiver = zmq_socket(context, ZMQ_PAIR)
int rc;
rc = zmq_bind(pair_receiver, "inproc://#1")
assert(rc == 0);
rc = zmq_connect(pair_receiver, "inproc://#1")
assert(rc == 0);

// Native pattern
void *stream_receiver = zmq_socket(context, ZMQ_STREAM)

3. I/O模型

  • (同步)阻塞式I/O模型
    • 預設情形下,所有的socket都是阻塞的。以使用者執行緒在核心進行IO操作為例,使用者在發起read系統呼叫後發生阻塞,轉到核心空間,核心等到資料到達後將資料拷到使用者空間,完成操作,執行緒返回。如下圖所示:


      3143954-9719d29821c1f34d.png
      阻塞式I/O模型
  • (同步)非阻塞式I/O模型
    • 通過將socket設定為非阻塞式的,可以在IO不能立即響應時返回一個錯誤,此時並沒有資料返回。使用者執行緒需不斷重複發起IO請求,直到資料到達後才能得到真正的資料,剩餘部分和阻塞式I/O相同。如下圖所示:


      3143954-b8c84fa0b9c13b5c.png
      非阻塞式I/O模型
    • 非阻塞式I/O模型一般很少直接使用,往往實在其他模型中使用非阻塞這一特性。因為該模型需要反覆發起IO請求,消耗了大量的CPU資源。
  • (同步)I/O複用模型
    • 建立在核心提供的select基礎之上,可以避免非阻塞式I/O模型中輪詢等待的問題。與阻塞式I/O不同,I/O複用模型會阻塞在select或poll呼叫處,直到等待的套接字中有變為可讀的。如下圖所示:


      3143954-56acec17da5d1b3f.png
      I/O複用模型
  • 非同步I/O模型
    • 非同步I/O需要作業系統更強的支援,與I/O複用不同,非同步I/O不需要使用者執行緒自行讀取資料、處理資料,而是講這些工作講給作業系統完成,只接收核心通知I/O事件已經完成。如下圖所示:


      3143954-50adc558f946bce5.png
      非同步I/O模型
    • 非同步I/O中,使用者需要自行編寫處理資料的回撥程式並遞交給作業系統。相比較於I/O複用,非同步I/O並十分常用。很多高效能併發伺服器使用I/O複用加多執行緒任務處理的架構基本可以滿足需求。

相關文章