У меня есть большая программа, в которой while True
l oop в основном выполняет много задач, а также та же программа должна выступать в качестве REST-сервера, например, получать сообщения через POST и предоставлять информацию клиентам через ответы на GETs. Лучший способ объяснить мою текущую дилемму - это пример скелета:
Main.py:
# Main.py
import collections
import Database as Database
flaskApp = Flask(__name__)
messageQueue = collections.deque()
def main():
global messageQueue
Database.init()
# start Flask on a separate thread so main can keep going
# (i.e. calling flaskApp.run() here not on a separate thread would block)
flaskAppThread = threading.Thread(target=flaskApp.run)
flaskAppThread.start()
# various other init stuff here
# beginning of main loop that does various things and runs as long as the program is running
while True:
# other item 1 here
# other item 2 here
# etc.
# based on the results of item 1 and 2, add stuff to the database
Database.addStuff(stuffToAdd)
importantInfo = Database.getImportantInfo()
# do something with important info here
# if the messageQueue is not empty, process any messages
if messageQueue:
# process messageQueue contents here
# clear the queue once done processing queued up messages
messageQueue = collections.deque()
# end if
# other item 3 here
# other item 4 here
# etc.
if someExitCondition:
break
# end if
# end while
# close down hardware I/O, threads, database connections, etc. here
# end main
# Flask REST function to make a list of stuff available to client upon request
@flaskApp.route('/get_list_of_stuff', methods=['GET'])
def restGetListOfStuff():
listOfStuff = Database.getListOfStuff()
return jsonify({'listOfStuff': listOfStuff})
# end function
# Flas REST function to receive a message from client via POST, the message will contain some sort of information
@flaskApp.route('/message', methods=['POST'])
def restMessage():
message = request.json['message']
messageQueue.append(message)
return jsonify( { 'message': '' } ), 200
# end function
if __name__ == '__main__':
main()
Database.py:
# Database.py
import sqlite3
class Database(object):
@staticmethod
def init():
Database.conn = sqlite3.connect('/path/to/database.sqlite')
Database.cur = Database.conn.cursor()
# end function
@staticmethod
def addStuff(stuffToAdd):
Database.cur.execute('INSERT INTO SomeTable ' + str(stuffToAdd))
# end function
@staticmethod
def getImportantInfo():
Database.cur.execute('SELECT * FROM ImportantInfoTable')
results = Database.cur.fetchall()
return results
# end function
@staticmethod
def getListOfStuff():
Database.cur.execute('SELECT * FROM TableWithStuff')
results = Database.cur.fetchall()
return results
# end function
# many other database functions here
# end class
Обратите внимание на следующее:
-Main должен получать REST-сообщения от клиентов через POST, затем изменять поведение на основе полученных сообщений
-Main должен предоставлять информацию клиентам по запросу через REST GET
-Main имеет некоторое время True l oop, которое всегда работает во время работы программы и должно выполнять различные действия, поэтому нет никаких строк о someActivity.run()
, какой блок может быть запущен в main
в главном потоке
- в настоящее время я использую SQLite в качестве базы данных, при необходимости могу изменить на MySQL (в настоящее время у меня сложилось впечатление, что это может не помочь)
- Очередь сообщений должна быть добавлена функциями Flask
и считана с помощью main
, поэтому здесь нельзя использовать мультипроцессы, потому что тогда в памяти очереди сообщений будут отдельные копии вместо одной копии, которую могут оба ee
С вышеупомянутой архитектурой проблема заключается в том, что программа запускается в функции restGetListOfStuff
на линии listOfStuff = Database.getListOfStuff()
Я получаю эту ошибку:
sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 140597538834240 and this is thread id 140593565001472.
Я понимаю, что это за ошибка утверждая, что существуют отдельные потоки, один в main
, а другой в restGetListOfStuff
, оба обращаются к базе данных, и что SQLite не позволяет этого.
Поэтому мой вопрос, как эту архитектуру можно изменить на избежать этой ошибки?
Я искал волшебный c способ заставить линию в main
:
importantInfo = Database.getImportantInfo()
работать в том же потоке, что и flaskApp
но я не смог найти способ сделать это до сих пор. Я не уверен, что это даже возможно. Я попытался создать отдельную функцию, украшенную @flaskApp.route
, которая вызывала Database.getImportantInfo()
и вызывала ее из main, но это не сработало, потому что эта функция все еще выполняется в том же потоке, что и main
, если вызывается из main.
Какие у меня есть варианты? Будет только одна база данных, поэтому мне очень нравится, что есть только один класс базы данных, который фактически является c (шаблоны проектирования люди называют это «синглтоном»). Есть ли способ сохранить эту общую архитектуру, которая мне нравится, но обойти ошибку SQLite в отдельном потоке?
Большинство решений, которые я могу найти, включают в себя такие вещи, как открытие отдельного соединения с базой данных внутри каждого Flask
функция, которая включала бы дублирование кода базы данных как в основной, так и в основной базе данных, что я бы предпочел не делать, а также я обеспокоен тем, что открытие и закрытие многих подключений к базе данных может вызвать проблемы с производительностью, поэтому я бы предпочел не go в этом направлении, если этого можно избежать.