在同一个 Locust 文件中混合使用休息和 WebSocket 性能测试。

huangapple go评论69阅读模式
英文:

locust mixed rest and websocket performance testing on the same locustfile

问题

我试图编写一个具有以下要求的测试:

  1. 测试包括两个调用,这两个调用必须按顺序执行。
  2. 第一个调用是一个REST调用(HTTP POST)。
  3. 第二个调用是一个WebSocket调用。

我如何可以在同一个Locust文件中实现这一点?这是可能的吗?

这是我的测试:

import time
import json
import logging
import re
import gevent
import websocket

from locust import User, FastHttpUser, SequentialTaskSet, task, between

headers = [
    "auth: headers",
]

class MySocketUserClass(User):
    # ...(你的WebSocket相关代码)...

class WebSocketCall(MySocketUserClass):
    @task
    def my_task(self):

        self.connect("wss://example.com", header=headers)

        # 示例订阅
        self.send({'example': 'payload'})

        # 等待附加的推送,同时偶尔发送心跳,就像真实客户端一样
        self.sleep_with_heartbeat(10)

    def on_message(self, message):
        # TO BE IMPLEMENTED
        pass

class RestCall(FastHttpUser):
    # ...(你的REST调用相关代码)...

class UserBehaviour(SequentialTaskSet):
    tasks = [RestCall, WebSocketCall]

但只有RestCall被执行。

英文:

I'm trying to write a test with the following requirementes:

  1. the test has two calls, the calls has to be exectuted in sequence
  2. the first call is a REST call (http post)
  3. the second one is a websocket call

How I can implement this in the same locustfile?
Is this possible?

This is my test:

import time
import json
import logging
import re
import gevent
import websocket

from locust import User, FastHttpUser, SequentialTaskSet, task, between

headers = [
    "auth: headers",
]


class MySocketUserClass(User):
    def connect(self, host: str, header=[], **kwargs):
        self.ws = websocket.create_connection(host, header=header, **kwargs)
        gevent.spawn(self.receive_loop)

    def send(self, body, context={}):
        self.environment.events.request.fire(
            request_type="WSS",
            name='test',
            response_time=None,
            response_length=len(body),
            exception=None,
            context={**self.context(), **context},
        )
        logging.debug(f"WSS: {body}")
        self.ws.send(json.dumps(body))

    def on_message(self, message):  # override this method in your subclass for custom handling
        print(message)
        self.environment.events.request.fire(
            request_type="WSR",
            name='test',
            response_time=0,
            response_length=len(message),
            exception=None,
            context=self.context(),
        )

    def receive_loop(self):
        while True:
            message = self.ws.recv()
            logging.debug(f"WSR: {message}")
            self.on_message(message)
    
    def sleep_with_heartbeat(self, seconds):
        while seconds >= 0:
            gevent.sleep(min(15, seconds))
            seconds -= 15
            self.send({})

class WebSocketCall(MySocketUserClass):
    @task
    def my_task(self):

        self.connect("wss://example.com", header=headers)

        # example of subscribe
        self.send({'example': 'payload'})

        # wait for additional pushes, while occasionally sending heartbeats, like a real client would
        self.sleep_with_heartbeat(10)

    def on_message(self, message):
        # TO BE IMPLEMENTED
        pass

class RestCall(FastHttpUser):
    default_headers = {
        "accept": "application/json",
        "accept-encoding": "gzip, deflate, br",
        "accept-language": "en-US,en;q=0.9,it;q=0.8",
        "content-type": "application/json",
        "sec-ch-ua": '"Chromium";v="110", "Not A(Brand";v="24", "Microsoft Edge";v="110"',
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": '"Windows"',
        "sec-fetch-dest": "empty",
        "sec-fetch-mode": "cors",
        "sec-fetch-site": "same-origin",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.50",
        "origin": "https://example.com",
        "referer": "https://example.com",
    }

    def call_rest(self):
        self.iam = 'rest'

        with self.rest(
            "POST",
            "https://example.com",
            json={
                "example": "payload"
            },
        ) as resp:
            # CHECK RESPONSE
            print(resp)

    @task
    def sent1(self):
        self.call_rest()

class UserBehaviour(SequentialTaskSet):
    tasks = [RestCall, WebSocketCall]

but only the RestCall is executed

答案1

得分: 0

问题可能在于你将多个Users放入了SequentialTaskSet任务中。我之前没见过有人这样做,所以我不知道行为会是什么样。我建议将你的MySocketUserClass设置为一个不继承自User的泛型类对象。你可以在初始化时传入environment,以便仍然可以触发事件。然后,不要再创建WebSocketCall作为另一个类,只需将task作为SequentialTaskSet内的函数即可。对于RestCallUser,你也可以做同样的事情。然后,创建一个FastHttpUser,将SequentialTaskSet作为其tasks。查看此Stack Overflow问题以获取示例:

https://stackoverflow.com/a/63573818/7355637

或者,你也可以不使用SequentialTaskSet,而是将sent1任务变得更通用,并在同一个任务内首先调用你的websocket my_task相关的内容。一个单一任务中可以包含多个函数调用。Locust仍然会适当地触发任何事件。

英文:

The problem may be that you're putting multiple Users in your SequentialTaskSet tasks. I haven't seen anyone do that before so I don't know what the behavior would be. I'd recommend making your MySocketUserClass a generic class object that doesn't inherit from User. You could make it take the environment on init so it could still fire events. Then instead of having WebSocketCall be yet another class, just make the task a function inside the SequentialTaskSet. You can actually do the same for RestCallUser. Then you make a FastHttpUser with the SequentialTaskSet as its tasks. See this SO question for an example:

https://stackoverflow.com/a/63573818/7355637

Alternatively, you can also just not use SequentialTaskSet and instead make the sent1 task more generic and call your websocket my_task stuff first inside the same task. A single task can include multiple function calls inside it. Locust will still fire whatever events appropriately.

答案2

得分: 0

以下是翻译好的内容:

你可以使用一个单独的 User 类来实现这个。

正如 SocketIOUser 的文档所述,它是:

一个包括 Socket.IO WebSocket 连接的用户。你可以轻松地将它用作普通 WebSocket 的模板,Socket.IO 只是我的使用案例。你可以使用多重继承将其与 HttpUser 结合起来(class MyUser(HttpUser, SocketIOUser))。

所以可以这样:

class MySocketIOUser(HttpUser, SocketIOUser):
    @task
    def my_task(self):
        self.connect("wss://...")
        self.client.post(...)
        self.send("something")

有关 WebSocket 部分的更多信息,请参阅 https://github.com/SvenskaSpel/locust-plugins/blob/master/examples/socketio_ex.py

英文:

You can use a single User class for this.

As SocketIOUser’s documentation states, it is:

> A User that includes a socket io websocket connection.
You could easily use this a template for plain WebSockets,
socket.io just happens to be my use case. You can use multiple
inheritance to combine this with an HttpUser
(class MyUser(HttpUser, SocketIOUser)

So something like:

class MySocketIOUser(HttpUser, SocketIOUser):
    @task
    def my_task(self):
        self.connect("wss://...")
        self.client.post(...)
        self.send("something")

See https://github.com/SvenskaSpel/locust-plugins/blob/master/examples/socketio_ex.py for more about the WebSocket-parts.

huangapple
  • 本文由 发表于 2023年3月3日 20:16:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/75626969.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定