golang开发高性能proxy心得
[文章作者:磨延城 转载请注明原文出处: https://mo2g.com/view/153/ ]
有一段时间没有更新博客了,主要是当爹了,然后各种没时间,久了就搁置了. 这一篇主要分享自己使用golang在开发一个简单proxy功能的过程中总结的心得.先简单介绍一下proxy的功能.
有一段时间没有更新博客了,主要是当爹了,然后各种没时间,久了就搁置了。
这一篇主要分享自己使用golang在开发一个简单proxy功能的过程中总结的心得。先简单介绍一下proxy的功能。
场景:小M使用浏览器访问www.facebook.com
没有proxy
浏览器直接打开www.facebook.com
有proxy
1)浏览器打开proxy.mo2g.com/www.facebook.com
2)proxy程序访问www.facebook.com
3)proxy把www.facebook.com返回的数据转发给浏览器
以上只是proxy的简单用法,更高级的,就根据自己的需求去定制开发吧。这个proxy Demo主要使用了以下包跟框架
compress/gzip包、io/ioutil包、net/http包、net/url包、gin框架(https://github.com/gin-gonic/gin)
默认使用8080端口,废话不多说了,先上代码。
package main import ( "compress/gzip" "github.com/gin-gonic/gin" "io/ioutil" "net/http" "net/url" ) var router = gin.Default() func main() { router.Any("/*path", proxy) router.Run() } func proxy(c *gin.Context) { path := c.Param("path") if path == "/favicon.ico" { c.Status(http.StatusNotFound) return } var ( req *http.Request resp *http.Response err error body []byte remote *url.URL ) path = c.Query("url") path = "https://" + path // 转发header、body到新地址 remote, err = url.Parse(path) req = &http.Request{ URL: remote, Method: c.Request.Method, Header: c.Request.Header, Body: c.Request.Body, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, } client := &http.Client{} resp, err = client.Do(req) if err != nil { c.JSON(http.StatusForbidden, gin.H{ "errmsg": err, }) return } defer resp.Body.Close() if resp.Header.Get("Content-Encoding") == "gzip" { reader, err := gzip.NewReader(resp.Body) if err != nil { c.JSON(http.StatusForbidden, gin.H{ "errmsg": err, }) } defer reader.Close() body, err = ioutil.ReadAll(reader) if err != nil { c.JSON(http.StatusForbidden, gin.H{ "errmsg": err, }) } } else { body, err = ioutil.ReadAll(resp.Body) } if err != nil { c.JSON(http.StatusForbidden, gin.H{ "errmsg": err, }) return } c.Data(http.StatusOK, resp.Header.Get("Content-Type"), body) }
今晚先写到这了,之后再把剩下的补上。Good Night.
2016/12/3更新,关键代码讲解。
router.Any("/*path", proxy)
把所有类型的请求(GET、POST、PUT、PATCH、HEAD、OPTIONS、DELETE、CONNECT、TRACE)都使用proxy函数来处理。
path := c.Param("path") if path == "/favicon.ico" { c.Status(http.StatusNotFound) return }
忽略favicon.ico请求,某些浏览器会自动请求favicon.ico,例如Chrome。
path = c.Query("url")
获取url的值,例如:proxy.mo2g.com/?url=www.baidu.com
remote, err = url.Parse(path) req = &http.Request{ URL: remote, Method: c.Request.Method, Header: c.Request.Header, Body: c.Request.Body, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, } client := &http.Client{} resp, err = client.Do(req)
这段为proxy的关键代码,c.Request包含了所有的请求数据,我们从中提取Method、Header、Body新建一个Request,URL为url.Parse(path)生成的新地址。
client.Do(req)方法发送请求,返回HTTP回复。(如果返回值err为nil,resp.Body总是非nil的,调用者应该在读取完resp.Body后关闭它)
if resp.Header.Get("Content-Encoding") == "gzip" { reader, err := gzip.NewReader(resp.Body) defer reader.Close() } else { }
判断返回的数据是否经过gzip压缩,是否需要解压数据。
body, err = ioutil.ReadAll(resp.Body)
读取数据直到EOF或遇到error,返回读取的数据和遇到的错误。
就先这样吧,有事留言。(逃
下一篇:阿里云的潜规则
我来说两句: