我正在尝试构建服务器来采样流价格订阅源并使用SQLAlchemy更新postgres数据库。我正在使用映射类的线程实例,这似乎可以工作,但不稳定。
Stream类的1个或2个实例没有问题,但是说10个,线程随机且无提示地失败。每次失败之前,SQLAlchemy都会给出一条错误消息,因此这似乎是杀死线程的原因。流没有任何问题,它始终稳定。
我是否错过了SQLAlchemy设置?是否有更好的方式将多个实时订阅馈入SQL?
代码:
import time
import json
from threading import Thread, Lock
import sqlalchemy as db
from sqlalchemy.orm import scoped_session, sessionmaker, relationship
from sqlalchemy.ext.declarative import declarative_base
# Setup SQLAlchemy
engine = db.create_engine('postgresql://localhost:5432/Project', echo=False)
metadata = db.MetaData(bind=engine)
Session = scoped_session(sessionmaker(bind=engine))
Base = declarative_base()
Base.metadata.create_all(engine)
session = Session()
#DB classes
#static data table
class StockMaster(Base):
__tablename__ = 'stock_master'
id = db.Column(db.Integer, primary_key=True)
ticker = db.Column(db.String)
stock_name = db.Column(db.String)
@classmethod
def find_by_ticker(cls,ticker):
return session.query(StockMaster).filter(StockMaster.ticker==ticker).first()
#live data table
class StockLive(Base):
__tablename__ = 'stock_live'
id = db.Column(db.Integer, primary_key=True)
quote = db.Column(db.Numeric)
timestamp = db.Column(db.Numeric)
ticker_id = db.Column(db.Integer, db.ForeignKey('stock_master.id'))
ticker = relationship("StockMaster", foreign_keys=[ticker_id])
def __init__(self, quote, ticker_id, timestamp):
self.quote=quote
self.ticker_id=ticker_id
self.timestamp=timestamp
def save_to_db(self):
session.add(self)
session.commit()
@classmethod
def find_by_ticker_id(cls,ticker_id):
return session.query(StockLive).filter(StockLive.ticker_id==ticker_id).first()
@classmethod
def find_by_ticker(cls,ticker):
ticker_id = StockMaster.find_by_ticker(ticker).id
return session.query(StockLive).filter(StockLive.ticker_id==ticker_id).first()
class Stream(Thread):
def __init__(self,ticker):
Thread.__init__(self)
self.ticker=ticker
self.quote=1
self.data_set = StockLive.find_by_ticker(self.ticker)
self.count=0
def run(self):
con.subscribe(self.ticker)
current_mid=1
while True:
new_data = json.loads(con.get_price(self.ticker).to_json())
new_mid = new_data['Mid']
if new_mid == current_mid:
pass
else:
current_mid = new_mid
self.data_set.quote = current_mid
self.data_set.timestamp = time.time()
try:
self.data_set.save_to_db()
self.count+=1
except:
self.data_set = StockLive.find_by_ticker(self.ticker)
print('error saving to db for '+self.ticker)
time.sleep(.1)
if __name__ == '__main__':
threads={}
for ticker in tickerlist:
try:
threads[ticker]=Stream(ticker)
threads[ticker].setName('Thread ' + ticker)
threads[ticker].start()
except:
print('Error setting up '+ticker)
while True:
for ticker in tickerlist:
if threads[ticker].isAlive()==False:
threads[ticker]=Stream(ticker)
SQLAlchemy错误消息:
/anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py:2323: SAWarning:当前未使用'Session.add()'操作 在刷新过程的执行阶段受支持。结果可能 不一致。考虑使用备用事件侦听器或 连接级操作。 % 方法) /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py:2425: SAWarning:属性历史事件累积在1个先前清除的事件上 内部刷新事件处理程序中的实例已被重置,并且将 不会导致数据库更新。考虑使用set_committed_value() 在内部冲洗事件处理程序中避免此警告。 %len_) 线程MSFT线程中的异常:追溯(最近一次调用):
文件 “ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, _flush中的第2436行 transaction.commit()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, 第465行,正在提交 self._assert_active(prepared_ok = True)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, _assert_active中的第285行 引发sa_exc.ResourceClosedError(closed_msg)sqlalchemy.exc.ResourceClosedError:此交易已关闭在处理上述异常期间,发生了另一个异常:
回溯(最近通话最近):文件 “”,第48行,正在运行 self.data_set.save_to_db()在save_to_db中的文件““,第44行 session.commit()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, 第954行,正在提交 self.transaction.commit()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, 第467行,正在提交 self._prepare_impl()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, _prepare_impl中的第447行 self.session.flush()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, 2313行,齐平 self._flush(objects)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, _flush中的第2440行 transaction.rollback(_capture_exception = True)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/util/langhelpers.py”, 第76行,退出 compat.reraise(类型_,值,回溯)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/util/compat.py”, 第249行,加价 提高价值文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, _flush中的第2440行 transaction.rollback(_capture_exception = True)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, 回滚中的第483行 self._assert_active(prepared_ok = True,rollback_ok = True)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, _assert_active中的第285行 引发sa_exc.ResourceClosedError(closed_msg)sqlalchemy.exc.ResourceClosedError:此交易已关闭
在处理上述异常期间,发生了另一个异常:
回溯(最近通话最近):文件 _bootstrap_inner中的“ /anaconda3/lib/python3.7/threading.py”,第917行 self.run()运行中的文件“”,第53行 self.data_set = StockLive.find_by_ticker(self.ccy)文件“”,第52行,在find_by_ticker中 ticker_id = StockMaster.find_by_ticker(ticker).id文件“”,在find_by_ticker的第23行 返回session.query(StockMaster).filter(StockMaster.ticker == ticker).first() 文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/query.py”, 首先是2895行 ret = list(self [0:1])文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/query.py”,行 2687,在获取中 返回列表文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/query.py”,行 2994,在 iter 中 self.session._autoflush()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, _autoflush中的第1493行 self.flush()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, 2313行,齐平 self._flush(objects)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, _flush中的第2400行 subtransactions = True)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, 865行,开始 nested = nested)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, _begin中的第297行 self._assert_active()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”, _assert_active中的第264行 “此会话处于“准备”状态;没有其他” sqlalchemy.exc.InvalidRequestError:此会话处于“准备”状态 州;在此事务内无法发出进一步的SQL。
答案 0 :(得分:0)
您的代码可能还有其他问题,但是一个明显的问题是您与多个线程共享session
。
应该在每个线程中创建它们,而不是设置全局session
。
我无法运行您的代码,但是您可以尝试执行以下操作:
完全删除全局会话变量。您不需要它。然后修改您的方法和线程以包括本地会话:
@classmethod
def find_by_ticker(cls,ticker, session):
return session.query(StockMaster).filter(StockMaster.ticker==ticker).first()
...
class Stream(Thread):
def __init__(self,ticker):
Thread.__init__(self)
self.ticker=ticker
self.quote=1
self.session = Session()
self.data_set = StockLive.find_by_ticker(self.ticker, self.session)
self.count=0
或者类似的东西。现在,这将使每个线程都有自己的会话,并且您的代码将开始表现得更好。