真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

go語言報(bào)表控件 golang 報(bào)表工具

golang csv parse error on line 1, column 1: bare " in non-quoted-field

根據(jù)需求做一個(gè)csv報(bào)表數(shù)據(jù)導(dǎo)入入庫功能,運(yùn)行多天突然運(yùn)維告知導(dǎo)入數(shù)據(jù)有問題,有問題那就排查唄。。。

卓尼網(wǎng)站建設(shè)公司成都創(chuàng)新互聯(lián),卓尼網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為卓尼1000多家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)公司要多少錢,請找那個(gè)售后服務(wù)好的卓尼做網(wǎng)站的公司定做!

題外話:這個(gè)問題足足浪費(fèi)了我2天時(shí)間,期間還出了其他的一些問題著實(shí)讓我抓狂,另外這篇文章希望能幫到你,不要在采坑了?。?!

說在前面,這個(gè)csv表格數(shù)據(jù)是有中文的。ok繼續(xù)

通過日志打點(diǎn)發(fā)現(xiàn)了以下錯(cuò)誤

找了一圈,著實(shí)頭痛,我也知道中文需要做處理轉(zhuǎn)化這個(gè)我已經(jīng)做了,并且已經(jīng)明確是utf-8了通過標(biāo)準(zhǔn)庫方法進(jìn)行判斷的還是報(bào)錯(cuò)。著實(shí)讓我摸不著頭腦。判斷utf-8方法如下

ok,最后借助了百度,google找了一大圈功夫不負(fù)有心人,最后發(fā)現(xiàn)utf-8還有 utf-8 bom 這種編碼格式,我想罵娘。。。至于編碼規(guī)則啥的就不詳細(xì)說了,有興趣自行百度吧。

解決方案如下:

至于其他編碼方式以及轉(zhuǎn)換可以查看golang官方擴(kuò)展庫 golang.org/x/text/encoding 庫中的源碼。

一個(gè)用golang的無名小卒

完~

從PHP 到Golang 的筆記 ( 轉(zhuǎn) )

———文章來源 YamiOdymel/PHP-to-Golang

PHP和模塊之間的關(guān)系令人感到煩躁,假設(shè)你要讀取 yaml 檔案,你需要有一個(gè) yaml 的模塊,為此,你還需要將其編譯然后將編譯后的模塊擺放至指定位置,之后換了一臺伺服器你還要重新編譯,這點(diǎn)到現(xiàn)在還是沒有改善;順帶一提之后出了PHP 7效能確實(shí)提升了許多(比Python 3快了些),但PHP仍令我感到臃腫,我覺得是時(shí)候

(轉(zhuǎn)行)了。

PHP 和Golang 的效能我想毋庸置疑是后者比較快(而且是以倍數(shù)來算),也許有的人會認(rèn)為兩種不應(yīng)該被放在一起比較,但Golang 本身就是偏向Web 開發(fā)的,所以這也是為什么我考慮轉(zhuǎn)用Golang 的原因,起初我的考慮有幾個(gè):Node.js 和Rust 還有最終被選定的Golang;先談?wù)凬ode.js 吧。

Node.js的效能可以說是快上PHP 3.5倍至6倍左右 ,而且撰寫的語言還是JavaScript,蒸蚌,如此一來就不需要學(xué)習(xí)新語言了!搭配Babel更可以說是萬能,不過那跟「跳跳虎」一樣的Async邏輯還有那恐怖的Callback Hell,有人認(rèn)為前者是種優(yōu)點(diǎn),這點(diǎn)我不否認(rèn),但是對學(xué)習(xí)PHP的我來說太過于"Mind Fuck",至于后者的Callback Hell雖然有Promise,但是那又是另一個(gè)「Then Hell」的故事了。相較于Golang之下,Node.js似乎就沒有那么吸引我了。你確實(shí)可以用Node.js寫出很多東西,不過那V8引擎的效能仍然有限,而且要學(xué)習(xí)新的事物,不就應(yīng)該是「全新」的嗎;)?

題外話: 為什么Node.js不適合大型和商業(yè)專案?

