日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

python高性能服务器编写,Tornado的高性能服务器开发常用方法

發(fā)布時(shí)間:2025/3/19 python 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python高性能服务器编写,Tornado的高性能服务器开发常用方法 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

最近一直開(kāi)發(fā)AI人臉識(shí)別相關(guān)的項(xiàng)目,需要提供給客戶(hù)一些服務(wù),所以我需要開(kāi)發(fā)一些服務(wù)端程序。由于AI算法都是用python3寫(xiě)的,所以我就索性用起了python開(kāi)發(fā)服務(wù)端,畢竟速度也快,以前用過(guò)Flask、Django,這次決定有Tornado來(lái)做,對(duì)該框架做了一系列的調(diào)用,癡迷于他的異步非阻塞的功能,項(xiàng)目開(kāi)發(fā)完之后有了一些經(jīng)驗(yàn),特此對(duì)以前的資料查詢(xún)做一個(gè)總結(jié),以便后面可以復(fù)用。

高性能源于Tornado基于Epoll(unix為kqueue)的異步網(wǎng)絡(luò)IO。因?yàn)閠ornado的單線(xiàn)程機(jī)制,一不小心就容易寫(xiě)出阻塞服務(wù)[block]的代碼。不但沒(méi)有性能提高,反而會(huì)讓性能急劇下降。因此,探索tornado的異步使用方式很有必要。

簡(jiǎn)而言之,Tornado的異步包括兩個(gè)方面,異步服務(wù)端和異步客戶(hù)端。無(wú)論服務(wù)端和客戶(hù)端,具體的異步模型又可以分為回調(diào)[callback]和協(xié)程[coroutine]。具體應(yīng)用場(chǎng)景,也沒(méi)有很明確的界限。往往一個(gè)請(qǐng)求服務(wù)里還包含對(duì)別的服務(wù)的客戶(hù)端異步請(qǐng)求。

服務(wù)端的異步方式

服務(wù)端異步,可以理解為一個(gè)tornado請(qǐng)求之內(nèi),需要做一個(gè)耗時(shí)的任務(wù)。直接寫(xiě)在業(yè)務(wù)邏輯里可能會(huì)block整個(gè)服務(wù)。因此可以把這個(gè)任務(wù)放到異步處理,實(shí)現(xiàn)異步的方式就有兩種,一種是yield掛起函數(shù),另外一種就是使用類(lèi)線(xiàn)程池的方式。

請(qǐng)看一個(gè)同步例子(借用的):

class SyncHandler(tornado.web.RequestHandler):

def get(self, *args, **kwargs):

# 耗時(shí)的代碼

os.system("ping -c 2 www.google.com")

self.finish('It works')

此時(shí)耗時(shí)動(dòng)作將嚴(yán)重阻塞系統(tǒng)的性能,導(dǎo)致并發(fā)量很小,因?yàn)樘幚硪粋€(gè)請(qǐng)求的時(shí)間就好幾秒。

一、我們將以上代碼改成異步的,使用回調(diào)函數(shù)

from tornado.ioloop import IOLoop

class AsyncHandler(tornado.web.RequestHandler):

@tornado.web.asynchronous

@tornado.gen.coroutine

def get(self, *args, **kwargs):

IOLoop.instance().add_timeout(1, callback=functools.partial(self.ping, 'www.google.com'))

# do something others

self.finish('It works')

@tornado.gen.coroutine

def ping(self, url):

os.system("ping -c 2 www.google.com")

return 'after'

這種寫(xiě)法就使耗時(shí)的任務(wù)在后臺(tái)運(yùn)行了,從而顯著提高并發(fā),但是此時(shí),我們有兩個(gè)知識(shí)點(diǎn)需要了解:

1、裝飾器

@tornado.web.asynchronous

@tornado.gen.coroutine

兩個(gè)問(wèn)題:

為什么要使用這兩個(gè)裝飾器?

為什么要先用asynchronous在用coroutine呢?或著說(shuō)為什么要用這種調(diào)用順序?

這兩個(gè)裝飾器的作用:

1.1、@tornado.web.asynchronous

首先我們要明白同步和異步的作用

同步的情況下,web請(qǐng)求到來(lái)之后必須處理完成之后在返回,這是一個(gè)阻塞的過(guò)程。也就是說(shuō)當(dāng)一個(gè)請(qǐng)求被處理時(shí),服務(wù)器進(jìn)程會(huì)被掛起直至請(qǐng)求完成。而這會(huì)影響服務(wù)器的并發(fā)能力。

