python web server 體悟(非基礎(chǔ),非高級(jí))

前言

本文意在表達(dá):如何用 python 寫(xiě)好 web server

在這里铭污,不會(huì)手把手告訴你怎么寫(xiě)代碼,也不會(huì)告訴你什么是高級(jí)的 web server膀篮。

我所寫(xiě)的僅僅是嘹狞,我眼里的 python web server。

希望能對(duì)你有所幫助誓竿,有所啟示刁绒。

歡迎指正。

需要額外提示的是烤黍,該文主要討論的是無(wú)狀態(tài)服務(wù)的編寫(xiě)知市。有狀態(tài)的不在此處討論。

環(huán)境

文檔編寫(xiě)時(shí)間:2020.05.29
wsgi app: flask
wsgi server: gunicorn
模式:前后端分離速蕊,此處僅討論后端

正文

web server 可以分為兩個(gè)部分:框架業(yè)務(wù)功能

大多時(shí)候框架部分的代碼嫂丙,都是在解決如何使用好一個(gè)第三方框架,而非自己去實(shí)現(xiàn)一個(gè)框架规哲。

業(yè)務(wù)功能部分便是實(shí)現(xiàn)具體業(yè)務(wù)功能的代碼了跟啤。

每個(gè)部分解決自己的問(wèn)題,讓工作有條不紊的進(jìn)行。沒(méi)有孰優(yōu)孰劣之分隅肥,業(yè)務(wù)功能代碼會(huì)更加考驗(yàn)?zāi)愕拇a邏輯竿奏。

業(yè)務(wù)功能部分

我想大多數(shù)人接觸 web server 時(shí)首先接觸的便是這塊內(nèi)容,或者正處于這個(gè)階段腥放,所以先提這部分內(nèi)容泛啸。

對(duì)于實(shí)現(xiàn)業(yè)務(wù)功能,有兩個(gè)點(diǎn)需要著重考慮:規(guī)范秃症、模塊化編寫(xiě)

規(guī)范

規(guī)范涉及:方法命名格式規(guī)范候址、url 格式規(guī)范、接口文檔規(guī)范种柑、接口數(shù)據(jù)格式規(guī)范以及 pep8

每個(gè)規(guī)范的具體參考在文章的最后【擴(kuò)展章節(jié)】給出岗仑。

在協(xié)作開(kāi)發(fā)中首先制定好規(guī)范十分重要,這是你必須應(yīng)該做的事聚请,哪怕只有你自己在開(kāi)發(fā)荠雕。沒(méi)有規(guī)范的代碼,后期維護(hù)將一團(tuán)糟驶赏。

模塊化編寫(xiě)

模塊化代碼舞虱,可以增加代碼的可閱讀性,增加代碼的擴(kuò)展性母市,增加代碼的健壯性矾兜。

個(gè)人偏向于將總體代碼分為三層(非照搬MVC):路由層、控制層患久、接口層椅寺。

每層負(fù)責(zé)自己的事情

路由層(Router)

  1. 定義 apidoc (接口文檔)
  2. 全局 try catch
  3. 定義 url 及 url 的入口。

控制層(Manager):

  1. 輸入?yún)?shù)檢查
  2. 顯式定義輸入?yún)?shù)(在這一層將 request 拆解出來(lái)蒋失,不要再往下傳遞 request)
  3. 代碼邏輯層(組合訪問(wèn)下層接口返帕,使用多線程之類)

接口層(Client,該層的方法功能盡可能遵守單一職責(zé)篙挽,以便上層組合使用)

  1. 與外部交互
  2. 格式化返回參數(shù)

框架部分