在拋棄改用Node.js 之后我曾經(jīng)花了一天的時(shí)間嘗試Rust 和Iron 框架,嗯??Rust 太強(qiáng)大了,強(qiáng)大到讓我覺得Rust 不應(yīng)該用在這里,這想法也許很蠢,但Rust 讓我覺得適合更應(yīng)該拿來用在系統(tǒng)或者是部分底層的地方,而不應(yīng)該是網(wǎng)路服務(wù)。

Golang是我最終的選擇,主要在于我花了一天的時(shí)間來研究的時(shí)候意外地發(fā)現(xiàn)Golang夭壽簡潔( 關(guān)鍵字只有25個(gè) ),相較之下Rust太過于「強(qiáng)大」令我怯步;而且Golang帶有許多工具,例如 go fmt 會自動幫你整理程式碼、 go doc 會自動幫你生產(chǎn)文件、 go test 可以自動單元測試并生產(chǎn)覆蓋率報(bào)表、也有 go get 套件管理工具(雖然沒有版本功能),不過都很實(shí)用,而且也不需要加上分號( ; ),真要說不好的地方??大概就是強(qiáng)迫你花括號不能換行放吧(沒錯(cuò),我就是花括號會換行放的人)。

當(dāng)我在撰寫這份文件的時(shí)候 我會先假設(shè)你有一定的基礎(chǔ) ,你可以先閱讀下列的手冊,他們都很不錯(cuò)。

你能夠在PHP 里面想建立一個(gè)變數(shù)的時(shí)候就直接建立,夭壽贊,是嗎?

蒸蚌!那么Golang 呢?在Golang 中變數(shù)分為幾類:「新定義」、「預(yù)先定義」、「自動新定義」、「覆蓋」。讓我們來看看范例:

在PHP中你會很常用到 echo 來顯示文字,像這樣。

然而在Golang中你會需要 fmt 套件,關(guān)于「什么是套件」的說明你可以在文章下述了解。

這很簡單,而且兩個(gè)語言的用法相差甚少,下面這是PHP:

只是Golang 稍微聒噪了一點(diǎn),你必須在函式后面宣告他最后會回傳什么資料型別。

在PHP 中你要回傳多個(gè)資料你就會用上陣列,然后將資料放入陣列里面,像這樣。

然而在Golang 中你可以不必用到一個(gè)陣列,函式可以一次回傳多個(gè)值:

兩個(gè)語言的撰寫方式不盡相同。

主要是PHP 的陣列能做太多事情了,所以在PHP 里面要儲存什么用陣列就好了。

在Golang里??沒有這么萬能的東西,首先要先了解Golang中有這些型態(tài): array , slice , map , interface ,

你他媽的我到底看了三洨,首先你要知道Golang是個(gè)強(qiáng)型別語言,意思是你的陣列中 只能有一種型態(tài) ,什么意思?當(dāng)你決定這個(gè)陣列是用來擺放字串資料的時(shí)候,你就只能在里面放字串。沒有數(shù)值、沒有布林值,就像你沒有女朋友一樣。

先撇開PHP 的「萬能陣列」不管,Golang 中的陣列既單純卻又十分腦殘,在定義一個(gè)陣列的時(shí)候,你必須給他一個(gè)長度還有其內(nèi)容存放的資料型態(tài),你的陣列內(nèi)容不一定要填滿其長度,但是你的陣列內(nèi)容不能超過你當(dāng)初定義的長度。

切片??這聽起來也許很奇怪,但是你確實(shí)可以「切」他,讓我們先談?wù)劇盖衅贡绕稹戈嚵小挂迷谀睦铮骸改悴挥枚x其最大長度,而且你可以直接賦予值」,沒了。

我們剛才有提到你可以「切」他,記得嗎?這有點(diǎn)像是PHP中的 array_slice() ,但是Golang直接讓Slice「內(nèi)建」了這個(gè)用法,其用法是: slice[開始:結(jié)束] 。

在PHP中倒是沒有那么方便,在下列PHP范例中你需要不斷地使用 array_slice() 。

你可以把「映照」看成是一個(gè)有鍵名和鍵值的陣列,但是記?。骸改阈枰孪榷x其鍵名、鍵值的資料型態(tài)」,這仍限制你沒辦法在映照中存放多種不同型態(tài)的資料。

