本文目錄一覽:
- 1、基礎知識 – Golang 中的格式化輸入輸出
- 2、golang如何實現urldecode
- 3、求教golang中http發送post請求gb2312編碼的解決方案
- 4、golang 新人求助:%v %q\n和Println、printf
- 5、Golang 中更好的錯誤處理:理論和實踐技巧
- 6、go語言實現一個簡單的簡單網關
基礎知識 – Golang 中的格式化輸入輸出
【格式化輸出】
// 格式化輸出:將 arg 列表中的 arg 轉換為字符串輸出
// 使用動詞 v 格式化 arg 列表,非字符串元素之間添加空格
Print(arg列表)
// 使用動詞 v 格式化 arg 列表,所有元素之間添加空格,結尾添加換行符
Println(arg列表)
// 使用格式字符串格式化 arg 列表
Printf(格式字符串, arg列表)
// Print 類函數會返回已處理的 arg 數量和遇到的錯誤信息。
【格式字符串】
格式字符串由普通字符和佔位符組成,例如:
“abc%+ #8.3[3]vdef”
其中 abc 和 def 是普通字符,其它部分是佔位符,佔位符以 % 開頭(註:%% 將被轉義為一個普通的 % 符號,這個不算開頭),以動詞結尾,格式如下:
%[旗標][寬度][.精度][arg索引]動詞
方括號中的內容可以省略。
【旗標】
旗標有以下幾種:
空格:對於數值類型的正數,保留一個空白的符號位(其它用法在動詞部分說明)。
0 :用 0 進行寬度填充而不用空格,對於數值類型,符號將被移到所有 0 的前面。
其中 “0” 和 “-” 不能同時使用,優先使用 “-” 而忽略 “0”。
【寬度和精度】
“寬度”和“精度”都可以寫成以下三種形式:
數值 | * | arg索引*
其中“數值”表示使用指定的數值作為寬度值或精度值,“ ”表示使用當前正在處理的 arg 的值作為寬度值或精度值,如果這樣的話,要格式化的 arg 將自動跳轉到下一個。“arg索引 ”表示使用指定 arg 的值作為寬度值或精度值,如果這樣的話,要格式化的 arg 將自動跳轉到指定 arg 的下一個。
寬度值:用於設置最小寬度。
精度值:對於浮點型,用於控制小數位數,對於字符串或字節數組,用於控制字符數量(不是字節數量)。
對於浮點型而言,動詞 g/G 的精度值比較特殊,在適當的情況下,g/G 會設置總有效數字,而不是小數位數。
【arg 索引】
“arg索引”由中括號和 arg 序號組成(就像上面示例中的 [3]),用於指定當前要處理的 arg 的序號,序號從 1 開始:
‘[‘ + arg序號 + ‘]’
【動詞】
“動詞”不能省略,不同的數據類型支持的動詞不一樣。
[通用動詞]
v:默認格式,不同類型的默認格式如下:
布爾型:t
整 型:d
浮點型:g
複數型:g
字符串:s
通 道:p
指 針:p
無符號整型:x
T:輸出 arg 的類型而不是值(使用 Go 語法格式)。
[布爾型]
t:輸出 true 或 false 字符串。
[整型]
b/o/d:輸出 2/8/10 進制格式
x/X :輸出 16 進制格式(小寫/大寫)
c :輸出數值所表示的 Unicode 字符
q :輸出數值所表示的 Unicode 字符(帶單引號)。對於無法顯示的字符,將輸出其轉義字符。
U :輸出 Unicode 碼點(例如 U+1234,等同於字符串 “U+%04X” 的顯示結果)
對於 o/x/X:
如果使用 “#” 旗標,則會添加前導 0 或 0x。
對於 U:
如果使用 “#” 旗標,則會在 Unicode 碼點後面添加相應的 ‘字符’(前提是該字符必須可顯示)
[浮點型和複數型]
b :科學計數法(以 2 為底)
e/E:科學計數法(以 10 為底,小寫 e/大寫 E)
f/F:普通小數格式(兩者無區別)
g/G:大指數(指數 = 6)使用 %e/%E,其它情況使用 %f/%F
[字符串或字節切片]
s :普通字符串
q :雙引號引起來的 Go 語法字符串
x/X:十六進制編碼(小寫/大寫,以字節為元素進行編碼,而不是字符)
對於 q:
如果使用了 “+” 旗標,則將所有非 ASCII 字符都進行轉義處理。
如果使用了 “#” 旗標,則輸出反引號引起來的字符串(前提是
字符串中不包含任何製表符以外的控制字符,否則忽略 # 旗標)
對於 x/X:
如果使用了 ” ” 旗標,則在每個元素之間添加空格。
如果使用了 “#” 旗標,則在十六進制格式之前添加 0x 前綴。
[指針類型]
p :帶 0x 前綴的十六進制地址值。
[符合類型]
複合類型將使用不同的格式輸出,格式如下:
結 構 體:{字段1 字段2 …}
數組或切片:[元素0 元素1 …]
映 射:map[鍵1:值1 鍵2:值2 …]
指向符合元素的指針:{}, [], map[]
複合類型本身沒有動詞,動詞將應用到複合類型的元素上。
結構體可以使用 “+v” 同時輸出字段名。
【注意】
1、如果 arg 是一個反射值,則該 arg 將被它所持有的具體值所取代。
2、如果 arg 實現了 Formatter 接口,將調用它的 Format 方法完成格式化。
3、如果 v 動詞使用了 # 旗標(%#v),並且 arg 實現了 GoStringer 接口,將調用它的 GoString 方法完成格式化。
如果格式化操作指定了字符串相關的動詞(比如 %s、%q、%v、%x、%X),接下來的兩條規則將適用:
4。如果 arg 實現了 error 接口,將調用它的 Error 方法完成格式化。
5。如果 arg 實現了 string 接口,將調用它的 String 方法完成格式化。
在實現格式化相關接口的時候,要避免無限遞歸的情況,比如:
type X string
func (x X) String() string {
return Sprintf(“%s”, x)
}
在格式化之前,要先轉換數據類型,這樣就可以避免無限遞歸:
func (x X) String() string {
return Sprintf(“%s”, string(x))
}
無限遞歸也可能發生在自引用數據類型上面,比如一個切片的元素引用了切片自身。這種情況比較罕見,比如:
a := make([]interface{}, 1)
a[0] = a
fmt.Println(a)
【格式化輸入】
// 格式化輸入:從輸入端讀取字符串(以空白分隔的值的序列),
// 並解析為具體的值存入相應的 arg 中,arg 必須是變量地址。
// 字符串中的連續空白視為單個空白,換行符根據不同情況處理。
// \r\n 被當做 \n 處理。
// 以動詞 v 解析字符串,換行視為空白
Scan(arg列表)
// 以動詞 v 解析字符串,換行結束解析
Scanln(arg列表)
// 根據格式字符串中指定的格式解析字符串
// 格式字符串中的換行符必須和輸入端的換行符相匹配。
Scanf(格式字符串, arg列表)
// Scan 類函數會返回已處理的 arg 數量和遇到的錯誤信息。
【格式字符串】
格式字符串類似於 Printf 中的格式字符串,但下面的動詞和旗標例外:
p :無效
T :無效
e/E/f/F/g/G:功能相同,都是掃描浮點數或複數
s/v :對字符串而言,掃描一個被空白分隔的子串
對於整型 arg 而言,v 動詞可以掃描帶有前導 0 或 0x 的八進制或十六進制數值。
寬度被用來指定最大掃描寬度(不會跨越空格),精度不被支持。
如果 arg 實現了 Scanner 接口,將調用它的 Scan 方法掃描相應數據。只有基礎類型和實現了 Scanner 接口的類型可以使用 Scan 類方法進行掃描。
【注意】
連續調用 FScan 可能會丟失數據,因為 FScan 中使用了 UnreadRune 對讀取的數據進行撤銷,而參數 io.Reader 只有 Read 方法,不支持撤銷。比如:
golang如何實現urldecode
首先你的理解是錯的,不管用戶態的API(syscall)是否是同步還是異步,在kernel層面都是異步的。
其實實現原理很簡單,就是利用C(嵌入彙編)語言可以直接修改寄存器(setcontext/setjmp/longjmp均是類似原理,修改程序指針eip實現跳轉,棧指針實現上線文切換)來實現從func_a調進去,從func_b返回出來這種行為。對於golang來說,func_a/func_b屬於不同的goroutine,從而就實現了goroutine的調度切換。
另外對於所有可能阻塞的syscall,golang對其進行了封裝,底層實際是epoll方式做的,註冊回調後切換到另一個runnable的goroutine。
求教golang中http發送post請求gb2312編碼的解決方案
不要用PostForm,至於編碼可以用code.google.com/p/mahonia這個庫把utf8編碼攥成gb2312,附上代碼片段
resp, err := http.Post(reqUrl,
“application/x-www-form-urlencoded”,
strings.NewReader(fmt.Sprintf(
“__VIEWSTATE=%s__EVENTVALIDATION=%stxtUid=000000000txtPwd=xxxxxxxxselKind=1selKind=1btLogin=%B5%C7%C2%BD”,
url.QueryEscape(viewstate),
url.QueryEscape(eventvalidation),
)),
)
golang 新人求助:%v %q\n和Println、printf
fmt.Printf 會根據後面參數格式化前面的字符串 ,fmt.Println 不會。
Golang 中更好的錯誤處理:理論和實踐技巧
雲和安全管理服務專家新鈦雲服 張春翻譯
這種方法有幾個缺點。首先,它可以對程序員隱藏錯誤處理路徑,特別是在捕獲異常不是強制性的情況下,例如在 Python 中。即使在具有必須處理的 Java 風格的檢查異常的語言中,如果在與原始調用不同的級別上處理錯誤,也並不總是很明顯錯誤是從哪裡引發的。
我們都見過長長的代碼塊包裝在一個 try-catch 塊中。在這種情況下,catch 塊實際上充當 goto 語句,這通常被認為是有害的(奇怪的是,C 中的關鍵字被認為可以接受的少數用例之一是錯誤後清理,因為該語言沒有 Golang- 樣式延遲語句)。
如果你確實從源頭捕獲異常,你會得到一個不太優雅的 Go 錯誤模式版本。這可能會解決混淆代碼的問題,但會遇到另一個問題:性能。在諸如 Java 之類的語言中,拋出異常可能比函數的常規返回慢數百倍。
Java 中最大的性能成本是由打印異常的堆棧跟蹤造成的,這是昂貴的,因為運行的程序必須檢查編譯它的源代碼 。僅僅進入一個 try 塊也不是空閑的,因為需要保存 CPU 內存寄存器的先前狀態,因為它們可能需要在拋出異常的情況下恢復。
如果您將異常視為通常不會發生的異常情況,那麼異常的缺點並不重要。這可能是傳統的單體應用程序的情況,其中大部分代碼庫不必進行網絡調用——一個操作格式良好的數據的函數不太可能遇到錯誤(除了錯誤的情況)。一旦您在代碼中添加 I/O,無錯誤代碼的夢想就會破滅:您可以忽略錯誤,但不能假裝它們不存在!
try {
doSometing()
} catch (IOException e) {
// ignore it
}
與大多數其他編程語言不同,Golang 接受錯誤是不可避免的。 如果在單體架構時代還不是這樣,那麼在今天的模塊化後端服務中,服務通常和外部 API 調用、數據庫讀取和寫入以及與其他服務通信 。
以上所有方法都可能失敗,解析或驗證從它們接收到的數據(通常在無模式 JSON 中)也可能失敗。Golang 使可以從這些調用返回的錯誤顯式化,與普通返回值的等級相同。從函數調用返回多個值的能力支持這一點,這在大多數語言中通常是不可能的。Golang 的錯誤處理系統不僅僅是一種語言怪癖,它是一種將錯誤視為替代返回值的完全不同的方式!
重複 if err != nil
對 Go 錯誤處理的一個常見批評是被迫重複以下代碼塊:
res, err := doSomething()
if err != nil {
// Handle error
}
對於新用戶來說,這可能會覺得沒用而且浪費行數:在其他語言中需要 3 行的函數很可能會增長到 12 行 :
這麼多行代碼!這麼低效!如果您認為上述內容不優雅或浪費代碼,您可能忽略了我們檢查代碼中的錯誤的全部原因:我們需要能夠以不同的方式處理它們!對 API 或數據庫的調用可能會被重試。
有時事件的順序很重要:調用外部 API 之前發生的錯誤可能不是什麼大問題(因為數據從未通過發送),而 API 調用和寫入本地數據庫之間的錯誤可能需要立即注意,因為 這可能意味着系統最終處於不一致的狀態。即使我們只想將錯誤傳播給調用者,我們也可能希望用失敗的解釋來包裝它們,或者為每個錯誤返回一個自定義錯誤類型。
並非所有錯誤都是相同的,並且向調用者返回適當的錯誤是 API 設計的重要部分,無論是對於內部包還是 REST API 。
不必擔心在你的代碼中重複 if err != nil ——這就是 Go 中的代碼應該看起來的樣子。
自定義錯誤類型和錯誤包裝
從導出的方法返回錯誤時,請考慮指定自定義錯誤類型,而不是單獨使用錯誤字符串。字符串在意外代碼中是可以的,但在導出的函數中,它們成為函數公共 API 的一部分。更改錯誤字符串將是一項重大更改——如果沒有明確的錯誤類型,需要檢查返回錯誤類型的單元測試將不得不依賴原始字符串值!事實上,基於字符串的錯誤也使得在私有方法中測試不同的錯誤案例變得困難,因此您也應該考慮在包中使用它們。回到錯誤與異常的爭論,返回錯誤也使代碼比拋出異常更容易測試,因為錯誤只是要檢查的返回值。不需要測試框架或在測試中捕獲異常 。
可以在 database/sql 包中找到簡單自定義錯誤類型的一個很好的示例。它定義了一個導出常量列表,表示包可以返回的錯誤類型,最著名的是 sql.ErrNoRows。雖然從 API 設計的角度來看,這種特定的錯誤類型有點問題(您可能會爭辯說 API 應該返回一個空結構而不是錯誤),但任何需要檢查空行的應用程序都可以導入該常量並在代碼中使用它不必擔心錯誤消息本身會改變和破壞代碼。
對於更複雜的錯誤處理,您可以通過實現返回錯誤字符串的 Error() 方法來定義自定義錯誤類型。自定義錯誤可以包括元數據,例如錯誤代碼或原始請求參數。如果您想表示錯誤類別,它們很有用。DigitalOcean 的本教程展示了如何使用自定義錯誤類型來表示可以重試的一類臨時錯誤。
通常,錯誤會通過將低級錯誤與更高級別的解釋包裝起來,從而在程序的調用堆棧中傳播。例如,數據庫錯誤可能會以下列格式記錄在 API 調用處理程序中:調用 CreateUser 端點時出錯:查詢數據庫時出錯:pq:檢測到死鎖。這很有用,因為它可以幫助我們跟蹤錯誤在系統中傳播的過程,向我們展示根本原因(數據庫事務引擎中的死鎖)以及它對更廣泛系統的影響(調用者無法創建新用戶)。
自 Go 1.13 以來,此模式具有特殊的語言支持,並帶有錯誤包裝。通過在創建字符串錯誤時使用 %w 動詞,可以使用 Unwrap() 方法訪問底層錯誤。除了比較錯誤相等性的函數 errors.Is() 和 errors.As() 外,程序還可以獲取包裝錯誤的原始類型或標識。這在某些情況下可能很有用,儘管我認為在確定如何處理所述錯誤時最好使用頂級錯誤的類型。
Panics
不要 panic()!長時間運行的應用程序應該優雅地處理錯誤而不是panic。即使在無法恢復的情況下(例如在啟動時驗證配置),最好記錄一個錯誤並優雅地退出。panic比錯誤消息更難診斷,並且可能會跳過被推遲的重要關閉代碼。
Logging
我還想簡要介紹一下日誌記錄,因為它是處理錯誤的關鍵部分。通常你能做的最好的事情就是記錄收到的錯誤並繼續下一個請求。
除非您正在構建簡單的命令行工具或個人項目,否則您的應用程序應該使用結構化的日誌庫,該庫可以為日誌添加時間戳,並提供對日誌級別的控制。最後一部分特別重要,因為它將允許您突出顯示應用程序記錄的所有錯誤和警告。通過幫助將它們與信息級日誌分開,這將為您節省無數時間。
微服務架構還應該在日誌行中包含服務的名稱以及機器實例的名稱。默認情況下記錄這些時,程序代碼不必擔心包含它們。您也可以在日誌的結構化部分中記錄其他字段,例如收到的錯誤(如果您不想將其嵌入日誌消息本身)或有問題的請求或響應。只需確保您的日誌沒有泄露任何敏感數據,例如密碼、API 密鑰或用戶的個人數據!
對於日誌庫,我過去使用過 logrus 和 zerolog,但您也可以選擇其他結構化日誌庫。如果您想了解更多信息,互聯網上有許多關於如何使用這些的指南。如果您將應用程序部署到雲中,您可能需要日誌庫上的適配器來根據您的雲平台的日誌 API 格式化日誌 – 沒有它,雲平台可能無法檢測到日誌級別等某些功能。
如果您在應用程序中使用調試級別日誌(默認情況下通常不記錄),請確保您的應用程序可以輕鬆更改日誌級別,而無需更改代碼。更改日誌級別還可以暫時使信息級別甚至警告級別的日誌靜音,以防它們突然變得過於嘈雜並開始淹沒錯誤。您可以使用在啟動時檢查以設置日誌級別的環境變量來實現這一點。
原文:
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 計數;一種是鏈式調用。
具體代碼
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/197260.html