muduo网络库学习之muduo_inspect 库涉及到的类

muduo inspect 库通过HTTP方式为服务器提供监控接口, 现在只实现进程相关信息的监控,通过成员ProcessInspector 实现。

ProcessInspector // 通过ProcessInfo返回进程信息     

ProcessInfo // 获取进程相关信息

我们可以参照 ProcessInspector 实现一个如TcpStateInspector,作为Inspector的成员,这样就可以实现对Tcp连接状态的监控。比如实现以下功能:

接受了多少个TCP连接 当前有多少个活动连接 一共响应了多少次请求 每次请求的平均响应时间多少毫秒 ......

Inspector类:

// A internal inspector of the running process, usually a singleton.
class Inspector : boost::noncopyable
{
public:
    typedef std::vector<string> ArgList;
    typedef boost::function<string (HttpRequest::Method, const ArgList &args)> Callback;


    // 如add("proc", "pid", ProcessInspector::pid, "print pid");
    // http://192.168.159.188:12345/proc/pid这个http请求就会相应的调用ProcessInspector::pid来处理
    void add(const string &module,
             const string &command,
             const Callback &cb,
             const string &help);

private:
    typedef std::map<string, Callback> CommandList;
    typedef std::map<string, string> HelpList;

    void start()
    {
        server_.start();
    }
    void onRequest(const HttpRequest &req, HttpResponse *resp);

    HttpServer server_;
    boost::scoped_ptr<ProcessInspector> processInspector_;
    MutexLock mutex_;
    std::map<string, CommandList> commands_;
    std::map<string, HelpList> helps_;
};

在Inspector 构造函数中(Inspector对象一般在主线程中构造):

server_.setHttpCallback(boost::bind(&Inspector::onRequest, this, _1, _2));
processInspector_->registerCommands(this);
// 这样子做法是为了防止竞态问题
// 如果直接调用start,(当前线程不是loop所属的IO线程,是主线程)那么有可能,当前构造函数还没返回,
// HttpServer所在的IO线程可能已经收到了http客户端的请求了(因为这时候HttpServer已启动),那么就会回调
// Inspector::onRequest,而这时候构造函数还没返回,也就是说对象还没完全构造好
loop->runAfter(0.1, boost::bind(&Inspector::start, this)); // little race condition

首先注册请求到来时的回调函数,由前面http 库分析可知,需要由Inspector::onRequest() 函数填充httpResponse。接着注册命令的回调函数, 在ProcessInspector::registerCommands()函数中调用了Inspector::add() 函数将命令填充到commands_ 和 helps_ 中, 并且启动Inspector 服务器。

当请求到来,调用Inspector::onRequest() 函数:

void Inspector::onRequest(const HttpRequest &req, HttpResponse *resp)
{
    if (req.path() == "/")
    {
        string result;
        ....
        resp->setStatusCode(HttpResponse::k200Ok);
        resp->setStatusMessage("OK");
        resp->setContentType("text/plain");
        resp->setBody(result);
    }
    else
    {
        // 以"/"进行分割,将得到的字符串保存在result中
        std::vector<string> result = split(req.path());

        ArgList args(result.begin() + 2, result.end());     // 传递给回调函数的参数表
        if (it->second)
        {
            resp->setStatusCode(HttpResponse::k200Ok);
            resp->setStatusMessage("OK");
            resp->setContentType("text/plain");
            const Callback &cb = it->second;
            resp->setBody(cb(req.method(), args));      // 调用cb将返回的字符串传给setBody
            ok = true;
        }
    }
}

如果请求的是根目录,直接返回 /module/command  help

如果是其他目录,需要将命令的回调函数执行结果填充到body,

resp->setBody(cb(req.method(), args)); //当前的命令都没有传递参数

测试程序:

Inspector_test.cc:

#include <muduo/net/inspect/Inspector.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/EventLoopThread.h>

using namespace muduo;
using namespace muduo::net;

int main()
{
    EventLoop loop; //正常服务线程
    EventLoopThread t;     // 监控线程
    Inspector ins(t.startLoop(), InetAddress(12345), "test");
    loop.loop();
}

运行程序,用浏览器访问,输出如下:

服务器端输出如下:

