版权声明: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下
- reactor(一个IO线程)
- multiple reactor(多个IO线程)
- 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线程池的工作量。
与<多线程版本>数独求解服务器相比,增添以及改变了下面的代码段:
- 成员函数中多添加一个ThreadPool计算线程池对象
ThreadPool threadPool_; - start()函数中增加了一句代码用来启动了计算线程池,即threadPool_.start(numThreads_);
void start()
{
LOG_INFO << "starting " << numThreads_ << " threads.";
threadPool_.start(numThreads_); //启动了计算线程池
server_.start();
}
- 将计算任务的函数,放入计算线程池中的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();
}