四个服务器设计模型

游戏服务器架构:游戏服务器架构设计进化,附图及相关的案例 游戏服务器架构经历了从单体到云原生的进化历程:1)单体架构(单进程全功能)适合小规模验证;2)分区分服(水平复制)通过多服务器组解决规模问题,但导致世界割裂;3)分布式架构(功能拆分)实现无缝大世界和复杂交互,但复杂度高;4)云原生架构(容器化/Serverless)实现弹性伸缩和全球化部署。每个阶段的演进都旨在突破性能瓶颈,提升玩家体验,反映了游戏行业对规模、实时性和运维自动化的持续追求。典型案例包括早期MMO到现代《逆水寒》等游戏的技术实践。 阅读详情

五个简单TCP协议(MuduoManual.pdf P50)
muduo库网络模型使用示例(sudoku求解服务器MuduoManual.pdf P35 )

reactor(一个IO线程)

reactor + threadpool (一个IO + 多个计算线程)

multiple reactor (多个IO线程)

one loop per thread + thread pool (多个IO线程 + 计算线程池)




网络编程关注4个半事件:
连接建立
连接断开
消息到达
信息发送完毕(对于低流量的服务来说,通常不需要关注该事件)

如何实现server
1 提供一个xxxServer类
2 在该类中包含一个TcpServer对象
注册一些事件
OnConnection
OnMessage
OnWriteComplete

TcpConnection::shutdown () 内部实现,只关闭写入这一半

--------------------------------------

----------------------------------------

下面的程序都是用来解 数独 的,数独的实现如下

sudoku.h

  1. #ifndef MUDUO_EXAMPLES_SUDOKU_SUDOKU_H  
  2. #define MUDUO_EXAMPLES_SUDOKU_SUDOKU_H  
  3.   
  4.   
  5. #include <muduo/base/Types.h>  
  6.   
  7. // FIXME, use (const char*, len) for saving memory copying.  
  8. muduo::string solveSudoku(const muduo::string& puzzle);  
  9. const int kCells = 81;  
  10.   
  11. #endif  


