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,返回读取的数据和遇到的错误。
就先这样吧,有事留言。(逃
下一篇:阿里云的潜规则

我来说两句: