go http编程
- Go原生支持http,import(“net/http”)
- Go的http服务性能和nginx比较接近
- 几行代码就可以实现一个web服务
1、http server
package main import ( "fmt" "net/http" ) func Hello(w http.ResponseWriter, r *http.Request) { fmt.Println("handle hello") fmt.Fprintf(w, "hello ") } func login(w http.ResponseWriter, r *http.Request) { fmt.Println("handle login") fmt.Fprintf(w, "login ") } func history(w http.ResponseWriter, r *http.Request) { fmt.Println("handle history") fmt.Fprintf(w, "history ") } func main() { http.HandleFunc("/", Hello) http.HandleFunc("/user/login", login) http.HandleFunc("/user/history", history) err := http.ListenAndServe("0.0.0.0:8880", nil) if err != nil { fmt.Println("http listen failed") } }
2、http client
package main import ( "fmt" "io/ioutil" "net/http" ) func main() { res, err := http.Get("https://www.baidu.com/") if err != nil { fmt.Println("get err:", err) return } data, err := ioutil.ReadAll(res.Body) if err != nil { fmt.Println("get data err:", err) return } fmt.Println(string(data)) }
3、http常见请求方法
- Get请求
- Post请求
- Put请求
- Delete请求
- Head请求
4、http head
package main import ( "fmt" "net/http" ) var url = []string{ "http://www.baidu.com", "http://google.com", "http://taobao.com", } func main() { for _, v := range url { resp, err := http.Head(v) if err != nil { fmt.Printf("head %s failed, err:%v ", v, err) continue } fmt.Printf("head succ, status:%v ", resp.Status) } }
- 超时控制
package main import ( "fmt" "net/http" "net" "time" ) var url = []string{ "http://www.baidu.com", "http://google.com", "http://taobao.com", } func main() { for _, v := range url { c := http.Client{ Transport: &http.Transport { Dial:func(network, addr string) (net.Conn, error){ timeout := time.Second*2 return net.DialTimeout(network, addr, timeout) }, }, } resp, err := c.Head(v) if err != nil { fmt.Printf("head %s failed, err:%v ", v, err) continue } fmt.Printf("head succ, status:%v ", resp.Status) } }
5、http 表单处理
package main import ( "io" "net/http" ) const form = `<html><body><form action="#" method="post" name="bar"> <input type="text" name="in"/> <input type="text" name="in"/> <input type="submit" value="Submit"/> </form></html></body>` func SimpleServer(w http.ResponseWriter, request *http.Request) { io.WriteString(w, "<h1>hello, world</h1>") } func FormServer(w http.ResponseWriter, request *http.Request) { w.Header().Set("Content-Type", "text/html") switch request.Method { case "GET": io.WriteString(w, form) case "POST": request.ParseForm() io.WriteString(w, request.Form["in"][0]) io.WriteString(w, " ") io.WriteString(w, request.FormValue("in")) } } func main() { http.HandleFunc("/test1", SimpleServer) http.HandleFunc("/test2", FormServer) if err := http.ListenAndServe(":8088", nil); err != nil { } }
6、panic处理
package main import ( "io" "log" "net/http" ) const form = `<html><body><form action="#" method="post" name="bar"> <input type="text" name="in"/> <input type="text" name="in"/> <input type="submit" value="Submit"/> </form></body></html>` func SimpleServer(w http.ResponseWriter, request *http.Request) { io.WriteString(w, "hello, world") panic("test test") } func FormServer(w http.ResponseWriter, request *http.Request) { w.Header().Set("Content-Type", "text/html") switch request.Method { case "GET": io.WriteString(w, form) case "POST": request.ParseForm() io.WriteString(w, request.Form["in"][1]) io.WriteString(w, " ") io.WriteString(w, request.FormValue("in")) } } func main() { http.HandleFunc("/test1", logPanics(SimpleServer)) http.HandleFunc("/test2", logPanics(FormServer)) if err := http.ListenAndServe(":8088", nil); err != nil { } } func logPanics(handle http.HandlerFunc) http.HandlerFunc { return func(writer http.ResponseWriter, request *http.Request) { defer func() { if x := recover(); x != nil { log.Printf("[%v] caught panic: %v", request.RemoteAddr, x) } }() handle(writer, request) } }
http 模板
1、替换
main.go
package main import ( "fmt" "os" "text/template" ) type Person struct { Name string Title string age string } func main() { t, err := template.ParseFiles("e:/wangjian/go/project/src/go_dev/day10/template/index.html") if err != nil { fmt.Println("parse file err:", err) return } p := Person{Name: "Mary", age: "31", Title: "我的个人网站"} if err := t.Execute(os.Stdout, p); err != nil { fmt.Println("There was an error:", err.Error()) } }
index.html
<html> <head> <title>{{.Title}}</title> </head> <body> <p> hello, {{.Name}}</p> <p> {{.}}</p> </body> </html>
2、if判断
<html> <head> </head> <body> {{if gt .Age 18}} <p>hello, old man, {{.Name}}</p> {{else}} <p>hello,young man, {{.Name}}</p> {{end}} </body> </html>
if常见操作符
- not 非 {{if not .condition}} {{end}}
- and 与 {{if and .condition1 .condition2}} {{end}}
- or 或 {{if or .condition1 .condition2}} {{end}}
- eq 等于 {{if eq .var1 .var2}} {{end}}
- ne 不等于 {{if ne .var1 .var2}} {{end}}
- lt 小于 (less than) {{if lt .var1 .var2}} {{end}}
- le 小于等于 {{if le .var1 .var2}} {{end}}
- gt 大于 {{if gt .var1 .var2}} {{end}}
- ge 大于等于 {{if ge .var1 .var2}} {{end}}
3、{{.}}
<html> <head> </head> <body> <p>hello, old man, {{.}}</p> </body> </html>
4、{{with .Var}} {{end}}
<html> <head> </head> <body> {{with .Name}} <p>hello, old man, {{.}}</p> {{end}} </body> </html>
5、循环{{range.}} {{end }}
<html> <head> </head> <body> {{range .}} {{if gt .Age 18}} <p>hello, old man, {{.Name}}</p> {{else}} <p>hello,young man, {{.Name}}</p> {{end}} {{end}} </body> </html>
样例
index.html
<html> <head> </head> <body> <p>hello world</p> <table border="1"> {{range .}} <tr> <td>{{.Name}}</td> <td>{{.Age}}</td><td>{{.Title}}</td> </tr> {{end}} </table> </body> </html>
main.go
package main import ( "fmt" "html/template" "io" "net/http" ) var myTemplate *template.Template type Result struct { output string } func (p *Result) Write(b []byte) (n int, err error) { fmt.Println("called by template") p.output += string(b) return len(b), nil } type Person struct { Name string Title string Age int } func userInfo(w http.ResponseWriter, r *http.Request) { fmt.Println("handle hello") //fmt.Fprintf(w, "hello ") var arr []Person p := Person{Name: "Mary001", Age: 10, Title: "我的个人网站"} p1 := Person{Name: "Mary002", Age: 10, Title: "我的个人网站"} p2 := Person{Name: "Mary003", Age: 10, Title: "我的个人网站"} arr = append(arr, p) arr = append(arr, p1) arr = append(arr, p2) resultWriter := &Result{} io.WriteString(resultWriter, "hello world") err := myTemplate.Execute(w, arr) if err != nil { fmt.Println(err) } fmt.Println("template render data:", resultWriter.output) //myTemplate.Execute(w, p) //myTemplate.Execute(os.Stdout, p) //file, err := os.OpenFile("C:/test.log", os.O_CREATE|os.O_WRONLY, 0755) //if err != nil { // fmt.Println("open failed err:", err) // return //} } func initTemplate(filename string) (err error) { myTemplate, err = template.ParseFiles(filename) if err != nil { fmt.Println("parse file err:", err) return } return } func main() { initTemplate("e:/wangjian/go/project/src/go_dev/day10/template_http/index.html") http.HandleFunc("/user/info", userInfo) err := http.ListenAndServe("0.0.0.0:8880", nil) if err != nil { fmt.Println("http listen failed") } }
参数介绍
const ( MaxIdleConns int = 100 MaxIdleConnsPerHost int = 100 IdleConnTimeout int = 90 ) client := &http.Client{ Transport: &http.Transport{ DisableKeepAlives: true, Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }).DialContext, MaxIdleConns: MaxIdleConns, MaxIdleConnsPerHost: MaxIdleConnsPerHost, IdleConnTimeout: time.Duration(IdleConnTimeout)* time.Second, }, Timeout: 15 * time.Second, }
- DisableKeepAlives,表示是否开启http keepalive功能,也即是否重用连接,默认开启(false)
- MaxIdleConns,表示连接池对所有host的最大链接数量,host也即dest-ip,默认为无穷大(0),但是通常情况下为了性能考虑都要严格限制该数目(实际使用中通常利用压测 二分得到该参数的最佳近似值)。太大容易导致客户端和服务端的socket数量剧增,导致内存吃满,文件描述符不足等问题;太小则限制了连接池的socket数量,资源利用率较低。
- MaxIdleConnsPerHost,表示连接池对每个host的最大链接数量,从字面意思也可以看出:如果客户端只需要访问一个host,那么最好将
MaxIdleConnsPerHost
与MaxIdleConns
设置为相同,这样逻辑更加清晰。
- IdleConnTimeout,空闲timeout设置,也即socket在该时间内没有交互则自动关闭连接(注意:该timeout起点是从每次空闲开始计时,若有交互则重置为0),该参数通常设置为分钟级别,例如:90秒。
- DialContext,该函数用于创建http(非https)连接,通常需要关注
Timeout
和KeepAlive
参数。前者表示建立Tcp链接超时时间;后者表示底层为了维持http keepalive状态 每隔多长时间发送Keep-Alive报文。Timeout
通常设置为30s(网络环境良好),KeepAlive
通常设置为30s(与IdleConnTimeout要对应)。