Python-FastAPI 異步博客開(kāi)發(fā)(四)加入Go語(yǔ)言

[圖片上傳失敗...(image-9d243d-1592273073087)]

項(xiàng)目地址
https://github.com/LouisYZK/Frodo
博客原文地址
http://zhikai.pro/

Frodo-v2.0 沒(méi)有添加新功能山宾,而是將后端最重要的部分妨马,后臺(tái)API使用golang重構(gòu)白胀,python現(xiàn)在只負(fù)責(zé)前臺(tái)模板的渲染。這樣原本的單服務(wù)應(yīng)用就成了多服務(wù)。本文將簡(jiǎn)介v2.0的調(diào)整思路和golang異步的特性闯团,新版本的部署文檔請(qǐng)參看項(xiàng)目地址

{
  'user': 'LouisYZK',
  'repo': 'Frodo',
  'right': 0
}

主要重構(gòu)的模塊為:

  • 博文函匕、用戶、標(biāo)簽等的后臺(tái)CRUD接口
  • 緩存清理模塊
  • JWT認(rèn)證模塊

why golang?

golang是年輕的語(yǔ)言耍目,新世紀(jì)的靜態(tài)語(yǔ)言膏斤。設(shè)計(jì)理念很好地平衡了C++javascript/python等動(dòng)態(tài)語(yǔ)言的優(yōu)劣,獨(dú)具特色的goroutine設(shè)計(jì)范式旨在告別多線程式的并發(fā)邪驮。而web后臺(tái)和微服務(wù)是go語(yǔ)言用的的最多的領(lǐng)域莫辨,故我將后臺(tái)純api部分拿go來(lái)重寫。

我是2019年開(kāi)始使用kubernetes時(shí)開(kāi)始接觸的go語(yǔ)言毅访,當(dāng)時(shí)項(xiàng)目有需求想要擴(kuò)展一個(gè)k8s的api, 官方給的意見(jiàn)是如果想真正的contributor,最好使用golang開(kāi)發(fā)沮榜。go語(yǔ)言的代表一定是dockerkubernetes這一最主流的容器與容器編排工具。

其次喻粹,使用go語(yǔ)言對(duì)我并不痛苦蟆融,他的風(fēng)格介于靜態(tài)和動(dòng)態(tài)語(yǔ)言,因此你要使用指針來(lái)避免冗余的對(duì)象復(fù)制守呜,同時(shí)你也能使用便捷的如python里的動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu)型酥。比類C的好處在于他不是那么地接近底層,只需考慮必要的指針操作和類型問(wèn)題查乒。

而python也越來(lái)越多地提倡使用顯示類型弥喉,在v1.0中就已經(jīng)使用了類型檢查,在python中雖然不能帶來(lái)性能的提升玛迄,但有利于調(diào)試和對(duì)接靜態(tài)語(yǔ)言由境。因此golang的語(yǔ)并不會(huì)帶來(lái)困難。

帶類型的python與golang風(fēng)格十分相似:

async def get_user_by_id(id: int) -> User:
    user: User = await User.async_first(id)
    return user

在golang中類型是強(qiáng)制的:

func GetUsers(page int) (users []User) {
    DB.Where(...).First(&user)
    return
}

再來(lái)看C++, 明顯的不同時(shí)類型的位置不一樣:

User* getUserById(int id) {
    user = User{id}
    User::first(&user)
    return user
}

最后蓖议,最重要的是golang的圈子如何虏杰,跟python-web比,golang可選擇的余地并不是很多拒担,但也足夠用嘹屯。這次選擇的框架是gingorm都是輕量且簡(jiǎn)單的框架。

Challenge

golang的輪子

寫習(xí)慣動(dòng)態(tài)原因的人(尤其是python/js)會(huì)感覺(jué)golang數(shù)據(jù)結(jié)構(gòu)的麻煩:

  • map/struct不能動(dòng)態(tài)添加新屬性
  • 沒(méi)有in這一經(jīng)常使用的特性
  • 任意類型interface{}到其他類型的轉(zhuǎn)換并沒(méi)有那么簡(jiǎn)單
  • struct从撼,map, json之間的轉(zhuǎn)換并不是很自然
  • 值傳遞和地址傳遞時(shí)刻要注意
  • 沒(méi)有方便的集合運(yùn)算州弟,如交并差钧栖,如排序,如格式化生成等婆翔。
  • ...

慶幸的是拯杠,go語(yǔ)言的開(kāi)源社區(qū)做的很不錯(cuò),可以直接飲用github的他人完成的包啃奴,很多輪子都有現(xiàn)成的實(shí)現(xiàn)潭陪,首先可以去 https://godoc.org/ 去搜索官方支持的輪子,這些一般是穩(wěn)定的最蕾,受官方認(rèn)可的依溯,同時(shí)可以方便地查看他們的文檔。如集合運(yùn)算我就使用了goset這個(gè)庫(kù)瘟则。如果沒(méi)有在官方找到黎炉,可以直接尋求github,直接引用倉(cāng)庫(kù)地址即可醋拧。(感覺(jué)golang包模塊很方便嗎慷嗜?目前看來(lái)是的,但其實(shí)坑也不少...

多服務(wù)網(wǎng)絡(luò)結(jié)構(gòu)部署

沒(méi)想到V2.0版本麻煩最多是在部署上...

這樣我們的博客系統(tǒng)就有兩個(gè)服務(wù)了丹壕,golang和uvicorn分別占兩個(gè)端口庆械,靜態(tài)文件中做相應(yīng)的調(diào)整,但因?yàn)槲业牟渴鹬荒鼙┞兑粋€(gè)端口(因?yàn)橛蛎麊?wèn)題菌赖,見(jiàn)下圖)缭乘,這樣只能借助nginx來(lái)轉(zhuǎn)發(fā)了。

