golang單機,golang單機並發量

本文目錄一覽:

【Minio】基於AWS S3協議搭建個人雲存儲服務

在2007年,GlusterFS演變為大型分佈式存儲方案後,任何配備合適硬件的公司,單位都可以利用個做分佈式的流媒體,數據分析。在2011年,Red Hat收購了GlusterFS.

Minio是GlusterFS創始人之一Anand Babu Periasamy發佈新的開源項目。Minio兼容Amason的S3分佈式對象存儲項目,採用Golang實現,客戶端支持Java,Python,Javacript, Golang語言。

Minio 提供對象存儲服務,兼容了 AWS S3 存儲協議,用於非結構化的數據存。非結構化對象,比如圖像,音、視頻,日誌文件,備份鏡像…等等管理不方便,不定長,大小變化大、類型多,雲端的訪問複雜,minio就是來解決這種場景的。非結構化的文件從數KB到5TB都能很好的支持。開源並且用 Go 語言開發,有web操作界面,我們可以用它來搭建兼容S3協議的存儲雲服務。

Minio可以做為雲存儲的解決方案用來保存海量的圖片,視頻,文檔。由於採用Golang實現,服務端可以工作在Windows,Linux, OS X和FreeBSD上。配置簡單,基本是複製可執行程序,單行命令可以運行起來。

官網:

那麼,如何自己搭建一個私有的S3存儲雲服務呢?

官方的話是推薦用Docker來搞,我們先用普通的二進制文件來直接解決了!

######################################################################################

# mkdir /data/aws_s3

# wget  

# mv  minio /usr/local/bin/

#  chmod  755  /usr/local/bin/minio 

# minio server  /data/aws_s3

#############################################################

Created minio configuration file successfully at /root/.minio

Endpoint:    

AccessKey: U3XLU4IMXY3IDKHU268F 

SecretKey: /6NCL6HGacviaCgRqr2qLbVOjhkkJdRpV7wz0JJD 

Region:    us-east-1

SQS ARNs:  

Browser Access:

   

Command-line Access: 

################################################################

$ mc config host add myminio   U3XLU4IMXY3IDKHU268F /6NCL6HGacviaCgRqr2qLbVOjhkkJdRpV7wz0JJD

Object API (Amazon S3 compatible):

Go: 

Java: 

Python: 

JavaScript: 

Drive Capacity: 8.3 GiB Free, 9.1 GiB Total

##############################################################

我們就成功啟動了minio的s3服務,默認端口9000,可以通過網頁訪問:

 

 

注意 :第一次打開時候需要填寫AccessKey和SecretKey才能進入,我們上面啟動服務的時候,已經看到屏幕有輸出:

AccessKey: U3XLU4IMXY3IDKHU268F 

SecretKey:6NCL6HGacviaCgRqr2qLbVOjhkkJdRpV7wz0JJD

把這兩個Key填入,就能順利進入,進入後展開頁面如下:

這就是我們的S3雲存儲的管理頁面了,看着是不是和七牛什麼的提供雲存儲的產品頁面挺像的,大家都是基於S3協議開發的!

上傳個文件試試:

點擊右下角的紅色小加號按鈕,彈出的菜單選擇」create bucket」則會創建一個桶,輸入名字」test」

點擊剛才那個紅色小加號按鈕,這次選擇」Upload file」上傳文件,給這個桶上傳了一個叫login.txt的文本文檔

此時頁面如下:

至此我們可以看到文件已經上傳,要訪問這個文件,可以點擊文件右側的三個點的按鈕,選擇分享就可以得到一個外鏈,在瀏覽器中訪問這個外鏈就可以直接訪問文件。

那麼文件到底被存到哪裡去了呢,我們啟動命令中其實指定了工作路徑/data/aws_s3/,所以到服務器這個目錄下看看:

# ls /data/aws_s3/ 

test

# ls /data/aws_s3/test/

login.txt 

桶名稱test是一個目錄,其下就有上傳的login.txt文件。

如果想指定ip和端口,可以這樣寫:

# minio server /data/aws_s3 –address=0.0.0.0:9000

如果想讓服務在後台運行:

# nohup minio server /data/aws_s3   –address=0.0.0.0:443 

[1] 19882

// nohup: 忽略輸入並把輸出追加到啟動命令的當前目錄下的 “nohup.out”文件

minio可以用來搭建分佈式存儲系統 GlusterFS,這樣就成了真正的雲存儲了,有時間再研究下把它從現在的單機測試,變成一朵存儲雲!

minio官網:

minio官方文檔:

minio github主頁:

如何實現支持數億用戶的長連消息系統

