从零开始用Go写代理

学习一门语言还是需要理论与实践相结合的,于是决定开始利用Go语言实现Proxy的功能。

1.需求分析


  • 实现http协议的代理。
  • 高性能,高并发。

大概写一个排期吧

6-16 – 6-23 把基本的架子搭起来,实现访问代理,代理返回后台的数据。

6-23 – 6-30 查看理论,进一步分析现有的功能,对第一期的代码重构。

2.理论分析


想到了自己比较常用的几款proxy。查了查,简单分析下:

  1. Nginx, Haproxy 反向代理:

Client <– –> Nginx, Haproxy <– –> Server

  1. ShadowSocks

Client <– –> (local ss client) <– –> (remote ss server) <– –> server


我的目标是实现诸如Nginx与Haproxy的反向代理。

从配置文件中可以看出,将请求导入到代理之后,根据host名以及url来做导向。

刚才想到一点,可以不解析body,根据head就可以确定导向。

3. 实践部分

6.16 完成了初步的框架。一个请求可以转接。

有一个问题:
问题背景: 
server: 监听8081端口,有请求时返回字符串 "hello"
proxy: 监听8080端口,外界的请求到达时,启动go程,go程内去构建新的请求,去8081访问,并写结果。
client: 采用wrk模式,并发1000个。

报错:

go程内client.do函数:
error: Get http://192.168.177.128:8081: dial tcp 192.168.177.128:8081: 
socket: too many open files
proxy本身:
2017/06/16 08:32:42 http: Accept error: accept tcp [::]:8080: accept4: 
too many open files; retrying in 10ms


刚才发现不同的shell中ulimit 不一样。

首先,可以确定:
1. wrk中使用了 1000个 套接字,利用这1000个 Fd 不停的发送请求。
2. proxy 使用了1个监听 8080 端口。 
    http.Client并没有建立请求,所以,实际是 来一个request, Go程就得 调用dial即connect一次
    Client结构体文档说有缓存http请求。这点不明确,需要再查询。
3. server 使用了1个监听8081 端口。当 accept 时,accept一个,内核也得建立新的 open file.
    这些应该是不断增加的,目前不确定是不是及时关掉了。


1. 验证不同的shell中限制不一样。

 之前默认是1024. 

 ulimit -n 分别修改了 proxy 之后,wrk 之后,二者不再显示错误。然而 server 出现了 accept 错误。
    
 也就是通过增大open file数量,可以解决问题,然而上面提到的问题还没有确定。

/******增加ulimit 方法***
/etc/security/limits.conf  增加: du hard nofile 10000  //系统限制
~/.bash_profile  增加:ulimit -n 10000                  //该用户每次登陆shell执行ulimit.
***********************/

/********增加的代码**
不再使用默认的配置文件,Client的配置文件
var tr *http.Transport = &http.Transport{
            Dial: (&net.Dialer{
                Timeout:   30 * time.Second,
                KeepAlive: 30 * time.Second,
            }).Dial,
            DisableKeepAlives : false,
            
            //这个值就是关键,含义是:保持长链接的TCP连接个数。不设置默认为2.
            MaxIdleConnsPerHost : 1000,  
}
*****************/
var client *http.Client = &http.Client{Transport: tr}

结果: 5000并发无压力。
[du@localhost ~]$ wrk -t4 -c5000 -d3s http://127.0.0.1:8080
Running 3s test @ http://127.0.0.1:8080
  4 threads and 5000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   177.49ms   50.95ms 447.81ms   73.99%
    Req/Sec     3.44k     1.84k    7.03k    64.55%
  40019 requests in 3.05s, 5.00MB read
Requests/sec:  13132.85
Transfer/sec:      1.64MB

4.实现

利用了 reverse.go,站在了巨人的肩膀上,只是简单的改写了director.

github: proxy