Since uWSGI 1.9, WebSocket is supported as described in here. It seemed easy to do, so when I actually tried to run the sample, it failed many times, and after investigating and noticing it, I spent more than a whole day. Originally, I tried to post the contents together with "Non-blocking with Python + uWSGI", but it could not be realized by the same method, so it also serves as a memorandum. I decided to post.
Below, execute in the environment where Python3.5 and Docker are installed. It was built in a Mac (macOS Sierra) environment.
The directory structure is as follows.
nginx.conf
and nginx.repo
under nginx-python / conf
are "Create an environment of Python + uWSGI + Nginx with Docker" Is the same, so it is omitted. The basic usage of the docker-compose
and docker
commands is also described there.
Created in Mac environment. First, install ʻopenssl with the
brew` command.
$ brew install openssl
If you want to use the latest ʻopenssl command installed with
brew, add the following to the last line of
.bashrc`.
~/.bashrc
export PATH=$(brew --prefix openssl)/bin:$PATH
After adding, execute the following.
$ source ~/.bashrc
I need to answer some questions when creating csr
, but all defaulted (only enter key pressed).
$ 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
Place the created CRT and KEY files under the ʻapp` directory (in this example, there is CSR, but it is not necessary).
After the handshake with the client is completed, it waits for the data to be received and sends the received data to the client as it is.
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")
A sample that connects to the server, sends data, and waits for a response when the button is pressed.
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>
docker-compose.yml Forward port number 80 to 8180 and 443 to 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
For ʻuWSGI,
git clone is done and built. Otherwise, SSL connection was not possible. Also, here, it is assumed that ʻasyncio
is used and non-blocking mode is also supported, but WebSocket
can be used even if non-blocking mode is not supported. In that case, you can safely delete 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
I replaced the contents of / usr / bin / yum
with the sed
command on the way, but when I link to Python 3.5 with the python
command, yum
cannot be used.
I just couldn't find a way to use it with the ws
protocol without a framework. Since it is the wss
protocol, the reverse proxy is set to access with 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;
}
}
If you build ʻuWSGI to not use non-blocking mode, you don't need the
--asyncio 10 --greenlet` option (if it is specified, you will get an error).
Also, it is assumed that you will access via HTTPS connection.
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
Build and start with docker-compose up --build
. After starting normally, access https://127.0.0.1:8443
.
When you click the button, alerts will be displayed one after another each time you press "OK" as shown below.
In addition, the following is output to the log on the server side (here, /var/www/html/app/uwsgi.log
).
receive: b'Hello from client'
end
I haven't realized what I originally wanted to do without blocking, such as handling socket reception as a separate task, so I would like to investigate not only ʻasyncio but also
geventand
greenlet. .. It hurts that there is not much information about ʻuWSGI
's non-blocking mode and WebSocket.
Recommended Posts