此文是根據周洋在【高可用架構群】中的分享內容整理而成,轉發請註明出處。周洋,360手機助手技術經理及架構師,負責360長連接消息系統,360手機助手架構的開發與維護。不知道咱們群名什麼時候改為「Python高可用架構群」了,所以不得不說,很榮幸能在接下來的一個小時里在Python群里討論golang….360消息系統介紹360消息系統更確切的說是長連接push系統,目前服務於360內部多個產品,開發平台數千款app,也支持部分聊天業務場景,單通道多app復用,支持上行數據,提供接入方不同粒度的上行數據和用戶狀態回調服務。目前整個系統按不同業務分成9個功能完整的集群,部署在多個idc上(每個集群覆蓋不同的idc),實時在線數億量級。通常情況下,pc,手機,甚至是智能硬件上的360產品的push消息,基本上是從我們系統發出的。關於push系統對比與性能指標的討論很多同行比較關心go語言在實現push系統上的性能問題,單機性能究竟如何,能否和其他語言實現的類似系統做對比么?甚至問如果是創業,第三方雲推送平台,推薦哪個?其實各大廠都有類似的push系統,市場上也有類似功能的雲服務。包括我們公司早期也有erlang,nodejs實現的類似系統,也一度被公司要求做類似的對比測試。我感覺在討論對比數據的時候,很難保證大家環境和需求的統一,我只能說下我這裡的體會,數據是有的,但這個數據前面估計會有很多定語~第一個重要指標:單機的連接數指標做過長連接的同行,應該有體會,如果在穩定連接情況下,連接數這個指標,在沒有網絡吞吐情況下對比,其實意義往往不大,維持連接消耗cpu資源很小,每條連接tcp協議棧會佔約4k的內存開銷,系統參數調整後,我們單機測試數據,最高也是可以達到單實例300w長連接。但做更高的測試,我個人感覺意義不大。因為實際網絡環境下,單實例300w長連接,從理論上算壓力就很大:實際弱網絡環境下,移動客戶端的斷線率很高,假設每秒有1000分之一的用戶斷線重連。300w長連接,每秒新建連接達到3w,這同時連入的3w用戶,要進行註冊,加載離線存儲等對內rpc調用,另外300w長連接的用戶心跳需要維持,假設心跳300s一次,心跳包每秒需要1w tps。單播和多播數據的轉發,廣播數據的轉發,本身也要響應內部的rpc調用,300w長連接情況下,gc帶來的壓力,內部接口的響應延遲能否穩定保障。這些集中在一個實例中,可用性是一個挑戰。所以線上單實例不會hold很高的長連接,實際情況也要根據接入客戶端網絡狀況來決定。第二個重要指標:消息系統的內存使用量指標這一點上,使用go語言情況下,由於協程的原因,會有一部分額外開銷。但是要做兩個推送系統的對比,也有些需要確定問題。比如系統從設計上是否需要全雙工(即讀寫是否需要同時進行)如果半雙工,理論上對一個用戶的連接只需要使用一個協程即可(這種情況下,對用戶的斷線檢測可能會有延時),如果是全雙工,那讀/寫各一個協程。兩種場景內存開銷是有區別的。另外測試數據的大小往往決定我們對連接上設置的讀寫buffer是多大,是全局復用的,還是每個連接上獨享的,還是動態申請的。另外是否全雙工也決定buffer怎麼開。不同的策略,可能在不同情況的測試中表現不一樣。第三個重要指標:每秒消息下發量這一點上,也要看我們對消息到達的QoS級別(回復ack策略區別),另外看架構策略,每種策略有其更適用的場景,是純粹推?還是推拉結合?甚至是否開啟了消息日誌?日誌庫的實現機制、以及緩衝開多大?flush策略……這些都影響整個系統的吞吐量。另外為了HA,增加了內部通信成本,為了避免一些小概率事件,提供閃斷補償策略,這些都要考慮進去。如果所有的都去掉,那就是比較基礎庫的性能了。所以我只能給出大概數據,24核,64G的服務器上,在QoS為message at least,純粹推,消息體256B~1kB情況下,單個實例100w實際用戶(200w+)協程,峰值可以達到2~5w的QPS…內存可以穩定在25G左右,gc時間在200~800ms左右(還有優化空間)。我們正常線上單實例用戶控制在80w以內,單機最多兩個實例。事實上,整個系統在推送的需求上,對高峰的輸出不是提速,往往是進行限速,以防push系統瞬時的高吞吐量,轉化成對接入方業務服務器的ddos攻擊所以對於性能上,我感覺大家可以放心使用,至少在我們這個量級上,經受過考驗,go1.5到來後,確實有之前投資又增值了的感覺。消息系統架構介紹下面是對消息系統的大概介紹,之前一些同學可能在gopher china上可以看到分享,這裡簡單講解下架構和各個組件功能,額外補充一些當時遺漏的信息:架構圖如下,所有的service都 written by golang.幾個大概重要組件介紹如下:dispatcher service根據客戶端請求信息,將應網絡和區域的長連接服務器的,一組IP傳送給客戶端。客戶端根據返回的IP,建立長連接,連接Room service.room Service,長連接網關,hold用戶連接,並將用戶註冊進register service,本身也做一些接入安全策略、白名單、IP限制等。register service是我們全局session存儲組件,存儲和索引用戶的相關信息,以供獲取和查詢。coordinator service用來轉發用戶的上行數據,包括接入方訂閱的用戶狀態信息的回調,另外做需要協調各個組件的異步操作,比如kick用戶操作,需要從register拿出其他用戶做異步操作.saver service是存儲訪問層,承擔了對redis和mysql的操作,另外也提供部分業務邏輯相關的內存緩存,比如廣播信息的加載可以在saver中進行緩存。另外一些策略,比如客戶端sdk由於被惡意或者意外修改,每次加載了消息,不回復ack,那服務端就不會刪除消息,消息就會被反覆加載,形成死循環,可以通過在saver中做策略和判斷。(客戶端總是不可信的)。center service提供給接入方的內部api服務器,比如單播或者廣播接口,狀態查詢接口等一系列api,包括運維和管理的api。舉兩個常見例子,了解工作機制:比如發一條單播給一個用戶,center先請求Register獲取這個用戶之前註冊的連接通道標識、room實例地址,通過room service下發給長連接 Center Service比較重的工作如全網廣播,需要把所有的任務分解成一系列的子任務,分發給所有center,然後在所有的子任務里,分別獲取在線和離線的所有用戶,再批量推到Room Service。通常整個集群在那一瞬間壓力很大。deployd/agent service用於部署管理各個進程,收集各組件的狀態和信息,zookeeper和keeper用於整個系統的配置文件管理和簡單調度關於推送的服務端架構常見的推送模型有長輪訓拉取,服務端直接推送(360消息系統目前主要是這種),推拉結合(推送只發通知,推送後根據通知去拉取消息).拉取的方式不說了,現在並不常用了,早期很多是nginx+lua+redis,長輪訓,主要問題是開銷比較大,時效性也不好,能做的優化策略不多。直接推送的系統,目前就是360消息系統這種,消息類型是消耗型的,並且對於同一個用戶並不允許重複消耗,如果需要多終端重複消耗,需要抽象成不同用戶。推的好處是實時性好,開銷小,直接將消息下發給客戶端,不需要客戶端走從接入層到存儲層主動拉取.但純推送模型,有個很大問題,由於系統是異步的,他的時序性無法精確保證。這對於push需求來說是夠用的,但如果復用推送系統做im類型通信,可能並不合適。對於嚴格要求時序性,消息可以重複消耗的系統,目前也都是走推拉結合的模型,就是只使用我們的推送系統發通知,並附帶id等給客戶端做拉取的判斷策略,客戶端根據推送的key,主動從業務服務器拉取消息。並且當主從同步延遲的時候,跟進推送的key做延遲拉取策略。同時也可以通過消息本身的QoS,做純粹的推送策略,比如一些「正在打字的」低優先級消息,不需要主動拉取了,通過推送直接消耗掉。哪些因素決定推送系統的效果?首先是sdk的完善程度,sdk策略和細節完善度,往往決定了弱網絡環境下最終推送質量.SDK選路策略,最基本的一些策略如下:有些開源服務可能會針對用戶hash一個該接入區域的固定ip,實際上在國內環境下不可行,最好分配器(dispatcher)是返回散列的一組,而且端口也要參開,必要時候,客戶端告知是retry多組都連不上,返回不同idc的服務器。因為我們會經常檢測到一些case,同一地區的不同用戶,可能對同一idc內的不同ip連通性都不一樣,也出現過同一ip不同端口連通性不同,所以用戶的選路策略一定要靈活,策略要足夠完善.另外在選路過程中,客戶端要對不同網絡情況下的長連接ip做緩存,當網絡環境切換時候(wifi、2G、3G),重新請求分配器,緩存不同網絡環境的長連接ip。客戶端對於數據心跳和讀寫超時設置,完善斷線檢測重連機制針對不同網絡環境,或者客戶端本身消息的活躍程度,心跳要自適應的進行調整並與服務端協商,來保證鏈路的連通性。並且在弱網絡環境下,除了網絡切換(wifi切3G)或者讀寫出錯情況,什麼時候重新建立鏈路也是一個問題。客戶端發出的ping包,不同網絡下,多久沒有得到響應,認為網絡出現問題,重新建立鏈路需要有個權衡。另外對於不同網絡環境下,讀取不同的消息長度,也要有不同的容忍時間,不能一刀切。好的心跳和讀寫超時設置,可以讓客戶端最快的檢測到網絡問題,重新建立鏈路,同時在網絡抖動情況下也能完成大數據傳輸。結合服務端做策略另外系統可能結合服務端做一些特殊的策略,比如我們在選路時候,我們會將同一個用戶盡量映射到同一個room service實例上。斷線時,客戶端盡量對上次連接成功的地址進行重試。主要是方便服務端做閃斷情況下策略,會暫存用戶閃斷時實例上的信息,重新連入的 時候,做單實例內的遷移,減少延時與加載開銷.客戶端保活策略很多創業公司願意重新搭建一套push系統,確實不難實現,其實在協議完備情況下(最簡單就是客戶端不回ack不清數據),服務端會保證消息是不丟的。但問題是為什麼在消息有效期內,到達率上不去?往往因為自己app的push service存活能力不高。選用雲平台或者大廠的,往往sdk會做一些保活策略,比如和其他app共生,互相喚醒,這也是雲平台的push service更有保障原因。我相信很多雲平台旗下的sdk,多個使用同樣sdk的app,為了實現服務存活,是可以互相喚醒和保證活躍的。另外現在push sdk本身是單連接,多app復用的,這為sdk實現,增加了新的挑戰。綜上,對我來說,選擇推送平台,優先會考慮客戶端sdk的完善程度。對於服務端,選擇條件稍微簡單,要求部署接入點(IDC)越要多,配合精細的選路策略,效果越有保證,至於想知道哪些雲服務有多少點,這個群里來自各地的小夥伴們,可以合夥測測。go語言開發問題與解決方案下面講下,go開發過程中遇到挑戰和優化策略,給大家看下當年的一張圖,在第一版優化方案上線前一天截圖~可以看到,內存最高佔用69G,GC時間單實例最高時候高達3~6s.這種情況下,試想一次悲劇的請求,經過了幾個正在執行gc的組件,後果必然是超時… gc照成的接入方重試,又加重了系統的負擔。遇到這種情況當時整個系統最差情況每隔2,3天就需要重啟一次~當時出現問題,現在總結起來,大概以下幾點1.散落在協程里的I/O,Buffer和對象不復用。當時(12年)由於對go的gc效率理解有限,比較奔放,程序里大量short live的協程,對內通信的很多io操作,由於不想阻塞主循環邏輯或者需要及時響應的邏輯,通過單獨go協程來實現異步。這回會gc帶來很多負擔。針對這個問題,應盡量控制協程創建,對於長連接這種應用,本身已經有幾百萬並發協程情況下,很多情況沒必要在各個並發協程內部做異步io,因為程序的並行度是有限,理論上做協程內做阻塞操作是沒問題。如果有些需要異步執行,比如如果不異步執行,影響對用戶心跳或者等待response無法響應,最好通過一個任務池,和一組常駐協程,來消耗,處理結果,通過channel再傳回調用方。使用任務池還有額外的好處,可以對請求進行打包處理,提高吞吐量,並且可以加入控量策略.2.網絡環境不好引起激增go協程相比較以往高並發程序,如果做不好流控,會引起協程數量激增。早期的時候也會發現,時不時有部分主機內存會遠遠大於其他服務器,但發現時候,所有主要profiling參數都正常了。後來發現,通信較多系統中,網絡抖動阻塞是不可免的(即使是內網),對外不停accept接受新請求,但執行過程中,由於對內通信阻塞,大量協程被 創建,業務協程等待通信結果沒有釋放,往往瞬時會迎來協程暴漲。但這些內存在系統穩定後,virt和res都並沒能徹底釋放,下降後,維持高位。處理這種情況,需要增加一些流控策略,流控策略可以選擇在rpc庫來做,或者上面說的任務池來做,其實我感覺放在任務池裡做更合理些,畢竟rpc通信庫可以做讀寫數據的限流,但它並不清楚具體的限流策略,到底是重試還是日誌還是緩存到指定隊列。任務池本身就是業務邏輯相關的,它清楚針對不同的接口需要的流控限制策略。3.低效和開銷大的rpc框架早期rpc通信框架比較簡單,對內通信時候使用的也是短連接。這本來短連接開銷和性能瓶頸超出我們預期,短連接io效率是低一些,但端口資源夠,本身吞吐可以滿足需要,用是沒問題的,很多分層的系統,也有http短連接對內進行請求的但早期go版本,這樣寫程序,在一定量級情況,是支撐不住的。短連接大量臨時對象和臨時buffer創建,在本已經百萬協程的程序中,是無法承受的。所以後續我們對我們的rpc框架作了兩次調整。第二版的rpc框架,使用了連接池,通過長連接對內進行通信(復用的資源包括client和server的:編解碼Buffer、Request/response),大大改善了性能。但這種在一次request和response還是佔用連接的,如果網絡狀況ok情況下,這不是問題,足夠滿足需要了,但試想一個room實例要與後面的數百個的register,coordinator,saver,center,keeper實例進行通信,需要建立大量的常駐連接,每個目標機幾十個連接,也有數千個連接被佔用。非持續抖動時候(持續逗開多少無解),或者有延遲較高的請求時候,如果針對目標ip連接開少了,會有瞬時大量請求阻塞,連接無法得到充分利用。第三版增加了Pipeline操作,Pipeline會帶來一些額外的開銷,利用tcp的全雙特性,以盡量少的連接完成對各個服務集群的rpc調用。4.Gc時間過長Go的Gc仍舊在持續改善中,大量對象和buffer創建,仍舊會給gc帶來很大負擔,尤其一個佔用了25G左右的程序。之前go team的大咖郵件也告知我們,未來會讓使用協程的成本更低,理論上不需要在應用層做更多的策略來緩解gc.改善方式,一種是多實例的拆分,如果公司沒有端口限制,可以很快部署大量實例,減少gc時長,最直接方法。不過對於360來說,外網通常只能使用80和433。因此常規上只能開啟兩個實例。當然很多人給我建議能否使用SO_REUSEPORT,不過我們內核版本確實比較低,並沒有實踐過。另外能否模仿nginx,fork多個進程監控同樣端口,至少我們目前沒有這樣做,主要對於我們目前進程管理上,還是獨立的運行的,對外監聽不同端口程序,還有配套的內部通信和管理端口,實例管理和升級上要做調整。解決gc的另兩個手段,是內存池和對象池,不過最好做仔細評估和測試,內存池、對象池使用,也需要對於代碼可讀性與整體效率進行權衡。這種程序一定情況下會降低並行度,因為用池內資源一定要加互斥鎖或者原子操作做CAS,通常原子操作實測要更快一些。CAS可以理解為可操作的更細行為粒度的鎖(可以做更多CAS策略,放棄運行,防止忙等)。這種方式帶來的問題是,程序的可讀性會越來越像C語言,每次要malloc,各地方用完後要free,對於對象池free之前要reset,我曾經在應用層嘗試做了一個分層次結構的「無鎖隊列」上圖左邊的數組實際上是一個列表,這個列表按大小將內存分塊,然後使用atomic操作進行CAS。但實際要看測試數據了,池技術可以明顯減少臨時對象和內存的申請和釋放,gc時間會減少,但加鎖帶來的並行度的降低,是否能給一段時間內的整體吞吐量帶來提升,要做測試和權衡…在我們消息系統,實際上後續去除了部分這種黑科技,試想在百萬個協程裏面做自旋操作申請復用的buffer和對象,開銷會很大,尤其在協程對線程多對多模型情況下,更依賴於golang本身調度策略,除非我對池增加更多的策略處理,減少忙等,感覺是在把runtime做的事情,在應用層非常不優雅的實現。普遍使用開銷理論就大於收益。但對於rpc庫或者codec庫,任務池內部,這些開定量協程,集中處理數據的區域,可以嘗試改造~對於有些固定對象復用,比如固定的心跳包什麼的,可以考慮使用全局一些對象,進行復用,針對應用層數據,具體設計對象池,在部分環節去復用,可能比這種無差別的設計一個通用池更能進行效果評估.消息系統的運維及測試下面介紹消息系統的架構迭代和一些迭代經驗,由於之前在其他地方有過分享,後面的會給出相關鏈接,下面實際做個簡單介紹,感興趣可以去鏈接裏面看架構迭代~根據業務和集群的拆分,能解決部分灰度部署上線測試,減少點對點通信和廣播通信不同產品的相互影響,針對特定的功能做獨立的優化.消息系統架構和集群拆分,最基本的是拆分多實例,其次是按照業務類型對資源佔用情況分類,按用戶接入網絡和對idc布點要求分類(目前沒有條件,所有的產品都部署到全部idc)系統的測試go語言在並發測試上有獨特優勢。對於壓力測試,目前主要針對指定的服務器,選定線上空閑的服務器做長連接壓測。然後結合可視化,分析壓測過程中的系統狀態。但壓測早期用的比較多,但實現的統計報表功能和我理想有一定差距。我覺得最近出的golang開源產品都符合這種場景,go寫網絡並發程序給大家帶來的便利,讓大家把以往為了降低複雜度,拆解或者分層協作的組件,又組合在了一起。QAQ1:協議棧大小,超時時間定製原則?移動網絡下超時時間按產品需求通常2g,3G情況下是5分鐘,wifi情況下5~8分鐘。但對於個別場景,要求響應非常迅速的場景,如果連接idle超過1分鐘,都會有ping,pong,來校驗是否斷線檢測,儘快做到重新連接。Q2:消息是否持久化?消息持久化,通常是先存後發,存儲用的redis,但落地用的mysql。mysql只做故障恢復使用。Q3:消息風暴怎麼解決的?如果是發送情況下,普通產品是不需要限速的,對於較大產品是有發送隊列做控速度,按人數,按秒進行控速度發放,發送成功再發送下一條。Q4:golang的工具鏈支持怎麼樣?我自己寫過一些小程序千把行之內,確實很不錯,但不知道代碼量上去之後,配套的debug工具和profiling工具如何,我看上邊有分享說golang自帶的profiling工具還不錯,那debug呢怎麼樣呢,官方一直沒有出debug工具,gdb支持也不完善,不知你們用的什麼?是這樣的,我們正常就是println,我感覺基本上可以定位我所有問題,但也不排除由於並行性通過println無法復現的問題,目前來看只能靠經驗了。只要常見並發嘗試,經過分析是可以找到的。go很快會推出調試工具的~Q5:協議棧是基於tcp嗎?是否有協議拓展功能?協議棧是tcp,整個系統tcp長連接,沒有考慮擴展其功能~如果有好的經驗,可以分享~Q6:問個問題,這個系統是接收上行數據的吧,系統接收上行數據後是轉發給相應系統做處理么,是怎麼轉發呢,如果需要給客戶端返回調用結果又是怎麼處理呢?系統上行數據是根據協議頭進行轉發,協議頭裏面標記了產品和轉發類型,在coordinator裏面跟進產品和轉發類型,回調用戶,如果用戶需要阻塞等待回復才能後續操作,那通過再發送消息,路由回用戶。因為整個系統是全異步的。Q7:問個pushsdk的問題。pushsdk的單連接,多app復用方式,這樣的情況下以下幾個問題是如何解決的:1)系統流量統計會把所有流量都算到啟動連接的應用吧?而啟動應用的連接是不固定的吧?2)同一個pushsdk在不同的應用中的版本號可能不一樣,這樣暴露出來的接口可能有版本問題,如果用單連接模式怎麼解決?流量只能算在啟動的app上了,但一般這種安裝率很高的app承擔可能性大,常用app本身被檢測和殺死可能性較少,另外消息下發量是有嚴格控制 的。整體上用戶還是省電和省流量的。我們pushsdk盡量向上兼容,出於這個目的,push sdk本身做的工作非常有限,抽象出來一些常見的功能,純推的系統,客戶端策略目前做的很少,也有這個原因。Q8:生產系統的profiling是一直打開的么?不是一直打開,每個集群都有採樣,但需要開啟哪個可以後台控制。這個profling是通過接口調用。Q9:面前系統中的消息消費者可不可以分組?類似於Kafka。客戶端可以訂閱不同產品的消息,接受不同的分組。接入的時候進行bind或者unbind操作Q10:為什麼放棄erlang,而選擇go,有什麼特別原因嗎?我們現在用的erlang?erlang沒有問題,原因是我們上線後,其他團隊才做出來,經過qa一個部門對比測試,在沒有顯著性能提升下,選擇繼續使用go版本的push,作為公司基礎服務。Q11:流控問題有排查過網卡配置導致的idle問題嗎?流控是業務級別的流控,我們上線前對於內網的極限通信量做了測試,後續將請求在rpc庫內,控制在小於內部通信開銷的上限以下.在到達上限前作流控。Q12:服務的協調調度為什麼選擇zk有考慮過raft實現嗎?golang的raft實現很多啊,比如Consul和ectd之類的。3年前,還沒有後兩者或者後兩者沒聽過應該。zk當時公司內部成熟方案,不過目前來看,我們不準備用zk作結合系統的定製開發,準備用自己寫的keeper代替zk,完成配置文件自動轉數據結構,數據結構自動同步指定進程,同時裏面可以完成很多自定義的發現和控制策略,客戶端包含keeper的sdk就可以實現以上的所有監控數據,profling數據收集,配置文件更新,啟動關閉等回調。完全抽象成語keeper通信sdk,keeper之間考慮用raft。Q13:負載策略是否同時在服務側與CLIENT側同時做的 (DISPATCHER 會返回一組IP)?另外,ROOM SERVER/REGISTER SERVER連接狀態的一致性可用性如何保證? 服務側保活有無特別關注的地方? 安全性方面是基於TLS再加上應用層加密?會在server端做,比如重啟操作前,會下髮指令類型消息,讓客戶端進行主動行為。部分消息使用了加密策略,自定義的rsa+des,另外滿足我們安全公司的需要,也定製開發很多安全加密策略。一致性是通過冷備解決的,早期考慮雙寫,但實時狀態雙寫同步代價太高而且容易有臟數據,比如register掛了,調用所有room,通過重新刷入指定register來解決。Q14:這個keeper有開源打算嗎?還在寫,如果沒耦合我們系統太多功能,一定會開源的,主要這意味着,我們所有的bind在sdk的庫也需要開源~Q15:比較好奇lisence是哪個如果開源?

nsq介紹和使用

最近一直在尋找一個高性能,高可用的消息隊列做內部服務之間的通訊。一開始想到用zeromq,但在查找資料的過程中,意外的發現了Nsq這個由golang開發的消息隊列,畢竟是golang原汁原味的東西,功能齊全,關鍵是性能還不錯。其中支持動態拓展,消除單點故障等特性, 都可以很好的滿足我的需求

下面上一張Nsq與其他mq的對比圖,看上去的確強大。下面簡單記錄一下Nsq的使用方法

圖片來自golang2017開發者大會

在使用Nsq服務之前,還是有必要了解一下Nsq的幾個核心組件

整個Nsq服務包含三個主要部分

先看看官方的原話是怎麼說:

nsqlookupd是守護進程負責管理拓撲信息。客戶端通過查詢 nsqlookupd 來發現指定話題(topic)的生產者,並且 nsqd 節點廣播話題(topic)和通道(channel)信息

簡單的說nsqlookupd就是中心管理服務,它使用tcp(默認端口4160)管理nsqd服務,使用http(默認端口4161)管理nsqadmin服務。同時為客戶端提供查詢功能

總的來說,nsqlookupd具有以下功能或特性

官方原話:是一套 WEB UI,用來彙集集群的實時統計,並執行不同的管理任務

總的來說,nsqadmin具有以下功能或特性

nsqadmin默認的訪問地址是

官方原話:nsqd 是一個守護進程,負責接收,排隊,投遞消息給客戶端

簡單的說,真正幹活的就是這個服務,它主要負責message的收發,隊列的維護。nsqd會默認監聽一個tcp端口(4150)和一個http端口(4151)以及一個可選的https端口

總的來說,nsqd 具有以下功能或特性

這是官方的圖,第一個channel(meteics)因為有多個消費者,所以觸發了負載均衡機制。後面兩個channel由於沒有消費者,所有的message均會被緩存在相應的隊列里,直到消費者出現

這裡想到一個問題是,如果一個channel只有生產者不停的在投遞message,會不會導致服務器資源被耗盡?也許nsqd內部做了相應處理,但還是要避免這種情況的出現

了解nsqlookupd,nsqd與客戶端中消費者和生產者的關係

消費者有兩種方式與nsqd建立連接

生產者必須直連nsqd去投遞message(網上說,可以連接到nsqlookupd,讓nsqlookupd自動選擇一個nsqd去完成投遞,但是我用Producer的tcp是連不上nsqlookupd的,不知道http可不可以…),

這裡有一個問題就是如果生產者所連接的nsqd炸了,那麼message就會投遞失敗,所以在客戶端必須自己實現相應的備用方案

執行完後檢查godep是否已經安裝在bin目錄下,一般都會自動安裝,如果沒有,用go install手動安裝下

如果安裝成功,bin目錄里就會出現一大堆nsq_…開頭的可執行文件

nsqd是一個獨立的服務,啟動一個nsqd就可以完成message的收發,啟動一個單機的nsqd,很簡單

客戶端可以使用http,也可以使用tcp,這裡我使用是官方的go-nsq包做客戶端,使用tcp進行message的收發

//Nsq發送測試

//Nsq接收測試

golang 協程什麼時候切換

應puppet大拿劉宇的邀請,我去西山居運維團隊做了一個簡短分享,談談為什麼我要將我們的項目從python轉向go。

坦白的講,在一幫python用戶面前講為什麼放棄python轉而用go其實是一件壓力蠻大的事情,語言之爭就跟vim和emacs之爭一樣,是一個永恆的無解話題,稍微不注意就可能導致粉絲強烈地反擊。所以我只會從我們項目實際情況出發,來講講為什麼我最終選擇了go。

為什麼放棄python

首先,我其實得說說為什麼我們會選擇python。在我加入企業快盤團隊之前,整個項目包括更早的金山快盤都是採用python進行開發的。至於為什麼這麼選擇,當時的架構師蔥頭告訴我,主要是因為python上手簡單,開發迅速。對於團隊裏面大部分完全沒服務端開發經驗的同學來說,python真的是一個很好的選擇。

python的簡單高效,我是深有體會的。當時私有雲項目也就幾個程序員,但是我們要服務多家大型企業,進行定製化的開發,多虧了python,我們才能快速出活。後來企業快盤掛掉之後,我們啟動輕辦公項目,自然也使用python進行了原始版本的構建。

python雖然很強大,但我們在使用的時候也碰到了一些問題,主要由如下幾個方面:

動態語言

python是一門動態強類型語言。但是,仍然可能出現int + string這樣的運行時錯誤,因為對於一個變量,在寫代碼的時候,我們有時候很容易就忘記這個變量到底是啥類型的了。

在python裏面,可以允許同名函數的出現,後一個函數會覆蓋前一個函數,有一次我們系統一個很嚴重的錯誤就是因為這個導致的。

上面說到的這些,靜態語言在編譯的時候就能幫我們檢測出來,而不需要等到運行時出問題才知道。雖然我們有很完善的測試用例,但總有case遺漏的情況。所以每次出現運行時錯誤,我心裏都想着如果能在編譯的時候就發現該多好。

性能

其實這個一直是很多人吐槽python的地方,但python有它適合乾的事情,硬是要用python進行一些高性能模塊的開發,那也有點難為它了。

python的GIL導致無法真正的多線程,大家可能會說我用多進程不就完了。但如果一些計算需要涉及到多進程交互,進程之間的通訊開銷也是不得不考慮的。

無狀態的分佈式處理使用多進程很方便,譬如處理http請求,我們就是在nginx後面掛載了200多個django server來處理http的,但這麼多個進程自然導致整體機器負載偏高。

