基於線程池、消息隊列和epoll模型實現並發服務器架構


引言

並發是什么?企業在進行產品開發過程中為什么需要考慮這個問題?想象一下天貓的雙11和京東的618活動,一秒的點擊量就有幾十萬甚至上百萬,這么多請求一下子涌入到服務器,服務器需要對這么多的請求逐個進行消化掉,假如服務器一秒的處理能力就幾萬,那么剩下的不能及時得到處理的這些請求作何處理?總不能讓用戶界面一直等着,因此消息隊列應運而生,所有的請求都統一放入消息隊列,工作線程從消息隊列不斷的消費,消息隊列相當於一個緩沖區,可達到解藕、異步和削峰的目的。

Kafka、ActiveMQ、RabbitMQ和RockerMQ都是消息隊列的典型,每一種都有其自身的優勢和劣勢。本文我用自己編寫的Buffer類模擬消息隊列,如果是企業級需要上線的應用,一般都是基於業界已有的MQ框架上開發。

需求原型

  1. N個Client從標准輸入接收數據,然后連續不斷的發送到Server端;
  2. Server端接收來自每個Client的數據,將數據中的小寫字母全部轉換成大寫字母,其他字符保持不變,最后把轉換結果發送給對應的Client。

需求分解

  1. 拿到需求,第一步要做的就是分析需求並選擇合適的設計架構,考慮到Server需要和Client進行通信,Client來自四面八方,端對端通信自然選擇TCP,因此Server端需要能夠監聽新的連接請求和已有連接的業務請求;
  2. 又由於Server需要響應多個Client的業務請求,我們希望把業務處理交給Server端的工作線程(消費者)來做;
  3. 同時還需要一個IO線程負責監聽Socket描述符,當IO線程監聽到已有連接的業務請求時,立即把請求內容封裝成一個任務推入消息隊列尾;
  4. IO線程與工作線程互斥訪問消息隊列,當然工作線程消費一個任務或者IO線程添加一個任務都需要通知對方,也就是同步;
  5. 工作線程處理完畢后,把處理結果交給IO線程,由IO線程負責把結果發送給對應的Client,也就是IO線程與工作線程的分離,這里工作線程通知IO線程的方式我用eventfd來實現;
  6. 我們希望引入Log4cpp記錄服務端的日志,並能夠保存到文件中;
  7. 分析完這些,一個整體架構和大體的樣子在腦海中就已經形成了,接着就需要編寫設計文檔和畫流程圖、類圖和時序圖了。

詳細設計文檔

1.UML靜態類圖:

2.UML動態時序圖:

效果

1.如圖,開了三個Client,運行結果正確:

2.Server端通過Log4cpp把日志寫到文件中:

源碼獲取

https://github.com/icoty/cs_threadpool_epoll_mq

目錄結構

.
├── client					 // 客戶端Demo
│   ├── Client.cc
│   ├── Client.exe
│   ├── client.sh	 // 進入該目錄下啟動Client Demo: sh client.sh
│   ├── Log4func.cc // 引入日志模塊重新瘋轉
│   ├── Log4func.h
│   └── Makefile	 // 編譯方式:make
├── conf
│   └── my.conf // IP,Port配置文件, 從這里進行修改
├── include // 頭文件
│   ├── Configuration.hpp // 配置文件,單例類,my.conf的內存化
│   ├── FileName.hpp // 全局定義,Configuration會用到
│   ├── log // 日志模塊頭文件
│   │   └── Log4func.hpp
│   ├── net // 網絡框架模塊頭文件
│   │   ├── EpollPoller.hpp
│   │   ├── InetAddress.hpp
│   │   ├── Socket.hpp
│   │   ├── SockIO.hpp
│   │   ├── TcpConnection.hpp
│   │   └── TcpServer.hpp
│   ├── String2Upper.hpp // 工作線程轉換成大寫實際走的這里面的接口
│   ├── String2UpperServer.hpp // Server端的整個工廠
│   └── threadpool // 線程池、鎖、條件變量和消息隊列的封裝
│       ├── Buffer.hpp
│       ├── Condition.hpp
│       ├── MutexLock.hpp
│       ├── Noncopyable.hpp
│       ├── Pthread.hpp
│       ├── Task.hpp
│       └── Threadpool.hpp
├── log // Server端的日志通過Log4cpp記錄到這個文件中
│   └── log4test.log
├── Makefile // 編譯方式:make
├── README.md 
├── server // server端Demo
│   ├── server.exe
│   └── server.sh // 進入該目錄下啟動Server Demo:sh server.sh
└── src // 源文件
    ├── Configuration.cpp
    ├── log
    │   └── Log4func.cpp
    ├── main.cpp
    ├── net
    │   ├── EpollPoller.cpp
    │   ├── InetAddress.cpp
    │   ├── Socket.cpp
    │   ├── SockIO.cpp
    │   ├── TcpConnection.cpp
    │   └── TcpServer.cpp
    ├── String2Upper.cpp
    ├── String2UpperServer.cpp
    └── threadpool
    ├── Buffer.cpp
    ├── Condition.cpp
    ├── MutexLock.cpp // MutexLockGuard封裝
    ├── Pthread.cpp
    └── Threadpool.cpp

參考文獻

[1] UNIX環境高級編程第3版
[2] cpp reference
[3] UML時序圖
[4] Log4cpp官網下載
[5] Log4cpp安裝


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM