如果没有看前篇,请点击Go实现FastCgi Proxy Client 系列(一),文内篇一,都是指这个
http header的构建
golang的http包
golang的http包网上demo实在是太多了,我就不copy了,从golang的core/src来看吧。
net/http
首先进入到golang的安装目录,可以看到src目录,进入到其中,可以找到net/http目录。

可以发现,这里几乎涵盖了http所有要用的功能。
那我们要讲请求转发,自然就要看到request.go。
golang http的request
golang 对Request对象(姑且以面向对象的方式解释)定义如下:
type Request struct { //忽略这mardown语法影响显示} Method string URL *url.URL Proto string ProtoMajor int ProtoMinor int Header Header Body io.ReadCloser ContentLength int64 TransferEncoding []string Close bool Host string Form url.Values PostForm url.Values MultipartForm *multipart.Form Trailer Header RemoteAddr string RequestURI string TLS *tls.ConnectionState Cancel <-chan struct{} Response *Response ctx context.Context }
那我们就可以找到大部分我们要的东西。
- 请求header头,我们可以从Header中获取
- 请求体,我们可以从Body中获取,如果是表单,我们就要从Form、PostForm或者MultipartForm获取
- 其他信息,比如host,url之类,我们就可以完美的获取到要的信息。
按照篇一所讲,我们要生成的Header,可以在这里获取,当然,不是全部。比如document_root这种,具体的代码实现在这。
response
response这里就不详细介绍了,主要看我们如何接收数据吧,功能主要是讲接收到的数据返回给用户。
接收数据
篇一稍微贴了一个代码,为了更直观的体现,我这里再贴一次。
rec := &record{} //建立一个记录缓冲 var err1 error // recive untill EOF or FCGI_END_REQUEST for { err1 = rec.read(cgi.rwc) //从cgi建立的链接读取数据 if err1 != nil { //判断是否有错误,判断错误是否为终止符 if err1 != io.EOF { // keepalive on的时候 不会有终止符 这个要注意 err = err1 } break } switch { //根据返回的类型,将内容写到响应的字节数组中 case rec.h.Type == typeStdout: retout = append(retout, rec.content()...) case rec.h.Type == typeStderr: reterr = append(reterr, rec.content()...) case rec.h.Type == typeEndRequest: fallthrough default: break } }
仔细观察type,就是我们在篇一说的消息类型的几种。
我们在假设没有错误出现的情况下,retout这个数组自然是获取所有的返回值。这个时候,golang存储的是字节[22 35 97 ......],直接将其转化成字符串就可以看到我们熟悉的Http协议的Response了。
如:
头部: Cache-Control: no-cache, must-revalidate, max-age=0 Connection: keep-alive Content-Type: text/html; charset=utf-8 Date: Tue, 03 Apr 2018 15:10:58 GMT Expires: Wed, 11 Jan 1984 05:00:00 GMT Server: nginx/1.12.2 Transfer-Encoding: chunked X-Powered-By: PHP/7.1.9 正文(记住正文上面有2个换行符): //......
这个时候,我们只需要将我们获取到的内容格式化我们想要的内容即可。
代码太多,我就不贴了,给个链接 spinx http Response的实现。
坑的记录
content-length
我实现好之后,用fpm做试验,测试post的时候,发现如论如何php都接收不到post的值。(包括$_POST和php://input)
但是我用官方实现的 net/http/fcgi 做了一个demo又能收到数据,迷糊了半天。
后来在swoole群里获得了群友 大白菜、夕阳下的奔跑、Neo 的帮助,发现是PHP有必须对http的content-length进行读,得到长度之后,才会对 body的包进行读取。
所以,我们要加上 header map对content-length的设置
cookies无法设置成功
这个位置我重写了我的赋值位置,不能直接使用获取的 Set-Cookies 的Header进行处理,需要将重写成Cookies,并且 http.SetCookie(write,cookie) 用内置的方法输出。
性能测试
几乎和nginx没区别,证明我fpm进程数开得不够多,我下次多开点fpm再来进行压测(以wordpress进行的基准测试)。
new version test(wordpress): Server Software: spinx Server Hostname: www.test.com Server Port: 18000 Document Path: / Document Length: 53302 bytes Concurrency Level: 100 Time taken for tests: 20.158 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 53507000 bytes HTML transferred: 53302000 bytes Requests per second: 49.61 [#/sec] (mean) Time per request: 2015.799 [ms] (mean) Time per request: 20.158 [ms] (mean, across all concurrent requests) Transfer rate: 2592.17 [Kbytes/sec] received ========================================================== Server Software: nginx/1.13.9 Server Hostname: www.test.com Server Port: 18000 Document Path: / Document Length: 53302 bytes Concurrency Level: 100 Time taken for tests: 20.277 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 53548000 bytes HTML transferred: 53302000 bytes Requests per second: 49.32 [#/sec] (mean) Time per request: 2027.685 [ms] (mean) Time per request: 20.277 [ms] (mean, across all concurrent requests) Transfer rate: 2578.95 [Kbytes/sec] received ============================================================= Performance has caught up with nginx.
spinx的github
给力点,大哥们,来点star吧。
spinx 一个golang实现的fastcgi proxy client