但即使我們使用了多個django進程來處理http請求,對於一些超大量請求,python仍然處理不過來。所以我們使用openresty,將高頻次的http請求使用lua來實現。可這樣又導致使用兩種開發語言,而且一些邏輯還得寫兩份不同的代碼。

同步網絡模型

django的網絡是同步阻塞的,也就是說,如果我們需要訪問外部的一個服務,在等待結果返回這段時間,django不能處理任何其他的邏輯(當然,多線程的除外)。如果訪問外部服務需要很長時間,那就意味着我們的整個服務幾乎在很長一段時間完全不可用。

為了解決這個問題,我們只能不斷的多開django進程,同時需要保證所有服務都能快速的處理響應,但想想這其實是一件很不靠譜的事情。

異步網絡模型

tornado的網絡模型是異步的,這意味着它不會出現django那樣因為外部服務不可用導致這個服務無法響應的問題。話說,比起django,我可是非常喜歡tornado的,小巧簡單,以前還寫過幾篇深入剖析tornado的文章了。

雖然tornado是異步的,但是python的mysql庫都不支持異步,這也就意味着如果我們在tornado裏面訪問數據庫,我們仍然可能面臨因為數據庫問題造成的整個服務不可用。

其實異步模型最大的問題在於代碼邏輯的割裂,因為是事件觸發的,所以我們都是通過callback進行相關處理,於是代碼裏面就經常出現干一件事情,傳一個callback,然後callback裏面又傳callback的情況,這樣的結果就是整個代碼邏輯非常混亂。

python沒有原生的協程支持,雖然可以通過gevent,greenlet這種的上patch方式來支持協程,但畢竟更改了python源碼。另外,python的yield也可以進行簡單的協程模擬,但畢竟不能跨堆棧,局限性很大,不知道3.x的版本有沒有改進。

開發運維部署

當我第一次使用python開發項目,我是沒成功安裝上項目需要的包的,光安裝成功mysql庫就弄了很久。後來,是一位同事將他整個python目錄打包給我用,我才能正常的將項目跑起來。話說,現在有了docker,是多麼讓人幸福的一件事情。

而部署python服務的時候,我們需要在服務器上面安裝一堆的包,光是這一點就讓人很麻煩,雖然可以通過puppet,salt這些自動化工具解決部署問題,但相比而言,靜態編譯語言只用扔一個二進制文件,可就方便太多了。

代碼失控

python非常靈活簡單,寫c幾十行代碼才能搞定的功能,python一行代碼沒準就能解決。但是太簡單,反而導致很多同學無法對代碼進行深層次的思考,對整個架構進行細緻的考量。來了一個需求,啪啪啪,鍵盤敲完開速實現,結果就是代碼越來越混亂,最終導致了整個項目代碼失控。

雖然這也有我們自身的原因,譬如沒好的代碼review機制,沒有好的項目規範,但個人感覺,如果一個程序員沒經過良好的編碼訓練,用python很容易就寫出爛的代碼,因為太自由了。

當然,我這裡並不是說用python無法進行大型項目的開發,豆瓣,dropbox都是很好的例子,只是在我們項目中,我們的python代碼失控了。

上面提到的都是我們在實際項目中使用python遇到的問題,雖然最終都解決了,但是讓我愈發的覺得,隨着項目複雜度的增大,流量性能壓力的增大,python並不是一個很好的選擇。

為什麼選擇go

說完了python,現在來說說為什麼我們選擇go。其實除了python,我們也有其他的選擇,java,php,lua(openresty),但最終我們選擇了go。

雖然java和php都是最好的編程語言(大家都這麼爭的),但我更傾向一門更簡單的語言。而openresty,雖然性能強悍,但lua仍然是動態語言,也會碰到前面說的動態語言一些問題。最後,前金山許式偉用的go,前快盤架構師蔥頭也用的go,所以我們很自然地選擇了go。

go並不是完美,一堆值得我們吐槽的地方。

error,好吧,如果有語言潔癖的同學可能真的受不了go的語法,尤其是約定的最後一個返回值是error。項目裏面經常會充斥這樣的代碼:

if _, err := w.Write(data1); err != nil {

returun err

}

if _, err := w.Write(data2); err != nil {

returun err

}

難怪有個梗是對於一個需求,java的程序員在寫配置的時候,go程序員已經寫了大部分代碼,但是當java的程序員寫完的時候,go程序員還在寫err != nil。

這方面,errors-are-values倒是推薦了一個不錯的解決方案。

包管理,go的包管理太弱了,只有一個go get,也就是如果不小心更新了一個外部庫,很有可能就導致現有的代碼編譯不過了。雖然已經有很多開源方案,譬如godep以及現在才出來的gb等,但畢竟不是官方的。貌似google也是通過vendor機制來管理第三方庫的。希望go 1.5或者之後的版本能好好處理下這個問題。

GC,java的GC發展20年了,go才這麼點時間,gc鐵定不完善。所以我們仍然不能隨心所欲的寫代碼,不然在大請求量下面gc可能會卡頓整個服務。所以有時候,該用對象池,內存池的一定要用,雖然代碼丑了點,但好歹性能上去了。

泛型,雖然go有inteface,但泛型的缺失會讓我們在實現一個功能的時候寫大量的重複代碼,譬如int32和int64類型的sort,我們得為分別寫兩套代碼,好冗餘。go 1.4之後有了go generate的支持,但這種的仍然需要自己根據go的AST庫來手動寫相關的parser,難度也挺大的。雖然也有很多開源的generate實現,但畢竟不是官方的。

當然還有很多值得吐槽的地方,就不一一列舉了,但是go仍舊有它的優勢。

