تلفیق Tornado و Django
Django یکی از فریمورکهای پایتونی برای ایجاد صفحات پویای وب است. این فریمورک در کنار Flask بیشترین محبوبیت را در بین فریمورکهای پایتونی دارند. اما فریمورکهای دیگری نیز وجود دارند که به دلایلی کمتر از آنها استفاده میشود اما این فریمورکها مزایایی دارند که فریمورکهای مشهوری مانند Django از آنها بیبهرهاند. اگر تجربه کدنویسی با فریمورک Django را داشته باشید،میدانید که این فریمورک زمان توسعه را به حداقل رسانده است. در Django کارهای زیادی تنها با یک خط کد انجام میشود. اما Django نقاط ضعفی دارد. یکی از نقاط ضعف این فریمورک عدم پشتیبانی از WebSocket است. وبسوکتها کانکشنهای پایداری بین کلاینت و سرور هستند. این ارتباط مانا معمولاً بدون نیاز به رفرش صفحه توسط کاربر، اطلاعات را بین کلاینت و سرور جابهجا میکند. Django سعی کرده است که فقدان WebSocketها را با Channels جبران کند. اما کارایی چنلها هرگز به اندازه وبسوکتها نبوده و نیست. یکی از مزایای پایتون این است که میتوان فریمورکها را با هم تلفیق کرد کاری که در سایر زبانها مانند PHP عملاً غیرممکن است. پس اگر فریمورکی باشد که WebSocketها را به صورت native پشتیبانی کند، میتوان آن فریمورک را با Django تلفیق کرد. و اینجاست که Tornado وارد میشود.
Tornado یک فریمورک پایتونی و یک کتابخانه ناهمزمان شبکه است و گزینهای مناسب برای
long polling
و websocket
و نیز کاربردهایی است که در آن برای کاربران به کانکشنهای طولانی نیاز باشد. این کتابخانه برای حل مسئله ۱۰ هزار کانکشن همزمان ایجاد شده است. پس تا اینجا میدانیم که نقطه قوت Tornado دو موردی است که در بالا ذکر شد(نقاط قوت فقط websocket و long polling نیست. مثلاً یکی دیگر از نقاط قوت تورنادو قابلیت شخصی سازی گسترده آن است البته از آن به عنوان نقطه ضعف هم یاد میشود).
تورنادو long polling را با استفاده از ناهمزمانی انجام میدهد و websocket هم به صورت native در این فریمورک گنجانده شده است. در این مطلب قصدی برای آموزش Tornado ندارم و تنها این فریمورک را با Django تلفیق خواهیم کرد. بعد از این مقدمه نسبتاً طولانی به سراغ اصل مطلب میرویم.
نصب Django و Tornado
برای نصب این دو فریمورک بهتر است که یک محیط مجازی(virtual env) ایجاد کنید. بعد با استفاده ازpip
این دو فریمورک را نصب کنید.
pip install django
pip install tornado
بعد از نصب این دو فریمورک، باید یک پروژه جدید جنگو ایجاد کنید. بدین منظور از دستور زیر استفاده میشود.
django-admin startproject PROJECTNAME
به جای PROJECTNAME اسم پروژه مد نظر خود را قرار دهید. من برای این مطلب اسم پروژه را matrix قرار میدهم. اکنون وارد پوشه پروژه شوید. در داخل این پوشه، یک پوشه دیگر با همان نام و یک فایل manage.py وجود دارد(البته قصدی برای آموزش Django هم وجو ندارد). حالا یک فایل به هر اسمی که دوست داشتید ایجاد کنید(من با نام run_tornado.py ایجاد کردم). پروژه شما باید به صورت زیرباشد.
matrix\
matrix\
manage.py
run_tornado.py
در داخل فایل run_tornado.py باید کدی را که برای اجرای پروژه نیاز است، وارد کنیم. البته ذکر این نکته ضروری است که Django برای اجرا از wsgi استفاده میکند اما Tornado بر روی یک نخ و با سرور خودش اجرا میشود. Tornado برای ارتباط با سایر فریمورکهایی که بر بستر wsgi اجرا میشوند، از tornado.wsgi استفاده میکند. برای routeها هم از یک FallbackHandler استفاده میکند. شاید توضیحات گیجکننده به نظر بیاید پس شروع به نوشتن کد میکنیم. در فایل run_tornado.py کدهای زیر را مینویسیم. در صورت لزوم در جلوی کدها توضیحات نوشته شده است.
import sys
import os
from tornado.web import RequestHandler, Application, FallbackHandler, url
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.options import options, define, parse_command_line
from tornado.wsgi import WSGIContainer # Tornado wsgi container for comnunicate with other wsgi frameworks
from django.core.wsgi import get_wsgi_application # Get Django wsgi app
define('port', default=8001, type=int, help='run on the given port') # Define command line arguments
class Reganto(Application): # class for define routes and settings
def __init__(self):
wsgi_app = get_wsgi_application() # get Django wsgi app
container = WSGIContainer(wsgi_app) # add Django wsgi app to Tornado wsgi container
handlers = [ # define handlers
# Tornado's routes MUST be first routes
url(r'/dev/', DevHandler, name='dev'),
url(r'.*', FallbackHandler, {'fallback': container}) # Fallback handler
]
settings = {
'debug': True,
}
super(Reganto, self).__init__(handlers, **settings) # call superclass constructor with specific arguments
class DevHandler(RequestHandler):
def get(self):
self.write(
{
'status': 'dev',
}
)
def main():
os.environ['DJANGO_SETTINGS_MODULE'] = 'matrix.settings' # Add this Django setting in run time, substitute matrix with your project name
sys.path.append('./matrix') # Append your project name to path
parse_command_line() # parse command line arguments --> port
http_server = HTTPServer(Application()) # Create Tornado http server *
http_server.listen(options.port) # http server listen on specific port that given from command line or use default
IOLoop.instance().start() # start sever
if __name__ == "__main__":
main()
بعد از تکمیل کدها در run_tornado.py باید پروژه را بالا بیاوریم. بدین منظور به بش رفته و با دستور زیر پروژه را اجرا کنید.
python run_tornado.py --port=8001
اگر پورت را تعیین نکنید به صورت پیش فرض از 8001 استفاده میشود. نکتهای که حائز اهمیت است اینکه در این فایل DevHandler قرار داده شده است. اما شما میتوانید کدهای تورنادوی خودتان را در یک پوشه جداگانه قرار دهید. برای اینکار یک پوشه جدید با هر نامی که دوست داشتید، ایجاد کنید. من با نام tornadostorm یک پوشه جدید ایجاد میکنم. بعد درون این پوشه یک فایل با هر نامی که دوست داشتید ایجاد کنید. من با نام fasthon.py یک فایل جدید ایجاد میکنم. بعد درون این فایل کدهای تورنادوی خود را قرار دهید و در فایل run_tornado.py این پوشه را import کنید. با این تغییرات، کد به صورت زیر خواهد شد.
import sys
import os
from tornado.web import RequestHandler, Application, FallbackHandler, url
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.options import options, define, parse_command_line
from tornado.wsgi import WSGIContainer
from django.core.wsgi import get_wsgi_application
from tornadostorm import fasthon # change
define('port', default=8001, type=int, help='run on the given port')
class Reganto(Application):
def __init__(self):
wsgi_app = get_wsgi_application()
container = WSGIContainer(wsgi_app)
handlers = [
url(r'/dev/', fasthon.DevHandler, name='dev'), # Change
url(r'.*', FallbackHandler, {'fallback': container})
]
settings = {
'debug': True,
}
super(Reganto, self).__init__(handlers, **settings)
def main():
os.environ['DJANGO_SETTINGS_MODULE'] = 'matrix.settings'
sys.path.append('./matrix')
parse_command_line()
http_server = HTTPServer(Application())
http_server.listen(options.port)
IOLoop.instance().start()
if __name__ == "__main__":
main()
حالا سادگی Django و سرعت Tornado در اختیار شماست.
Tornado برای ارسال روتها به فریمورک دیگر از FallbackHandler استفاده میکند. به رجکس توجه کنید.
پروژههای مرتبط را میتوانید در لینکهای زیر بیابید.