sudoku.cc


  1. #include "sudoku.h"  
  2.   
  3. #include <vector>  
  4. #include <assert.h>  
  5. #include <string.h>  
  6.   
  7. using namespace muduo;  
  8.   
  9. struct Node;  
  10. typedef Node Column;  
  11. struct Node  
  12. {  
  13.     Node* left;  
  14.     Node* right;  
  15.     Node* up;  
  16.     Node* down;  
  17.     Column* col;  
  18.     int name;  
  19.     int size;  
  20. };  
  21.   
  22. const int kMaxNodes = 1 + 81*4 + 9*9*9*4;  
  23. const int kMaxColumns = 400;  
  24. const int kRow = 100, kCol = 200, kBox = 300;  
  25.   
  26. class SudokuSolver  
  27. {  
  28.  public:  
  29.     SudokuSolver(int board[kCells])  
  30.       : inout_(board),  
  31.         cur_node_(0)  
  32.     {  
  33.         stack_.reserve(100);  
  34.   
  35.         root_ = new_column();  
  36.         root_->left = root_->right = root_;  
  37.         memset(columns_, 0, sizeof(columns_));  
  38.   
  39.         bool rows[kCells][10] = { {false} };  
  40.         bool cols[kCells][10] = { {false} };  
  41.         bool boxes[kCells][10] = { {false} };  
  42.   
  43.         for (int i = 0; i < kCells; ++i) {  
  44.             int row = i / 9;  
  45.             int col = i % 9;  
  46.             int box = row/3*3 + col/3;  
  47.             int val = inout_[i];  
  48.             rows[row][val] = true;  
  49.             cols[col][val] = true;  
  50.             boxes[box][val] = true;  
  51.         }  
  52.   
  53.         for (int i = 0; i < kCells; ++i) {  
  54.             if (inout_[i] == 0) {  
  55.                 append_column(i);  
  56.             }  
  57.         }  
  58.   
  59.         for (int i = 0; i < 9; ++i) {  
  60.             for (int v = 1; v < 10; ++v) {  
  61.                 if (!rows[i][v])  
  62.                     append_column(get_row_col(i, v));  
  63.                 if (!cols[i][v])  
  64.                     append_column(get_col_col(i, v));  
  65.                 if (!boxes[i][v])  
  66.                     append_column(get_box_col(i, v));  
  67.             }  
  68.         }  
  69.   
  70.         for (int i = 0; i < kCells; ++i) {  
  71.             if (inout_[i] == 0) {  
  72.                 int row = i / 9;  
  73.                 int col = i % 9;  
  74.                 int box = row/3*3 + col/3;  
  75.                 //int val = inout[i];  
  76.                 for (int v = 1; v < 10; ++v) {  
  77.                     if (!(rows[row][v] || cols[col][v] || boxes[box][v])) {  
  78.                         Node* n0 = new_row(i);  
  79.                         Node* nr = new_row(get_row_col(row, v));  
  80.                         Node* nc = new_row(get_col_col(col, v));  
  81.                         Node* nb = new_row(get_box_col(box, v));  
  82.                         put_left(n0, nr);  
  83.                         put_left(n0, nc);  
  84.                         put_left(n0, nb);  
  85.                     }  
  86.                 }  
  87.             }  
  88.         }  
  89.     }  
  90.   
  91.     bool solve()  
  92.     {  
  93.         if (root_->left == root_) {  
  94.             for (size_t i = 0; i < stack_.size(); ++i) {  
  95.                 Node* n = stack_[i];  
  96.                 int cell = -1;  
  97.                 int val = -1;  
  98.                 while (cell == -1 || val == -1) {  
  99.                     if (n->name < 100)  
  100.                         cell = n->name;  
  101.                     else  
  102.                         val = n->name % 10;  
  103.                     n = n->right;  
  104.                 }  
  105.   
  106.                 //assert(cell != -1 && val != -1);  
  107.                 inout_[cell] = val;  
  108.             }  
  109.             return true;  
  110.         }  
  111.   
  112.         Column* const col = get_min_column();  
  113.         cover(col);  
  114.         for (Node* row = col->down; row != col; row = row->down) {  
  115.             stack_.push_back(row);  
  116.             for (Node* j = row->right; j != row; j = j->right) {  
  117.                 cover(j->col);  
  118.             }  
  119.             if (solve()) {  
  120.                 return true;  
  121.             }  
  122.             stack_.pop_back();  
  123.             for (Node* j = row->left; j != row; j = j->left) {  
  124.                 uncover(j->col);  
  125.             }  
  126.         }  
  127.         uncover(col);  
  128.         return false;  
  129.     }  
  130.   
  131.  private:  
  132.   
  133.     Column* root_;  
  134.     int*    inout_;  
  135.     Column* columns_[400];  
  136.     std::vector<Node*> stack_;  
  137.     Node    nodes_[kMaxNodes];  
  138.     int     cur_node_;  
  139.   
  140.     Column* new_column(int n = 0)  
  141.     {  
  142.         assert(cur_node_ < kMaxNodes);  
  143.         Column* c = &nodes_[cur_node_++];  
  144.         memset(c, 0, sizeof(Column));  
  145.         c->left = c;  
  146.         c->right = c;  
  147.         c->up = c;  
  148.         c->down = c;  
  149.         c->col = c;  
  150.         c->name = n;  
  151.         return c;  
  152.     }  
  153.   
  154.     void append_column(int n)  
  155.     {  
  156.         assert(columns_[n] == NULL);  
  157.   
  158.         Column* c = new_column(n);  
  159.         put_left(root_, c);  
  160.         columns_[n] = c;  
  161.     }  
  162.   
  163.     Node* new_row(int col)  
  164.     {  
  165.         assert(columns_[col] != NULL);  
  166.         assert(cur_node_ < kMaxNodes);  
  167.   
  168.         Node* r = &nodes_[cur_node_++];  
  169.   
  170.         //Node* r = new Node;  
  171.         memset(r, 0, sizeof(Node));  
  172.         r->left = r;  
  173.         r->right = r;  
  174.         r->up = r;  
  175.         r->down = r;  
  176.         r->name = col;  
  177.         r->col = columns_[col];  
  178.         put_up(r->col, r);  
  179.         return r;  
  180.     }  
  181.   
  182.     int get_row_col(int row, int val)  
  183.     {  
  184.         return kRow+row*10+val;  
  185.     }  
  186.   
  187.     int get_col_col(int col, int val)  
  188.     {  
  189.         return kCol+col*10+val;  
  190.     }  
  191.   
  192.     int get_box_col(int box, int val)  
  193.     {  
  194.         return kBox+box*10+val;  
  195.     }  
  196.   
  197.     Column* get_min_column()  
  198.     {  
  199.         Column* c = root_->right;  
  200.         int min_size = c->size;  
  201.         if (min_size > 1) {  
  202.             for (Column* cc = c->right; cc != root_; cc = cc->right) {  
  203.                 if (min_size > cc->size) {  
  204.                     c = cc;  
  205.                     min_size = cc->size;  
  206.                     if (min_size <= 1)  
  207.                         break;  
  208.                 }  
  209.             }  
  210.         }  
  211.         return c;  
  212.     }  
  213.   
  214.     void cover(Column* c)  
  215.     {  
  216.         c->right->left = c->left;  
  217.         c->left->right = c->right;  
  218.         for (Node* row = c->down; row != c; row = row->down) {  
  219.             for (Node* j = row->right; j != row; j = j->right) {  
  220.                 j->down->up = j->up;  
  221.                 j->up->down = j->down;  
  222.                 j->col->size--;  
  223.             }  
  224.         }  
  225.     }  
  226.   
  227.     void uncover(Column* c)  
  228.     {  
  229.         for (Node* row = c->up; row != c; row = row->up) {  
  230.             for (Node* j = row->left; j != row; j = j->left) {  
  231.                 j->col->size++;  
  232.                 j->down->up = j;  
  233.                 j->up->down = j;  
  234.             }  
  235.         }  
  236.         c->right->left = c;  
  237.         c->left->right = c;  
  238.     }  
  239.   
  240.     void put_left(Column* old, Column* nnew)  
  241.     {  
  242.         nnew->left = old->left;  
  243.         nnew->right = old;  
  244.         old->left->right = nnew;  
  245.         old->left = nnew;  
  246.     }  
  247.   
  248.     void put_up(Column* old, Node* nnew)  
  249.     {  
  250.         nnew->up = old->up;  
  251.         nnew->down = old;  
  252.         old->up->down = nnew;  
  253.         old->up = nnew;  
  254.         old->size++;  
  255.         nnew->col = old;  
  256.     }  
  257. };  
  258.   
  259. string solveSudoku(const string& puzzle)  
  260. {  
  261.   assert(puzzle.size() == implicit_cast<size_t>(kCells));  
  262.   
  263.   string result = "NoSolution";  
  264.   
  265.   int board[kCells] = { 0 };  
  266.   bool valid = true;  
  267.   for (int i = 0; i < kCells; ++i)  
  268.   {  
  269.     board[i] = puzzle[i] - '0';  
  270.     valid = valid && (0 <= board[i] && board[i] <= 9);  
  271.   }  
  272.   
  273.   if (valid)  
  274.   {  
  275.     SudokuSolver s(board);  
  276.     if (s.solve())  
  277.     {  
  278.       result.clear();  
  279.       result.resize(kCells);  
  280.       for (int i = 0; i < kCells; ++i)  
  281.       {  
  282.         result[i] = static_cast<char>(board[i] + '0');  
  283.       }  
  284.     }  
  285.   }  
  286.   return result;  
  287. }  






reactor模型