異步的情況下,web服務(wù)器進(jìn)程在等待請(qǐng)求處理的時(shí)候,會(huì)將IO循環(huán)打開(kāi),繼續(xù)來(lái)接受請(qǐng)求。而拿到處理結(jié)果之后會(huì)調(diào)用回調(diào)函數(shù),將結(jié)果返回。這要既不影響處理請(qǐng)求,也不影響接受請(qǐng)求,能夠顯著的提升并發(fā)能力。

我們必須要明白,在同步的情況下,web服務(wù)進(jìn)程,接受請(qǐng)求,處理請(qǐng)求,然后返回結(jié)果,最后自己來(lái)關(guān)閉連接。這個(gè)關(guān)閉的動(dòng)作是自動(dòng)的。

而異步的情況下,因?yàn)樵谔幚硪粋€(gè)請(qǐng)求的時(shí)候還沒(méi)有的到結(jié)果,所以需要保持連接的打開(kāi),最后返回結(jié)果之后,關(guān)閉連接,這個(gè)關(guān)閉動(dòng)作必須要手動(dòng)關(guān)閉。也就是必須手動(dòng)調(diào)用self.finish.

tornado中使用@tornado.web.asynchronous裝飾器作用是保持連接一直開(kāi)啟,

上面的例子中使用的回調(diào)函數(shù)的缺點(diǎn)是,可能引起回調(diào)深淵,系統(tǒng)將難以維護(hù),比如回調(diào)中調(diào)用回調(diào)等。

因?yàn)閷?shí)現(xiàn)異步需要保持連接一直打開(kāi),而不能在handler執(zhí)行完畢的時(shí)候關(guān)掉。

所以總的來(lái)說(shuō),@tornado.web.asynchronous的作用就是:把http連接變成長(zhǎng)連接,直到調(diào)用self.finish,連接都在等待狀態(tài)。

1.2、@tornado.gen.coroutine

這個(gè)函數(shù)的作用就是簡(jiǎn)化異步編程,讓代碼的編寫(xiě)更像同步代碼,同時(shí)實(shí)現(xiàn)的確實(shí)異步的。這樣避免了寫(xiě)回調(diào)函數(shù)。而且使用的是協(xié)程的方式來(lái)來(lái)實(shí)現(xiàn)異步編程。最新版的tornado,其實(shí)不一定需要寫(xiě)@tornado.web.asynchronous。

1.3、順序

@asynchronous會(huì)監(jiān)聽(tīng)@gen.coroutine的返回結(jié)果(Future),并在@gen.coroutine裝飾的代碼段執(zhí)行完成后自動(dòng)調(diào)用finish。從Tornado 3.1版本開(kāi)始,只使用@gen.coroutine就可以了。

2、函數(shù)

IOLoop.instance().add_timeout()

functools.partial()

2.1、IOLoop.instance().add_timeout()

首先我們需要了解IOLoop,以及IOLoop.instance()也就是實(shí)例化動(dòng)作。

IOLoop?是基于 epoll 實(shí)現(xiàn)的底層網(wǎng)絡(luò)I/O的核心調(diào)度模塊,用于處理 socket 相關(guān)的連接、響應(yīng)、異步讀寫(xiě)等網(wǎng)絡(luò)事件。每個(gè) Tornado 進(jìn)程都會(huì)初始化一個(gè)全局唯一的 IOLoop 實(shí)例,在 IOLoop 中通過(guò)靜態(tài)方法 instance() 進(jìn)行封裝,獲取 IOLoop 實(shí)例直接調(diào)用此方法即可。

Tornado 服務(wù)器啟動(dòng)時(shí)會(huì)創(chuàng)建監(jiān)聽(tīng) socket,并將 socket 的 file descriptor 注冊(cè)到 IOLoop 實(shí)例中,IOLoop 添加對(duì) socket 的IOLoop.READ 事件監(jiān)聽(tīng)并傳入回調(diào)處理函數(shù)。當(dāng)某個(gè) socket 通過(guò) accept 接受連接請(qǐng)求后調(diào)用注冊(cè)的回調(diào)函數(shù)進(jìn)行讀寫(xiě)。接下來(lái)主要分析IOLoop 對(duì) epoll 的封裝和 I/O 調(diào)度具體實(shí)現(xiàn)。

epoll是Linux內(nèi)核中實(shí)現(xiàn)的一種可擴(kuò)展的I/O事件通知機(jī)制,是對(duì)POISX系統(tǒng)中?select?和?poll?的替代,具有更高的性能和擴(kuò)展性,FreeBSD中類(lèi)似的實(shí)現(xiàn)是kqueue。Tornado中基于Python C擴(kuò)展實(shí)現(xiàn)的的epoll模塊(或kqueue)對(duì)epoll(kqueue)的使用進(jìn)行了封裝,使得IOLoop對(duì)象可以通過(guò)相應(yīng)的事件處理機(jī)制對(duì)I/O進(jìn)行調(diào)度。