simba@ubuntu:~/Documents/build/debug/bin$ ./inspector_test  20131113 10:50:13.759144Z  4532 TRACE IgnoreSigPipe Ignore SIGPIPE - EventLoop.cc:51 20131113 10:50:13.761764Z  4532 TRACE updateChannel fd = 4 events = 3 - EPollPoller.cc:104 20131113 10:50:13.762399Z  4532 TRACE EventLoop EventLoop created 0xBFE11FD4 in thread 4532 - EventLoop.cc:76 20131113 10:50:13.762869Z  4532 TRACE updateChannel fd = 5 events = 3 - EPollPoller.cc:104 20131113 10:50:13.764532Z  4533 TRACE updateChannel fd = 7 events = 3 - EPollPoller.cc:104 20131113 10:50:13.765484Z  4533 TRACE EventLoop EventLoop created 0xB73D9F44 in thread 4533 - EventLoop.cc:76 20131113 10:50:13.766082Z  4533 TRACE updateChannel fd = 8 events = 3 - EPollPoller.cc:104 20131113 10:50:13.767294Z  4532 TRACE loop EventLoop 0xBFE11FD4 start looping - EventLoop.cc:108 20131113 10:50:13.768760Z  4533 TRACE loop EventLoop 0xB73D9F44 start looping - EventLoop.cc:108 20131113 10:50:13.769373Z  4533 TRACE poll 1 events happended - EPollPoller.cc:65 20131113 10:50:13.770502Z  4533 TRACE printActiveChannels {8: IN }  - EventLoop.cc:271 20131113 10:50:13.771500Z  4533 TRACE poll 1 events happended - EPollPoller.cc:65 20131113 10:50:13.772050Z  4533 TRACE printActiveChannels {7: IN}  - EventLoop.cc:271 20131113 10:50:13.772521Z  4533 TRACE readTimerfd TimerQueue::handleRead() 1 at 1384339813.772477 - TimerQueue.cc:62 20131113 10:50:13.772950Z  4533 WARN  HttpServer[Inspector:test] starts listenning on 0.0.0.0:12345 - HttpServer.cc:155 20131113 10:50:13.772992Z  4533 TRACE updateChannel fd = 9 events = 3 - EPollPoller.cc:104 20131113 10:50:18.749088Z  4533 TRACE poll 1 events happended - EPollPoller.cc:65 20131113 10:50:18.749152Z  4533 TRACE printActiveChannels {9: IN}  - EventLoop.cc:271 20131113 10:50:18.749195Z  4533 INFO  TcpServer::newConnection [Inspector:test] - new connection [Inspector:test:0.0.0.0:12345#1] from 192.168.56.1:4610 - TcpServer.cc:93 20131113 10:50:18.749425Z  4533 DEBUG TcpConnection TcpConnection::ctor[Inspector:test:0.0.0.0:12345#1] at 0xB6A00748 fd=11 - TcpConnection.cc:65 20131113 10:50:18.749442Z  4533 TRACE newConnection [1] usecount=1 - TcpServer.cc:111 20131113 10:50:18.749455Z  4533 TRACE newConnection [2] usecount=2 - TcpServer.cc:113 20131113 10:50:18.749504Z  4533 TRACE updateChannel fd = 11 events = 3 - EPollPoller.cc:104 20131113 10:50:18.749581Z  4533 TRACE newConnection [5] usecount=2 - TcpServer.cc:123 20131113 10:50:18.760683Z  4533 TRACE poll 1 events happended - EPollPoller.cc:65 20131113 10:50:18.760745Z  4533 TRACE printActiveChannels {11: IN}  - EventLoop.cc:271 20131113 10:50:23.778871Z  4532 TRACE poll  nothing happended - EPollPoller.cc:74 20131113 10:50:28.772115Z  4533 TRACE poll  nothing happended - EPollPoller.cc:74 20131113 10:50:33.602472Z  4533 TRACE poll 1 events happended - EPollPoller.cc:65 20131113 10:50:33.602541Z  4533 TRACE printActiveChannels {11: IN }  - EventLoop.cc:271 20131113 10:50:33.806468Z  4532 TRACE poll  nothing happended - EPollPoller.cc:74 20131113 10:50:43.613810Z  4533 TRACE poll  nothing happended - EPollPoller.cc:74 20131113 10:50:43.816815Z  4532 TRACE poll  nothing happended - EPollPoller.cc:74

第一次访问的是根目录,给出的是可用的命令提示,如第一行 

/proc/opened_files	count /proc/self/fd

即/module/command  help   

可用的四个命令的module都是proc(ProcessInspector),如前所述,如果我们再实现一个TcpStateInspector作为Inspector的成员,可以做成模块/tcpstate. 

help 只是对命名功能的描述。

接着根据提示进入某个目录如/proc/status,就会给出当前服务器进程的一些状态信息。

程序运行的时候有两个线程,两个loop,主线程的loop 占据3,4,5 (epollfd, timerfd, eventfd)文件描述符但什么都不做(在实际工作中可以作为正常的服务线程);而监控线程占据6,7,8 (epollfd, timerfd, eventfd)文件描述符而且监听9 描述符。fd=10是idlefd_。fd =8 发生可读事件是因为在主线程中(不在loop所在,inspector服务所在线程)调用loop->runAfter(),故需要唤醒一下inspector线程,其实只是add一个定时器。fd=7发生可读事件是因为定时器超时,处理过程中会回调Inspector::start(),也就是开始启动TcpServer监听。 浏览器访问,建立连接,fd=11,前后访问两个目录,发生两次可读事件。

参考:

《UNP》

muduo manual.pdf

《linux 多线程服务器编程:使用muduo c++网络库》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏码洞

【动画】当我们在读写Socket时,我们究竟在读写什么?

套接字socket是大多数程序员都非常熟悉的概念,它是计算机网络编程的基础,TCP/UDP收发消息都靠它。我们熟悉的web服务器底层依赖它,我们用到的MySQL...

771
来自专栏架构说

如何避免TCP的TIME_WAIT状态(高并发)

如何减少 tcp time_wait 状态 方法1 :线程池 线程池作用socket连接不关闭 自然减少time_wait状态 方法2: 通过sets...

4245
来自专栏Java帮帮-微信公众号-技术文章全总结

虚拟机11.JVM调优_调优方法

虚拟机11.JVM调优_调优方法 ENTER TITLE JVM调优工具 Jconsole,jProfile,VisualVM Jconsole : jdk自...

3546
来自专栏技术分享

Redis 数据结构与内存管理策略(下)

Redis 数据结构与内存管理策略(下) 标签: Redis Redis数据结构 Redis内存管理策略 Redis数据类型 Redis类型映射 作者:王清培...

2828
来自专栏陈福荣的专栏

十问 Linux 虚拟内存管理 ( 二 )

最近在做 MySQL 版本升级时( 5.1->5.5 ) , 发现了 mysqld 疑似“内存泄露”现象,但通过 valgrind 等工具检测后,并没发现类似的...

3.2K2
来自专栏张善友的专栏

PostgreSQL 与 MySQL 相比,优势何在?

一、 PostgreSQL 的稳定性极强, Innodb 等引擎在崩溃、断电之类的灾难场景下抗打击能力有了长足进步,然而很多 MySQL 用户都遇到过Serve...

2316
来自专栏java达人

Kafka漫游记

我是一条消息,从我被生产者发布到topic的时候,我就清楚自己的使命:被消费者获取消费。但我一直很纳闷,把我直接推送给消费者不就行了,为什么一定要先推送到类似队...

3735
来自专栏架构师之路

连接池原来这么简单(一分钟系列)

应网友要求,写一写连接池实现细节。 一、如何通过连接访问下游 工程架构中有很多访问下游的需求,下游包括但不限于服务/数据库/缓存,其通讯步骤是为: (1)与下游...

3477
来自专栏SDNLAB

SDN私享汇(十三):DCFabirc控制器实现高级OpenStack网络功能

DCFabirc的主要特性和基于DCFabric的OpenStack网络的高性能优势。DCFabric的最新版本“秦”在峰会上正式发布,新版本主要在多线程优化、...

2342
来自专栏决胜机器学习

《Redis设计与实现》读书笔记(十七) ——Redis时间事件与事件调度 (原创内容,转载请注明来源,谢谢)

《Redis设计与实现》读书笔记(十七) ——Redis时间事件与事件调度 (原创内容,转载请注明来源,谢谢) 一、时间事件 1、概述 re...

3194

扫码关注云+社区