只有一个IO线程:
    这个IO线程既负责listenfd也负责connfd

  1. #include "sudoku.h"  
  2.   
  3. #include <muduo/base/Atomic.h>  
  4. #include <muduo/base/Logging.h>  
  5. #include <muduo/base/Thread.h>  
  6. #include <muduo/net/EventLoop.h>  
  7. #include <muduo/net/InetAddress.h>  
  8. #include <muduo/net/TcpServer.h>  
  9.   
  10. #include <boost/bind.hpp>  
  11.   
  12. #include <utility>  
  13.   
  14. #include <mcheck.h>  
  15. #include <stdio.h>  
  16. #include <unistd.h>  
  17.   
  18. using namespace muduo;  
  19. using namespace muduo::net;  
  20.   
  21. class SudokuServer  
  22. {  
  23.  public:  
  24.   SudokuServer(EventLoop* loop, const InetAddress& listenAddr)  
  25.     : loop_(loop),  
  26.       server_(loop, listenAddr, "SudokuServer"),  
  27.       startTime_(Timestamp::now())  
  28.   {  
  29.     server_.setConnectionCallback(  
  30.         boost::bind(&SudokuServer::onConnection, this, _1));  
  31.     server_.setMessageCallback(  
  32.         boost::bind(&SudokuServer::onMessage, this, _1, _2, _3));  
  33.   }  
  34.   
  35.   void start()  
  36.   {  
  37.     server_.start();  
  38.   }  
  39.   
  40.  private:  
  41.   void onConnection(const TcpConnectionPtr& conn)  
  42.   {  
  43.     LOG_TRACE << conn->peerAddress().toIpPort() << " -> "  
  44.         << conn->localAddress().toIpPort() << " is "  
  45.         << (conn->connected() ? "UP" : "DOWN");  
  46.   }  
  47.   
  48.   void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)  
  49.   {  
  50.     LOG_DEBUG << conn->name();  
  51.     size_t len = buf->readableBytes();  
  52.     while (len >= kCells + 2)  
  53.     {  
  54.       const char* crlf = buf->findCRLF();  
  55.       if (crlf)  
  56.       {  
  57.         string request(buf->peek(), crlf);  
  58.         buf->retrieveUntil(crlf + 2);  
  59.         len = buf->readableBytes();  
  60.         if (!processRequest(conn, request))  
  61.         {  
  62.           conn->send("Bad Request!\r\n");  
  63.           conn->shutdown();  
  64.           break;  
  65.         }  
  66.       }  
  67.       else if (len > 100) // id + ":" + kCells + "\r\n"  
  68.       {  
  69.         conn->send("Id too long!\r\n");  
  70.         conn->shutdown();  
  71.         break;  
  72.       }  
  73.       else  
  74.       {  
  75.         break;  
  76.       }  
  77.     }  
  78.   }  
  79.   
  80.   bool processRequest(const TcpConnectionPtr& conn, const string& request)  
  81.   {  
  82.     string id;  
  83.     string puzzle;  
  84.     bool goodRequest = true;  
  85.   
  86.     string::const_iterator colon = find(request.begin(), request.end(), ':');  
  87.     if (colon != request.end())  
  88.     {  
  89.       id.assign(request.begin(), colon);  
  90.       puzzle.assign(colon+1, request.end());  
  91.     }  
  92.     else  
  93.     {  
  94.       puzzle = request;  
  95.     }  
  96.   
  97.     if (puzzle.size() == implicit_cast<size_t>(kCells))  
  98.     {  
  99.       LOG_DEBUG << conn->name();  
  100.       string result = solveSudoku(puzzle);  
  101.       if (id.empty())  
  102.       {  
  103.         conn->send(result+"\r\n");  
  104.       }  
  105.       else  
  106.       {  
  107.         conn->send(id+":"+result+"\r\n");  
  108.       }  
  109.     }  
  110.     else  
  111.     {  
  112.       goodRequest = false;  
  113.     }  
  114.     return goodRequest;  
  115.   }  
  116.   
  117.   EventLoop* loop_;  
  118.   TcpServer server_;  
  119.   Timestamp startTime_;  
  120. };  
  121.   
  122. int main(int argc, char* argv[])  
  123. {  
  124.   LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid();  
  125.   EventLoop loop;  
  126.   InetAddress listenAddr(9981);  
  127.   SudokuServer server(&loop, listenAddr);  
  128.   
  129.   server.start();  
  130.   
  131.   loop.loop();  
  132. }  





multiple reactor
IO线程的数目多个
EventLoopThreadPoll IO线程池
直接设置server_.setThreadNum(numThreads)就OK了

