1.为什么选择tornado作为web开发框架?码分
2.å¦ä½ç解 Tornado
3.Tornado之ioloop源码学习
4.Tornado主要特点
5.djangoè·drfåªä¸ªå¥½(djangoådrf)
为什么选择tornado作为web开发框架?
Tornado框架之所以被知乎选中,关键在于其异步非阻塞的码分I/O模型,特别适合处理大量Comet长轮询连接,码分这与FriendFeed开发Tornado的码分labview html源码初衷不谋而合。知乎同样需要实时更新动态信息流,码分而Comet技术能有效满足这一需求。码分选择Tornado,码分对知乎来说,码分是码分一次技术上的精准对接。
然而,码分Tornado并非全能。码分rancher源码安装其单线程模型意味着当请求阻塞I/O时,码分进程将无法处理新请求或完成其他阻塞请求,码分类似PHP FastCGI进程的码分运行方式。处理会阻塞I/O的码分请求通常会借助Tornado内置的异步HTTP客户端,转而由其他动态后端执行。竞价指标源码
因此,在实际应用中,Tornado常与Nginx结合使用,Nginx负责处理静态文件等大量I/O操作,以充分利用Tornado的传世源码售卖高效I/O特性。Tornado的I/O时间成本高昂,不宜过多用于此类操作。
针对性能测试,实际上应用中的逻辑处理会阻塞I/O,这将严重影响Tornado性能。源码产品授权在测试代码前加入模拟阻塞的指令,可以直观地观察性能变化。至于Tornado文档不足的问题,阅读其源代码会是一个高效的学习途径,因为代码清晰且注释详尽,容易理解。
记住,利用原生异步特性是发挥Tornado优势的关键。虽然Tornado自带的MySQL库不是异步的,可能导致性能瓶颈,但通过异步调用的简化,gen等工具依然能提高开发效率。在实际应用中,确保所有调用异步化,才能真正释放Tornado的潜力。
å¦ä½ç解 Tornado
åè®¾ä½ è¿ä¸ç¥éTornadoæ¯ä»ä¹ä¹ä¸ç¥é为ä»ä¹åºè¯¥å¯¹å®æå ´è¶£ï¼é£æå°ç¨ç®ççè¯æ¥ä»ç»Tornadoè¿ä¸ªé¡¹ç®ãå¦æä½ å·²ç»å¯¹å®æäºå ´è¶£ï¼ä½ å¯ä»¥è·³å»çä¸ä¸èå 容ã
Tornadoæ¯ä¸ä¸ªç¨Pythonç¼åçå¼æ¥HTTPæå¡å¨ï¼åæ¶ä¹æ¯ä¸ä¸ªwebå¼åæ¡æ¶ã该æ¡æ¶æå¡äºFriendFeedç½ç«ï¼æè¿Facebookä¹å¨ä½¿ç¨å®ãFriendFeedç½ç«æç¨æ·æ°å¤ååºç¨å®æ¶æ§å¼ºçç¹ç¹ï¼æ以æ§è½åå¯æ©å±æ§æ¯å¾åéè§çãç±äºç°å¨å®æ¯å¼æºçäºï¼è¿å¾å½åäºFacebookï¼ï¼æ们å¯ä»¥å½»åºç对å®æ¯å¦ä½å·¥ä½çä¸æ¢ç©¶ç«ã
æè§å¾å¯¹éé»å¡å¼IO (nonblocking IO) åå¼æ¥IO (asynchronous IO AIO)å¾æå¿ è¦è°ä¸è°ãå¦æä½ å·²ç»å®å ¨ç¥éä»ä»¬æ¯ä»ä¹äºï¼å¯ä»¥è·³å»çä¸ä¸èãæå°½å¯è½ç使ç¨ä¸äºä¾åæ¥è¯´æå®ä»¬æ¯ä»ä¹ã
让æ们åè®¾ä½ æ£å¨åä¸ä¸ªéè¦è¯·æ±ä¸äºæ¥èªå ¶ä»æå¡å¨ä¸çæ°æ®ï¼æ¯å¦æ°æ®åºæå¡ï¼åæ¯å¦æ°æµªå¾®åçopen apiï¼çåºç¨ç¨åºï¼ç¶åå¢è¿äºè¯·æ±å°è±è´¹ä¸ä¸ªæ¯è¾é¿çæ¶é´ï¼å设éè¦è±è´¹5ç§éã大å¤æ°çwebå¼åæ¡æ¶ä¸å¤ç请æ±ç代ç 大æ¦é¿è¿æ ·ï¼
def handler_request(self, request):
answ = self.remote_server.query(request) # this takes 5 seconds
request.write_response(answ)
å¦æè¿äºä»£ç è¿è¡å¨å个线ç¨ä¸ï¼ä½ çæå¡å¨åªè½æ¯5ç§æ¥æ¶ä¸ä¸ªå®¢æ·ç«¯ç请æ±ãå¨è¿5ç§éçæ¶é´éï¼æå¡å¨ä¸è½å¹²å ¶ä»ä»»ä½äºæ ï¼æ以ï¼ä½ çæå¡æçæ¯æ¯ç§0.2个请æ±ï¼å¦ï¼è¿å¤ªç³ç³äºã
å½ç¶ï¼æ²¡äººé£ä¹å¤©çï¼å¤§é¨åæå¡å¨ä¼ä½¿ç¨å¤çº¿ç¨ææ¯æ¥è®©æå¡å¨ä¸æ¬¡æ¥æ¶å¤ä¸ªå®¢æ·ç«¯ç请æ±ï¼æ们åè®¾ä½ æ个线ç¨ï¼ä½ å°å¨æ§è½ä¸è·å¾åçæé«ï¼æ以ç°å¨ä½ çæå¡å¨æçæ¯æ¯ç§æ¥å4个请æ±ï¼ä½è¿è¿æ¯å¤ªä½äºï¼å½ç¶ï¼ä½ å¯ä»¥éè¿ä¸æå°æé«çº¿ç¨çæ°éæ¥è§£å³è¿ä¸ªé®é¢ï¼ä½æ¯ï¼çº¿ç¨å¨å ååè°åº¦æ¹é¢çå¼éæ¯æè´µçï¼ææçå¦æä½ ä½¿ç¨è¿ç§æé«çº¿ç¨æ°éçæ¹å¼å°æ°¸è¿ä¸å¯è½è¾¾å°æ¯ç§ä¸ªè¯·æ±çæçã
å¦æ使ç¨AIOï¼è¾¾å°æ¯ç§ä¸å个请æ±çæçæ¯é常轻æ¾çäºæ ãæå¡å¨è¯·æ±å¤çç代ç å°è¢«æ¹æè¿æ ·ï¼
def handler_request(self, request):
self.remote_server.query_async(request, self.response_received)
def response_received(self, request, answ): # this is called 5 seconds later
request.write(answ)
AIOçææ³æ¯å½æ们å¨çå¾ ç»æçæ¶åä¸é»å¡ï¼è½¬èæ们ç»æ¡æ¶ä¸ä¸ªåè°å½æ°ä½ä¸ºåæ°ï¼è®©æ¡æ¶å¨æç»æçæ¶åéè¿åè°å½æ°éç¥æ们ãè¿æ ·ï¼æå¡å¨å°±å¯ä»¥è¢«è§£æ¾å»æ¥åå ¶ä»å®¢æ·ç«¯ç请æ±äºã
ç¶èè¿ä¹æ¯AIOä¸å¤ªå¥½çå°æ¹ï¼ä»£ç æç¹ä¸ç´è§äºãè¿æï¼å¦æä½ ä½¿ç¨åTornadoè¿æ ·çå线ç¨AIOæå¡å¨è½¯ä»¶ï¼ä½ éè¦æ¶å»å°å¿ä¸è¦å»é»å¡ä»ä¹ï¼å 为æææ¬è¯¥å¨å½åè¿åç请æ±é½ä¼åä¸è¿°å¤çé£æ ·è¢«å»¶è¿è¿åã
å ³äºå¼æ¥IOï¼æ¯å½åè¿ç¯è¿åç®åçä»ç»æ´å¥½çå¦ä¹ èµæ请ç The CK problemã
æºä»£ç
该项ç®ç±githubæ管ï¼ä½ å¯ä»¥éè¿å¦ä¸å½ä»¤è·å¾ï¼è½ç¶éè¿é 读è¿ç¯æç« ä½ ä¹å¯ä»¥ä¸éè¦å®æ¯å§ã
git clone git://github.com/facebook/tornado.git
å¨tornadoçåç®å½ä¸ï¼æ¯ä¸ªæ¨¡åé½åºè¯¥æä¸ä¸ª.pyæ件ï¼ä½ å¯ä»¥éè¿æ£æ¥ä»ä»¬æ¥å¤æä½ æ¯å¦ä»å·²ç»ä»ä»£ç ä»åºä¸å®æ´çè¿åºäºé¡¹ç®ãå¨æ¯ä¸ªæºä»£ç çæ件ä¸ï¼ä½ é½å¯ä»¥åç°è³å°ä¸ä¸ªå¤§æ®µè½çç¨æ¥è§£é该模åçdoc stringï¼doc stringä¸ç»åºäºä¸å°ä¸¤ä¸ªå ³äºå¦ä½ä½¿ç¨è¯¥æ¨¡åçä¾åã
IOLoop模å
让æ们éè¿æ¥çioloop.pyæ件ç´æ¥è¿å ¥æå¡å¨çæ ¸å¿ãè¿ä¸ªæ¨¡åæ¯å¼æ¥æºå¶çæ ¸å¿ãå®å å«äºä¸ç³»åå·²ç»æå¼çæ件æ述符ï¼è¯è ï¼ä¹å°±æ¯æ件æéï¼åæ¯ä¸ªæ述符çå¤çå¨ï¼handlersï¼ãå®çåè½æ¯éæ©é£äºå·²ç»åå¤å¥½è¯»åçæ件æ述符ï¼ç¶åè°ç¨å®ä»¬åèªçå¤çå¨ï¼ä¸ç§IOå¤è·¯å¤ç¨çå®ç°ï¼å ¶å®å°±æ¯socketä¼å¤IO模åä¸çselect模åï¼å¨Javaä¸å°±æ¯NIOï¼è¯è 注ï¼ã
å¯ä»¥éè¿è°ç¨add_handler()æ¹æ³å°ä¸ä¸ªsocketå å ¥IO循ç¯ä¸ï¼
def add_handler(self, fd, handler, events):
"""Registers the given handler to receive the given events for fd."""
self._handlers[fd] = handler
self._impl.register(fd, events | self.ERROR)
_handlersè¿ä¸ªåå ¸ç±»åçåéä¿åçæ件æ述符ï¼å ¶å®å°±æ¯socketï¼è¯è 注ï¼å°å½è¯¥æ件æ述符åå¤å¥½æ¶éè¦è°ç¨çæ¹æ³çæ å°ï¼å¨Tornadoä¸ï¼è¯¥æ¹æ³è¢«ç§°ä¸ºå¤çå¨ï¼ãç¶åï¼æ件æ述符被注åå°epollï¼unixä¸çä¸ç§IO轮询æºå¶ï¼è²ä¼¼ï¼è¯è 注ï¼å表ä¸ãTornadoå ³å¿ä¸ç§ç±»åçäºä»¶ï¼æåçå¨æ件æè¿°ä¸çäºä»¶ï¼è¯è 注ï¼ï¼READï¼WRITE å ERRORãæ£å¦ä½ æè§ï¼ERRORæ¯é»è®¤ä¸ºä½ èªå¨æ·»å çã
self._implæ¯select.epoll()åselet.select()两è ä¸çä¸ä¸ªãæ们ç¨åå°çå°Tornadoæ¯å¦ä½å¨å®ä»¬ä¹é´è¿è¡éæ©çã
ç°å¨è®©æ们æ¥ççå®é ç主循ç¯ï¼ä¸ç¥ä½æ ï¼è¿æ®µä»£ç 被æ¾å¨äºstart()æ¹æ³ä¸ï¼
def start(self):
"""Starts the I/O loop.
The loop will run until one of the I/O handlers calls stop(), which
will make the loop stop after the current event iteration completes.
"""
self._running = True
while True:
[ ... ]
if not self._running:
break
[ ... ]
try:
event_pairs = self._impl.poll(poll_timeout)
except Exception, e:
if e.args == (4, "Interrupted system call"):
logging.warning("Interrupted system call", exc_info=1)
continue
else:
raise
# Pop one fd at a time from the set of pending fds and run
# its handler. Since that handler may perform actions on
# other file descriptors, there may be reentrant calls to
# this IOLoop that update self._events
self._events.update(event_pairs)
while self._events:
fd, events = self._events.popitem()
try:
self._handlers[fd](fd, events)
except KeyboardInterrupt:
raise
except OSError, e:
if e[0] == errno.EPIPE:
# Happens when the client closes the connection
pass
else:
logging.error("Exception in I/O handler for fd %d",
fd, exc_info=True)
except:
logging.error("Exception in I/O handler for fd %d",
fd, exc_info=True)
poll()æ¹æ³è¿åä¸ä¸ªå½¢å¦(fd: events)çé®å¼å¯¹ï¼å¹¶èµå¼ç»event_pairsåéãç±äºå½ä¸ä¸ªä¿¡å·å¨ä»»ä½ä¸ä¸ªäºä»¶åçåå°æ¥æ¶ï¼Cå½æ°åºä¸çpoll()æ¹æ³ä¼è¿åEINTRï¼å®é æ¯ä¸ä¸ªå¼ä¸º4çæ°å¼ï¼ï¼æ以"Interrupted system call"è¿ä¸ªç¹æ®çå¼å¸¸éè¦è¢«æè·ãæ´è¯¦ç»ç请æ¥çman pollã
å¨å é¨çwhile循ç¯ä¸ï¼event_pairsä¸çå 容被ä¸ä¸ªä¸ä¸ªçååºï¼ç¶åç¸åºçå¤çå¨ä¼è¢«è°ç¨ãpipe å¼å¸¸å¨è¿éé»è®¤ä¸è¿è¡å¤çã为äºè®©è¿ä¸ªç±»éåºæ´ä¸è¬çæ åµï¼å¨ï¼å¸¸è¢«äººè¯´ææ§è½é®é¢çRubyåRailsï¼ä¸æ¯ç §æ ·å¯ä»¥å¼ååºtwitteråï¼åè ç°å¨ç硬件ã带宽ææ¬å ¶å®æ¯å¾ä½çï¼ç¹å«æäºäºè®¡ç®å¹³å°åï¼äººåææ¬ææ¯æè´µçï¼æ²¡æä¸ä¸çIPæ ¹æ¬å°±ä¸ç¨å¤ªå¨ææ§è½é®é¢ï¼æµéä¸å»äºè±ç¹é±ä¹°ç¹æå¡å¨ç©ºé´å¥½äºï¼ç®åå¿«éç解å³æ§è½é®é¢ã?注ï¼åé¢æç½åè´¨çæâQuoraæ¯ç¨Pylonså¼åçâè¿æ ·ç说æ³ä¸å®¢è§ï¼ç¹è¯´æä¸ä¸ï¼è¿éæ说çæ个ç½ç«Aæ¯ç¨Bå¼åçï¼åªæ¯æA主è¦æé¨åæ¯ç±Bå¼åçï¼å¤§å®¶å°±ä¸è¦åå»çº ç»Aè¿ç¨Cäºã
å ³äºpythonwebï¼å»ºè®®å¤å¦ä¹ ä¸ä¸å¤§ç¥çæ¡ä¾ãä»éé¢æåç²¾é«çä¸è¥¿å 以å¸æ¶ï¼Pythonå¦ä¹ æå请çä¸é¢ç代ç
learning?=?input('Do?you?want?to?learn?Python?now(Yes?or?No):')
a?=?str(learning)
if?a?==?'Yes':
print('QQ')
else:
print('Thanks!!')
Pythonä¸å¤§webæ¡æ¶åå«æ¯ä»ä¹åªä¸ªæ´å¥½ã导读ãç®åï¼Pythonæ¯è¾ç«çä¸å¤§webæ¡æ¶æDjangoãFlaskåTornadoï¼è¦è®ºè¿ä¸ä¸ªWebæ¡æ¶åªä¸ªæ´å¥½çè¯ï¼å»ºè®®ä¸ç¹ï¼Django帮æ们äºå æ建äºå¥½å¤ï¼ä¸æä¼å¿«ä¸äºï¼å¦ä¹ çè¯å¯ä»¥å ä»Djangoå¦èµ·ï¼ç¶ååå¦ä¹ FlaskåTornadoï¼ä¸é¢æ们就æ¥å ·ä½äºè§£ä¸ä¸Pythonä¸å¤§webæ¡æ¶ç详æ ã
1ãDjango
Djangoæ¯ä¸ä¸ªå¼æ¾æºä»£ç çWebåºç¨æ¡æ¶ï¼ç±Pythonåæãéç¨äºMTVçæ¡æ¶æ¨¡å¼ï¼å³æ¨¡åMï¼æ¨¡æ¿Tåè§å¾Vãå®æåæ¯è¢«å¼åæ¥ç¨äºç®¡çå³ä¼¦æ¯åºçéå¢æä¸çä¸äºä»¥æ°é»å 容为主çç½ç«çï¼å³æ¯CMS(å 容管çç³»ç»)软件ã
2ãFlask
Flaskæ¯ä¸ä¸ªä½¿ç¨Pythonç¼åçè½»é级Webåºç¨æ¡æ¶ãå ¶WSGIå·¥å ·ç®±éç¨Werkzeugï¼æ¨¡æ¿å¼æå使ç¨Jinja2
ãFlask使ç¨BSDææã
Flaskä¹è¢«ç§°ä¸ºâmicroframeworkâï¼å 为å®ä½¿ç¨ç®åçæ ¸å¿ï¼ç¨extension
å¢å å ¶ä»åè½ãFlask没æé»è®¤ä½¿ç¨çæ°æ®åºãçªä½éªè¯å·¥å ·ã
Flaskå¾è½»ï¼è±å¾å°çææ¬å°±è½å¤å¼åä¸ä¸ªç®åçç½ç«ãé常éååå¦è å¦ä¹ ãFlaskæ¡æ¶å¦ä¼ä»¥åï¼å¯ä»¥èèå¦ä¹ æ件ç使ç¨ãä¾å¦ä½¿ç¨WTForm+
Flask-WTFormæ¥éªè¯è¡¨åæ°æ®ï¼ç¨SQLAlchemy+Flask-SQLAlchemyæ¥å¯¹ä½ çæ°æ®åºè¿è¡æ§å¶ã
3ãTornado
Tornadoæ¯ä¸ç§Webæå¡å¨è½¯ä»¶çå¼æºçæ¬ãTornadoåç°å¨ç主æµWebæå¡å¨æ¡æ¶(å æ¬å¤§å¤æ°Python
çæ¡æ¶)æçææ¾çåºå«ï¼å®æ¯éé»å¡å¼æå¡å¨ï¼èä¸é度ç¸å½å¿«ã
å¾å©äºå ¶éé»å¡çæ¹å¼å对epollçè¿ç¨ï¼Tornadoæ¯ç§å¯ä»¥å¤çæ°ä»¥å计çè¿æ¥ï¼å æ¤Tornadoæ¯å®æ¶Webæå¡çä¸ä¸ª
çæ³æ¡æ¶ã
å ³äºPythonä¸å¤§webæ¡æ¶çç®åä»ç»ï¼å°±ç»å¤§å®¶å享å°è¿éäºï¼å½ç¶å¦ä¹ æ¯æ°¸æ æ¢å¢çï¼å¦ä¹ ä¸é¡¹æè½æ´æ¯åçç»èº«ï¼æ以ï¼åªè¦è¯åªåå¦ï¼ä»ä¹æ¶åå¼å§é½ä¸æï¼å¸æ大家æç´§æ¶é´è¿è¡å¦ä¹ å§ã
DjangoåFlaskæ¯è¾å°åºåªä¸ªæ¯è¾å¥½ç¨Flaskæ¯å°èç²¾çå¾®æ¡æ¶ï¼å®ä¸åDjangoé£æ ·å¤§èå ¨ï¼å¦æ使ç¨Flaskå¼åï¼å¼åè éè¦èªå·±å³å®ä½¿ç¨åªä¸ªæ°æ®åºORMã模åç³»ç»ãç¨æ·è®¤è¯ç³»ç»çï¼éè¦èªå·±ç»æã
ä¸éç¨Djangoå¼å对æ¯ï¼å¼åè å¨é¡¹ç®å¼å§çæ¶åå¯è½éè¦è±è´¹æ´å¤çæ¶é´å»äºè§£ãæéå个ç»ä»¶ï¼å æ¤Flaskå¼åççµæ´»åº¦æ´é«ï¼å¼åè å¯ä»¥æ ¹æ®èªå·±çéè¦å»éæ©åéçæ件ã
å½ç¶Flaskåå²ç¸å¯¹è¾çï¼ç¬¬ä¸æ¹APPèªç¶æ²¡æDjangoé£ä¹å ¨é¢ã
æä½³æ件ä¸ä¼ ç»ä»¶ââfilepondè¿ä¸ªç»ä»¶ä¸ºå端æä¾äºvueéé vue-filepondï¼ä¸ºå端æä¾äºDjangoéé django-drf-filepond.äºè ç¸äºé åï¼å¼ç®±å³ç¨ï¼è½å¤å®ç°æ件ä¸ä¼ ç个æ§åå®å¶ã主è¦å æ¬ä¸ä¸å 个æ¹é¢ï¼
å ¶å¸¸è§æä½æµç¨ä¸º
æ·»å æ件åï¼vue-filepondä¼èªå¨ä¼ ç»server.url.è¿éå端éè¦é åå®ç°è¿ä¸ªä¸´æ¶æ件çåå¨ï¼å¹¶ä¸è¿å临æ¶æ件çIDã
å¦æå端ç¨äºdjango-drf-filepondåºçè¯ï¼ä¸é¢è¿ä¸ªè¿ç¨å°±ä¸ç¨èªå·±å¨æåäºï¼åªéè¦é ç½®ä¸æ¡è·¯ç±path('fp/',include('django_drf_filepond.urls'))å³å¯ãåæ¶ï¼è¿æ¡è·¯ç±è½å¤è·å端é åï¼å®æ临æ¶æ件çä¸ä¼ ãéä¼ ãå é¤çåè½ã
常è§çåºæ¯æ¯ï¼ç¨æ·å°æ件è·è¡¨åçå ¶å®å段ä¸åæ交è³æå¡å¨ãå¨ç¹å»ç¡®è®¤åï¼filepondå·²ç»å®æäºä¸´æ¶æ件çä¸ä¼ ãæ以ï¼ç¹å»ç¡®è®¤åï¼æå¡å¨åªç¨å°ç¡¬çéç临æ¶æ件æä¹ åå³å¯ï¼ç¼©çäºçå¾ æ¶é´ï¼ç¨æ·ä½éªä¼æ´å¥½ã
pythonæ¾å·¥ä½æ¯å¦Django好è¿æ¯Flask好ï¼è¿ä¿©é½æºç®åçï¼Djangoåflaské½å¦ä¸ä¸æ¯è¾å¥½ï¼Pythonåºç¡ä¹å¾éè¦ãè¿ä¿©å¦å¥½äºï¼å·¥ä½ä¸æï¼èªèµè¿æ¯çå ·ä½æ åµã
ç»è¯ï¼ä»¥ä¸å°±æ¯é¦å¸CTOç¬è®°ä¸ºå¤§å®¶æ´ççå ³äºdjangoè·drfåªä¸ªå¥½çå ¨é¨å 容äºï¼æè°¢æ¨è±æ¶é´é 读æ¬ç«å 容ï¼å¸æ对æ¨ææ帮å©ï¼æ´å¤å ³äºdjangoè·drfåªä¸ªå¥½çç¸å ³å 容å«å¿äºå¨æ¬ç«è¿è¡æ¥æ¾åã