autobuild.sh 这是一个自动编译 + 自动安装muduo的一键部署脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # !/bin/bash set -e # 如果没有build目录,创建该目录 if [ ! -d `pwd`/build ]; then mkdir `pwd`/build fi rm -rf `pwd`/build/* cd `pwd`/build && cmake .. && make # 回到项目根目录 cd .. # 把头文件拷贝到 /usr/include/mymuduo so库拷贝到 /usr/lib PATH if [ ! -d /usr/include/lpzmuduo ]; then mkdir /usr/include/lpzmuduo fi for header in `ls *.h` do cp $header /usr/include/lpzmuduo done cp `pwd`/lib/liblpzmuduo.so /usr/lib ldconfig
作用:
编译代码
把 .so 动态库 安装到系统目录
把头文件安装到系统目录
让系统能找到你的库
具体语法细节这里就不讲了
编译脚本
chmod +x autobuild.sh:给 autobuild.sh 脚本 添加 可执行权限 ,执行这行后autobuild.sh在ls查看会变色(白色 → 绿色)
项目测试代码
创建一个example文件夹,在这个文件夹里创建testserver.cc和Makefile两个文件
testserver.cc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include <lpzmuduo/TcpServer.h> #include <lpzmuduo/Logger.h> #include <lpzmuduo/TcpConnection.h> #include <string> #include <functional> class EchoServer { public : EchoServer (EventLoop* loop, const InetAddress& addr, const std::string& name) : server_ (loop, addr, name) , loop_ (loop) { server_.setConnectionCallback (std::bind (&EchoServer::onConnection, this , std::placeholders::_1)); server_.setMessageCallback ( std::bind (&EchoServer::onMessage, this , std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) ); server_.setThreadNum (3 ); } void start () { server_.start (); } private : void onConnection (const TcpConnectionPtr &conn) { if (conn->connected ()) { LOG_INFO ("Connection UP : {}" , conn->peerAddr ().toIpPort ()); } else { LOG_INFO ("Connection DOWN : {}" , conn->peerAddr ().toIpPort ()); } } void onMessage (const TcpConnectionPtr &conn, Buffer *buf, Timestamp time) { std::string msg = buf->retrieveAllAsString (); conn->send (msg); conn->shutdown (); } EventLoop* loop_; TcpServer server_; }; int main () { EventLoop loop; InetAddress addr (8000 ) ; EchoServer server (&loop, addr, "EchoServer-01" ) ; server.start (); loop.loop (); return 0 ; }
测试代码实现一个简单的echo服务器,客户端发什么,服务器返回什么,然后主动断开连接
构造函数 1 2 3 EchoServer (EventLoop* loop, const InetAddress& addr, const std::string& name) : server_ (loop, addr, name) , loop_ (loop)
创建 TcpServer
绑定主 EventLoop
注册两大核心回调 1 2 3 4 5 server_.setConnectionCallback (...); server_.setMessageCallback (...);
这是 muduo 核心思想:不调用框架,让框架调用你(好莱坞原则)
设置 IO 线程数 1 server_.setThreadNum (3 );
1 个 main loop(accept 线程)
3 个 sub loop(IO 读写线程)
4 线程高性能 muduo 服务器
连接建立 / 断开回调 1 2 3 4 5 6 7 8 9 10 11 void onConnection (const TcpConnectionPtr &conn) { if (conn->connected ()) { LOG_INFO ("Connection UP : {}" , conn->peerAddr ().toIpPort ()); } else { LOG_INFO ("Connection DOWN : {}" , conn->peerAddr ().toIpPort ()); } }
客户端连接 → 打印 Connection UP
客户端断开 → 打印 Connection DOWN
消息处理回调 1 2 3 4 5 6 void onMessage (const TcpConnectionPtr &conn, Buffer *buf, Timestamp time) { std::string msg = buf->retrieveAllAsString (); conn->send (msg); conn->shutdown (); }
逻辑:
从缓冲区读取客户端消息
原样发回 (回显)
主动关闭连接
main 函数 1 2 3 4 5 6 7 8 9 10 11 12 int main () { EventLoop loop; InetAddress addr (8000 ) ; EchoServer server (&loop, addr, "EchoServer-01" ) ; server.start (); loop.loop (); return 0 ; }
创建主反应器 EventLoop
启动服务器
永久循环监听事件
流程图 1 2 3 4 5 6 7 8 9 1. main() 创建 EventLoop 2. 创建 EchoServer → 内部创建 TcpServer 3. TcpServer 启动 listen 4. 客户端连接 → Acceptor 接受 5. TcpServer 创建 TcpConnection 6. 调用 onConnection 输出 "Connection UP" 7. 客户端发消息 → Channel 分发 → onMessage 8. 服务器回显消息 → 关闭连接 9. 调用 onConnection 输出 "Connection DOWN"
Makefile 1 2 3 4 5 6 7 all : testserver testserver : g++ -o testserver testserver.cc -llpzmuduo -lpthread -g clean : rm -f testserver
all : testserver:默认目标 = 编译 testserver
你在命令行输入:make它就会自动去找 testserver 这个目标。
-g加入调试信息,方便 gdb 调试,这是我之前调试用的,可删
clean : rm -f testserver:清除编译结果
输入命令:make clean就会删除 testserver 可执行文件。
测试代码执行
因为我之前改bug时打了很多日志没有删,所以会有点乱。
服务器跑起来后,我们可以用xshell做客户端测试一下
用xshell连到自己的虚拟机,执行telnet 127.0.0.1 8000。执行后可随意敲点内容(图示的”dwd”)再按回车即可发送。因为是echo server,客户端发完,服务器会立即回包,然后断开连接,就如图示这样。
出现newConnection日志,连接成功
发送完数据,会打印有一个事件发生,并且因为立即断开连接,会有Connection DOWN日志
压测脚本 stress.sh 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # !/bin/bash # 目标服务器 IP=127.0.0.1 PORT=8000 # 并发数(可以改 500、1000、2000) CONCURRENCY=10000 echo "开始高并发压测:$CONCURRENCY 条连接..." for ((i=0; i<CONCURRENCY; i++)) do ( # 连接 -> 发数据 -> 收回声 -> 退出 echo "muduo high concurrency test" | nc $IP $PORT ) & done wait echo "压测完成!"
高并发短连接压测脚本 ,瞬间创建大量 TCP 连接,攻击式压测 muduo 服务器(可以把并发数先改小一点,从1000开始)
在example文件夹下创建这个文件
运行
先把服务器启动
另开一个终端
1 2 chmod +x stress.sh./stress.sh
perf 脚本运行时可以用perf进行性能分析
先启动服务器
拿到服务器PID
1 ps aux | grep testserver
1 2 coco 6096 0.5 0.1 305168 12520 pts/6 Sl+ 12:27 0:08 ./testserver coco 142989 0.0 0.0 9444 2460 pts/1 S+ 12:53 0:00 grep --color=auto testserver
启动perf监控
执行./stress.sh
没有出现任何单函数占比超过 10% 的热点,也没有锁竞争、死循环等异常,且占比较高的也都在内核 和系统调用 上,是正常现象。