main reactor 负责listenfd accept
sub reactor  负责connfd
使用roundbin轮叫策略
来一个连接,就选择下一个EventLoop,这样让多个连接分配给若干个EventLoop来处理,
而每个EventLoop属于一个IO线程,也就意味着,多个连接分配给若干个IO线程来处理。

  1. include "sudoku.h"  
  2.   
  3. #include <muduo/base/Atomic.h>  
  4. #include <muduo/base/Logging.h>  
  5. #include <muduo/base/Thread.h>  
  6. #include <muduo/net/EventLoop.h>  
  7. #include <muduo/net/InetAddress.h>  
  8. #include <muduo/net/TcpServer.h>  
  9.   
  10. #include <boost/bind.hpp>  
  11.   
  12. #include <utility>  
  13.   
  14. #include <mcheck.h>  
  15. #include <stdio.h>  
  16. #include <unistd.h>  
  17.   
  18. using namespace muduo;  
  19. using namespace muduo::net;  
  20.   
  21. class SudokuServer  
  22. {  
  23.  public:  
  24.   SudokuServer(EventLoop* loop, const InetAddress& listenAddr, int numThreads)  
  25.     : loop_(loop),  
  26.       server_(loop, listenAddr, "SudokuServer"),  
  27.       numThreads_(numThreads),  
  28.       startTime_(Timestamp::now())  
  29.   {  
  30.     server_.setConnectionCallback(  
  31.         boost::bind(&SudokuServer::onConnection, this, _1));  
  32.     server_.setMessageCallback(  
  33.         boost::bind(&SudokuServer::onMessage, this, _1, _2, _3));  
  34.     server_.setThreadNum(numThreads);  
  35.   }  
  36.   
  37.   void start()  
  38.   {  
  39.     LOG_INFO << "starting " << numThreads_ << " threads.";  
  40.     server_.start();  
  41.   }  
  42.   
  43.  private:  
  44.   void onConnection(const TcpConnectionPtr& conn)  
  45.   {  
  46.     LOG_TRACE << conn->peerAddress().toIpPort() << " -> "  
  47.         << conn->localAddress().toIpPort() << " is "  
  48.         << (conn->connected() ? "UP" : "DOWN");  
  49.   }  
  50.   
  51.   void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)  
  52.   {  
  53.     LOG_DEBUG << conn->name();  
  54.     size_t len = buf->readableBytes();  
  55.     while (len >= kCells + 2)  
  56.     {  
  57.       const char* crlf = buf->findCRLF();  
  58.       if (crlf)  
  59.       {  
  60.         string request(buf->peek(), crlf);  
  61.         buf->retrieveUntil(crlf + 2);  
  62.         len = buf->readableBytes();  
  63.         if (!processRequest(conn, request))  
  64.         {  
  65.           conn->send("Bad Request!\r\n");  
  66.           conn->shutdown();  
  67.           break;  
  68.         }  
  69.       }  
  70.       else if (len > 100) // id + ":" + kCells + "\r\n"  
  71.       {  
  72.         conn->send("Id too long!\r\n");  
  73.         conn->shutdown();  
  74.         break;  
  75.       }  
  76.       else  
  77.       {  
  78.         break;  
  79.       }  
  80.     }  
  81.   }  
  82.   
  83.   bool processRequest(const TcpConnectionPtr& conn, const string& request)  
  84.   {  
  85.     string id;  
  86.     string puzzle;  
  87.     bool goodRequest = true;  
  88.   
  89.     string::const_iterator colon = find(request.begin(), request.end(), ':');  
  90.     if (colon != request.end())  
  91.     {  
  92.       id.assign(request.begin(), colon);  
  93.       puzzle.assign(colon+1, request.end());  
  94.     }  
  95.     else  
  96.     {  
  97.       puzzle = request;  
  98.     }  
  99.   
  100.     if (puzzle.size() == implicit_cast<size_t>(kCells))  
  101.     {  
  102.       LOG_DEBUG << conn->name();  
  103.       string result = solveSudoku(puzzle);  
  104.       if (id.empty())  
  105.       {  
  106.         conn->send(result+"\r\n");  
  107.       }  
  108.       else  
  109.       {  
  110.         conn->send(id+":"+result+"\r\n");  
  111.       }  
  112.     }  
  113.     else  
  114.     {  
  115.       goodRequest = false;  
  116.     }  
  117.     return goodRequest;  
  118.   }  
  119.   
  120.   EventLoop* loop_;  
  121.   TcpServer server_;  
  122.   int numThreads_;  
  123.   Timestamp startTime_;  
  124. };  
  125.   
  126. int main(int argc, char* argv[])  
  127. {  
  128.   LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid();  
  129.   int numThreads = 0;  
  130.   if (argc > 1)  
  131.   {  
  132.     numThreads = atoi(argv[1]);  
  133.   }  
  134.   EventLoop loop;  
  135.   InetAddress listenAddr(9981);  
  136.   SudokuServer server(&loop, listenAddr, numThreads);  
  137.   
  138.   server.start();  
  139.   
  140.   loop.loop();  
  141. }  





reactor + threadPool

