WebSocket avec Python + uWSGI

Réaliser WebSocket avec Python3.5 + uWSGI

Depuis uWSGI 1.9, WebSocket est pris en charge comme décrit dans ici. Cela semblait facile à faire, alors quand j'ai essayé d'exécuter l'échantillon, cela a échoué à plusieurs reprises, et après l'avoir étudié et remarqué, j'ai passé plus d'une journée entière. À l'origine, j'ai essayé de publier le contenu avec "Non-blocking with Python + uWSGI", mais cela n'a pas pu être réalisé par la même méthode, donc cela sert également de mémorandum. J'ai décidé de poster.

Environnement

Ci-dessous, exécutez dans l'environnement où Python3.5 et Docker sont installés. Il a été construit dans un environnement Mac (macOS Sierra).

Structure du répertoire

La structure des répertoires est la suivante.

スクリーンショット 2017-07-04 0.54.59.png

nginx.conf et nginx.repo sous nginx-python / conf sont "Créer un environnement de Python + uWSGI + Nginx avec Docker" Est le même, donc il est omis. L'utilisation de base des commandes docker-compose et docker y est également décrite.

Créer CRT et KEY pour le certificat SSL

Installer openssl

Créé dans un environnement Mac. Tout d'abord, installez ʻopenssl avec la commande brew`.

$ brew install openssl

