从零开始用Go写代理
Thu ,Jun 15 ,2017学习一门语言还是需要理论与实践相结合的,于是决定开始利用Go语言实现Proxy的功能。
1.需求分析
- 实现http协议的代理。
- 高性能,高并发。
大概写一个排期吧
6-16 – 6-23 把基本的架子搭起来,实现访问代理,代理返回后台的数据。
6-23 – 6-30 查看理论,进一步分析现有的功能,对第一期的代码重构。
2.理论分析
想到了自己比较常用的几款proxy。查了查,简单分析下:
- Nginx, Haproxy 反向代理:
Client <– –> Nginx, Haproxy <– –> Server
- 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