一个IO线程,多个计算thread的模式
EventLoop + threadpool + numThreads_

  1. include "sudoku.h"  
  2.   
  3. #include <muduo/base/Atomic.h>  
  4. #include <muduo/base/Logging.h>  
  5. #include <muduo/base/Thread.h>  
  6. #include <muduo/base/ThreadPool.h>  
  7. #include <muduo/net/EventLoop.h>  
  8. #include <muduo/net/InetAddress.h>  
  9. #include <muduo/net/TcpServer.h>  
  10.   
  11. #include <boost/bind.hpp>  
  12.   
  13. #include <utility>  
  14.   
  15. #include <mcheck.h>  
  16. #include <stdio.h>  
  17. #include <unistd.h>  
  18.   
  19. using namespace muduo;  
  20. using namespace muduo::net;  
  21.   
  22. class SudokuServer  
  23. {  
  24.  public:  
  25.   SudokuServer(EventLoop* loop, const InetAddress& listenAddr, int numThreads)  
  26.     : loop_(loop),  
  27.       server_(loop, listenAddr, "SudokuServer"),  
  28.       numThreads_(numThreads),  
  29.       startTime_(Timestamp::now())  
  30.   {  
  31.     server_.setConnectionCallback(  
  32.         boost::bind(&SudokuServer::onConnection, this, _1));  
  33.     server_.setMessageCallback(  
  34.         boost::bind(&SudokuServer::onMessage, this, _1, _2, _3));  
  35.   }  
  36.   
  37.   void start()  
  38.   {  
  39.     LOG_INFO << "starting " << numThreads_ << " threads.";  
  40.     threadPool_.start(numThreads_);//线程池的线程个数  
  41.     server_.start();  
  42.   }  
  43.   
  44.  private:  
  45.   void onConnection(const TcpConnectionPtr& conn)  
  46.   {  
  47.     LOG_TRACE << conn->peerAddress().toIpPort() << " -> "  
  48.         << conn->localAddress().toIpPort() << " is "  
  49.         << (conn->connected() ? "UP" : "DOWN");  
  50.   }  
  51.   
  52.   void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)  
  53.   {  
  54.     LOG_DEBUG << conn->name();  
  55.     size_t len = buf->readableBytes();  
  56.     while (len >= kCells + 2)  
  57.     {  
  58.       const char* crlf = buf->findCRLF();  
  59.       if (crlf)  
  60.       {  
  61.         string request(buf->peek(), crlf);  
  62.         buf->retrieveUntil(crlf + 2);  
  63.         len = buf->readableBytes();  
  64.         if (!processRequest(conn, request))  
  65.         {  
  66.           conn->send("Bad Request!\r\n");  
  67.           conn->shutdown();  
  68.           break;  
  69.         }  
  70.       }  
  71.       else if (len > 100) // id + ":" + kCells + "\r\n"  
  72.       {  
  73.         conn->send("Id too long!\r\n");  
  74.         conn->shutdown();  
  75.         break;  
  76.       }  
  77.       else  
  78.       {  
  79.         break;  
  80.       }  
  81.     }  
  82.   }  
  83.   
  84.   bool processRequest(const TcpConnectionPtr& conn, const string& request)  
  85.   {  
  86.     string id;  
  87.     string puzzle;  
  88.     bool goodRequest = true;  
  89.   
  90.     string::const_iterator colon = find(request.begin(), request.end(), ':');  
  91.     if (colon != request.end())  
  92.     {  
  93.       id.assign(request.begin(), colon);  
  94.       puzzle.assign(colon+1, request.end());  
  95.     }  
  96.     else  
  97.     {  
  98.       puzzle = request;  
  99.     }  
  100.     /*计算线程中的线程进行处理*/  
  101.     if (puzzle.size() == implicit_cast<size_t>(kCells))  
  102.     {  
  103.       threadPool_.run(boost::bind(&solve, conn, puzzle, id));  
  104.     }  
  105.     else  
  106.     {  
  107.       goodRequest = false;  
  108.     }  
  109.     return goodRequest;  
  110.   }  
  111.   
  112.   static void solve(const TcpConnectionPtr& conn,  
  113.                     const string& puzzle,  
  114.                     const string& id)  
  115.   {  
  116.     LOG_DEBUG << conn->name();  
  117.     string result = solveSudoku(puzzle);  
  118.     /*这里处理完数据后,conn->send() 还是在IO线程中发送,而不是 
  119. 在计算线程中处理 
  120.     */  
  121.     if (id.empty())  
  122.     {  
  123.       conn->send(result+"\r\n");  
  124.     }  
  125.     else  
  126.     {  
  127.       conn->send(id+":"+result+"\r\n");  
  128.     }  
  129.   }  
  130.   
  131.   EventLoop* loop_;  
  132.   TcpServer server_;  
  133.   ThreadPool threadPool_; //计算线程池  
  134.   int numThreads_;  
  135.   Timestamp startTime_;  
  136. };  
  137.   
  138. int main(int argc, char* argv[])  
  139. {  
  140.   LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid();  
  141.   int numThreads = 0;  
  142.   if (argc > 1)  
  143.   {  
  144.     numThreads = atoi(argv[1]);  
  145.   }  
  146.   EventLoop loop;  
  147.   InetAddress listenAddr(9981);  
  148.   SudokuServer server(&loop, listenAddr, numThreads);  
  149.   
  150.   server.start();  
  151.   
  152.   loop.loop();  
  153. }  






4 、multiple reactors+threadpool

EventLoopThreadPoll + threadpool + IOnumThreads_ + ThreadPoolnumThreads_

  1. include "sudoku.h"  
  2.   
  3. #include <muduo/base/Atomic.h>  
  4. #include <muduo/base/Logging.h>  
  5. #include <muduo/base/Thread.h>  
  6. #include <muduo/base/ThreadPool.h>  
  7. #include <muduo/net/EventLoop.h>  
  8. #include <muduo/net/InetAddress.h>  
  9. #include <muduo/net/TcpServer.h>  
  10.   
  11. #include <boost/bind.hpp>  
  12.   
  13. #include <utility>  
  14.   
  15. #include <mcheck.h>  
  16. #include <stdio.h>  
  17. #include <unistd.h>  
  18.   
  19. using namespace muduo;  
  20. using namespace muduo::net;  
  21.   
  22. class SudokuServer  
  23. {  
  24.  public:  
  25.   SudokuServer(EventLoop* loop, const InetAddress& listenAddr, int numThreads)  
  26.     : loop_(loop),  
  27.       server_(loop, listenAddr, "SudokuServer"),  
  28.       numThreads_(numThreads),  
  29.       startTime_(Timestamp::now())  
  30.   {  
  31.     server_.setConnectionCallback(  
  32.         boost::bind(&SudokuServer::onConnection, this, _1));  
  33.     server_.setMessageCallback(  
  34.         boost::bind(&SudokuServer::onMessage, this, _1, _2, _3));  
  35.      server_.setThreadNum(numThreads);//IO线程池的初始化  
  36.   }  
  37.   
  38.   void start()  
  39.   {  
  40.     LOG_INFO << "starting " << numThreads_ << " threads.";  
  41.     threadPool_.start(numThreads_);  
  42.     server_.start();  
  43.   }  
  44.   
  45.  private:  
  46.   void onConnection(const TcpConnectionPtr& conn)  
  47.   {  
  48.     LOG_TRACE << conn->peerAddress().toIpPort() << " -> "  
  49.         << conn->localAddress().toIpPort() << " is "  
  50.         << (conn->connected() ? "UP" : "DOWN");  
  51.   }  
  52.   
  53.   void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)  
  54.   {  
  55.     LOG_DEBUG << conn->name();  
  56.     size_t len = buf->readableBytes();  
  57.     while (len >= kCells + 2)  
  58.     {  
  59.       const char* crlf = buf->findCRLF();  
  60.       if (crlf)  
  61.       {  
  62.         string request(buf->peek(), crlf);  
  63.         buf->retrieveUntil(crlf + 2);  
  64.         len = buf->readableBytes();  
  65.         if (!processRequest(conn, request))  
  66.         {  
  67.           conn->send("Bad Request!\r\n");  
  68.           conn->shutdown();  
  69.           break;  
  70.         }  
  71.       }  
  72.       else if (len > 100) // id + ":" + kCells + "\r\n"  
  73.       {  
  74.         conn->send("Id too long!\r\n");  
  75.         conn->shutdown();  
  76.         break;  
  77.       }  
  78.       else  
  79.       {  
  80.         break;  
  81.       }  
  82.     }  
  83.   }  
  84.   
  85.   bool processRequest(const TcpConnectionPtr& conn, const string& request)  
  86.   {  
  87.     string id;  
  88.     string puzzle;  
  89.     bool goodRequest = true;  
  90.   
  91.     string::const_iterator colon = find(request.begin(), request.end(), ':');  
  92.     if (colon != request.end())  
  93.     {  
  94.       id.assign(request.begin(), colon);  
  95.       puzzle.assign(colon+1, request.end());  
  96.     }  
  97.     else  
  98.     {  
  99.       puzzle = request;  
  100.     }  
  101.     /*计算线程中的线程进行处理*/  
  102.     if (puzzle.size() == implicit_cast<size_t>(kCells))  
  103.     {  
  104.       threadPool_.run(boost::bind(&solve, conn, puzzle, id));  
  105.     }  
  106.     else  
  107.     {  
  108.       goodRequest = false;  
  109.     }  
  110.     return goodRequest;  
  111.   }  
  112.   
  113.   static void solve(const TcpConnectionPtr& conn,  
  114.                     const string& puzzle,  
  115.                     const string& id)  
  116.   {  
  117.     LOG_DEBUG << conn->name();  
  118.     string result = solveSudoku(puzzle);  
  119.     /*这里处理完数据后,conn->send() 还是在IO线程中发送,而不是 
  120. 在计算线程中处理 
  121.     */  
  122.     if (id.empty())  
  123.     {  
  124.       conn->send(result+"\r\n");  
  125.     }  
  126.     else  
  127.     {  
  128.       conn->send(id+":"+result+"\r\n");  
  129.     }  
  130.   }  
  131.   
  132.   EventLoop* loop_;  
  133.   TcpServer server_;  
  134.   ThreadPool threadPool_;  
  135.   int numThreads_;  
  136.   Timestamp startTime_;  
  137. };  
  138.   
  139. int main(int argc, char* argv[])  
  140. {  
  141.   LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid();  
  142.   int numThreads = 0;  
  143.   if (argc > 1)  
  144.   {  
  145.     numThreads = atoi(argv[1]);  
  146.   }  
  147.   EventLoop loop;  
  148.   InetAddress listenAddr(9981);  
  149.   SudokuServer server(&loop, listenAddr, numThreads);  
  150.   
  151.   server.start();  
  152.   
  153.   loop.loop();  
  154. }  


