测试平台接入 HttpRunner V4(六)性能问题排查及解决

性能测试资源占用问题排查及解决

  • 运行压测前资源消耗

img.png

  • 运行压测时(10个并发用户)时资源消耗

img_1.png

  • 压测数据丢失,从压测报告中发现有大量的测试数据丢失,导致数据不完整

img_5.png

  • 通过日志分析,启动压测时大量运行python插件,通过进程管理,goland下挂着N多的python程序,还好每次运行插件的目录是同一个,否则就可以看到运行时生成N多的debugtalk.py文件

img_2.png

img_3.png

问题排查

  • 根据日志得出,有大量开始运行python插件的信息,根据start to prepare python plugin查到initPlugin下调用了BuildPlugin
  • NewCaseRunner的时候调用了initPlugin
  • boomer中有2个地方调用了NewCaseRunner函数
    img_4.png

解决问题思路及优缺点

  1. 在用例运行结束时停止plugin的操作
    • 优点:不用太多操作,不用考虑线程问题
    • 缺点:每个并发数都会运行一个plugin,循环运行,如果一千个用户,那么运行就会有一千个python进程,从上面看,每个python进程需要13M,大约需要13G内存,如果一万个用户需要130G内存,显然也不是很好
  2. 修改NewCaseRunner,在调用initPlugin时增加限制,一个debugtalk只运行一次
    • 优点:一次并发只运行一次
    • 缺点:会造成plugin压力比较大,一万个用户同时操作可能会导致问题

综合考虑考虑下,采用2方式会更加稳妥,缺点可以采用分布式压测做分流问题,分布式压测后续可以通过k8s部署多个工作节点

解决方案

  1. 每次运行时有个唯一值,以此来确定是否需要执行initPlugin,好在之前的设计的 debugtalk path 是唯一值,这次不用改动到其他代码
  2. 通过单例设计模式实现,因为是多线程运行,所以需要加锁
  3. 通过map实现可以同时运行多个实例,主要是兼容接口测试,否则不同项目访问到的可能是同一个plugin实例
// Package hrp NewCaseRunner 方法下把initPlugin修改成cheetahInitPlugin
package hrp

func (r *HRPRunner) NewCaseRunner(testcase *TestCase) (*CaseRunner, error) {
   caseRunner := &CaseRunner{
      testCase:  testcase,
      hrpRunner: r,
      parser:    newParser(),
   }

   // init parser plugin
   //plugin, err := initPlugin(testcase.Config.Path, r.venv, r.pluginLogOn)
   //压测运行时会同时运行多个plugin,用单例方式控制每次压测任务只能运行一个plugin
   plugin, err := cheetahInitPlugin(testcase.Config.Path, r.venv, r.pluginLogOn)

   if err != nil {
      return nil, errors.Wrap(err, "init plugin failed")
   }
   if plugin != nil {
      caseRunner.parser.plugin = plugin
      caseRunner.rootDir = filepath.Dir(plugin.Path())
   }

   // ... 省略其他代码
}
// Package hrp 增加 cheetahInitPlugin 函数
package hrp

import (
   "github.com/httprunner/funplugin"
   "github.com/pkg/errors"
   "sync"
)

var cheetahPlugin = make(map[string]*funplugin.IPlugin)
var mutex sync.Mutex

func cheetahInitPlugin(path, venv string, logOn bool) (plugin funplugin.IPlugin, err error) {
   plugins := cheetahPlugin[path]
   if plugins == nil {
      mutex.Lock()
      defer mutex.Unlock()
      if plugins == nil {
         plugin, err = initPlugin(path, venv, logOn)
         if err != nil {
            return nil, errors.Wrap(err, "init plugin failed")
         }
         cheetahPlugin[path] = &plugin
         plugins = &plugin
      }
   }
   return *plugins, nil
}

解决后效果

  • 数据不再丢失

img_6.png

  • cpu、内存数据正常
    img_7.png

往期文档

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的v4l2接口的测试程序,可以获取摄像头的视频流并显示在屏幕上: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <linux/videodev2.h> #include <errno.h> #define BUFFER_COUNT 4 struct buffer { void *start; size_t length; }; int main(int argc, char **argv) { int fd; struct v4l2_capability cap; struct v4l2_format fmt; struct v4l2_requestbuffers req; struct buffer *buffers; struct v4l2_buffer buf; unsigned int i, n_buffers; fd = open("/dev/video0", O_RDWR); if (fd == -1) { perror("open"); return EXIT_FAILURE; } if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) { perror("VIDIOC_QUERYCAP"); return EXIT_FAILURE; } if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { fprintf(stderr, "The device does not support video capture.\n"); return EXIT_FAILURE; } memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 640; fmt.fmt.pix.height = 480; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; fmt.fmt.pix.field = V4L2_FIELD_NONE; if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) { perror("VIDIOC_S_FMT"); return EXIT_FAILURE; } memset(&req, 0, sizeof(req)); req.count = BUFFER_COUNT; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) { perror("VIDIOC_REQBUFS"); return EXIT_FAILURE; } buffers = calloc(req.count, sizeof(*buffers)); if (!buffers) { fprintf(stderr, "Out of memory.\n"); return EXIT_FAILURE; } for (i = 0; i < req.count; ++i) { memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) { perror("VIDIOC_QUERYBUF"); return EXIT_FAILURE; } buffers[i].length = buf.length; buffers[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); if (buffers[i].start == MAP_FAILED) { perror("mmap"); return EXIT_FAILURE; } } for (i = 0; i < req.count; ++i) { memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) { perror("VIDIOC_QBUF"); return EXIT_FAILURE; } } enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) { perror("VIDIOC_STREAMON"); return EXIT_FAILURE; } for (i = 0; i < BUFFER_COUNT; ++i) { memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) { perror("VIDIOC_DQBUF"); return EXIT_FAILURE; } // Process the frame here. // ... if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) { perror("VIDIOC_QBUF"); return EXIT_FAILURE; } } type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) { perror("VIDIOC_STREAMOFF"); return EXIT_FAILURE; } for (i = 0; i < req.count; ++i) { if (munmap(buffers[i].start, buffers[i].length) == -1) { perror("munmap"); return EXIT_FAILURE; } } free(buffers); if (close(fd) == -1) { perror("close"); return EXIT_FAILURE; } return EXIT_SUCCESS; } ``` 这个程序可以获取摄像头的视频流,对每一帧进行处理后再显示。其中,V4L2_PIX_FMT_YUYV 是常见的一种像素格式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值