本文目錄一覽:
golang是什麼牌子的單車
永久牌單車。
永久單車的歷史可追溯到1940年,它是中國Z早的單車整車製造廠家之一,至今已有近80年的歷史。作為Z大的國有單車廠為中國單車行業的發展作出了不可磨滅的貢獻,永久研製了統一全國單車標準規格的標定車。
go語言實現一個簡單的簡單網關
網關=反向代理+負載均衡+各種策略,技術實現也有多種多樣,有基於 nginx 使用 lua 的實現,比如 openresty、kong;也有基於 zuul 的通用網關;還有就是 golang 的網關,比如 tyk。
這篇文章主要是講如何基於 golang 實現一個簡單的網關。
轉自: troy.wang/docs/golang/posts/golang-gateway/
整理:go語言鍾文文檔:
啟動兩個後端 web 服務(代碼)
這裡使用命令行工具進行測試
具體代碼
直接使用基礎庫 httputil 提供的NewSingleHostReverseProxy即可,返回的reverseProxy對象實現了serveHttp方法,因此可以直接作為 handler。
具體代碼
director中定義回調函數,入參為*http.Request,決定如何構造向後端的請求,比如 host 是否向後傳遞,是否進行 url 重寫,對於 header 的處理,後端 target 的選擇等,都可以在這裡完成。
director在這裡具體做了:
modifyResponse中定義回調函數,入參為*http.Response,用於修改響應的信息,比如響應的 Body,響應的 Header 等信息。
最終依舊是返回一個ReverseProxy,然後將這個對象作為 handler 傳入即可。
參考 2.2 中的NewSingleHostReverseProxy,只需要實現一個類似的、支持多 targets 的方法即可,具體實現見後面。
作為一個網關服務,在上面 2.3 的基礎上,需要支持必要的負載均衡策略,比如:
隨便 random 一個整數作為索引,然後取對應的地址即可,實現比較簡單。
具體代碼
使用curIndex進行累加計數,一旦超過 rss 數組的長度,則重置。
具體代碼
輪詢帶權重,如果使用計數遞減的方式,如果權重是5,1,1那麼後端 rs 依次為a,a,a,a,a,b,c,a,a,a,a…,其中 a 後端會瞬間壓力過大;參考 nginx 內部的加權輪詢,或者應該稱之為平滑加權輪詢,思路是:
後端真實節點包含三個權重:
操作步驟:
具體代碼
一致性 hash 算法,主要是用於分佈式 cache 熱點/命中問題;這裡用於基於某 key 的 hash 值,路由到固定後端,但是只能是基本滿足流量綁定,一旦後端目標節點故障,會自動平移到環上最近的那麼個節點。
實現:
具體代碼
每一種不同的負載均衡算法,只需要實現添加以及獲取的接口即可。
然後使用工廠方法,根據傳入的參數,決定使用哪種負載均衡策略。
具體代碼
作為網關,中間件必不可少,這類包括請求響應的模式,一般稱作洋蔥模式,每一層都是中間件,一層層進去,然後一層層出來。
中間件的實現一般有兩種,一種是使用數組,然後配合 index 計數;一種是鏈式調用。
具體代碼
(十一)golang 內存分析
編寫過C語言程序的肯定知道通過malloc()方法動態申請內存,其中內存分配器使用的是glibc提供的ptmalloc2。 除了glibc,業界比較出名的內存分配器有Google的tcmalloc和Facebook的jemalloc。二者在避免內存碎片和性能上均比glic有比較大的優勢,在多線程環境中效果更明顯。
Golang中也實現了內存分配器,原理與tcmalloc類似,簡單的說就是維護一塊大的全局內存,每個線程(Golang中為P)維護一塊小的私有內存,私有內存不足再從全局申請。另外,內存分配與GC(垃圾回收)關係密切,所以了解GC前有必要了解內存分配的原理。
為了方便自主管理內存,做法便是先向系統申請一塊內存,然後將內存切割成小塊,通過一定的內存分配算法管理內存。 以64位系統為例,Golang程序啟動時會向系統申請的內存如下圖所示:
預申請的內存劃分為spans、bitmap、arena三部分。其中arena即為所謂的堆區,應用中需要的內存從這裡分配。其中spans和bitmap是為了管理arena區而存在的。
arena的大小為512G,為了方便管理把arena區域劃分成一個個的page,每個page為8KB,一共有512GB/8KB個頁;
spans區域存放span的指針,每個指針對應一個page,所以span區域的大小為(512GB/8KB)乘以指針大小8byte = 512M
bitmap區域大小也是通過arena計算出來,不過主要用於GC。
span是用於管理arena頁的關鍵數據結構,每個span中包含1個或多個連續頁,為了滿足小對象分配,span中的一頁會劃分更小的粒度,而對於大對象比如超過頁大小,則通過多頁實現。
根據對象大小,劃分了一系列class,每個class都代表一個固定大小的對象,以及每個span的大小。如下表所示:
上表中每列含義如下:
class: class ID,每個span結構中都有一個class ID, 表示該span可處理的對象類型
bytes/obj:該class代表對象的位元組數
bytes/span:每個span佔用堆的位元組數,也即頁數乘以頁大小
objects: 每個span可分配的對象個數,也即(bytes/spans)/(bytes/obj)waste
bytes: 每個span產生的內存碎片,也即(bytes/spans)%(bytes/obj)上表可見最大的對象是32K大小,超過32K大小的由特殊的class表示,該class ID為0,每個class只包含一個對象。
span是內存管理的基本單位,每個span用於管理特定的class對象, 跟據對象大小,span將一個或多個頁拆分成多個塊進行管理。src/runtime/mheap.go:mspan定義了其數據結構:
以class 10為例,span和管理的內存如下圖所示:
spanclass為10,參照class表可得出npages=1,nelems=56,elemsize為144。其中startAddr是在span初始化時就指定了某個頁的地址。allocBits指向一個位圖,每位代表一個塊是否被分配,本例中有兩個塊已經被分配,其allocCount也為2。next和prev用於將多個span鏈接起來,這有利於管理多個span,接下來會進行說明。
有了管理內存的基本單位span,還要有個數據結構來管理span,這個數據結構叫mcentral,各線程需要內存時從mcentral管理的span中申請內存,為了避免多線程申請內存時不斷的加鎖,Golang為每個線程分配了span的緩存,這個緩存即是cache。src/runtime/mcache.go:mcache定義了cache的數據結構
alloc為mspan的指針數組,數組大小為class總數的2倍。數組中每個元素代表了一種class類型的span列表,每種class類型都有兩組span列表,第一組列表中所表示的對象中包含了指針,第二組列表中所表示的對象不含有指針,這麼做是為了提高GC掃描性能,對於不包含指針的span列表,沒必要去掃描。根據對象是否包含指針,將對象分為noscan和scan兩類,其中noscan代表沒有指針,而scan則代表有指針,需要GC進行掃描。mcache和span的對應關係如下圖所示:
mchache在初始化時是沒有任何span的,在使用過程中會動態的從central中獲取並緩存下來,跟據使用情況,每種class的span個數也不相同。上圖所示,class 0的span數比class1的要多,說明本線程中分配的小對象要多一些。
cache作為線程的私有資源為單個線程服務,而central則是全局資源,為多個線程服務,當某個線程內存不足時會向central申請,當某個線程釋放內存時又會回收進central。src/runtime/mcentral.go:mcentral定義了central數據結構:
lock: 線程間互斥鎖,防止多線程讀寫衝突
spanclass : 每個mcentral管理着一組有相同class的span列表
nonempty: 指還有內存可用的span列表
empty: 指沒有內存可用的span列表
nmalloc: 指累計分配的對象個數線程從central獲取span步驟如下:
將span歸還步驟如下:
從mcentral數據結構可見,每個mcentral對象只管理特定的class規格的span。事實上每種class都會對應一個mcentral,這個mcentral的集合存放於mheap數據結構中。src/runtime/mheap.go:mheap定義了heap的數據結構:
lock: 互斥鎖
spans: 指向spans區域,用於映射span和page的關係
bitmap:bitmap的起始地址
arena_start: arena區域首地址
arena_used: 當前arena已使用區域的最大地址
central: 每種class對應的兩個mcentral
從數據結構可見,mheap管理着全部的內存,事實上Golang就是通過一個mheap類型的全局變量進行內存管理的。mheap內存管理示意圖如下:
系統預分配的內存分為spans、bitmap、arean三個區域,通過mheap管理起來。接下來看內存分配過程。
針對待分配對象的大小不同有不同的分配邏輯:
(0, 16B) 且不包含指針的對象: Tiny分配
(0, 16B) 包含指針的對象:正常分配
[16B, 32KB] : 正常分配
(32KB, -) : 大對象分配其中Tiny分配和大對象分配都屬於內存管理的優化範疇,這裡暫時僅關注一般的分配方法。
以申請size為n的內存為例,分配步驟如下:
Golang內存分配是個相當複雜的過程,其中還摻雜了GC的處理,這裡僅僅對其關鍵數據結構進行了說明,了解其原理而又不至於深陷實現細節。1、Golang程序啟動時申請一大塊內存並劃分成spans、bitmap、arena區域
2、arena區域按頁劃分成一個個小塊。
3、span管理一個或多個頁。
4、mcentral管理多個span供線程申請使用
5、mcache作為線程私有資源,資源來源於mcentral。
Net Core已經開源好幾年了, 為什麼不像JVM那樣很多人研究和調優其GC算法?
我們已經上線了好幾個.net core的項目,基本上都是docker+.net core 2/3。說實話,
.net core的GC非常的優秀,基本上不需要像做Java時候,還要做很多的優化。因此沒有多少人研究很正常。換句話,如果一個GC還要做很多優化,這肯定不是好的一個GC。當然平時編程的時候,常用的非託管的對象處理等等還是要必須掌握的。
這和國內的開發環境有很大關係。
一方面,這裡有個路徑依賴的問題,這個問題在國內尤為突出。這幾年,國內其他語言的開發者多一些,生態好一些,轉換則意味着成本。
另一方面,浮躁之風過盛,拿來主義盛行。這裡舉兩個例子來說明。一個是國產操作系統的內核問題。為什麼要使用linux內核而不是重新寫一個呢?給出的理由無非是linux生態好,穩定,沒有必要進行重複製作。真的沒必要嗎?那國外為什麼流行要用rust重新寫幾個,而且開源呢?「沒必要」是假,「不想」才是真,畢竟基礎建設周期長,成本高,沒有拿來主義好呀。另一個例子是最近matlab在國內停止授權的事情。在這件事情上,很多人都覺得問題不大,問題不大的原因在於還有一個開源的scilab可以拿來用。
舉這兩個例子,也許不太妥切,但是,管中窺豹,略見一斑,也足以說明時下的浮躁氛圍了。
既然這裡說到net core底層問題,今年新出的《.NET Core底層入門》,也許值得一讀。這是國內的研究者寫的,從中可以看出國內在這方面的進展,也說不定。總而言之,雖然net core已經開源了幾年,但是在國內,開發者的成長和生態的建設,還需要更長的時間。
因為不需要,java就像半成品,無論是語法 編譯器 等等,要是沒有spring系列,估計那些996早就變成了007了。
微軟的產品化能力是有目共睹的,.net比起JAVA體系,更加完善,包括產品本身和後期的維護都比JAVA好,所以商業化項目最好還是用. net平台。
這似乎挺正常的,如同它購買了GitHub後,眾開源項目就紛紛遷移GH。開源社區普遍不信任微軟,其意定非在開源本身。.Net 開源估計也是市場佔有率在降,沒人真心愿意用它。
.net core,哪裡還需要什麼GC優化?那是jvm天生缺陷導致的問題。.net 5再性能上更進一步,只要你的程序不是寫得稀爛,根本不用操心底層運行時的性能會出問題。
不能用jvm的眼光看.net,java界已經進入固步自封的狀態,版本更新那麼快,實質性的東西並沒有什麼突破。而很多公司堅守在java1.6上不放手,實在頑固。
優化肯定是需要的,再好的程序都是有優化空間的。只是現在dotnet平台上目前缺少大型的應用。正常的業務場景下,難以達到框架性能的瓶頸。
dotnet 雖然開源了,但是開源太晚。要是早幾年,在Android興起之前,在大數據興起之前,現在還會是這般場景嗎。眼看着國內的大企業一波波地轉向了Java和其他語言,作為一名dotnet程序員心裏是大大的不甘心。
dotnet 在語言層面相比 Java 有太多優勢,Java 新版新增的一些語言特性也都是照抄的 dotnet。但即便是這樣,依然是叫好不叫座。
開源太晚,錯過了幾波行業發展紅利。以至於現在,大數據領域缺 Hadoop,搜索領域缺 Elasticsearch ,移動端雖有xamarin,但依然是雞肋般的存在。要是有這些殺手級應用在,dotnet 生態肯定會繁榮起來,向著更強的方向優化。
還能說什麼呢,只能期望即將到來的dotnet 5 能一統現在混亂的局面,發揮好自己的特長,繁榮dotnet的生態環境。
首先.net的原裝GC一直都不錯。流暢到可以支持3D 遊戲 開發。所以不怎麼需要調優。要知道文章多不用不一定是好事,95%的技術文章其實只不過是要解決一個BUG而已。其次C#的語法和運行時設計也好,對GC的壓力小很多。比如范性支持基本類型,這樣List 之類的結構,是整體分配和釋放的。而某蛙就需要每個元素拆箱裝箱。慢死,對GC來說也要算更多的引用鏈。此外C#還支持matrx4x4之類的SIMD數據類型。也是提高運行速度和減少GC的好東西
這些東西已經優化的非常好了,程序員不需要關注這些,讓程序員更多的精力放在業務實現上。
相對java來說.netcore的語言更加優雅,高級和完善,所以,不需要關注相對比較底層的東西。
很簡單:沒有關鍵業務跑在 net core 上。
曾經,jd 業務用 .net,但幾年後就全換成 Java 了。這足以說明問題了。
像 zhihu 這種小廠,開始用 python,業務量大了之後就轉 golang ,而不是轉 net core 。足以說明問題了。
有人說,netcore 如何如何好,根本不用自己優化……。實際上根本沒達到極限,不到優化的時候。
原創文章,作者:LNIG,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/134872.html