V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
drymonfidelia
V2EX  ›  Python

Flask+SQLite+Tornado 在 Windows 2019 下 100 并发都撑不住是怎么回事?

  •  
  •   drymonfidelia · 7 天前 · 2392 次点击

    非常简易的一个测试程序

    # -*- coding: UTF-8 -*-
    
    from flask import Flask, request
    import json
    import time
    import re
    
    import sys
    import asyncio
    
    from tornado.ioloop import IOLoop
    from tornado.wsgi import WSGIContainer
    from tornado.httpserver import HTTPServer
    
    import sqlalchemy
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String, create_engine
    from sqlalchemy.orm import sessionmaker
    
    engine = create_engine('sqlite:///database.db?check_same_thread=False')
    
    Base = declarative_base()
    
    
    class ClientLog(Base):
        __tablename__ = 'clientlog'
        log_id = Column(Integer, primary_key=True, autoincrement=True)
        message = Column(String(512))
    
        def __repr__(self):
            return "A"
    
    
    from sqlalchemy.ext.declarative import DeclarativeMeta
    
    
    class AlchemyEncoder(json.JSONEncoder):
        def default(self, obj):
            if isinstance(obj.__class__, DeclarativeMeta):
                # an SQLAlchemy class
                fields = {}
                for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
                    data = obj.__getattribute__(field)
                    try:
                        json.dumps(data)  # this will fail on non-encodable values, like other classes
                        fields[field] = data
                    except TypeError:
                        fields[field] = None
                # a json-encodable dict
                return fields
    
            return json.JSONEncoder.default(self, obj)
    
    
    Base.metadata.create_all(engine, checkfirst=True)
    
    Session = sessionmaker(bind=engine)
    session = Session()
    
    
    app = Flask("app")
    
    @app.route('/api', methods=['GET', 'POST'])
    @app.route('/api/', methods=['GET', 'POST'])
    def api1():
        session.add(ClientLog(message="test"))
        session.commit()
        return "test"
    
    def launch_server():
        if sys.platform == 'win32':
            asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
        http_server = HTTPServer(WSGIContainer(app))
        http_server.listen(8080)
        IOLoop.current().start()
    
    launch_server()
    

    在阿里云的 Windows 2019 模板下 100 并发就会崩溃退出,报错

    Exception in thread Tornado selector:
    Traceback (most recent call last):
      File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 1045, in _bootstrap_inner
        self.run()
      File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 982, in run
        self._target(*self._args, **self._kwargs)
      File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\tornado\platform\asyncio.py", line 574, in _run_select
        rs, ws, xs = select.select(to_read, to_write, to_write)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ValueError: too many file descriptors in select() 
    

    但是我在 Windows 10 的电脑上测试并发没问题 之前测试换了另外一个对 Windows 更友好的 web framework (想不起来名字,找了半天没找到)可以是可以,但是估计它用了不止一个进程写 SQLite ,导致冲突无法提交更改 业务需求调用一个 Windows Only 的 pip 包 ,无法更换 Linux 服务器

    19 条回复    2025-01-12 16:25:10 +08:00
    drymonfidelia
        2
    drymonfidelia  
    OP
       7 天前
    @sagaxu 这篇回答我看过了,之前试过 ProactorEventLoop 好像没效果
    so1n
        3
    so1n  
       7 天前   ❤️ 1
    哇,你這個搭配也太奇妙了...tornado 驅動 flask
    drymonfidelia
        4
    drymonfidelia  
    OP
       7 天前
    @so1n 那在 Windows 下应该搭配什么呢?
    DeWjjj
        5
    DeWjjj  
       7 天前
    @drymonfidelia 直接 fastapi 不行么?
    dingyaguang117
        6
    dingyaguang117  
       7 天前
    异步框架里用同步 IO ( MySQL ),高起来才怪呢,不伦不类啊
    roundgis
        7
    roundgis  
       7 天前 via Android
    @drymonfidelia tornado 在 windows 下用 selector loop 的 你設也沒用。selector fd size 為 512 要想大要自己改

    Tornado 設計上就沒考慮過支持 proactor

    再說你要全異步才有可能把並發數提上去 包括和數據庫連接的驅動
    raycool
        8
    raycool  
       7 天前
    为什么 flask 要和 tornado 一起搭配使用。
    iorilu
        9
    iorilu  
       7 天前
    没用 aysnc 本来并发就不可能高阿, 100 并不算少
    raptor
        10
    raptor  
       7 天前
    换成 linux/macos,mysql/pgsql 就没问题了。python 的异步框架在 windows 下基本都不能并行,比如 gevent/asyncio 之类,sqlite 也是为单机应用设计,并发也不行。
    yagamil
        11
    yagamil  
       7 天前
    sqlite 多线程会有问题的。放弃吧
    heimoshuiyu
        12
    heimoshuiyu  
       6 天前
    sqlite 没有并发,一切写入都是单线程。如果你硬是开多线程那么之后造成互相竞争锁更慢
    heimoshuiyu
        13
    heimoshuiyu  
       6 天前
    sqlite 文档 FAQ 里就写道

    “Threads are evil. Avoid them. ”
    drymonfidelia
        14
    drymonfidelia  
    OP
       6 天前
    @yagamil
    @heimoshuiyu
    @raptor
    @roundgis 那如果我需要一个简易的可以支持高并发的 API 服务器,又不想打包一个完整的 mysql 进我的程序,应该怎么做呢?
    3085570450tt
        15
    3085570450tt  
       6 天前   ❤️ 1
    可以参考以下这篇文章: https://www.tornadoweb.org/en/branch5.1/wsgi.html#running-wsgi-apps-on-tornado-servers 。总之说,使用 wsgicontainer 的性能会低于使用 `gunicorn` 或者 `uwsgi` 等专业的 wsgi 服务器。如果你想使用 flask 写异步的话,倒不至于这么麻烦,https://flask.palletsprojects.com/en/stable/async-await/ flask 也支持了初步的异步,或者直接使用 quart (完全异步版 flask) 。或者你想换新能高一点的,可以去这个网站看一下 benchmark 自行选择。还有 sqlite 不支持异步,如果你想用 sqlite, 又想使用异步接口的话,可以看看这个 limbo https://turso.tech/blog/introducing-limbo-a-complete-rewrite-of-sqlite-in-rust
    3085570450tt
        16
    3085570450tt  
       6 天前   ❤️ 1
    @3085570450tt 那个 benchmark 的网站忘记贴上去了,https://web-frameworks-benchmark.netlify.app/result?l=python
    drymonfidelia
        17
    drymonfidelia  
    OP
       5 天前
    @3085570450tt 感谢,limbo 这个数据库我看了下介绍,有说 compatible with SQLite ,不知道这个是什么意思,是能直接把 SQLite 替换掉么?好像没有 ORM 支持,我研究了下好像是不能搭配 sqlalchemy 使用
    3085570450tt
        18
    3085570450tt  
       5 天前
    @drymonfidelia 你可以理解为完全兼容 sqlite, 只是加上了一些其他的特性,比如异步等,是可以直接进行替换的
    yinmin
        19
    yinmin  
       5 天前 via iPhone
    1. 改用 sqlite3 库能提升性能。
    2. sqlite 是文件型数据库,高并发写入是有瓶颈的。一般商用软件都同时支持 sqlite 和 mysql ,sqlite 用于低负载,高负载用 mysql 。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2704 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 05:12 · PVG 13:12 · LAX 21:12 · JFK 00:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.