作為框架的搭建者荆萤,需要額外考慮如下問(wèn)題:

  1. gunicorn 配置調(diào)優(yōu)(提供并行能力,推薦多進(jìn)程)
  2. 日志:
    1)標(biāo)準(zhǔn)輸出的內(nèi)置 debug 信息及格式铣卡。(框架自帶的debug链韭,比如 gunicorn 的 debug,一般輸出到屏幕)
    2)編寫(xiě)程序時(shí)手動(dòng)記錄的日志格式煮落。(輸出到文件敞峭,比如記錄 exception、traceback)
    3)自動(dòng)記錄下接口訪問(wèn)信息蝉仇。(輸出到文件或者屏幕旋讹,可記錄訪問(wèn)端地址殖蚕、接口花費(fèi)時(shí)間等)
  3. 線程池(解決并發(fā) IO 等待)
  4. 提供配置(單獨(dú)編寫(xiě)實(shí)現(xiàn)配置對(duì)象,方便程序內(nèi)使用相應(yīng)配置)
  5. 打包方式(setup 等)
  6. 應(yīng)用啟動(dòng)方式 (通過(guò) argparse 增加 -h沉迹、--conf 之類命令方便啟動(dòng)睦疫,注冊(cè)進(jìn) systemd 等)
  7. 項(xiàng)目基礎(chǔ)文檔,包含整體架構(gòu)設(shè)計(jì)鞭呕、某些細(xì)節(jié)功能設(shè)計(jì)(比如權(quán)限設(shè)計(jì))蛤育、如何構(gòu)建開(kāi)發(fā)環(huán)境等。

進(jìn)階

到這里琅拌,一個(gè)小型的中規(guī)中矩缨伊、能滿足大部分場(chǎng)景的 web server 基本齊全了摘刑。單節(jié)點(diǎn)萬(wàn)級(jí)并發(fā)进宝,多節(jié)點(diǎn)到十萬(wàn)并發(fā)也是沒(méi)太大問(wèn)題的。

為了讓它可以擁有進(jìn)化的能力枷恕,我們需要看的更遠(yuǎn)党晋,了解更多的東西。

以上的內(nèi)容完全基于個(gè)人經(jīng)驗(yàn)的沉淀徐块。以下將要介紹的東西未玻,僅供各位一看,并非來(lái)源于長(zhǎng)年累月的經(jīng)驗(yàn)胡控。

這個(gè)時(shí)候扳剿,我們應(yīng)該考慮分布式服務(wù)了。

分布式服務(wù)主要需要解決的問(wèn)題是:組件之間如何進(jìn)行通信昼激。

目前組件間主流通信方式如下:

  1. 以 restful 方式進(jìn)行通信庇绽。
  2. 以消息隊(duì)列(中間件)進(jìn)行通信。

restful 方式

該方式就是以 http 訪問(wèn)接口來(lái)實(shí)現(xiàn)功能橙困,就是普普通通的 web server瞧掺。

缺點(diǎn):

  1. 需要自己維護(hù)高可用。也就是凡傅,你得想明白辟狈,如果 3個(gè)A(A1,A2,A3)服務(wù)同時(shí)部署,由誰(shuí)來(lái)對(duì)外進(jìn)行服務(wù)夏跷,如果A1掛了哼转,由誰(shuí)來(lái)接手服務(wù)。這是高可用
  2. 需要自己維護(hù)負(fù)載均衡槽华。將一波流量分別引流至不同的服務(wù)節(jié)點(diǎn)上释簿,降低單個(gè)節(jié)點(diǎn)的壓力,這是負(fù)載均衡硼莽。(什么都不是一蹴而就的庶溶,首先考慮你現(xiàn)在需要這個(gè)嗎)
  3. 需要自己維護(hù)橫向擴(kuò)展性煮纵。如果現(xiàn)在已有3個(gè)節(jié)點(diǎn)同樣的A服務(wù),但是性能不夠用偏螺,我再加個(gè)A服務(wù)節(jié)點(diǎn)行疏,變成4節(jié)點(diǎn)同樣的服務(wù),你如何應(yīng)對(duì)套像?需要重啟現(xiàn)有節(jié)點(diǎn)嗎酿联?會(huì)中斷現(xiàn)有應(yīng)用嗎?(可結(jié)合服務(wù)發(fā)現(xiàn)解決)
  4. 需要仔細(xì)考慮認(rèn)證設(shè)計(jì)夺巩。由于組件可以獨(dú)立對(duì)外部提供服務(wù)贞让,因此如果一次請(qǐng)求跨越資源則需要重復(fù)的認(rèn)證。