IOLoop模塊對(duì)網(wǎng)絡(luò)事件類(lèi)型的封裝與epoll一致,分為READ / WRITE / ERROR三類(lèi)。

functools模塊用于高階函數(shù):作用于或返回其他函數(shù)的函數(shù)。一般而言,任何可調(diào)用對(duì)象都可以作為本模塊用途的函數(shù)來(lái)處理。

functools.partial返回的是一個(gè)可調(diào)用的partial對(duì)象,使用方法是partial(func,*args,**kw),func是必須要傳入的,而且至少需要一個(gè)args或是kw參數(shù)。

在這里就是添加一個(gè)回調(diào)函數(shù)的partial對(duì)象。

上面的這種寫(xiě)法不能獲取返回值。需要獲取返回值需要使用yield掛起函數(shù),并根據(jù)函數(shù)的return獲取返回值。

二、帶返回值的,同時(shí)使用協(xié)程來(lái)實(shí)現(xiàn)

class AsyncTaskHandler(tornado.web.RequestHandler):

@tornado.web.asynchronous

@tornado.gen.coroutine

def get(self, *args, **kwargs):

# yield 結(jié)果

response = yield tornado.gen.Task(self.ping, 'www.google.com')

print 'response', response

self.finish('hello')

@tornado.gen.coroutine

def ping(self, url):

os.system("ping -c 2 {}".format(url))

return 'after'

可以看到結(jié)果值也被返回了。有時(shí)候這種協(xié)程處理,未必就比同步快。在并發(fā)量很小的情況下,IO本身拉開(kāi)的差距并不大。甚至協(xié)程和同步性能差不多。但是在大并發(fā)量的情況下就不一樣了,因?yàn)椴l(fā)請(qǐng)求很多,越來(lái)越多的請(qǐng)求如果被耗時(shí)的處理阻塞,將會(huì)長(zhǎng)時(shí)間得不到結(jié)果。

yield掛起函數(shù)協(xié)程,盡管沒(méi)有block主線(xiàn)程,因?yàn)樾枰幚矸祷刂?#xff0c;掛起到響應(yīng)執(zhí)行還是有時(shí)間等待,相對(duì)于單個(gè)請(qǐng)求而言。另外一種使用異步和協(xié)程的方式就是在主線(xiàn)程之外,使用線(xiàn)程池,線(xiàn)程池依賴(lài)于futures。Python2需要額外安裝。

我認(rèn)為這種用法應(yīng)該是一種比較常用的用法。

三、使用線(xiàn)程池的方式修改為異步處理

from concurrent.futures import ThreadPoolExecutor

class FutureHandler(tornado.web.RequestHandler):

executor = ThreadPoolExecutor(10)

@tornado.web.asynchronous

@tornado.gen.coroutine

def get(self, *args, **kwargs):

url = 'www.google.com'

tornado.ioloop.IOLoop.instance().add_callback(functools.partial(self.ping, url))

self.finish('It works')

@tornado.concurrent.run_on_executor

def ping(self, url):

os.system("ping -c 2 {}".format(url))

想要返回值也很容易。再切換一下使用方式接口。使用tornado的gen模塊下的with_timeout功能(這個(gè)功能必須在tornado>3.2的版本)。

如:

class Executor(ThreadPoolExecutor):

_instance = None

def __new__(cls, *args, **kwargs):

if not getattr(cls, '_instance', None):

cls._instance = ThreadPoolExecutor(max_workers=10)

return cls._instance

class FutureResponseHandler(tornado.web.RequestHandler):

executor = Executor()

@tornado.web.asynchronous

@tornado.gen.coroutine

def get(self, *args, **kwargs):

future = Executor().submit(self.ping, 'www.google.com')

response = yield tornado.gen.with_timeout(datetime.timedelta(10), future,quiet_exceptions=tornado.gen.TimeoutError)

if response:

print 'response', response.result()

@tornado.concurrent.run_on_executor

def ping(self, url):

os.system("ping -c 1 {}".format(url))

return 'after'

具體使用何種方式,更多的依賴(lài)業(yè)務(wù),不需要返回值的往往需要處理callback,回調(diào)太多容易出錯(cuò),當(dāng)然如果需要很多回調(diào)嵌套,首先優(yōu)化的應(yīng)該是業(yè)務(wù)或產(chǎn)品邏輯。yield的方式很優(yōu)雅,寫(xiě)法可以異步邏輯同步寫(xiě),快是快了一些,但也會(huì)損失一定的性能。

總結(jié)

以上是生活随笔為你收集整理的python高性能服务器编写,Tornado的高性能服务器开发常用方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。