监视服务器

时间:2018-03-29 16:22:35

标签: c multithreading performance pipe monitoring

我使用gcc和pthreads开发了一个接收UDP数据包的C服务器,并根据配置将其丢弃或转发给特定目标。在某些情况下,这些数据包是不受影响的,只是重定向,在某些情况下,数据包中的标头会被修改,在其他情况下,服务器的另一个模块会修改数据包的每个字节。

要配置此服务器,有一个用Java编写的GUI,它使用TCP连接到C服务器(用于交换配置命令)。可以同时有多个连接的GUI。

为了测量服务器的利用率,我编写了一种模块,它启动了两个独立的线程(#2&#3)。执行整个转发工作的主线程(#1)基本上如下所示:

struct monitoring_struct data; //contains 2 * uint64_t for start and end time among other fields
for(;;){
    recvfrom();
    data.start = current_time();
    modifyPacket();
    sendPacket(); //sometimes to multiple destinations
    data.end = current_time();
    writeDataToPipe();
}

current_time函数:

 //give a timestamp in microsecond precision
    uint64_t current_time(void){
        struct timespec spec;
        clock_gettime(CLOCK_REALTIME, &spec);
        uint64_t ts = (uint64_t) ((((double) spec.tv_sec) * 1.0e6) +
 (((double) spec.tv_nsec) / 1.0e3));
        return ts;
    }

如主线程中所示,数据结构被写入管道,其中线程#2等待读取。每次有来自管道的数据时,线程#2使用给定的聚合函数将数据存储在内存中的另一个地方。线程#3是一个循环,它总是休眠约1秒,然后发出聚合值(中位数,平均值,平均值,最大值,下四分之一,上四分之一......),然后重置聚合数据。线程#2和#3由互斥锁同步。

GUI监听此数据(如果监控窗口打开),通过UDP发送给监听器(可能有更多),然后GUI将数字转换为图表,图形和"压力&#34 ;指标。

我想到了这一点,因为在我看来,这个解决方案干扰了线程#1中的最不重要(假设它在多核系统上运行,它始终是,并且除了操作系统和SSH之外)。

由于性能对我的服务器至关重要(版本" 1.0"更简单的配置能够管理使用千兆以太网可能实现的最大流量)我想问一下我的解决方案是否可能是不是我认为确保线程#1上的性能最低,如果你认为有更好的设计?至少我无法想到另一个解决方案是不使用数据本身的锁(避免管道,但可能锁定线程#1)或使用rwlock的共享列表实现,可能会有读者饥饿。

有些情况下数据包较大,但我们目前使用该模式进行性能测量,其中1个Streams每秒发送1000个数据包。我们目前希望确保2.0版本至少可以使用12个Streams(因此每秒12000个数据包),但之前服务器能够管理84个Streams。

将来我想在线程#1中添加其他里程碑时间戳,例如在modifyPacket()内(有多个步骤)和sendPacket()之前。

我尝试过修改current_time()函数,主要是尝试删除它以节省时间,只需存储clock_gettime()的值,但在我的简单测试程序中,current_time()函数总是击败clock_gettime。

提前感谢任何输入。

1 个答案:

答案 0 :(得分:0)

  

如果你认为有更好的设计呢?

简短的回答是使用Data Plane Development Kit (DPDK)及其设计模式和库。这可能是一个相当大的学习曲线,但就性能而言,它是目前最好的解决方案。它是免费和开源的(BSD许可证)。

更详细的答案:

  

将数据结构写入管道

由于线程#1和#2是同一进程的线程,因此使用共享内存而不是管道传递数据要快得多。就像你在线程#2和#3之间使用一样。

  

线程#2使用给定的聚合函数将数据存储在内存中的另一个地方

这两个线程似乎没必要。线程#2可以读取线程#1传递的数据,聚合并发送出来吗?

  

我无法想到另一个没有在数据本身上使用锁的解决方案

查看名为"rings" in DPDK的无锁队列。我们的想法是在线程之间有一个共同的circular buffer,并使用无锁算法从缓冲区入队/出队。

  

我们目前希望确保2.0版至少可以使用12个Streams(因此每秒12000个数据包),但之前服务器能够管理84个Streams。

衡量效果并找出瓶颈(似乎您仍然不能100%确定代码中的瓶颈是什么)。

仅供参考,英特尔发布performance reports for DPDK。 L3转发(即路由)的参考号码每秒高达3000万个数据包。

当然,您可能拥有功能较弱的处理器和NIC,但使用正确的技术可以很容易地达到每秒数百万个数据包。