我的課題主要分為以下三章,鬥魚在go的應用場景,go在業務中如何優化,我們在go中踩過了哪些坑。
在鬥魚我們將go的應用場景分為以下三類,快取型別資料,實時型別資料,cpu密集型任務。這三類應用場景都有著各自的特點。
針對這三種業務場景如何做優化,我們也是走了不少彎路。而且跟一些程式設計師一樣,容易陷入到特定的技術和思維當中去。舉個簡單的例子。早期我們在優化go的排序引擎的時候,上來就想著各種演算法優化,引入了跳躍表,歸併排序,看似優化了不少效能,benchmark資料也比較好看。但實際上排序的演算法時間和排序資料來源獲取的時間數量級差別很大。優化如果找不對方向,業務中的優化只能是事倍功半。所以在往後的工作中,我們基本上是按照如下圖所示的時間區域,找到業務優化的主要耗時區域。
從圖中,我們主要列舉了幾個時間分布,讓大家對這幾個數值有所了解。從客戶端到cdn回源到機房的時間大概是50ms到300ms。機房內部服務端之間通訊大概是5ms到50ms。我們訪問的記憶體資料庫redis返回資料大概是500us到1ms。go內部獲取記憶體資料耗時ns級別。了解業務的主要耗時區域,我們就可以知道應該著重優化哪個部分。
對於使用者訪問乙個url,我們假定這個url為/hello。這個url每個使用者返回的資料結構都是一樣的。我們通常有可能會向下面示例這樣做。對於開發而言,**是最直觀最可控的。但這種方式通常只是實現功能,但並不能夠提公升使用者體驗。因為對於快取資料我們沒有必要每次讓cdn回源到源站機房,增加使用者訪問的鏈路時間。
// echo instance e := echo.new() e.use(mw.cache) // routers e.get("/hello", handler(homehandler))
所以接下來,對於快取資料,我們不會用go進行快取,而是在前端cdn進行快取優化。cdn鏈路如下所示
為了讓大家更好的了解cdn,我先問大家乙個問題。從北京到深圳用光速行駛,大概要多久(7ms)。所以如圖所示,當乙個使用者訪問乙個快取資料,我們要盡量的讓資料快取在離使用者近的cdn節點,這種優化方式稱為cdn快取優化。通過該技術,cdn節點會把附件使用者的請求,聚合到一起統一回源到源站機房。這樣可以不僅節省機房流量頻寬,並且從物理層面上減少了一次鏈路。使得使用者可以更快的獲取到快取資料。
為了更好的模擬cdn的快取,我們拿nginx+go來描述這個流程。nginx就相當於圖中的基站,go服務就相當於北京的源站機房。
nginx 配置如下所示:
server }
go **如下所示
package mainimport ( "fmt" "io" "net/http")func main() ) err := http.listenandserve(":9090", nil) if err != nil }type servemux struct {}func (p *servemux) servehttp(w http.responsewriter, r *http.request)
啟動**後,我們可以發現。
正常使用者在訪問hellourl的時候,是通過介面引導,然後獲取hello資料。但是對於爬蟲使用者而言,他們為了獲取更加及時的爬蟲資料,會在url後面加各種隨機數hello?123456,這種行為會導致cdn快取失效,讓很多請求回源到源站機房。造成更大的壓力。所以一般這種情況下,我們可以在cdn做去問號快取。通過nginx可以模擬這種行為。nginx配置如下:
server location ~ /hello }
之前我們有講過如果突然之間有大型賽事開播,會出現大量使用者來訪問。這個時候可能會出現乙個場景,快取資料還沒有建立,大量使用者請求仍然可能回源到源站機房。導致服務負載過高。這個時候我們可以加入proxy_cache_lock和proxy_cache_lock_timeout引數
server location ~ /hello }
在上面我們還提到鬥魚快取型別的首頁,列表頁。這些頁面介面資料通常會返回大量資料。在這裡我們拿go模擬了一次請求中獲取120個資料的情況。將slice分為三種情況,未預設slice的長度,預設了slice長度,預設了slice長度並且使用了sync.map。**如下所示。這裡面每個goroutine相當於一次http請求。我們拿benchmark跑一次資料
得到以下結果。可以從最慢的12us降低到1us。
我們對於量大的實時資料,做了三層快取。第一層是白名單,這類資料主要是通過人工干預,預設一些記憶體資料。第二層是通過演算法,將我們的一些比較重要的房間資訊放入到服務記憶體裡,第三層是通過請求量動態調整。通過這三層快取設計。像大型賽事,大主播開播的時候,我們的請求是不會穿透到資料來源,直接伺服器的記憶體裡已經將資料返回。這樣的好處不僅減少了io操作,而且還對流量起到了鎮流的作用,使流量平穩的到達資料來源。
其他量級小的非實時資料,我們都是通過etcd進行推送
要充分理解redis的引數。只有這樣我們才能根據業務合理調整redis的引數。達到最佳效能。maxidle設定高點,可以保證突發流量情況下,能夠有足夠的連線去獲取redis,不用在高流量情況下建立連線。maxactive,readtimeout,writetimeout的設定,對redis是一種保護,相當於go服務對redis這塊做的一種簡單限流,降頻操作。
redigo 引數調優maxidle = 30maxactive = 500dialtimeout = "1s"readtimeout = "500ms"writetimeout = "500ms"idletimeout = "60s"
因為redis是記憶體資料庫,響應速度比較塊。服務裡可能會大量使用redis,很多時候我們服務的壓測,瓶頸不在**編寫上,而是在redis的吞吐效能上。因為redis是單執行緒模型,所以為了提高速度,我們通常做的方式是採用pipeline指令,增加redis從庫,這樣go就可以根據redis數量,併發拉取資料,達到效能最佳。以下我們模擬了這種場景。
從圖中,我們可以看出採用併發拉去加pipeline方式,效能可以提高5倍。
redis的優化方式還有很多。例如
1.增加redis從庫2.對批量資料,根據redis從庫數量,併發goroutine拉取資料3.對批量資料大量使用pipeline指令4.精簡key欄位5.redis的value解碼改為msgpack
坑踩得多,說明書看的少。
以上問題都可以在相關文獻中找到原因,具體原因請閱讀文件。
go語言開源專案
谷歌官方維護了乙個基於go語言的開源專案列表 其中有非常多的優秀專案值得學習,有幾百行 適合新手閱讀的專案,也有大型如nsq docker等的專案。1 cache2go 比較簡單的乙個快取庫,量很少,適合新手學習,可以學習到鎖 goroutines等。2 groupcache 與memcached同...
go語言基礎 快取通道 channel
非快取的通道,傳送和接收都是阻塞 帶快取的通道,有一塊快取區可以繼續傳送和接收資料。make chan t,size 快取區中已經滿了,才會阻塞程式 ch1 make chan int,5 ch1 100 不阻塞,因為有快取 ch1 200 ch1 300 ch1 400 ch1 500 快取區已滿...
go 專案 cmd目錄 go語言 安裝
go語言,是開源的程式設計軟體。可以在windows,linux,mac平台安裝。在windows安裝 在windows安裝了go語言之後,會看到如下的目錄 驗證安裝 開啟cmd,進入go語言的bin目錄,執行go go是乙個管理go源 的工具 go 命令 引數 go version 檢視go語言版...