這是目前中規(guī)中矩的分布式實(shí)現(xiàn)方案柳譬,成熟且原始喳张。

消息隊(duì)列方式

消息隊(duì)列,有的地方也稱之為中間件美澳,但是個(gè)人偏向直接這么稱呼销部,中間件是一個(gè)很寬泛的概念。

缺點(diǎn):

  1. 組件不能獨(dú)立對(duì)外提供服務(wù)制跟。
  2. 十分依賴消息隊(duì)列的性能舅桩,消息隊(duì)列十分可能成為你集群服務(wù)的瓶頸。
  3. 一定程度上的緊耦合雨膨,一段代碼既是生產(chǎn)者又是消費(fèi)者擂涛,這種情況下你的代碼邏輯會(huì)有點(diǎn)糟糕。

優(yōu)點(diǎn):

  1. 統(tǒng)一的入口聊记。實(shí)際上這既是缺點(diǎn)撒妈,也是優(yōu)點(diǎn)。統(tǒng)一的入口可以讓認(rèn)證變的十分高效簡(jiǎn)單甥雕,但是相對(duì)來(lái)說(shuō)統(tǒng)一入口踩身,會(huì)稍微紊亂代碼結(jié)構(gòu)。
  2. 顯著的增加并發(fā)社露、異步能力挟阻。消息可以堆積,然后慢慢去處理峭弟,當(dāng)然堆積處理不當(dāng)就是一場(chǎng)雪崩附鸽。
  3. 簡(jiǎn)單配置即可實(shí)現(xiàn)高可用、負(fù)載均衡瞒瘸、擴(kuò)展性坷备。

融合實(shí)現(xiàn)

前面提的兩種方式,可以融合起來(lái)對(duì)外提供服務(wù)情臭。

  1. 獨(dú)立的組件以 restful 風(fēng)格通信省撑。
  2. 組件內(nèi)部以消息隊(duì)列的方式進(jìn)行通信赌蔑,增加異步、并發(fā)能力竟秫。
  3. 額外增加服務(wù)發(fā)現(xiàn)(比如采用 etcd)娃惯,提供整個(gè)集群服務(wù)的高可用、負(fù)載均衡肥败、擴(kuò)展性趾浅。

這將會(huì)造就一個(gè)龐大而復(fù)雜的系統(tǒng)。除了認(rèn)證有弊端外(重復(fù)認(rèn)證)馒稍,似乎無(wú)所不能皿哨。

有一個(gè)好消息,一個(gè)跨語(yǔ)言的微服務(wù)框架(也稱服務(wù)網(wǎng)格)istio 正在變的越來(lái)越成熟纽谒。它可以幫助你簡(jiǎn)便的實(shí)現(xiàn)服務(wù)注冊(cè)证膨,服務(wù)之間的通信,服務(wù)的管理等佛舱,值得持續(xù)關(guān)注椎例,持續(xù)學(xué)習(xí)挨决。

擴(kuò)展

python 編寫(xiě) web server请祖,性能夠嗎?

性能足夠。

如果你的代碼涉及大量計(jì)算脖祈,十分敏感性能肆捕,不建議使用 python。但是如果你愿意接受調(diào)庫(kù)(C庫(kù))盖高,python 也依然性能足夠慎陵。

規(guī)范參考

方法命名規(guī)范

# 例如:user_create (創(chuàng)建用戶)
<資源>_<動(dòng)作>

url 格式規(guī)范

# 例如:/app1/project/user_add?project_id=123&user_id=456 (向app1 的項(xiàng)目中添加用戶)
/<1級(jí)資源>/<2級(jí)資源>/<3級(jí)資源>_<3級(jí)資源動(dòng)作>

目前有很多規(guī)范是將 uuid 放入前半部分中。例如:

/app1/<project_id>/user_add?user_id=456

