muduo库使用示例之3种网络模型(Sudoku求解服务器)

版权声明:guojawee https://blog.csdn.net/weixin_36750623/article/details/84720194

10种大并发服务器设计方案与muduo库网络模型使用https://blog.csdn.net/weixin_36750623/article/details/84329044

muduo库网络模型使用

代码存放在muduo-master\examples\sudoku下

  1. reactor(一个IO线程)
  2. multiple reactor(多个IO线程)
  3. one loop per thread + thread pool (多个IO线程+计算线程池)

<单线程版本>数独求解服务器

sudoku数独求解服务器MuduoManual.pdf (P35)
假设有一个网络编程任务:写一个求解数独的程序,并把它做成一个网络服务。
说明:写这么一个程序在网络编程方面的难度不高,跟写echo服务差不多(从网络读入一个Sudoku题目,算出答案,再发回给客户),挑战在于怎样做才能发挥现在多核硬件的功能?在谈这个问题之前,让我们先写一个基本的单线程版本。

<多线程版本>数独求解服务器

仅仅比单线程版本多了一句话:server_.setThreadNum(numThreads);

SudokuServer(EventLoop* loop, const InetAddress& listenAddr, int numThreads)
  : server_(loop, listenAddr, "SudokuServer"),
    numThreads_(numThreads),
    startTime_(Timestamp::now())
{
  server_.setConnectionCallback(
      std::bind(&SudokuServer::onConnection, this, _1));
  server_.setMessageCallback(
      std::bind(&SudokuServer::onMessage, this, _1, _2, _3));
  
  //添加sub Reactor,IO线程池
  server_.setThreadNum(numThreads); 
}

<多线程版本><计算线程池>数独求解服务器

将计算任务扔给计算线程池中的空闲线程处理,而不是给IO线程处理(如果计算时间比较久,会造成IO线程阻塞,就不能处理大并发连接) ⇒ 因此引出计算线程池,降低IO线程池的工作量。

与<多线程版本>数独求解服务器相比,增添以及改变了下面的代码段:

  1. 成员函数中多添加一个ThreadPool计算线程池对象
    ThreadPool threadPool_;
  2. start()函数中增加了一句代码用来启动了计算线程池,即threadPool_.start(numThreads_);
  void start()
  {
    LOG_INFO << "starting " << numThreads_ << " threads.";
    
	threadPool_.start(numThreads_); //启动了计算线程池
    server_.start(); 
  }
  1. 将计算任务的函数,放入计算线程池中的run(…,…)函数中
threadPool_.run(std::bind(&solve, conn, puzzle, id));

完整代码:

#include "sudoku.h"

#include <muduo/base/Atomic.h>
#include <muduo/base/Logging.h>
#include <muduo/base/Thread.h>
#include <muduo/base/ThreadPool.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>
#include <muduo/net/TcpServer.h>

#include <utility>

#include <stdio.h>
#include <unistd.h>

using namespace muduo;
using namespace muduo::net;

class SudokuServer
{
 public:
  SudokuServer(EventLoop* loop, const InetAddress& listenAddr, int numThreads)
    : server_(loop, listenAddr, "SudokuServer"),
      numThreads_(numThreads),
      startTime_(Timestamp::now())
  {
    server_.setConnectionCallback(
        std::bind(&SudokuServer::onConnection, this, _1));
    server_.setMessageCallback(
        std::bind(&SudokuServer::onMessage, this, _1, _2, _3));
		
	//添加sub Reactor,IO线程池
	server_.setThreadNum(4); 
  }

  void start()
  {
    LOG_INFO << "starting " << numThreads_ << " threads.";
    
	threadPool_.start(numThreads_); //2.启动了计算线程池
    server_.start(); //启动Tcp服务器
  }

 private:
  void onConnection(const TcpConnectionPtr& conn)
  {
    LOG_TRACE << conn->peerAddress().toIpPort() << " -> "
        << conn->localAddress().toIpPort() << " is "
        << (conn->connected() ? "UP" : "DOWN");
  }

  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)
  {
    LOG_DEBUG << conn->name();
    size_t len = buf->readableBytes();
    while (len >= kCells + 2)
    {
      const char* crlf = buf->findCRLF();
      if (crlf)
      {
        string request(buf->peek(), crlf);
        buf->retrieveUntil(crlf + 2);
        len = buf->readableBytes();
        if (!processRequest(conn, request))
        {
          conn->send("Bad Request!\r\n");
          conn->shutdown();
          break;
        }
      }
      else if (len > 100) // id + ":" + kCells + "\r\n"
      {
        conn->send("Id too long!\r\n");
        conn->shutdown();
        break;
      }
      else
      {
        break;
      }
    }
  }

  bool processRequest(const TcpConnectionPtr& conn, const string& request)
  {
    string id;
    string puzzle;
    bool goodRequest = true;

    string::const_iterator colon = find(request.begin(), request.end(), ':');
    if (colon != request.end())
    {
      id.assign(request.begin(), colon);
      puzzle.assign(colon+1, request.end());
    }
    else
    {
      puzzle = request;
    }

    if (puzzle.size() == implicit_cast<size_t>(kCells))
    {
	  //----3.将sudu求解,放入计算线程池中的solve----
	  /*即:将计算任务扔给计算线程池中的空闲线程处理,而不是
	    给IO线程处理(计算时间如果比较久,会造成IO线程阻塞,就不能
	  处理大并发连接)*/
      threadPool_.run(std::bind(&solve, conn, puzzle, id));
    }
    else
    {
      goodRequest = false;
    }
    return goodRequest;
  }

  static void solve(const TcpConnectionPtr& conn,
                    const string& puzzle,
                    const string& id)
  {
	//----sudo求解----
    LOG_DEBUG << conn->name();
    string result = solveSudoku(puzzle);
    if (id.empty())
    {
      conn->send(result+"\r\n");
    }
    else
    {
      conn->send(id+":"+result+"\r\n");
    }
	//----sudu求解----
  }

  TcpServer server_;
  ThreadPool threadPool_; //1.多添加一个ThreadPool计算线程池对象
  int numThreads_;
  Timestamp startTime_;
};

int main(int argc, char* argv[])
{
  LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid();
  int numThreads = 0;
  if (argc > 1)
  {
    numThreads = atoi(argv[1]);
  }
  EventLoop loop;
  InetAddress listenAddr(9981);
  //numThreads表示线程池中计算线程的个数
  SudokuServer server(&loop, listenAddr, numThreads);

  server.start();

  loop.loop();
}

猜你喜欢

转载自blog.csdn.net/weixin_36750623/article/details/84720194