--------------------------------------------------


sudoKu 求解服务器,既是一个IO密集型,又是一个计算密集型的服务

IO线程池 + 计算线程池
计算时间如果比较久,就会使得IO线程阻塞,IO线程很快就用尽,就不处理大量的并发连接
一个IO线程+计算线程池


使用muduo 库来编程还是比较容易的,有兴趣的朋友可以试一下!



muduo库手册 感觉不错,分享一下,它的使用手册,希望它对你有帮助 立即下载

相关推荐

Muduo_网络库使用手册

Muduo_网络库使用手册

第六天:通过python代码控制越疆机械臂完成抓取投递整个过程

文章目录一、前言二、机械臂的运动逻辑(直接上代码讲解,具体请看注释)三、总结 一、前言 有很多小伙伴问我怎么样控制机械臂完成整个过程,其实经过上一篇博文的探究,这个问题其实很容易就可以解决。把他们每个位姿连起来执行不过就行了吗,因为我们做这个的场景就是抓取——>放置——>投递,整个场景和动作很固定。 所以我们完全用不上所谓的路径规划,说实话,路径规划这玩意弄起来也不简单。所以这里就不去讨论这块问题。 后面有机会,等我去更加深入的自学一下,有机会可以交流! 二、机械臂的运动逻辑(直接上代码讲解,

瓜洲的博客 1万+

delphi线程池单元文件uThreadPool.pas

delphi线程池单元文件uThreadPool.pas,用法如下 type TRecvCommDataWorkItem=class(TWorkItem) public // updatetime,addtime:TDateTime; // orderid,ordertype,urljson,loadcount,savepath:string; url,Filename:string; total,order:Integer; _orderid:string; failedcount:Integer; IFCoverFile:Boolean; // 线程处理请求时触发的事件 procedure DealwithCommRecvData(Sender: TThreadsPool; WorkItem: TWorkItem; aThread: TProcessorThread); // 线程初始化时触发的事件 procedure TProcessorThreadInitializing(Sender: TThreadsPool; aThread:TProcessorThread); // 线程结束时触发的事件 procedure TProcessorThreadFinalizing(Sender: TThreadsPool; aThread:TProcessorThread); //任务队列空时触发的事件 procedure TQueueEmpty(Sender: TThreadsPool; EmptyKind: TEmptyKind); end; 先声明一个类 然后用法 FThreadPool := TThreadsPool.Create(nil); // 创建线程池 FThreadPool.ThreadsMin := 10; // 初始工作线程数 FThreadPool.ThreadsMax := 100; // 最大允许工作线程数 AWorkItem := TRecvCommDataWorkItem.Create; ISAllOverLoad:=False; AWorkItem.url:=urljson; AWorkItem.order:=i; AWorkItem.total:=JA.Count; AWorkItem.Filename:=savefilepath; AWorkItem._orderid:=orderid; AWorkItem.IFCoverFile:=IFCoverFile; FThreadPool.AddRequest(AWorkItem,True); // 向线程池分配一个任务 FThreadPool.OnProcessRequest := AWorkItem.DealwithCommRecvData; FThreadPool.OnThreadInitializing := AWorkItem.TProcessorThreadInitializing; FThreadPool.OnThreadFinalizing := AWorkItem.TProcessorThreadFinalizing; FThreadPool.OnQueueEmpty := AWorkItem.TQueueEmpty; 仔细看下线程池单元的函数说明轻松搞定。 procedure TRecvCommDataWorkItem.TQueueEmpty(Sender: TThreadsPool; EmptyKind: TEmptyKind); begin if EmptyKind=ekProcessingFinished then begin try if Assigned(geturl) then //存在的bug 如果下载文件存在的不行 begin //Sleep(200); //激活线程可能会发生在 休眠之前!! ISAl

常用的服务器模型

常用的服务器模型

weixin_48150445的博客 661

如何利用ADS中的耦合线设计带通滤波器并转换为HFSS模型

耦合线带通滤波器是一种常用的微带滤波器形式,由多个长度为四分之一波长的耦合线节组成,在所需频点形成谐振,构成滤波器的频率响应特性。首先我们先来了解一下耦合线带通滤波器的电路结构,见下图:包括两对耦合线,中间为公共连接点,其中两对耦合线的偶奇模阻抗为(Z1e,Z1o)和(Z2e,Z2o,端口输入阻抗为Z0)。2.指定一个初始奇偶模值,ADS优化出一个合适的结果,通过奇偶模值利用linecalc工具计算出微带耦合线尺寸。假设中心频率为2GHz,则对应频点的电长度为。5.运行结果,两个软件仿真结果基本一致。

auqihc的博客 2229

Matlab实战:水声信号DEMON谱分析从入门到精通(附完整源码)

本文详细介绍了使用Matlab进行水声信号DEMON谱分析的完整流程,从基础理论到实战应用,涵盖信号预处理、包络提取、频谱分析等关键步骤,并附完整源码。特别针对水声通信中的低信噪比环境,提供了多种优化方案和性能提升技巧,帮助读者快速掌握这一水下目标检测的核心技术。

gamma的博客 296

读懂华为V模型:连接业务与IT,用V模型指导服务化设计

以供应链仓储的入库业务为例,该业务包含了原材料仓人库、成品仓人库、分销中心人库、国家中心仓人库、站点人库等诸多场景,但所有场景都是围绕“入库单”这个业务对象的“增、删、改、查”,所以要围绕业务对象进行业务和IT 设计,包括业务活动、活动的输人和输出、作业步骤、作业规则和算法等(见下图),形成可被各种场景调用的服务。对这家面馆而言,牛肉面是产品,不同类型的面条、牛肉、西红柿是一个个预制好的组件,对企业IT而言,业务应用是产品,我们也可以通过预制好各种服务化的组件(见图5-7)来提升对业务的响应速度和灵活性。

数字化藏经阁 3544

C语言实现TCP聊天室:Socket编程与并发模型实战解析

网络编程是构建分布式系统的核心技术,其核心在于理解进程间通信的底层机制。TCP/IP协议栈提供了可靠的字节流传输服务,通过Socket API这一抽象接口,开发者可以在应用层建立连接、收发数据。为了高效处理多个并发连接,服务器需要采用多线程或I/O复用模型,前者为每个连接创建独立线程,逻辑清晰但资源消耗大;后者通过select、poll或epoll等系统调用监控多个文件描述符,显著提升并发能力。这些技术是构建高性能网络服务(如即时通讯、在线游戏服务器)的基础。本文以C语言实现一个简洁的TCP聊天室为例,详细

weixin_32642109的博客 292

基于muduo+mysql+jsoncpp的简易HTTPWebServer

该WebServer基于muduo网络库与HTTP协议栈,muduo网络库主要负责socket连接的管理以及数据的管理,而在连接建立时,在连接的上下文context中初始化HTTPcontext,然后在接收到HTTP请求时,通过调用onMessage回调函数将接收到的数据保存在连接的HTTPcontext中并且处理HTTP请求,然后通过onRequest回调函数根据请求生成HTTP响应,在哈希表(与数据库同步)中获取所有车牌返回对应的HTML页面。

m0_68224121的博客 1256

Linux Kernel入门到精通系列讲解 - 总目录

在学习Linux Kernel之前,需要先准备开发环境,方便后续进行开发。可以根据自身情况,选择性阅读该章节。学习该章节之前,你应该了解一下网络概念,明白为什么要讲这些,才能更有方向的去理解学习。Linux ARM平台开发系列讲解(设备树篇) 4.1.1 设备树的原理详解。本项目完全开源,仅供学习使用,如需其他用途,请与本人联系。),本章节你可以学习到。摄像头V4L2子系统、Linux kernel常用的子系统等,ARM架构、RISCV架构、Uboot等。