在Golang里可就沒這么簡單了,你需要先用 make() 宣告 map 。

也許你不喜歡「接口」這個(gè)詞,但用「介面」我怕會誤導(dǎo)大眾,所以,是的,接下來我會繼續(xù)稱其為「接口」。還記得你可以在PHP 的關(guān)聯(lián)陣列里面存放任何型態(tài)的資料嗎,像下面這樣?

現(xiàn)在你有福了!正因?yàn)镚olang中的 interface{} 可以接受任何內(nèi)容,所以你可以把它拿來存放任何型態(tài)的資料。

有時(shí)候你也許會有個(gè)不定值的變數(shù),在PHP 里你可以直接將一個(gè)變數(shù)定義成字串、數(shù)值、空值、就像你那變心的女友一樣隨時(shí)都在變。

在Golang中你必須給予變數(shù)一個(gè)指定的資料型別,不過還記得剛才提到的:「Golang中有個(gè) interface{} 能夠 存放任何事物 」嗎( 雖然也不是真的任何事物啦?? )?

當(dāng)我們程式中不需要繼續(xù)使用到某個(gè)資源或是發(fā)生錯(cuò)誤的時(shí)候,我們索性會將其關(guān)閉或是拋棄來節(jié)省資源開銷,例如PHP 里的讀取檔案:

在Golang中,你可以使用 defer 來在函式結(jié)束的時(shí)候自動執(zhí)行某些程式(其執(zhí)行方向?yàn)榉聪?。所以你就不需要在函式最后面結(jié)束最前面的資源。

defer 可以被稱為「推遲執(zhí)行」,實(shí)際上就是在函式結(jié)束后會「反序」執(zhí)行的東西,例如你按照了這樣的順序定義 defer : A-B-C-D ,那么執(zhí)行的順序其實(shí)會是 D-C-B-A ,這用在程式結(jié)束時(shí)還蠻有用的,讓我們看看Golang如何改善上述范例。

這東西很邪惡,不是嗎?又不是在寫B(tài)ASIC,不過也許有時(shí)候你會在PHP 用上呢。但是拜托,不要。

Golang中僅有 for 一種回圈但卻能夠達(dá)成 foreach 、 while 、 for 多種用法。普通 for 回圈寫法在兩個(gè)語言中都十分相近。

在Golang請記得:如果你的 i 先前并不存在,那么你就需要定義它,所以下面這個(gè)范例你會看見 i := 0 。

在PHP里, foreach() 能夠直接給你值和鍵名,用起來十分簡單。

Golang里面雖然僅有 for() 但卻可以使用 range 達(dá)成和PHP一樣的 foreach 方式。

一個(gè) while(條件) 回圈在PHP里面可以不斷地執(zhí)行區(qū)塊中的程式,直到 條件 為 false 為止。

在Golang里也有相同的做法,但仍是透過 for 回圈,請注意這個(gè) for 回圈并沒有任何的分號( ; ),而且一個(gè)沒有條件的 for 回圈會一直被執(zhí)行。

PHP中有 do .. while() 回圈可以先做區(qū)塊中的動作。

在Golang中則沒有相關(guān)函式,但是你可以透過一個(gè)無止盡的 for 回圈加上條件式來讓他結(jié)束回圈。

要是你真的希望完全符合像是PHP那樣的設(shè)計(jì)方式,或者你可以在Golang中使用很邪惡的 goto 。

在PHP中我們可以透過 date() 像這樣取得目前的日期。

在Golang就稍微有趣點(diǎn)了,因?yàn)镚olang中并不是以 Y-m-d 這種格式做為定義,而是 1 、 2 、 3 ,這令你需要去翻閱文件,才能夠知道 1 的定義是代表什么。

俗話說:「爆炸就是藝術(shù)」,可愛的PHP用詞真的很大膽,像是: explode() (爆炸)、 die() (死掉),回歸正傳,如果你想在PHP里面將字串切割成陣列,你可以這么做。

簡單的就讓一個(gè)字串給「爆炸」了,那么Golang 呢?

對了,記得引用 strings 套件。

這真的是很常用到的功能,就像物件一樣有著鍵名和鍵值,在PHP 里面你很簡單的就能靠陣列(Array)辦到。

