<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
最近和幾個小夥伴們在寫位元組跳動第五屆青訓營後端組的大作業。
雖然昨天已經提交了專案,但有很多地方值得總結一下,比如這一篇,來看看我們是如何管理應用的生命週期的。
先來看一段程式碼:(假設無 err 值)
func main() { // 1、啟動HTTP服務 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello, World!") }) http.ListenAndServe(":8080", nil) // 2、啟動GRPC服務 server := grpc.NewServer() listener, _ := net.Listen("tcp", ":1234") server.Serve(listener) }
這一段程式碼,相信你一眼就能看出問題,因為在啟動HTTP後,程序會堵塞住,下面啟動GRPC服務的程式碼,壓根就不會執行。
但是,如果想要同時啟動GRPC
服務呢?該怎麼做呢?
自己沒有時間,那麼就請一個幫手咯,讓它來為我們啟動GRPC服務,而這個幫手,就是go的攜程。
func main() { // 1、將HTTP服務放在後臺啟動 go start http // 2、將GRPC服務放在前臺啟動 start grpc }
但是調整成這樣之後,理想的情況就是,HTTP成功啟動後、GRPC也要啟動成功。HTTP意外退出後,GRPC也需要退出服務,他們倆需要共存亡。
但若出現了 HTTP 意外退出、GRPC還未退出,那麼就會浪費資源。還可能出現其他的問題。比如介面異常。這樣會很危險。那我們該利用什麼方式,讓同一服務內,啟動多個執行緒。並且讓他們共同存亡的呢?
瞭解了上面的問題,我們再來重新描述總結一下出現的問題。
一個服務,可能會啟動多個程序,比如說 HTTP API、GRPC API、服務的註冊,這些模組都是獨立的,都是需要在程式啟動的時候進行啟動。
而且如果需要關閉掉這個應用,還需要處理很多關閉的問題。比如說
而且,啟動的多個程序間,該如何通訊呢? 某些服務意外退出了,按理來說要關閉整個應用,該如何監聽到呢?
定義一個管理者物件,來管理我們應用所需要啟動的所有服務,比如這裡需要被我們啟動的服務有:HTTP、GRPC
這個管理者核心有兩個方法:start、stop
// 用於管理服務的開啟、和關閉 type manager struct { http *protocol.HttpService // HTTP生命週期的結構體[自定義] grpc *protocol.GRPCService // GRPC生命週期的結構體[自定義] l logger.Logger // 紀錄檔物件 }
不用關心這裡依賴的 http、grpc
結構體是什麼,我們在後面的章節,會詳細解釋。只需要知道,我們用manager
這個結構體,用於管理http、grpc
服務即可。
start
這個函數,核心只做了兩件事,分別啟動HTTP、GRPC
服務。
func (m *manager) start() error { // 列印載入好的服務 m.l.Infof("已載入的 [Internal] 服務: %s", ioc.ExistingInternalDependencies()) m.l.Infof("已載入的 [GRPC] 服務: %s", ioc.ExistingGrpcDependencies()) m.l.Infof("已載入的 [HTTP] 服務: %s", ioc.ExistingGinDependencies()) // 如果不需要啟動HTTP服務,需要才啟動HTTP服務 if m.http != nil { // 將HTTP放在後臺跑 go func() { // 注:這屬於正常關閉:"http: Server closed" if err := m.http.Start(); err != nil && err.Error() != "http: Server closed" { return } }() } // 將GRPC放入前臺啟動 m.grpc.Start() return nil }
又因為開頭說過了,啟動這兩任一服務,都會將程序堵塞住。
所以我們找了一個幫手(攜程)
來啟動HTTP
服務,然後將GRPC
服務放在前臺執行。
那為什麼我要將GRPC
服務放在前臺執行呢?其實理論上放誰都行,但由於我們的架構原因。我們有的服務不需要啟動HTTP
服務,而每一個服務都會啟動GRPC
服務。所以,將GRPC放置在前臺,會更合適。
至於裡面如何使用HTTP、GRPC
的服務物件啟動它們的服務。在這一節就不多贅述了。在之後的章節會有詳細的介紹~
看完了統一管理啟動的start
方法,那我們來看看如何停止服務吧
我們開啟了多個服務,並且有的還是放在後臺執行的。這就涉及到了多個攜程的間通訊的問題了
用什麼來通訊吶?我怎麼知道HTTP
服務掛沒掛?是意外掛的還是主動掛的?我們怎麼能夠優雅的統一關閉所有服務呢?
其實這一切的問題,Go
都為我們想好了:那就是使用Channels
。一個channel
是一個通訊機制,它可以讓一個攜程通過它給另一個攜程傳送值資訊。每個channel
都有一個特殊的型別,也就是channels
可傳送資料的型別。
我們把一個go程
當作一個人的化,那麼main
方法啟動的主go程
就是你自己。在你的程式中使用到的其他go程
,都是你的好幫手,你的好朋友,它們有給你去處理耗時邏輯的、有給你去執行業務無關的切面邏輯的。而且是你的好幫手,按理來說最好是由你自己去決定,要不要請一個好幫手。
當你請來了一個好幫手後,它們會在你的背後為你做你讓他們做的事情。那麼多個人之間的通訊,比較現代的方法,那可以是:打個電話?發個訊息?所以用到了一個溝通的通道:Channel
好了,當你瞭解了這些後,也就是接收到一些電話後,我們才需要去stop
。我們再回到Dousheng使用的情景:
主攜程是GRPC
服務這個人,我們請了一個幫手,給我啟動HTTP服務。這個時候,如果HTTP服務這個幫手意外出事了。既然是幫我麼你做事,那我們肯定得對別人負責是吧。但是我們也不知道它出不出意外啊,怎麼辦呢?這時候你想了兩個方法:
這就需要HTTP自己告訴我們,按理來說,應該是可以的。但是如果HTTP遇到了重大問題,根本來不及告訴我們呢?咱們又是一個負責的男人。為了避免這種情況發生,又請一個人,專門給我們看HTTP有沒有遇到重大問題。於是有了第二種方式:
signal.Notify
,幫助我們監聽HTTP可能會遇到的重大問題當我們收到HTTP出事的訊號後,那我們就可以統一的去優雅關閉服務了。就這樣,我們做了一個負責的人~
相信你已經瞭解了核心的思想,我們來看看,用程式碼該如何實現
signal.Notify
,用於監聽系統訊號我們已經分析過了,我們需要再請一個幫手,來給我們處理HTTP可能會遇到的重大事故:(syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGHUP, syscall.SIGINT)
// WaitSign 等待退出的訊號,實現優雅退出 func (m *manager) waitSign() { // 用於接收訊號的通道 ch := make(chan os.Signal, 1) // 接收這幾種訊號 signal.Notify(ch, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGHUP, syscall.SIGINT) // 需要在後臺等待關閉 go m.waitStop(ch) }
當signal.Notify
收到上面所列舉的訊號後,那麼就可以去做關閉的事情了,那如何關閉呢?
// WaitStop 中斷訊號,比如Terminal [關閉服務的方法] func (m *manager) waitStop(ch <-chan os.Signal) { // 等待訊號,若收到了,我們進行服務統一的關閉 for v := range ch { switch v { default: m.l.Infof("接受到訊號:%s", v) // 優雅關閉HTTP服務 if m.http != nil { if err := m.http.Stop(); err != nil { m.l.Errorf("優雅關閉 [HTTP] 服務出錯:%s", err.Error()) } } // 優雅關閉GRPC服務 if err := m.grpc.Stop(); err != nil { m.l.Errorf("優雅關閉 [GRPC] 服務出錯:%s", err.Error()) } } } }
這裡的邏輯比較簡單,就是當接收到訊號的時候,對HTTP、GRPC
做優雅關閉的邏輯。至於為什麼要進行優雅關閉,而不是直接os.Exit()
?我們在下一節講~
這裡值得一提的是,我們從chanel裡獲取資料,因為我們這裡只和單個攜程間進行通訊了,使用的是 for range
,並沒有使用for select
好了,這樣我們應用的生命週期算是被我們優雅的拿捏了。我們一直在講優雅關閉這個詞,我們來解釋一下什麼是優雅關閉?為什麼需要優雅關閉?
既然HTTP服務和GRPC服務都需要優雅關閉,我們這裡用HTTP服務來舉例。
先來看這張圖,假設有三個並行的請求至我們的HTTP服務。它們都期望得到伺服器的response
。HTTP伺服器正常執行的情況下,多半是沒問題的。
請求已發出,若提供的HTTP服務突然異常關閉了呢?我們繼續來把HTTP服務比作一個人。看看它是否優雅呢?
如果HTTP這個人不太優雅,是一個做事不怎麼負責的渣男。當自己異常over了之後,也不解決完自己的事情,就讓別人(request)
,找不到資源了。真的很不負責啊。
大致用一幅圖表示:
這個不優雅的HTTP服務,當有還未處理的請求時,自己就異常關閉了,那麼它根本不會理會原先的請求是否完成了。它只管自己退出程式。
看完了那個渣男HTTP(沒有優雅關閉),我們簡直想罵它了。那我們來看,當一個優雅的謙謙君子(有優雅關閉),又是如何看待這個問題的。
這是一個負責人的人,為什麼說他負責人、說它優雅呢?因為當它自己接收到異常關閉的訊號後。它不會只顧自己關閉。它大概還會做兩件事:
response
。正是因為它主要做了這兩件事,我們才說此時的HTTP服務,是一個優雅的謙謙君子。
而當有很多個請求到時候,我們怎麼知道是否會不會突然異常關閉呢?如果遇到了這種情況,我們應該處理完未完成的響應,拒絕新的請求建立連線,因為我們是一個優雅的人。
以上就是優雅管理Go Project生命週期的詳細內容,更多關於Go Project生命週期的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45