標籤:select drive customer 尋找 now() next 2.3 最大串連數 star
一、關於基礎的程式的實現
剛開始的時候程式是這樣實現的:
// Hellopackage mainimport ( "database/sql" "fmt" "io/ioutil" "log" "net/http" "strings" "time" _ "github.com/Go-SQL-Driver/MySQL")func main() { fmt.Println(time.Now().Format("2006-01-02 15:04:05")) openHttpListen() //saveToDb()}func openHttpListen() { http.HandleFunc("/monkeytest", receiveClientRequest) fmt.Println("go server start running...") err := http.ListenAndServe("1.2.3.4:5555", nil) //這裡監聽的地址要換成你自己的IP和連接埠;比如說你通過ifconfig查看自己的IP是15.34.67.23,則這裡就要替換成這個IP,不能是其他的IP,要不然會報錯 if err != nil { log.Fatal("ListenAndServe: ", err) }}func receiveClientRequest(w http.ResponseWriter, r *http.Request) { r.ParseForm() fmt.Println("收到用戶端請求: ", r.Form) fmt.Println("method:", r.Method) fmt.Println("path:", r.URL.Path) fmt.Println("scheme:", r.URL.Scheme) fmt.Println("url", r.URL) for k, v := range r.Form { fmt.Printf("----------\n") fmt.Println("key:", k) fmt.Println("value:", strings.Join(v, ", ")) } var className string var pkgName string var pkgVer string var leakRoot string var leakDetail string var pkgbuildtime string if len(r.Form["className"]) > 0 { className = r.Form["className"][0] } if len(r.Form["pkgName"]) > 0 { pkgName = r.Form["pkgName"][0] } if len(r.Form["pkgVer"]) > 0 { pkgVer = r.Form["pkgVer"][0] } if len(r.Form["leakRoot"]) > 0 { leakRoot = r.Form["leakRoot"][0] } if len(r.Form["leakDetail"]) > 0 { leakDetail = r.Form["leakDetail"][0] } if len(r.Form["buildtime"]) > 0 { pkgbuildtime = r.Form["buildtime"][0] } body, _ := ioutil.ReadAll(r.Body) //r.Body.Close() body_str := string(body) fmt.Println("body_str:", body_str) fmt.Println("header", r.Header) //fmt.Println("Customerid", r.Header.Get("Customerid")) w.Header().Set("Access-Control-Allow-Origin", "origin") var result string if len(leakDetail) != 0 { result = saveToDb(className, pkgName, pkgVer, leakRoot, leakDetail, pkgbuildtime) } else { result = "error" } fmt.Fprintf(w, result)}func saveToDb(className string, pkgName string, pkgVer string, leakRoot string, leakDetail string, pkgbuildtime string) string { db, err := sql.Open("mysql", "username:[email protected](2.3.4.5:3306)/myku?charset=utf8") //這裡的資料庫要換成自己的使用者名稱:密碼@資料庫地址:連接埠/資料庫名 checkErr(err) if err != nil { return "error" } //插入資料 stmt, err := db.Prepare("insert mytable SET className=?,pkgName=?,pkgVer=?,leakRoot=?,leakDetail=?,leakDate=?,pkgbuildtime=?") //這裡的mytable換成自己的table表名 //stmt, err := db.Prepare("insert into mytable(className,pkgName,pkgVer,leakRoot,leakDetail,leakDate,pkg) values (?,?,?,?,?,?)") //rows, err := db.Query("select * from mytable") checkErr(err) if err != nil { return "error" } /*fmt.Println("res.", rows) for rows.Next() { var className string rows.Columns() err = rows.Scan(&className) checkErr(err) fmt.Println(className) }*/ //checkErr(err) res, err := stmt.Exec(className, pkgName, pkgVer, leakRoot, leakDetail, time.Now().Format("2006-01-02 15:04:05"), pkgbuildtime) fmt.Println("res.", res) if err != nil { return "error" } else { return "success" }}func checkErr(err error) { if err != nil { fmt.Println("error.") //panic(err) }}
後來因為提示存在too many open files的問題,就做了一版修改,改成了:
// Hellopackage mainimport ( "database/sql" "fmt" "io/ioutil" "log" "net/http" "strings" "time" _ "github.com/Go-SQL-Driver/MySQL")func main() { fmt.Println(time.Now().Format("2006-01-02 15:04:05")) openHttpListen() //saveToDb()}func openHttpListen() { srv := &http.Server{ Addr: "1.2.3.4:5555", ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, } http.HandleFunc("/monkeytest", receiveClientRequest) fmt.Println("go server start running...") err := srv.ListenAndServe() if err != nil { log.Fatal("ListenAndServe: ", err) }}func receiveClientRequest(w http.ResponseWriter, r *http.Request) { r.ParseForm() fmt.Println("收到用戶端請求: ", r.Form) fmt.Println("method:", r.Method) fmt.Println("path:", r.URL.Path) fmt.Println("scheme:", r.URL.Scheme) fmt.Println("url", r.URL) for k, v := range r.Form { fmt.Printf("----------\n") fmt.Println("key:", k) fmt.Println("value:", strings.Join(v, ", ")) } var className string var pkgName string var pkgVer string var leakRoot string var leakDetail string var pkgbuildtime string if len(r.Form["className"]) > 0 { className = r.Form["className"][0] } if len(r.Form["pkgName"]) > 0 { pkgName = r.Form["pkgName"][0] } if len(r.Form["pkgVer"]) > 0 { pkgVer = r.Form["pkgVer"][0] } if len(r.Form["leakRoot"]) > 0 { leakRoot = r.Form["leakRoot"][0] } if len(r.Form["leakDetail"]) > 0 { leakDetail = r.Form["leakDetail"][0] } if len(r.Form["buildtime"]) > 0 { pkgbuildtime = r.Form["buildtime"][0] } defer r.Body.Close() body, _ := ioutil.ReadAll(r.Body) //r.Body.Close() body_str := string(body) fmt.Println("body_str:", body_str) fmt.Println("header", r.Header) //fmt.Println("Customerid", r.Header.Get("Customerid")) w.Header().Set("Access-Control-Allow-Origin", "origin") var result string if len(leakDetail) != 0 { result = saveToDb(className, pkgName, pkgVer, leakRoot, leakDetail, pkgbuildtime) } else { result = "error" } fmt.Fprintf(w, result)}func saveToDb(className string, pkgName string, pkgVer string, leakRoot string, leakDetail string, pkgbuildtime string) string { db, err := sql.Open("mysql", "username:[email protected](2.3.4.5:3306)/myku?charset=utf8") defer db.Close() checkErr(err) if err != nil { return "error" } //插入資料 stmt, err := db.Prepare("insert mytable SET className=?,pkgName=?,pkgVer=?,leakRoot=?,leakDetail=?,leakDate=?,pkgbuildtime=?") //stmt, err := db.Prepare("insert into mytable (className,pkgName,pkgVer,leakRoot,leakDetail,leakDate,pkg) values (?,?,?,?,?,?)") //rows, err := db.Query("select * from mytable") checkErr(err) if err != nil { return "error" } /*fmt.Println("res.", rows) for rows.Next() { var className string rows.Columns() err = rows.Scan(&className) checkErr(err) fmt.Println(className) }*/ //checkErr(err) res, err := stmt.Exec(className, pkgName, pkgVer, leakRoot, leakDetail, time.Now().Format("2006-01-02 15:04:05"), pkgbuildtime) fmt.Println("res.", res) if err != nil { return "error" } else { return "success" }}func checkErr(err error) { if err != nil { fmt.Println("error.") //panic(err) }}
然後在linux下通過nohup go run Hello.go &之後,程式正式跑起來,(注意:伺服器的IP一定是本地的IP地址才可以)就可以在瀏覽器裡面輸入:
http://1.2.3.4:5555/monkeytest,然後就能將請求提交到go的伺服器端
二、關於go中出現的問題的解決(包括髮現問題、解決問題的過程)
將一中的程式,在linux下運行之後,通過nohup go run Hello.go &運行之後,會將即時的資訊全部列印到nohup.out中,查看這個檔案,會出現這個提示:
2017/07/28 01:51:35 http: Accept error: accept tcp 10.252.149.243:9090: accept4: too many open files; retrying in 5ms
2017/07/28 01:51:35 http: Accept error: accept tcp 10.252.149.243:9090: accept4: too many open files; retrying in 10ms
2017/07/28 01:51:35 http: Accept error: accept tcp 10.252.149.243:9090: accept4: too many open files; retrying in 20ms
2017/07/28 01:51:35 http: Accept error: accept tcp 10.252.149.243:9090: accept4: too many open files; retrying in 40ms
2017/07/28 01:51:35 http: Accept error: accept tcp 10.252.149.243:9090: accept4: too many open files; retrying in 80ms
2017/07/28 01:51:35 http: Accept error: accept tcp 10.252.149.243:9090: accept4: too many open files; retrying in 160ms
2017/07/28 01:51:35 http: Accept error: accept tcp 10.252.149.243:9090: accept4: too many open files; retrying in 320ms
2017/07/28 01:51:35 http: Accept error: accept tcp 10.252.149.243:9090: accept4: too many open files; retrying in 640ms
2017/07/28 01:51:36 http: Accept error: accept tcp 10.252.149.243:9090: accept4: too many open files; retrying in 1s
2017/07/28 01:51:37 http: Accept error: accept tcp 10.252.149.243:9090: accept4: too many open files; retrying in 1s
這種情況下就會導致該寫入到資料庫中的內容無法寫入。
因為:在linux下一切都是檔案,所有不管是nohup.out還是socket串連都是檔案,所以這裡就可以進行尋找在當前pid下的檔案數有幾個了,可以通過下面2的(2)中的方式查看某個pid下的檔案數及詳情
經過尋找之後,發現這種情況可以通過以下的方式先緩解,首先通過:
1、ulimit -n查看最大串連數,如果是1024的話,可以嘗試將其修改為4096
2、這樣無法根本上解決問題,繼續查:
(1)開啟檔案太多,是否說明檔案控制代碼出現了泄漏,或者是:db操作出現了泄漏,那麼是否程式中沒有關閉呢?
查看之後,確實沒有關閉,因此增加:defer db.close()和defer f.close()的處理
這裡defer的含義是:代表在return之前執行關閉,但是有弊端,尤其是跟帶命名的返回參數一起使用時,具體是:https://tiancaiamao.gitbooks.io/go-internals/content/zh/03.4.html 文章中描述的這樣:
函數返回的過程是這樣的:先給傳回值賦值,然後調用defer運算式,最後才是返回到調用函數中。
其實原因就是:return xxx並不是一條原子指令
但是因為close的操作中沒有增加這個返回參數,所以影響不大可以這樣用
(2)然後修改之後,重新啟動程式,不斷創造串連資料庫及開啟檔案的處理操作:
通過下面的命令:
首先擷取go的pid值:ps aux | grep go,例如得到的結果是:29927
然後再執行:ls -l /proc/29927/fd/ | wc -l ,得到的結果就是:6,說明socket的數目沒有增長
然後再執行:ls -l /proc/29927/fd/ ,得到的結果就是:當前進程開啟的串連數的資訊詳情
備忘:在(1)中未增加defer close()操作之前, socket的數目會隨著http的請求和資料庫的串連增多
後面會繼續關注這裡,查看問題是否完全解決了。
【go語言實現伺服器接收http請求以及出現泄漏時的解決方案】