真是太棒了,那么Golang呢?用 map 是差不多啦。如果有必要的話,你可以稍微復(fù)習(xí)一下先前提到的「多資料儲存型態(tài)-Stores」。

你很常會在PHP里面用 isset() 檢查一個(gè)索引是否存在,不是嗎?

在Golang里面很簡單的能夠這樣辦到(僅適用于 map )。

指針(有時(shí)也做參照)是一個(gè)像是「變數(shù)別名」的方法,這種方法讓你不用整天覆蓋舊的變數(shù),讓我們假設(shè) A = 1; B = A; 這個(gè)時(shí)候 B 會復(fù)制一份 A 且兩者不相干,倘若你希望修改 B 的時(shí)候?qū)嶋H上也會修改到 A 的值,就會需要指針。

指針比起復(fù)制一個(gè)變數(shù),他會建立一個(gè)指向到某個(gè)變數(shù)的記憶體位置,這也就是為什么你改變指針,實(shí)際上是在改變某個(gè)變數(shù)。

在Golang你需要用上 * 還有 符號。

有些時(shí)候你會回傳一個(gè)陣列,這個(gè)陣列里面可能有資料還有錯(cuò)誤代號,而你會用條件式判斷錯(cuò)誤代號是否非空值。

在Golang中函式可以一次回傳多個(gè)值。為此,你不需要真的回傳一個(gè)陣列,不過要注意的是你將會回傳一個(gè)屬于 error 資料型態(tài)的錯(cuò)誤,所以你需要引用 errors 套件來幫助你做這件事。

該注意的是Golang沒有 try .. catch ,因?yàn)?Golang推薦這種錯(cuò)誤處理方式 ,你應(yīng)該在每一次執(zhí)行可能會發(fā)生錯(cuò)誤的程式時(shí)就處理錯(cuò)誤,而非后來用 try 到處包覆你的程式。

在 if 條件式里宣告變數(shù)會讓你只能在 if 內(nèi)部使用這個(gè)變數(shù),而不會污染到全域范圍。

也許你在PHP中更常用的會是 try .. catch ,在大型商業(yè)邏輯時(shí)經(jīng)常看見如此地用法,實(shí)際上這種用法令人感到聒噪(因?yàn)槟銜枰欢?try 區(qū)塊):

Golang中并沒有 try .. catch ,實(shí)際上Golang也 不鼓勵(lì)這種行為 (Golang推薦逐一處理錯(cuò)誤的方式),倘若你真想辦倒像是捕捉異常這樣的方式,你確實(shí)可以使用Golang中另類處理錯(cuò)誤的方式(可以的話盡量避免使用這種方式): panic() , recover() , defer 。

你可以把 panic() 當(dāng)作是 throw (丟出錯(cuò)誤),而這跟PHP的 exit() 有87%像,一但你執(zhí)行了 panic() 你的程式就會宣告而終,但是別擔(dān)心,因?yàn)槌淌浇Y(jié)束的時(shí)候會呼叫 defer ,所以我們接下來要在 defer 停止 panic() 。

關(guān)于 defer 上述已經(jīng)有提到了,他是一個(gè)反向執(zhí)行的宣告,會在函式結(jié)束后被執(zhí)行,當(dāng)你呼叫了 panic() 結(jié)束程式的時(shí)候,也就會開始執(zhí)行 defer ,所以我們要在 defer 內(nèi)使用 recover() 讓程式不再繼續(xù)進(jìn)行結(jié)束動作,這就像是捕捉異常。

recover() 可以看作 catch (捕捉),我們要在 defer 里面用 recover() 解決 panic() ,如此一來程式就會回歸正常而不會被結(jié)束。

還記得在PHP里要引用一堆檔案的日子嗎?到處可見的 require() 或是 include() ?到了Golang這些都不見了,取而代之的是「套件(Package)」?,F(xiàn)在讓我們來用PHP解釋一下。

這看起來很正常對吧?但假設(shè)你有一堆檔案,這馬上就成了 Include Hell ,讓我們看看Golang怎么透過「套件」解決這個(gè)問題。

「 蛤???殺小??? 」你可能如此地說道。是的, main.go 中除了引用 fmt 套件( 為了要輸出結(jié)果用的套件 )之外完全沒有引用到 a.go 。

「 蛤???殺小?????? 」你仿佛回到了幾秒鐘前的自己。

既然沒有引用其他檔案,為什么 main.go 可以輸出 foo 呢?注意到了嗎, 兩者都是屬于 main 套件 ,因此 他們共享同一個(gè)區(qū)域 ,所以接下來要介紹的是什么叫做「套件」。

套件是每一個(gè) .go 檔案都必須聲明在Golang原始碼中最開端的東西,像下面這樣:

這意味著目前的檔案是屬于 main 套件( 你也可以依照你的喜好命名 ),那么要如何讓同個(gè)套件之間的函式溝通呢?

接著是Golang;注意!你不需要引用任何檔案,因?yàn)橄铝袃蓚€(gè)檔案同屬一個(gè)套件。

一個(gè)由「套件」所掌握的世界,比起PHP的 include() 和 require() 還要好太多了,對嗎?

在Golang 中沒有引用單獨(dú)檔案的方式,你必須匯入一整個(gè)套件,而且你要記?。骸敢欢銋R入了,你就一定要使用它」,像下面這樣。

假如你不希望使用你匯入的套件,你只是為了要觸發(fā)那個(gè)套件的 main() 函式而引用的話??,那么你可以在前面加上一個(gè)底線( _ )。

如果你的套件出現(xiàn)了名稱沖突,你可以在套件來源前面給他一個(gè)新的名稱。

現(xiàn)在你知道可以匯入套件了,那么什么是「匯出」?同個(gè)套件內(nèi)的函式還有共享變數(shù)確實(shí)可以直接用,但那 并不表示可以給其他套件使用 ,其方法取決于 函式/變數(shù)的「開頭大小寫」 。

是的。 Golang依照一個(gè)函式/變數(shù)的開頭大小寫決定這個(gè)東西是否可供「匯出」 。

這用在區(qū)別函式的時(shí)候格外有用,因?yàn)樾戦_頭的任何事物都是不供匯出的,反之,大寫開頭的任何事物都是用來匯出供其他套件使用的。

一開始可能會覺得這是什么奇異的規(guī)定,但寫久之后,你就能發(fā)現(xiàn)比起JavaScript和Python以「底線為開頭的命名方式」還要來得更好;比起成天宣告 public 、 private 、 protected 還要來得更快。

在Golang 中沒有類別,但有所謂的「建構(gòu)體(Struct)」和「接口(Interface)」,這就能夠滿足幾乎所有的需求了,這也是為什么我認(rèn)為Golang 很簡潔卻又很強(qiáng)大的原因。

讓我們先用PHP 建立一個(gè)類別,然后看看Golang 怎么解決這個(gè)問題。

雖然Golang沒有類別,但是「建構(gòu)體(Struct)」就十分地堪用了,首先你要知道在Golang中「類別」的成員還有方法都是在「類別」外面所定義的,這跟PHP在類別內(nèi)定義的方式有所不同,在Golang中還有一點(diǎn),那就是他們沒有 public 、 private 、 protected 的種類。

在PHP中,當(dāng)有一個(gè)類別被 new 的時(shí)候會自動執(zhí)行該類別內(nèi)的建構(gòu)子( __construct() ),通常你會用這個(gè)來初始化一些類別內(nèi)部的值。

但是在Golang 里因?yàn)闆]有類別,也就沒有建構(gòu)子,不巧的是建構(gòu)體本身也不帶有建構(gòu)子的特性,這個(gè)時(shí)候你只能自己在外部建立一個(gè)建構(gòu)用函式。

讓我們假設(shè)你有兩個(gè)類別,你會把其中一個(gè)類別傳入到另一個(gè)類別里面使用,廢話不多說!先上個(gè)PHP 范例(為了簡短篇幅我省去了換行)。

在Golang中你也有相同的用法,但是請記得:「 任何東西都是在「類別」外完成建構(gòu)的 」。

在PHP 中沒有相關(guān)的范例,這部分會以剛才「嵌入」章節(jié)中的Golang 范例作為解說對象。

