项目目标,RESTfulAPI--->websocketAPI
1、关于部署和启动的坑
gunicorn.conf.py
import os
import stat
preload_app = True
# bind = '0.0.0.0:5005'
bind = '0.0.0.0:5003'
backlog = 512
chdir = os.path.dirname(os.path.abspath(__file__))
# worker_class = 'sync'
worker_class = "eventlet"
# worker_connections = 600
# backlog = 1024
workers = 1
threads = 8
loglevel = 'info'
access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"'
if not os.path.exists('logs'):
os.mkdir('logs')
os.chmod('logs', stat.S_IRWXU+stat.S_IRWXG+stat.S_IRWXO)
accesslog = os.path.join(chdir, "logs/gunicorn_access.log")
errorlog = os.path.join(chdir, "logs/gunicorn_error.log")
flasklog = os.path.join(chdir, "logs/flask.log")
gunicorn+flask-socketio刚开始部署的时候总是出现connect不正常连接的情况,多个客户端连接的时候,出现冲突,表现为连接总数始终为1。出现这种情况的原因是gunicorn里的workers一定要设为1,否则就会出现冲突。
2、多线程/进程下socketio的emit功能失效,已解决
在app里添加了一个redis_listener的线程。
现在我想在这个线程里调用socketio的emit发送给前端消息,发现发不过去。
class Expire_listener():
def __init__(self):
self._value_lock = threading.Lock()
# pass
def start(self):
sub_expire = redis_conn.pubsub()
# 事件通过 Redis 的订阅与发布功能(pub/sub)来进行分发,故需要订阅 __keyevent@0__:expired,其中0表示dbindex
sub_expire.psubscribe(**{'__keyevent@0__:expired': self.__expire_handler})
logger.info("redis listen start...")
def start_listen():
with cur_app.app_context():
for data in sub_expire.listen():
pass
threading.Thread(target=start_listen).start()
这个线程用来监听一个计时工具,计时完成就触发update事件来刷新页面,实现不用手动刷新界面就可以实时更新页面
这段代码是用while true实现的,也可以,就是很不好看,而且是伪实时,只是过了几秒钟自动刷新界面而已。
thread = None
@socketio.on("connect",namespace='/api/semaphore')
def connect():
#global client_dict, flag_client_dict, users
sid = request.sid
logger.info({sid})
#thread = socketio.start_background_task(target=sub, sid=sid)
SingletonUserMgr().user_login(sid,thread)
class SingletonUserMgr(object):
users = 0
client_dict = {}
flag_client_dict = {}
def __init__(self):
self.lock = threading.Lock()
def user_login(self,sid,thread):
with self.lock:
self.users += 1
self.flag_client_dict[sid] = True
self.client_dict[sid] = thread
if thread is None:
thread = socketio.start_background_task(target=sub)
print(self.flag_client_dict)
print(f"connect 当前连接数 {self.users}")
def sub():
while True:
socketio.sleep(5)
socketio.emit("update", data={'data':'update'},namespace='/api/semaphore')
使用消息队列,将两个线程进行通信
终于通过消息队列实现了两个线程之间的通信:
具体是在socketio初始化app的时候,将redis的消息队列作为参数传入了,然后就可以在redis_listener下进行emit消息了
class DevelopmentConfig(BaseConfig):
""" 开发配置 """
#开发环境的semaphore token
SEMAPHORE_TOKEN = "xxxxxxxxx"
#前端容器用户名以及容器名称
FRONTEND_USER = "xxxxx"
FRONTEND_CONTAINER = "xxxxxv1.6"
#Ansible Semaphore 的配置
SEMAPHORE_IP = "10.18.0.12"
SEMAPHORE_PORT = "3500"
# redis配置
REDIS_HOST = os.getenv('REDIS_HOST') or "10.xx.x.xx"
REDIS_PORT = int(os.getenv('REDIS_PORT') or 6380)
REDIS_PWD = '123456'
# redis默认16个数据库
REDIS_DB = 0
REDIS_URL = f'redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}'
# mysql 配置
MYSQL_USERNAME = os.getenv('MYSQL_USERNAME') or "root"
MYSQL_PASSWORD = os.getenv('MYSQL_PASSWORD') or "123456"
MYSQL_HOST = os.getenv('MYSQL_HOST') or "10.xx.x.xx"
MYSQL_PORT = int(os.getenv('MYSQL_PORT') or 3307)
MYSQL_DATABASE = os.getenv('MYSQL_DATABASE') or "book_portal"
# mysql 数据库的配置信息
SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{MYSQL_USERNAME}:{MYSQL_PASSWORD}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DATABASE}"
SQLALCHEMY_TRACK_MODIFICATIONS = True
SQLALCHEMY_ECHO = False