一、目的
最近晚上闲来无事,就顺便学习了一下python Tornado框架,学完它后,用它来写了一个聊天系统,技术栈主要是tornadao+websocket+bookstrap,实现了登录功能,好友在线状态以及群聊的功能,最后用nginx+supervisor部署起来,下面就我来介绍一下是怎么做的吧
二、效果图
1.登录
2.聊天界面
三、核心代码
1.服务端
from datetime import datetime
from tornado.web import RequestHandler, MissingArgumentError
from tornado.websocket import WebSocketHandler
from tornado.log import gen_log
clients = dict()
chat_record = list()
users = list()
class WebsocketChatHandler(WebSocketHandler):
@property
def username(self):
for item in users:
if item["user_id"] == self.user_id:
return item["username"]
return "anonymous"
def open(self):
self.user_id = self.get_argument("user_id")
clients[self.user_id] = {
"user_id": self.user_id,
"object": self,
}
self.set_user_status()
self.send_users()
gen_log.info(f"{self.username}|[{self.user_id}] is chatting")
# self.send_chat_record()
def set_user_status(self):
for user in users:
if user["user_id"] == self.user_id:
user["status"] = "online"
break
def on_message(self, message):
global chat_record
chat_message = {
"user_id": self.user_id,
"username": self.username,
"message": message,
"time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"avatar": avatar,
}
chat_record.append(chat_message)
# 只保存200条聊天记录
chat_record = chat_record[-200:]
self.send_multi_message(chat_message)
def on_close(self):
# 删除连接对象
del clients[self.user_id]
# 用户状态置为离线
for user in users:
if user["user_id"] == self.user_id:
user["status"] = "offline"
break
# 刷新用户状态
self.send_users()
gen_log.info(f"{self.username}|[{self.user_id}] offline")
def check_origin(self, origin: str) -> bool:
return True
# 发送用户列表
def send_users(self):
for key in clients.keys():
clients[key]["object"].write_message({"data": users, "type": 1})
# 群聊
def send_multi_message(self, message):
for key in clients.keys():
clients[key]["object"].write_message({"data": message, "type": 3})
2.客户端
function webSocketHandler() {
var host = window.document.location.host;
if ("WebSocket" in window) {
ws = new WebSocket(`ws://${host}/websocket?user_id=${USER_ID}`);
ws.onopen = function () {
// connect
};
ws.onmessage = function (evt) {
var data = JSON.parse(evt.data);
console.log(JSON.parse(evt.data));
if (data.type === 1) {
showUserList(data.data)
} else if (data.type === 2) {
} else if (data.type === 3) {
addMessage(data.data)
}
};
ws.onclose = function () {
console.log("quit")
}
} else {
alert("create websocket failed")
}
}
webSocketHandler();
上面代码只粘出了核心的websocket的连接与交互,登录很简单就不贴出来了
三、部署
1.nginx
upstream chat {
server xxxxx:8888;
}
server {
listen 8800;
#填写绑定证书的域名
server_name xxxxxx;
location / {
proxy_pass http://chat;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
#proxy_read_timeout 180s;
}
}
2.supervisor
[program:chat]
command=/home/apps/chat/env/bin/python3 main.py
directory=/home/apps/chat/src/
stderr_logfile=/home/apps/chat/logs/chat_err.log
user=chat
stopsignal=QUIT
autorestart=true
loglevel=info
redirect_stderr=true
startsecs=1
stopasgroup=true
killasgroup=true
花了一周时间终于搞定了,开心