DSMGUOGUO的博客 1万+

【Bug已解决】npm install -g @anthropic-ai/claude-code 出现 EACCES: permission denied 解决方案

方案适用场景推荐指数nvm 管理 Node长期开发环境,一次配置永久生效⭐⭐⭐⭐⭐修改 npm 全局目录不想引入 nvm,快速解决⭐⭐⭐⭐修复现有目录权限之前用过 sudo 装包,需要修复⭐⭐⭐Windows 管理员/改目录Windows 平台专用⭐⭐⭐⭐官方原生安装脚本想彻底避开 npm 权限问题⭐⭐⭐⭐报的本质是npm 全局安装目录的属主权限与当前执行用户不匹配,而不是 npm 或 Claude Code 本身有问题。不要用sudo"强行"解决。

一键难忘的博客 1万+

muduo网络库示例(聊天服务器)

聊天服务器(MuduoManual.pdf P66) examples/asio/chat/server.cc 单线程 examples/asio/chat/server_threaded.cc,多线程TcpServer,并用mutex来保护共享数据mutex examples/asio/chat/server_threaded_efficient.cc,借shared_ptr实现copy-on-write的手法来降低锁竞争 examples/asio/chat/server_threaded_hi

侯彩桦 1303

天爱验证码wasm验证码逆向

本文分析了某网站采用WASM加密技术的情况,发现其加密方式主要针对data和轨迹信息,整体加密逻辑变化不大。通过反混淆技术可以还原代码,并确认加密过程分为两步:先对原始参数逐个加密,再调用WASM模块进行最终加密。研究发现关键参数drives和ki都存储在WASM模块内,通过提取wasm_exec.js文件即可直接使用该加密功能。本文仅提供技术研究思路,强调严禁用于任何违法行为。

码王吴彦祖的博客 933

四个 服务器设计模型(42)

