@


前言

前面文章介绍了muduo网络库的单线程设计方式,即一个EventLoop 处理所有的事件,包括链接的建立、IO、计算、以及链接的销毁,本文介绍下muduo中的多线程设计方法。

多线程TcpServer

EventLoopThreadPool

多线程的muduo::TcpServer,主要通过添加一个EventLoopThreadPool 事件循环线程池实现,新建TcpConnection时从event loop pool里挑选一个loop给TcpConnection用。 也就是说多线程TcpServer自己的EventLoop只用来接受新连接, 而新连接会用其他EventLoop来执行IO。 (单线程TcpServer的EventLoop是与TcpConnection共享的。)

EventLoopThreadPooll 按one loop per thread的思想实现多线程TcpServer, 此时主线程循环只负责TCP链接的建立,及任务的分配,需要让哪个线程干活, 就把timer或IO(如TCP连接) 注册到那个线程的循环里即可;对实时性有要求的connection可以单独用一个线程; 数据量大的connection可以独占一个线程;并把数据处理任务分摊到另几个计算线程中(用线程池);其他次要的辅助性connections共享一个线程。

线程池设计模式

池是一种设计模式,线程是是一种资源,线程池设计模式通常是事先申请一定的资源,当需要使用时,去资源池中申请资源,用完后再放回资源池中。EventLoopThreadPool 正是借鉴了这种设计模式,虽然使用的线程并不会放回资源池中,但是本身的一个EventLoop即一个Reactor,本身就具备等待机制了。

muduo中的使用

TcpServer每次新建一条TcpConnection就会通过EventLoopThreadPool::getNextLoop()方法来取一个EventLoop, 目前的getNextLoop()只是循环的从池中取一条loop,如果提供给每条TcpConncetion的是均等的服务,那么这样就能很均匀的分配系统的资源了。

TcpServer的工作方式取决于EventLoopThreadPool中线程的创建数量。

0 意味着所有的I/O 都在TcpServer的主事件循环中,不会创建新的线程。

1 意味着所有的 I/O 在另一个线程中 ,TcpServer的主线程只负责建立连接。

N 意味着新的连接会被循环的分配到N条线程中工作。

连接的建立、消息、销毁

on_connection

1、对于新的连接new connection 会在 TcpServer的Loop 中由Acceptor建立,

void Acceptor::handleRead()
{
LOG_TRACE << "Acceptor::handleRead()";
p_loop->assertInLoopThread();
InetAddress peerAddr;
int connfd = m_acceptSocket.accept(&peerAddr);

2、之后从loop_thread_pool中取一个loop将新的connection注册上去。

void TcpServer::newConnetion(int sockfd, const InetAddress& peerAddr)
{
InetAddress localAddr(sockets::getLocalAddr(sockfd));
EventLoop* loop;
loop = ex_event_loop_thread_pool->getNextLoop();
TcpConnectionPtr conn(new TcpConnection(loop,
connName, sockfd, localAddr, peerAddr));
conn->setConnectionCallBack(m_connectionCallBack);
conn->setMessageCallBack(m_messageCallBack);
conn->setCloseCallBack(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1));
loop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));

on_message

1、message将在connection自己所处的loop中处理,无需多言。

on_close

1、连接的销毁稍微复杂一点,connection会在自己的loop中调用Tcp:Server::removeConnection,我们需要将他移动到TcpServer的Loop线程中先解除TcpServer对connection的使用,然后在回到connection自己的loop中销毁连接connectDestroyed()。

void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
// FIXME: unsafe
p_loop->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
} void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{
p_loop->assertInLoopThread();
LOG_INFO << "TcpServer::removeConnectionInLoop [" << m_name
<< "] - connection " << conn->name();
m_closeCallBack(conn);
EventLoop* ioLoop = conn->getLoop();
ioLoop->queueInLoop(
std::bind(&TcpConnection::connectDestroyed, conn));
}

简单透传服务实现

利用muduo多线程TcpServer 实现一个简单的透传服务,从connection中收到的消息直接转发给所有的其他connection。