這類 url 的前半部分十分可能包含過(guò)長(zhǎng)的 uuid(32位)喻奥,從而造成實(shí)際的 url 可讀性十分差席纽。因此個(gè)人傾向于將所有的 uuid 作為參數(shù)傳遞。

接口文檔規(guī)范

接口文檔撞蚕,在小型的開(kāi)發(fā)團(tuán)隊(duì)當(dāng)中均由個(gè)人編寫(xiě)润梯、維護(hù)。
可以采用 apidoc 之類的工具去方便的生成接口文檔甥厦。
個(gè)人常用接口文檔規(guī)范如下:

@api {GET} /kdcloud/networks?detail=true 獲取網(wǎng)絡(luò)列表
@apiGroup network
@apiName network_list
@apiDescription
    接口描述
@apiParamExample {json} 參數(shù)示例
    Args: detail=true 表示獲取更加詳細(xì)的信息
    Headers: {
        "token": "1234",
        "project_id": "1234",
        "role": "",
    }
    Body: 無(wú)
@apiSuccessExample {json} 成功返回值示例
    返回碼 200
    {...}
@apiErrorExample {json} 失敗返回值示例
    返回碼 500
    {...}

效果參考:


image.png

接口數(shù)據(jù)格式規(guī)范

接口數(shù)據(jù)規(guī)范定義纺铭,某一類的接口返回固定的數(shù)據(jù)格式。這就看你們的偏好了刀疙。

比如通過(guò)http調(diào)用的接口數(shù)據(jù)結(jié)構(gòu)規(guī)范:

# 成功時(shí):
{
    "status": "success",
    "inventory": {}
}
# 失敗時(shí):
{
    "status": "failure",
    "error": ""
}

比如統(tǒng)一方法返回值結(jié)構(gòu):返回字典列表而不是對(duì)象列表之類舶赔。

pep8

python 開(kāi)源項(xiàng)目一般均會(huì)采用 pep8 檢查代碼格式。其中包含很多雜項(xiàng)谦秧,比如文件末尾空行竟纳,單行不能超過(guò)80字符之類撵溃。
建議一定使用該規(guī)范約束自己寫(xiě)代碼。和大牛接軌锥累,總比自己瞎想來(lái)的實(shí)際征懈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市揩悄,隨后出現(xiàn)的幾起案子卖哎,更是在濱河造成了極大的恐慌,老刑警劉巖删性,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亏娜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蹬挺,警方通過(guò)查閱死者的電腦和手機(jī)维贺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)巴帮,“玉大人溯泣,你說(shuō)我怎么就攤上這事¢偶耄” “怎么了垃沦?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)用押。 經(jīng)常有香客問(wèn)我肢簿,道長(zhǎng),這世上最難降的妖魔是什么蜻拨? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任池充,我火速辦了婚禮,結(jié)果婚禮上缎讼,老公的妹妹穿的比我還像新娘收夸。我一直安慰自己,他們只是感情好血崭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布卧惜。 她就那樣靜靜地躺著,像睡著了一般功氨。 火紅的嫁衣襯著肌膚如雪序苏。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天捷凄,我揣著相機(jī)與錄音忱详,去河邊找鬼。 笑死跺涤,一個(gè)胖子當(dāng)著我的面吹牛匈睁,可吹牛的內(nèi)容都是我干的监透。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼航唆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼胀蛮!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起糯钙,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤粪狼,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后任岸,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體再榄,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年享潜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了困鸥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡剑按,死狀恐怖疾就,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情艺蝴,我是刑警寧澤猬腰,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站吴趴,受9級(jí)特大地震影響漆诽,放射性物質(zhì)發(fā)生泄漏侮攀。R本人自食惡果不足惜锣枝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望兰英。 院中可真熱鬧撇叁,春花似錦、人聲如沸畦贸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)薄坏。三九已至趋厉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胶坠,已是汗流浹背君账。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沈善,地道東北人乡数。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓椭蹄,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親净赴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子绳矩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355