五个简单TCP协议(MuduoManual.pdf P50) muduo库网络模型使用示例(sudoku求解服务器MuduoManual.pdf P35 ) reactor(一个IO线程) reactor + threadpool (一个IO + 多个计算线程) multiple reactor (多个IO线程) one loop per thread + thread po

Riak, Spark, Golang, Erlang, 云存储, 云计算, 数据挖掘 5652

PX4从放弃到精通(十一):混控及PWM输出

文章目录前言一、混控脚本二、使用步骤1.引入库2.读入数据总结 前言 一个人可以走的更快,一群人才能走的更远,交流学习加qq:2096723956 更多保姆级PX4+ROS学习视频:https://b23.tv/ZeUDKqy 分享知识,传递正能量,如有疏漏或不当之处,恳请指出. 一、混控脚本 混控脚本定义了不同 示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。 二、使用步骤 1.引入库 代码如下(示例): import numpy as np import pand

超维空间科技的博客 1万+

【肌电处理】肌电信号EMG积分肌电值+均方根值+中值频率+平均功率频率【含Matlab源码 3386期】

肌电信号EMG积分肌电值+均方根值+中值频率+平均功率频率 完整的代码,方可运行;可提供运行操作视频!适合小白!

订阅付费专栏Matlab(奶茶价版),可赠送奶茶价版付费专栏指定代码1份; 822

大模型实战:万亿赛道 !AI 服务器设计及解决方案 2024

自OpenAI的ChatGPT发布以来,生成式AI技术获得了广泛关注,一系列开创性研究成果相继发布,引领了人工智能的新一轮创新浪潮。AI大模型是生成式AI的关键底座,得益于泛化能力强、长尾数据依赖性低以及下游模型使用效率高,大模型被认为具备了“通用人工智能AGI”的雏形。相比传统AI模型,大模型的训练使用了更庞大的数据,具有更巨量的模型参数和更发散的下游应用场景,对全社会生产力和生产效率的提升、传统产业转型升级具有重大的价值潜力。

EnjoyEDU的博客 2999

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

基于 MCP 实现智能体案例架构设计

最近,AI Agent 和外部工具之间的互动变得越来越流行。**从 OpenAI 最先推出的 Function Calling 功能,到各种插件和框架**,大家都在努力让 AI 模型能更有效地调用外部的功能。不过,目前这些集成方式还是比较分散,开发者需要为每个服务手动设置接口、处理认证和逻辑,而且不同平台之间还互不兼容,确实挺麻烦的。

m0_59163425的博客 1569

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

如何选购一款合适的服务器

云服务器选型需综合考虑稳定性、安全性和长期成本,而非单纯追求低价或高配。阿里云ECS通过标准化实例、多重冗余设计和分层安全防护,为不同业务场景提供灵活的资源模型和基础安全保障。其监控运维工具能有效降低管理成本,使服务器成本成为可优化的变量。评估重点应放在平台的整体工程价值,包括扩展性和运维支持等长期效益,而非仅关注初始配置参数。

hl807092862的博客 535

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

高性能Socket服务器的开发需要注意事项

<br />几个必须涉及到的重中之重的课题:<br /> <br />1.需求.<br />因为方式方法,手法,模型结构太多,因此,明确需求,注意:这里说的是明确需求,就是非常明白并确定需求.<br />2.模型.<br />windows下五种SCOKET模型,根据需求选择合适的模型.最高性能的莫过于IOCP为核心的同步线程工作方式.<br />linux下使用epoll也是类似的手法.<br />bsd下使用kqueue.<br />以上都是系统提供的原始API,可靠性基本上由OS的基础架构保障,且基本

akulakk's memory stack 1053

无限的 AI 推理能力

Kamino Grando 服务器:六块4090显卡带来的强大推理能力 Kamino Grando 服务器是一款拥有六块4090显卡的强大推理机器。由于4090显卡尺寸较大,Kamino采用水冷系统来保证散热,并配备四个电源,总功率达到6千瓦。尽管4090不是专门为服务器设计的显卡,但其性价比极高,成为Kamino Grando服务器的最佳选择。 该服务器拥有强大的推理能力,价格却远低于单个H10...

sentdex的博客 226

网络服务器设计的模型及一些设计方法

1.常用服务器模型a.迭代服务器只有一个进程/线程处理请求设计方法:一般为单进程,加上select多路复用,非阻塞socketb.迭代/并发混合型服务器平时迭代处理,对消耗大的请求并发处理设计方法:平时是迭代型的服务器处理请求时设置一个超时,当请求的处理时间超时时,创建一个进程/线程,把处理转给新的进程/线程处理,主进程/线程继续处理其他请求c.并发服务器多个进程/线程并发处理请求设计方法:

人的一生应当这样度过:当他回首往事时,不因虚度年华而懊悔,也不因碌碌无为而羞愧! 4746

数据库设计的四个阶段是:需求分析、概念设计、逻辑设计和物理设计

**逻辑设计**:是把概念结构转换为选定的DBMS所支持的数据模型(如关系模型),并且对它进行优化,主要工作包括将E-R图等概念模型转换成关系模式、判断模式设计存在的问题、给出全局模式和外模式的定义等。 - **物理设计**:是为逻辑数据模型选取一个最适合应用环境的物理结构,包括存储结构和存取方法(如确定数据的存储结构、存储路径、建立索引等),需要根据逻辑模型来确定数据库的物理结构,对其进行评价,最终形成具体的物理模型。

博客域名:borter.blog.youkuaiyun.com 8377

高性能服务器设计——常用网络服务器设计模型

在业务服务开发过程中,一个优秀的开发框架,往往能够起到事半功倍的效果。现介绍总结一种在服务器开发过程中,比较常用的一种服务器开发模型。 在介绍之前,先总结一下目前比较常用的一些服务器开发模型。         1)  迭代性服务器         迭代性服务器,在开源社区的代表之作是Apache Web 服务器。该模型中,存在唯一的侦听进程,侦听进程accept新的链接之后,通过fork的

梦里花 4898
上一篇: Linux伙伴算法
下一篇: QueryPerformanceCounter用法
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值