//ws_tcp_server.hpp
#pragma once
#include <async_logging>
#include <muduo_server> namespace ws { extern std::unique_ptr<muduo::EventLoop> active_event_loop; class tcp_server{
public:
//! ctor
tcp_server(void);
//! dtor
~tcp_server(void) = default; //! copy ctor
tcp_server(const tcp_server&) = delete;
//! assignment operator
tcp_server& operator=(const tcp_server&) = delete; public: //! listen_and_serve
//! start server and listened on port
void listen_and_serve(std::size_t port); private: void on_connection(const muduo::TcpConnectionPtr&); void on_message(const muduo::TcpConnectionPtr& , muduo::Buffer*, ssize_t ); void on_close(const muduo::TcpConnectionPtr&);
private: //!
//typedef std::vector<muduo::TcpConnectionPtr> connections_t; //!
typedef std::map<std::string, muduo::TcpConnectionPtr> connections_map_t; private: //! master connection
//muduo::TcpConnectionPtr m_master_connection; //! slave connection
//connections_t m_connections; //! muduo tcp server
std::unique_ptr<muduo::TcpServer> m_tcp_server; //! connections map
connections_map_t m_connections_map; std::size_t cnt_connetions;
}; }
//ws_tcp_server.cpp
#include "ws_tcp_server.hpp" #include <assert.h> static muduo::EventLoopThread static_event_loop;
std::unique_ptr<muduo::EventLoop> ws::active_event_loop = std::unique_ptr<muduo::EventLoop>(static_event_loop.startLoop()); using namespace ws; tcp_server::tcp_server()
:cnt_connetions(0)
{ } void tcp_server::listen_and_serve(std::size_t port)
{
assert(active_event_loop); muduo::ex_event_loop_thread_pool = std::unique_ptr<muduo::EventLoopThreadPool>(new muduo::EventLoopThreadPool(ws::active_event_loop.get(), "event_loop", 3));
active_event_loop->runInLoop(std::bind(&muduo::EventLoopThreadPool::start, muduo::ex_event_loop_thread_pool.get())); m_tcp_server = std::unique_ptr<muduo::TcpServer>(new muduo::TcpServer(active_event_loop.get(), InetAddress(port))); m_tcp_server->setConnectionCallBack(std::bind(&tcp_server::on_connection, this, std::placeholders::_1));
m_tcp_server->setMessageCallBack(std::bind(&tcp_server::on_message, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_tcp_server->setCloseCallBack(std::bind(&tcp_server::on_close, this, std::placeholders::_1));
m_tcp_server->start();
} void tcp_server::on_connection(const muduo::TcpConnectionPtr& conn)
{
cnt_connetions++;
std::string conn_name = "tcp connection " + std::to_string(cnt_connetions);
conn->setConnectionName(conn_name); m_connections_map[conn_name] = std::move(conn); } void tcp_server::on_message(const muduo::TcpConnectionPtr& conn, muduo::Buffer* buf, ssize_t size)
{
connections_map_t::iterator iter;
for(iter = m_connections_map.begin(); iter != m_connections_map.end(); iter++)
{
if(iter->second != conn){
LOG_DEBUG << "con " << conn->name() << " send to " << iter->second->name();
iter->second->send(buf->peek(), size);
}
} buf->retrieve(size); } void tcp_server::on_close(const muduo::TcpConnectionPtr& conn)
{
size_t n = m_connections_map.erase(conn->name());
(void)n;
assert(n == 1);
}
// main.cpp
#include <async_logging>
#include "ws_tcp_server.hpp" int main()
{
Logger::setLogLevel(Logger::DEBUG); ws::tcp_server tcp_server;
tcp_server.listen_and_serve(8008); getchar();
}
../bin/ws_server
2019-03-19 16:22:17.202112 [TRACE] [EventLoopThread.cpp:41] [startLoop] EventLoopThread::startLoop() wait()
2019-03-19 16:22:17.203199 [TRACE] [TimerQueue.cpp:20] [createTimerfd] createTimerfd() fd : 4
2019-03-19 16:22:17.203376 [TRACE] [Epoll.cpp:115] [updateChannel] fd= 4 events 3
2019-03-19 16:22:17.203549 [TRACE] [EventLoop.cpp:23] [createEventfd] createEventfd() fd : 5
2019-03-19 16:22:17.203711 [INFO ] [EventLoop.cpp:43] EventLoop Create 0x7F1E72371B30 in thread 44057
2019-03-19 16:22:17.203872 [TRACE] [Epoll.cpp:115] [updateChannel] fd= 5 events 3
2019-03-19 16:22:17.204188 [TRACE] [EventLoopThread.cpp:65] [threadFunc] EventLoopThread::threadFunc() notify()
2019-03-19 16:22:17.204332 [TRACE] [EventLoop.cpp:74] [loop] EventLoop 0x7F1E72371B30 start loopig
2019-03-19 16:22:17.204477 [TRACE] [Epoll.cpp:72] [poll] Epoll::poll() maxConcurrencySize 2
2019-03-19 16:22:17.204794 [TRACE] [EventLoopThread.cpp:46] [startLoop] EventLoopThread::startLoop() wakeup
2019-03-19 16:22:17.210324 [INFO ] [EventLoop.cpp:43] EventLoop Create 0x7F1E71B70B30 in thread 44058
2019-03-19 16:22:17.212173 [INFO ] [EventLoop.cpp:43] EventLoop Create 0x7F1E7136FB30 in thread 44059
2019-03-19 16:22:17.212725 [INFO ] [EventLoop.cpp:43] EventLoop Create 0x7F1E70B6EB30 in thread 44060
2019-03-19 16:22:22.399790 [INFO ] [TcpServer.cpp:52] TcpServer::newConnetion() [Serv ] - new connection [Serv #1] from 127.0.0.1:51400
2019-03-19 16:22:22.399833 [INFO ] [TcpServer.cpp:61] Loop 0x7F1E71B70B30
2019-03-19 16:22:22.399842 [DEBUG] [TcpConnection.cpp:27] [TcpConnection] TcpConnection::ctor[Serv #1] at 0x7F1E6C0046F0 fd=17
2019-03-19 16:22:24.901612 [INFO ] [TcpServer.cpp:52] TcpServer::newConnetion() [Serv ] - new connection [Serv #2] from 127.0.0.1:51402
2019-03-19 16:22:24.901625 [INFO ] [TcpServer.cpp:61] Loop 0x7F1E7136FB30
2019-03-19 16:22:24.901635 [DEBUG] [TcpConnection.cpp:27] [TcpConnection] TcpConnection::ctor[Serv #2] at 0x7F1E6C006A30 fd=18
2019-03-19 16:22:25.903035 [DEBUG] [ws_tcp_server.cpp:58] [on_message] con tcp connection 2 send to tcp connection 1
2019-03-19 16:22:32.499161 [INFO ] [TcpServer.cpp:52] TcpServer::newConnetion() [Serv ] - new connection [Serv #3] from 127.0.0.1:51404
2019-03-19 16:22:32.499174 [INFO ] [TcpServer.cpp:61] Loop 0x7F1E70B6EB30
2019-03-19 16:22:32.499183 [DEBUG] [TcpConnection.cpp:27] [TcpConnection] TcpConnection::ctor[Serv #3] at 0x7F1E6C008D80 fd=19
2019-03-19 16:22:33.499744 [DEBUG] [ws_tcp_server.cpp:58] [on_message] con tcp connection 3 send to tcp connection 1
2019-03-19 16:22:33.499771 [DEBUG] [ws_tcp_server.cpp:58] [on_message] con tcp connection 3 send to tcp connection 2
2019-03-19 16:22:35.986563 [DEBUG] [ws_tcp_server.cpp:58] [on_message] con tcp connection 2 send to tcp connection 1
2019-03-19 16:22:35.986670 [DEBUG] [ws_tcp_server.cpp:58] [on_message] con tcp connection 2 send to tcp connection 3
2019-03-19 16:22:36.982713 [DEBUG] [ws_tcp_server.cpp:58] [on_message] con tcp connection 2 send to tcp connection 1
2019-03-19 16:22:36.982759 [DEBUG] [ws_tcp_server.cpp:58] [on_message] con tcp connection 2 send to tcp connection 3
2019-03-19 16:22:38.244229 [INFO ] [TcpServer.cpp:90] TcpServer::removeConnectionInLoop [Serv ] - connection tcp connection 2
2019-03-19 16:22:38.244281 [DEBUG] [TcpConnection.cpp:36] [~TcpConnection] TcpConnection::dtor[tcp connection 2] at 0x7F1E6C006A30 fd=18 state=kDisConnected

muduo学习笔记(六) 多线程的TcpServer的更多相关文章

  1. muduo学习笔记(二)Reactor关键结构

    目录 muduo学习笔记(二)Reactor关键结构 Reactor简述 什么是Reactor Reactor模型的优缺点 poll简述 poll使用样例 muduo Reactor关键结构 Chan ...

  2. 孙鑫VC学习笔记:多线程编程

    孙鑫VC学习笔记:多线程编程 SkySeraph Dec 11st 2010  HQU Email:zgzhaobo@gmail.com    QQ:452728574 Latest Modified ...

  3. java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

    java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...

  4. Learning ROS for Robotics Programming Second Edition学习笔记(六) indigo xtion pro live

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...

  5. Typescript 学习笔记六:接口

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  6. Muduo学习笔记(一) 什么都不做的EventLoop

    Muduo学习笔记(一) 什么都不做的EventLoop EventLoop EventLoop的基本接口包括构造.析构.loop(). One Loop Per Thread 一个线程只有一个Eve ...

  7. python3.4学习笔记(六) 常用快捷键使用技巧,持续更新

    python3.4学习笔记(六) 常用快捷键使用技巧,持续更新 安装IDLE后鼠标右键点击*.py 文件,可以看到Edit with IDLE 选择这个可以直接打开编辑器.IDLE默认不能显示行号,使 ...

  8. Go语言学习笔记六: 循环语句

    Go语言学习笔记六: 循环语句 今天学了一个格式化代码的命令:gofmt -w chapter6.go for循环 for循环有3种形式: for init; condition; increment ...

  9. 【opencv学习笔记六】图像的ROI区域选择与复制

    图像的数据量还是比较大的,对整张图片进行处理会影响我们的处理效率,因此常常只对图像中我们需要的部分进行处理,也就是感兴趣区域ROI.今天我们来看一下如何设置图像的感兴趣区域ROI.以及对ROI区域图像 ...

随机推荐

  1. Confluence 6 启用 HTTP 压缩

    在屏幕的右上角单击 控制台按钮 ,然后选择 基本配置(General Configuration) 链接. 在左侧的面板中选择 通用配置(General Configuration). 启用 HTTP ...

  2. deepin、Ubuntu安装Nginx

    deepin安装nginx 切换至root用户 su 密码: 基础库的安装 安装gcc g++的依赖库 sudo apt-get install build-essential && ...

  3. mysql视图的作用

    测试表:user有id,name,age,sex字段 测试表:goods有id,name,price字段 测试表:ug有id,userid,goodsid字段 视图的作用实在是太强大了,以下是我体验过 ...

  4. PDF如何设置书签,怎么在PDF上添加书签

    PDF文件现在作为我们使用最多的一种办公文件,当然我们在使用PDF文件的同时还会需要编辑PDF文件,在使用一个PDF文件页数比较多的时候就需要添加书签,不然每次使用的时候都需要从头开始查找是很麻烦又头 ...

  5. Git使用四:查看工作状态和历史提交

    查看当前的工作状态:git status On branch master:现在位于master分支里面nothing to commit, working tree clean:没有需要提交的文件, ...

  6. Chrome浏览器常用键盘快捷键介绍

    很多人喜欢使用键盘快捷键来操作电脑,因为在熟练的情况下,使用键盘会比使用鼠标点击更快.更高效.本文对Chrome浏览器常用的快捷键做个说明. 标签页和窗口快捷键 1.  Ctrl + n 打开新窗口 ...

  7. Python老男孩

    1.可以自己编写模块,但注意:如果想要调用该模块,需要将该模块放到site-packages目录下,或将该模块放在执行程序的路径下. 2.pyc文件是什么? 集合: set 集合可以去重:做交集.并集 ...

  8. 学习Maven POM

    什么是POM POM stands for "Project Object Model".It is an XML representation of a Maven projec ...

  9. Vue2.0 探索之路——生命周期和钩子函数的一些理解

    前言 在使用vue一个多礼拜后,感觉现在还停留在初级阶段,虽然知道怎么和后端做数据交互,但是对于mounted这个挂载还不是很清楚的.放大之,对vue的生命周期不甚了解.只知道简单的使用,而不知道为什 ...

  10. 一起学Hadoop——TotalOrderPartitioner类实现全局排序

    Hadoop排序,从大的范围来说有两种排序,一种是按照key排序,一种是按照value排序.如果按照value排序,只需在map函数中将key和value对调,然后在reduce函数中在对调回去.从小 ...