[圖片上傳失敗...(image-ae80af-1592273073087)]

上圖結(jié)構(gòu)有幾個(gè)配置上的難點(diǎn):

  • 靜態(tài)資源尋址盏袄、路由配置忿峻。v1.0但語(yǔ)言版本時(shí)比較好配置直接都映射本地地址即可。現(xiàn)在需要明確地分服務(wù)在nginx配置轉(zhuǎn)發(fā)辕羽。同時(shí)靜態(tài)資源上逛尚,也要將原先的本地地址更換為域名地址。

  • golang部分功能還要調(diào)用python的服務(wù)刁愿,如「動(dòng)態(tài)」的api的還是保留在python里绰寞,post的 api在golang, 而創(chuàng)建「文章」后需要?jiǎng)?chuàng)建動(dòng)態(tài),這時(shí)golang需要調(diào)用python的服務(wù)铣口。(這其實(shí)很正常滤钱,很大的項(xiàng)目也避免不了互相通信的需要。)好在在一臺(tái)機(jī)器上此問(wèn)題容易解決的多脑题。

  • 等等件缸,緩存會(huì)沖突嗎? 在「數(shù)據(jù)篇」中講到Frodo是有緩存機(jī)制的叔遂,現(xiàn)在發(fā)現(xiàn)python的前臺(tái)和golang的后臺(tái)都依賴緩存他炊,這點(diǎn)需要嚴(yán)格的key的統(tǒng)一來(lái)保證兩個(gè)緩存數(shù)據(jù)的一致性争剿。

Golang的異步與并發(fā)

既然將原來(lái)python的服務(wù)換為golang, 前面提到的異步特性golang能滿足嗎?其實(shí)思想是一致的痊末,只是從asycio和可等待對(duì)象變?yōu)榱薵oroutine, 拿「博文」創(chuàng)建接口舉例:

func CreatePost(data map[string]interface{}) {
    post := new(Post)
    post.Title = data["title"].(string)
    post.Summary = data["summary"].(string)
    post.Type = data["type"].(int)
    post.CanComment = data["can_comment"].(int)
    post.AuthorID = data["author_id"].(int)
    post.Status = data["status"].(int)

    tags := data["tags"].([]string)
    content := data["content"]
    DB.Create(&post)

    fmt.Println(post)

    go post.SetProps("content", content) // go設(shè)置內(nèi)容
    go post.UpdateTags(tags) // go 更新標(biāo)簽
    go post.Flush() // go 清除緩存
    go CreateActivity(post) // go 創(chuàng)建動(dòng)態(tài)
}

可以看到連續(xù)使用了4個(gè)go分發(fā)不能阻塞的任務(wù)蚕苇,這些都是goroutine, 配套的有對(duì)他們管理的通信工具和同步原語(yǔ),每個(gè)goroutine也可以繼續(xù)分發(fā)協(xié)程凿叠,如其中的更新標(biāo)簽:

func (post *Post) UpdateTags(tagNames []string) {
    var originTags []Posttag
    var originTagNames []string

    DB.Where("post_id = ?", post.ID).Find(&originTags)
    for _, item := range originTags {
        var tag Tag
        DB.Select("name").Where("id = ?", item.TagID).First(&tag)
        originTagNames = append(originTagNames, tag.Name)
    }
    _, _, deleteTagNames, addTagNames := goset.Difference(originTagNames, tagNames)
    for _, tag := range addTagNames.([]string) {
        go CreateTags(tag)
        go CreatePostTags(post.ID, tag)
    }
    for _, tag := range deleteTagNames.([]string) {
        go DeletePostTags(post.ID, tag)
    }
}

golang沒(méi)有類似asyncio.gather(*coros)式的分發(fā)涩笤,采用for循環(huán)是一樣的實(shí)現(xiàn)。

目前我已經(jīng)把簡(jiǎn)單的系統(tǒng)拆成了兩個(gè)不同技術(shù)類型的服務(wù)盒件,可以見(jiàn)到部署難題漸顯蹬碧,接下來(lái)的更新就是虛擬化解決環(huán)境依賴難題和自動(dòng)化部署了~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市履恩,隨后出現(xiàn)的幾起案子锰茉,更是在濱河造成了極大的恐慌,老刑警劉巖切心,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異片吊,居然都是意外死亡绽昏,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門俏脊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)全谤,“玉大人,你說(shuō)我怎么就攤上這事爷贫∪先唬” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵漫萄,是天一觀的道長(zhǎng)卷员。 經(jīng)常有香客問(wèn)我,道長(zhǎng)腾务,這世上最難降的妖魔是什么毕骡? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮岩瘦,結(jié)果婚禮上未巫,老公的妹妹穿的比我還像新娘。我一直安慰自己启昧,他們只是感情好叙凡,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著密末,像睡著了一般握爷。 火紅的嫁衣襯著肌膚如雪跛璧。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天饼拍,我揣著相機(jī)與錄音赡模,去河邊找鬼。 笑死师抄,一個(gè)胖子當(dāng)著我的面吹牛漓柑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播叨吮,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼辆布,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了茶鉴?” 一聲冷哼從身側(cè)響起锋玲,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涵叮,沒(méi)想到半個(gè)月后惭蹂,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡割粮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年盾碗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舀瓢。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡廷雅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出京髓,到底是詐尸還是另有隱情航缀,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布堰怨,位于F島的核電站芥玉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏诚些。R本人自食惡果不足惜飞傀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诬烹。 院中可真熱鬧砸烦,春花似錦、人聲如沸绞吁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)家破。三九已至颜说,卻和暖如春购岗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背门粪。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工喊积, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人玄妈。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓乾吻,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親拟蜻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子绎签,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359