你可以看見Golang在進(jìn)行 Foo 嵌入 Bar 的時(shí)候,會自動將 Foo 的成員暴露在 Bar 底下,那么假設(shè)「雙方之間有相同的成員名稱」呢?

這個(gè)時(shí)候被嵌入的成員就會被「遮蔽」,下面是個(gè)實(shí)際范例,還有你如何解決遮蔽問題:

雖然都是呼叫同一個(gè)函式,但是這個(gè)函式可以針對不同的資料來源做出不同的舉動,這就是多形。你也能夠把這看作是:「訊息的意義由接收者定義,而不是傳送者」。

目前PHP 中沒有真正的「多形」,不過你仍可以做出同樣的東西。

嗯??那么Golang呢?實(shí)際上更簡單而且更有條理了,在Golang中有 interface 可以幫忙完成這個(gè)工作。

如果你對Interface還不熟悉,可以試著查看「 解釋Golang中的Interface到底是什么 」文章。

謝謝你看到這里,可惜這篇文章卻沒有說出Golang 最重要的賣點(diǎn):「Goroutine」和「Channel」

如何實(shí)現(xiàn)支持?jǐn)?shù)億用戶的長連消息系統(tǒng)

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

ZbxTable——zabbix優(yōu)秀報(bào)表二開

zbxTable是一個(gè)開源zabbix報(bào)表系統(tǒng),go語言編寫(張思德,zabbix中國社區(qū)開源專家),目前版本已更新到1.15版本,支持zabbix5.4版本。

zbxTable部署有兩種方式:RPM部署和編譯安裝,如有特殊需要,可選擇編譯安裝,zbxtable用go語言編寫,對系統(tǒng)影響小,推薦用RPM安裝。

添加yum源

CentOS 6.x

rpm -Uvh

CentOS 7.X

rpm -Uvh

CentOS 8.X

rpm -Uvh

安裝

全新安裝直接復(fù)制命令即可

yum clean all

yum -y install zbxtable

安裝ms-agent

yum -y install ms-agent

配置

數(shù)據(jù)庫初始化

mysql -uroot -ppassword

create database zbxtable character set utf8 collate utf8_bin;

create user zbxtable@localhost identified by ‘zbxtablepwd123’;

這里不講其他數(shù)據(jù)庫,有興趣的可以看官網(wǎng)具體配置

系統(tǒng)初始化

配置文件需要初始化才能生成,步驟如下:

cd /usr/local/zbxtable/

./zbxtable init

這時(shí)候會進(jìn)入交互命令行,根據(jù)實(shí)際情況正確輸入數(shù)據(jù)庫庫的賬號密碼,zabbix鏈接信息,成功后會生成配置文件,否則無法看到配置文件。

配置MS-Agent

MS-Agent作為告警消息采集客戶端,采集zabbix產(chǎn)生的告警信息,發(fā)送到zbxtable平臺中,zbxtable需要在zabbix server配置相應(yīng)的action。配置如下。

cd /usr/local/zbxtable

./zbxtable install

這時(shí)會在zabbix上創(chuàng)建ms-agent用戶,密碼隨機(jī),權(quán)限管理員,最后輸出MS-Agent token為MS-Agent 與ZbxTable通信的token,需要和MS-Agent 配置文件里的token保持一致,否則無法正常收到告警。 Token可在conf/app.conf文件里找到。

啟動服務(wù)

systemctl enable --now zbxtable

重啟

systemctl restart zbxtable

查看狀態(tài)

systemctl status zbxtable

必須確保zbxtable服務(wù)是active狀態(tài),如果異常,查看日志文件:/usr/local/zbxtable/logs/zbxtable.log或者系統(tǒng)日志message

Zbxtable-web配置

zbxtable使用nginx做代理,安裝即可

yum -y install nginx

拷貝nginx配置文件

cp /usr/local/zbxtable/nginx.conf /etc/nginx/conf.d/

啟動nginx

systemctl start nginx

開機(jī)自啟動

systemctl enable nginx

zbxtable訪問地址:,默認(rèn)賬號密碼:admin/Zbxtable。


文章題目:go語言報(bào)表控件 golang 報(bào)表工具
分享鏈接:http://weahome.cn/article/dopjedd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部