[PYTHON] Test de charge Websocket avec Locust

Test de charge Websocket avec Locust

le criquet est bon, n'est-ce pas? criquet. Tous les tests de charge récents ont été réalisés avec des criquets. Il est vraiment facile d'utiliser python même pour des scénarios compliqués.

Cependant, récemment, le nombre d'applications toujours actives augmente et il semble qu'il existe de nombreuses situations dans lesquelles un simple modèle req / res ne peut pas être correctement appliqué.

J'ai donc essayé de charger la tâche Locust à l'aide de Websocket afin qu'elle puisse être confirmée à partir de l'écran Web.

websocketlocust.png

Par défaut, l'unité est en ms, mais si c'est en ms dans la communication socket, 0 est normalement répété, donc l'unité est μs. Comme vous pouvez le voir dans le code, je viens de créer une prise dans Task, de communiquer et de notifier le résultat aux criquets, donc je n'ai pas du tout touché au criquet lui-même.

Étant donné que les criquets ont une conception simple et que la partie pour la construction d'un environnement distribué et la partie pour demander et agréger les résultats sont séparées, il est bon de pouvoir le faire facilement.

locustfile.py


# -*- coding:utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
from __future__ import print_function

import json
import uuid
import time
import gevent

from websocket import create_connection
import six

from locust import HttpLocust, TaskSet, task
from locust.events import request_success


class ChatTaskSet(TaskSet):
    def on_start(self):
        self.user_id = six.text_type(uuid.uuid4())
        ws = create_connection('ws://127.0.0.1:5000/chat')
        self.ws = ws

        def _receive():
            while True:
                res = ws.recv()
                data = json.loads(res)
                end_at = time.time()
                response_time = int((end_at - data['start_at']) * 1000000)
                request_success.fire(
                    request_type='WebSocket Recv',
                    name='test/ws/chat',
                    response_time=response_time,
                    response_length=len(res),
                )

        gevent.spawn(_receive)

    @task
    def sent(self):
        start_at = time.time()
        body = json.dumps({'message': 'hello, world', 'user_id': self.user_id, 'start_at': start_at})
        self.ws.send(body)
        request_success.fire(
            request_type='WebSocket Sent',
            name='test/ws/chat',
            response_time=int((time.time() - start_at) * 1000000),
            response_length=len(body),
        )


        
class ChatLocust(HttpLocust):
    task_set = ChatTaskSet
    min_wait = 0
    max_wait = 100

Le but est de notifier le résultat avec events.request_success.fire et de lancer un thread gevent pour la réception. Si vous définissez normalement la réception dans @task, la tâche s'arrêtera pendant ce temps et les autres tâches du même TaskSet s'arrêteront également.

À propos, l'exemple de serveur Echo & PubSub avec la charge est le script suivant.

server.py


# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
from __future__ import print_function

from collections import defaultdict
import json

from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer
from flask import Flask, request
from werkzeug.exceptions import abort


app = Flask(__name__)

ctr = defaultdict(int)


@app.route('/echo')
def echo():
    ws = request.environ['wsgi.websocket']
    if not ws:
        abort(400)

    while True:
        message = ws.receive()
        if message is not None:
            r = json.loads(message)
            ctr[r['user_id']] += 1

        ws.send(message)


@app.route('/report')
def report():
    return '\n'.join(['{}:\t{}'.format(user_id, count) for user_id, count in ctr.items()])


socket_handlers = set()


@app.route('/chat')
def chat():
    ws = request.environ['wsgi.websocket']
    socket_handlers.add(ws)

    while True:
        message = ws.receive()
        for socket_handler in socket_handlers:
            try:
                socket_handler.send(message)
            except:
                socket_handlers.remove(socket_handler)


if __name__ == '__main__':
    http_server = WSGIServer(('', 5000), app, handler_class=WebSocketHandler)
    http_server.serve_forever()

Le fichier est également téléchargé dans le référentiel suivant. https://gist.github.com/yamionp/9112dd6e54694d594306

Recommended Posts

Test de charge Websocket avec Locust
Outil de test de charge Build Locust 1.1 sur Docker
Test de stress avec Locust écrit en Python
Préparez un environnement de test de charge distribué avec l'outil de test de charge Python Locust
Jugement des nombres premiers avec Python
Renforcez avec le test de code ⑦
Renforcez avec le test de code ⑨
Renforcez avec le test de code ③
Renforcez avec le test de code ⑤
Jugement des nombres premiers avec python
Renforcez avec le test de code ②
Divers outils de test de charge
WebSocket avec Python + uWSGI
Renforcez avec le test de code ①
CHARGER DES DONNÉES avec PyMysql
Renforcez avec le test de code ⑧
Renforcez avec le test de code ⑨
Charger json imbriqué avec des pandas
Tester les logiciels embarqués avec Google Test
Test unitaire du flacon avec pytest
Tester la sortie standard avec Pytest
Développement piloté par les tests avec Django Partie 3
Enregistrer et charger des données avec joblib, pickle
Charger les modules Django avec un interpréteur
Développement piloté par les tests avec Django Partie 6
Charger plusieurs fichiers JavaScript avec PyWebView
Développement piloté par les tests avec Django Partie 2
Charger une image gif avec Python + OpenCV
Développement piloté par les tests avec Django Partie 1
Développement piloté par les tests avec Django Partie 5
Contrôle des relances de test avec Luigi + pytest