Lorsque vous utilisez la dernière commande ʻopenssl installée avec brew, ajoutez ce qui suit à la dernière ligne de .bashrc`.

~/.bashrc


export PATH=$(brew --prefix openssl)/bin:$PATH

Après l'ajout, exécutez ce qui suit.

$ source ~/.bashrc

Créer un certificat

Je dois répondre à quelques questions lors de la création de csr, mais je les ai toutes définies par défaut (uniquement la touche Entrée enfoncée).

$ openssl genrsa -out server.key 2048
$ openssl req -new -key server.key -out server.csr
$ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

Placez les fichiers CRT et KEY créés dans le répertoire ʻapp` (dans cet exemple, il y a CSR, mais ce n'est pas nécessaire).

Exemple de code

Du côté serveur

Une fois la prise de contact avec le client terminée, il attend que les données soient reçues et envoie les données reçues au client telles quelles.

app/websocket.py


import uwsgi


def application(env, start_response):
    uwsgi.websocket_handshake(env['HTTP_SEC_WEBSOCKET_KEY'], env.get('HTTP_ORIGIN', ''))
    msg = uwsgi.websocket_recv()
    print("receive: %s" % msg)
    uwsgi.websocket_send(msg)
    print("end")

Côté client

Un exemple qui se connecte au serveur, envoie des données et attend une réponse lorsque le bouton est enfoncé.

app/client/index.htm


<!DOCTYPE HTML>
<html>
  <head>
    <script type="text/javascript">
      function WebSocketTest()
      {
        if ("WebSocket" in window) {
          alert("WebSocket is supported by your Browser!");
          var ws = new WebSocket("wss://127.0.0.1:8443/ws/");
          ws.onopen = function() {
            ws.send("Hello from client");
            alert("Message is sent...");
          };
          ws.onmessage = function (evt) {
            var received_msg = evt.data;
            alert("Message is received...");
          };
          ws.onclose = function() {
            alert("Connection is closed...");
          };
        } else {
          alert("WebSocket NOT supported by your Browser!");
        }
      }
    </script>
  </head>
  <body>
    <input type="button" onClick="WebSocketTest();" value="WebSocket Test">
  </body>
</html>

Divers fichiers de paramètres

docker-compose.yml Transférer le numéro de port 80 à 8180 et 443 à 8443.

docker-compose.yml


version: "2"
services:
  # nginx
  nginx-python:
    build: ./nginx-python
    ports:
      - "8180:80"
      - "8443:443"
    volumes:
      - ./app/:/var/www/html/app/
    environment:
      TZ: "Asia/Tokyo"

Dockerfile Pour ʻuWSGI, git clone est fait et construit. Sinon, la connexion SSL n'était pas possible. Ici aussi, on suppose que ʻasyncio est utilisé et que le mode non bloquant est également pris en charge, mais WebSocket peut être utilisé même si le mode non bloquant n'est pas pris en charge. Dans ce cas, vous pouvez supprimer en toute sécurité CFLAGS =" - I / usr / include / python3.5m "UWSGI_PROFILE =" asyncio ".

nginx-python/Dockerfile


FROM centos:6.8

ADD ./conf/nginx.repo /etc/yum.repos.d/

# nginx & python
RUN yum localinstall -y http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
RUN yum install -y https://centos6.iuscommunity.org/ius-release.rpm
RUN yum install -y nginx-1.10.1
RUN yum install -y make gcc
RUN yum install -y libxml2-devel
RUN yum install -y python35u python35u-libs python35u-devel python35u-pip
RUN yum clean all

RUN yum install -y git
RUN yum install -y openssl-devel

RUN ln -s /usr/bin/python3.5 /usr/bin/python3 && \
    unlink /usr/bin/python && \
    ln -s /usr/bin/python3 /usr/bin/python && \
    ln -s /usr/bin/pip3.5 /usr/bin/pip && \
    sed -i -e 's/python/python2.6/' /usr/bin/yum

RUN pip install greenlet && \
    cd /root  && \
    git clone https://github.com/unbit/uwsgi.git && \
    cd uwsgi && \
    CFLAGS="-I/usr/include/python3.5m" UWSGI_PROFILE="asyncio" python uwsgiconfig.py --build

# setting nginx
COPY conf/nginx.conf /etc/nginx/nginx.conf
ADD conf/default.conf /etc/nginx/conf.d/default.conf
RUN usermod -u 1000 nginx

EXPOSE 80
EXPOSE 443

ADD ./conf/start.sh /tmp/start.sh

CMD /bin/sh /tmp/start.sh

J'ai remplacé le contenu de / usr / bin / yum par la commande sed en cours de route, mais lorsque je suis lié à Python 3.5 avec la commande python, yum ne peut pas être utilisé.

fichier de configuration nginx

Je ne pouvais tout simplement pas trouver un moyen de l'utiliser avec le protocole ws sans cadre. Puisqu'il s'agit du protocole wss, le proxy inverse est configuré pour accéder avec https.

nginx-python/conf/default.conf


upstream websocket {
    server localhost:9090;
}

server {
    listen              443;
    server_name         _;
    ssl                 on;
    ssl_certificate     /var/www/html/app/server.crt;
    ssl_certificate_key /var/www/html/app/server.key;

    index index.html index.htm;
    charset utf-8;

    root /var/www/html/app/client;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location /ws/ {
        proxy_pass https://websocket/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    location = /favicon.ico {
        empty_gif;
    }
}

Lot de démarrage

Si vous construisez ʻuWSGIsans utiliser le mode non bloquant, vous n'avez pas besoin de l'option--asyncio 10 --greenlet` (si elle est spécifiée, vous obtiendrez une erreur). En outre, il est supposé que vous accéderez via une connexion HTTPS.

nginx-python/conf/start.sh


#!/bin/sh

/etc/init.d/nginx start
cd /var/www/html/app
chmod -R 777 .
 /root/uwsgi/uwsgi --asyncio 10 --greenlet --logto uwsgi.log --https :9090,server.crt,server.key --http-websockets --wsgi-file websocket.py

démo

Construisez et démarrez avec docker-compose up --build. Après avoir démarré normalement, accédez à https: //127.0.0.1: 8443.

スクリーンショット 2017-07-04 1.22.48.png

Lorsque vous cliquez sur le bouton, les alertes s'affichent l'une après l'autre chaque fois que vous appuyez sur «OK» comme indiqué ci-dessous.

スクリーンショット 2017-07-04 1.23.04.png スクリーンショット 2017-07-04 1.23.14.png スクリーンショット 2017-07-04 1.23.27.png スクリーンショット 2017-07-04 1.23.35.png

De plus, ce qui suit est affiché dans le journal côté serveur (ici, / var / www / html / app / uwsgi.log).

receive: b'Hello from client'
end

Autre

Je n'ai pas réalisé ce que je voulais faire à l'origine sans blocage, comme gérer la réception des sockets comme une tâche distincte, donc j'aimerais étudier non seulement ʻasyncio mais aussi geventetgreenlet. .. Le problème est qu'il n'y a pas beaucoup d'informations sur le mode non bloquant de ʻuWSGI et WebSocket.

Recommended Posts

WebSocket avec Python + uWSGI
Non bloquant avec Python + uWSGI
FizzBuzz en Python3
Grattage avec Python
Statistiques avec python
Grattage avec Python
Python avec Go
Twilio avec Python
Jouez avec 2016-Python
Testé avec Python
avec syntaxe (Python)
Bingo avec python
Excel avec Python
Micro-ordinateur avec Python
Cast avec python
Créer un environnement Python + uWSGI + Nginx avec Docker
Communication série avec Python
Zip, décompressez avec python
Django 1.11 a démarré avec Python3.6
Jugement des nombres premiers avec Python
Python avec eclipse + PyDev.
Communication de socket avec Python
Analyse de données avec python 2
Grattage en Python (préparation)
Essayez de gratter avec Python.
Apprendre Python avec ChemTHEATER 03
Recherche séquentielle avec Python
"Orienté objet" appris avec python
Exécutez Python avec VBA
Manipuler yaml avec python
Résolvez AtCoder 167 avec python
Communication série avec python
[Python] Utiliser JSON avec Python
Apprendre Python avec ChemTHEATER 05-1
Apprenez Python avec ChemTHEATER
1.1 Premiers pas avec Python
Binarisation avec OpenCV / Python
3. 3. Programmation IA avec Python
Méthode Kernel avec Python
Grattage avec Python + PhantomJS
Publier des tweets avec python
Conduisez WebDriver avec python
Utiliser mecab avec Python 3
Analyse vocale par python
Pensez à yaml avec python
Utiliser Kinesis avec Python
Premiers pas avec Python
Utiliser DynamoDB avec Python
Getter Zundko avec python
Hello World avec nginx + uwsgi + python sur EC2
Gérez Excel avec python
Loi d'Ohm avec Python
Jugement des nombres premiers avec python
Exécutez Blender avec python
Résoudre des maths avec Python
Python à partir de Windows 7
Créer un environnement de Nginx + uWSGI + Python (Django) avec docker
Carte thermique par Python + matplotlib
Multi-processus de manière asynchrone avec python
Programmation Python avec Atom
Utilisez Python 3.8 avec Anaconda