靜態語言,強類型。靜態編譯能幫我們檢查出來大量的錯誤,go的強類型甚至變態到不支持隱式的類型轉換。雖然寫代碼感覺很彆扭,但減少了犯錯的可能。

gofmt,應該這是我知道的第一個官方提供統一格式化代碼工具的語言了。有了gofmt,大家的代碼長一個樣了,也就沒有花括號到底放到結尾還是新開一行這種蛋疼的代碼風格討論了。因為大家的代碼風格一樣,所以看go的代碼很容易。

天生的並行支持,因為goroutine以及channel,用go寫分佈式應用,寫並發程序異常的容易。沒有了蛋疼的callback導致的代碼邏輯割裂,代碼邏輯都是順序的。

性能,go的性能可能趕不上c,c++以及openresty,但真的也挺強悍的。在我們的項目中,現在單機就部署了一個go的進程,就完全能夠勝任以前200個python進程乾的事情,而且CPU和MEM佔用更低。

運維部署,直接編譯成二進制,扔到服務器上面就成,比python需要安裝一堆的環境那是簡單的太多了。當然,如果有cgo,我們也需要將對應的動態庫給扔過去。

開發效率,雖然go是靜態語言,但我個人感覺開發效率真的挺高,直覺上面跟python不相上下。對於我個人來說,最好的例子就是我用go快速開發了非常多的開源組件,譬如ledisdb,go-mysql等,而這些最開始的版本都是在很短的時間裏面完成的。對於我們項目來說,我們也是用go在一個月就重構完成了第一個版本,並發佈。

實際項目中一些Go Tips

到現在為止,我們幾乎所有的服務端項目都已經轉向go,當然在使用的時候也遇到了一些問題,列出來算是經驗分享吧。

godep,我們使用godep進行第三方庫管理,但是godep我碰到的最大的坑就是build tag問題,如果一個文件有build tag,godep很有可能就會忽略這個文件。

IO deadline,如果能自己在應用層處理的都自己處理,go的deadline內部是timer來控制,但timer內部採用一個array來實現的heap,全局共用一個鎖,如果大並發量,並且timer數量過多,timeout變動太頻繁,很容易就引起性能問題。

GC,這個前面也說了,多用內存池,對象池,另外,我還發現,如果對象的生命周期跟goroutine一致,對性能的提升也不錯,也在go的group問過相關問題,大家猜測可能是因為一些對象其實是在goroutine的8k棧上面分配的,所以一起回收沒有額外GC了。

Go gob,如果要做RPC服務,gob並不是一個很好的選擇,首先就跟python的pickle不通用,然後為了做不同系統的數據傳入,任何包都必須帶上類型的詳細信息,size太大。go裏面現在還沒一套官方的RPC方案,gRPC貌似有上位的可能。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/184083.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-11-25 05:51
下一篇 2024-11-25 05:51

相關推薦

  • 使用Golang調用Python

    在現代軟件開發中,多種編程語言的協作是相當普遍的。其中一種使用場景是Golang調用Python,這使得在使用Python庫的同時,可以利用Golang的高性能和強大並發能力。這篇…

    編程 2025-04-29
  • 使用Golang創建黑色背景圖片的方法

    本文將從多個方面介紹使用Golang創建黑色背景圖片的方法。 一、安裝必要的代碼庫和工具 在開始創建黑色背景圖片之前,我們需要先安裝必要的代碼庫和工具: go get -u git…

    編程 2025-04-29
  • Golang中使用strings.Split函數進行字符串分割的方法

    一、Split函數的基本用法 字符串是編程中常見的數據類型,它們可以在程序中被處理、存儲和傳輸。在Go語言中,字符串也是一個基本的數據類型,而strings包提供了一些操作字符串的…

    編程 2025-04-23
  • Golang環境變量全面解析

    Golang是一門非常流行的開發語言,擁有高效的CGO、簡單易懂的語法、高並發能力等優點,然而它也需要使用環境變量來配置一些參數。在本篇文章中,我們將從多個方面對Golang環境變…

    編程 2025-04-23
  • 深入下探golang http server

    Go語言已經成為了軟件開發領域的熱門語言,它的高性能、應用廣泛、安全性好,使得它成為了眾多開發者心目中的首選編程語言。在眾多應用場景中,golang http server的應用非…

    編程 2025-04-23
  • Compacted:一個高性能的Golang緩存庫

    一、簡介 Compacted是一個使用Golang編寫的緩存庫,旨在提供高性能的內存緩存功能。相對於其他常見的緩存庫,Compacted在內存使用和性能方面都做了一定的優化。 緩存…

    編程 2025-04-23
  • Golang nil解析

    一、什麼是nil Nil是Golang語言中的一個預定義標識符,表示一個零值對象,通常表示一個空指針。Nil被定義為指針類型、函數類型、接口類型、map類型、Slice類型、Cha…

    編程 2025-04-23
  • Golang中文社區介紹

    Go語言或者叫Golang是一個開源項目,目前是由Google開發維護的一種靜態類型、並發安全、編譯型的編程語言。Go語言的特點是結構清晰、並發能力強、具有垃圾回收機制並且支持跨平…

    編程 2025-04-23
  • 詳解golang walk控件庫

    Golang提供的可視化庫有很多個,其中walk是一個比較好用且強大的庫。本文將從多個方面對walk進行詳細闡述,包括基本控件、布局、菜單、圖標等方面的內容。 一、控件基礎 Gol…

    編程 2025-04-22
  • Golang泛型詳解

    Golang泛型成為眾多開發人員關注的話題,因為它使得代碼更加通用、可重用、簡單、易於維護。那麼,什麼是泛型、為什麼它如此重要,如何使用它?本文將從多個方面為您詳細闡述Golang…

    編程 2025-04-20

發表回復

登錄後才能評論