Skip to content

Commit 05a6fbd

Browse files
committed
add request timeout
1 parent 276560a commit 05a6fbd

File tree

4 files changed

+141
-9
lines changed

4 files changed

+141
-9
lines changed

client.go

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ package gofcgi
22

33
import (
44
"errors"
5+
"log"
56
"net"
67
"net/http"
78
"sync"
89
"time"
910
)
1011

11-
var ErrClientDisconnect = errors.New("[fcgi]lost connection to server")
12+
var ErrClientDisconnect = errors.New("lost connection to server")
1213

1314
type Client struct {
1415
isFree bool
@@ -22,16 +23,35 @@ type Client struct {
2223

2324
locker sync.Mutex
2425

26+
expireTime time.Time
27+
expireLocker sync.Mutex
28+
2529
mock bool
2630
}
2731

2832
func NewClient(network string, address string) *Client {
29-
return &Client{
33+
client := &Client{
3034
isFree: true,
3135
isAvailable: false,
3236
network: network,
3337
address: address,
38+
expireTime: time.Now().Add(86400 * time.Second),
3439
}
40+
41+
// 处理超时
42+
go func() {
43+
for {
44+
time.Sleep(1 * time.Second)
45+
if time.Since(client.expireTime) > 0 {
46+
client.conn.Close()
47+
48+
client.expireLocker.Lock()
49+
client.expireTime = time.Now().Add(86400 * time.Second)
50+
client.expireLocker.Unlock()
51+
}
52+
}
53+
}()
54+
return client
3555
}
3656

3757
func (this *Client) KeepAlive() {
@@ -67,17 +87,28 @@ func (this *Client) Call(req *Request) (*http.Response, error) {
6787
return nil, errors.New("no connection to server")
6888
}
6989

90+
if req.timeout > 0 {
91+
this.beforeTime(req.timeout)
92+
}
7093
resp, err := req.CallOn(this.conn)
94+
this.endTime()
7195

7296
// 如果失去连接,则重新连接
7397
if err != nil {
98+
log.Println("[gofcgi]" + err.Error())
99+
74100
if err == ErrClientDisconnect {
75101
// 重试一次
76102
this.Close()
77103
err = this.Connect()
78104
if err == nil {
105+
if req.timeout > 0 {
106+
this.beforeTime(req.timeout)
107+
}
79108
resp, err = req.CallOn(this.conn)
109+
this.endTime()
80110
} else {
111+
log.Println("[gofcgi]again:" + err.Error())
81112
this.Close()
82113
}
83114
}
@@ -100,6 +131,7 @@ func (this *Client) Connect() error {
100131
// @TODO 设置并使用超时时间
101132
conn, err := net.Dial(this.network, this.address)
102133
if err != nil {
134+
log.Println("[gofcgi]" + err.Error())
103135
return err
104136
}
105137

@@ -112,3 +144,15 @@ func (this *Client) Connect() error {
112144
func (this *Client) Mock() {
113145
this.mock = true
114146
}
147+
148+
func (this *Client) beforeTime(timeout time.Duration) {
149+
this.expireLocker.Lock()
150+
this.expireTime = time.Now().Add(timeout)
151+
this.expireLocker.Unlock()
152+
}
153+
154+
func (this *Client) endTime() {
155+
this.expireLocker.Lock()
156+
this.expireTime = time.Now().Add(86400 * time.Second)
157+
this.expireLocker.Unlock()
158+
}

client_test.go

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package gofcgi
22

33
import (
4-
"testing"
5-
"io/ioutil"
64
"bytes"
5+
"io/ioutil"
6+
"strings"
7+
"sync"
8+
"testing"
79
"time"
810
)
911

@@ -63,7 +65,7 @@ func TestClientGetAlive(t *testing.T) {
6365
t.Fatal("connect err:", err.Error())
6466
}
6567

66-
for i := 0; i < 10; i ++ {
68+
for i := 0; i < 10; i++ {
6769
req := NewRequest()
6870
req.SetParams(map[string]string{
6971
"SCRIPT_FILENAME": "/Users/liuxiangchao/Documents/Projects/pp/apps/baleshop.ppk/index.php",
@@ -151,3 +153,78 @@ func TestClientPost(t *testing.T) {
151153
}
152154
t.Log("resp body:", string(data))
153155
}
156+
157+
func TestClientPerformance(t *testing.T) {
158+
threads := 100
159+
countRequests := 200
160+
countSuccess := 0
161+
countFail := 0
162+
locker := sync.Mutex{}
163+
beforeTime := time.Now()
164+
wg := sync.WaitGroup{}
165+
wg.Add(threads)
166+
167+
pool := SharedPool("tcp", "127.0.0.1:9000", 16)
168+
169+
for i := 0; i < threads; i++ {
170+
go func(i int) {
171+
defer wg.Done()
172+
173+
for j := 0; j < countRequests; j++ {
174+
client, err := pool.Client()
175+
if err != nil {
176+
t.Fatal("connect err:", err.Error())
177+
}
178+
179+
req := NewRequest()
180+
req.SetTimeout(5 * time.Second)
181+
req.SetParams(map[string]string{
182+
"SCRIPT_FILENAME": "/Users/liuxiangchao/Documents/Projects/pp/apps/baleshop.ppk/index.php",
183+
"SERVER_SOFTWARE": "gofcgi/1.0.0",
184+
"REMOTE_ADDR": "127.0.0.1",
185+
"QUERY_STRING": "name=value&__ACTION__=/@wx",
186+
187+
"SERVER_NAME": "wx.balefm.cn",
188+
"SERVER_ADDR": "127.0.0.1:80",
189+
"SERVER_PORT": "80",
190+
"REQUEST_URI": "/index.php?__ACTION__=/@wx",
191+
"DOCUMENT_ROOT": "/Users/liuxiangchao/Documents/Projects/pp/apps/baleshop.ppk/",
192+
"GATEWAY_INTERFACE": "CGI/1.1",
193+
"REDIRECT_STATUS": "200",
194+
"HTTP_HOST": "wx.balefm.cn",
195+
196+
"REQUEST_METHOD": "GET",
197+
})
198+
199+
resp, err := client.Call(req)
200+
if err != nil {
201+
locker.Lock()
202+
countFail++
203+
locker.Unlock()
204+
continue
205+
}
206+
207+
if resp.StatusCode == 200 {
208+
data, err := ioutil.ReadAll(resp.Body)
209+
if err != nil || strings.Index(string(data), "Welcome") == -1 {
210+
locker.Lock()
211+
countFail++
212+
locker.Unlock()
213+
} else {
214+
locker.Lock()
215+
countSuccess++
216+
locker.Unlock()
217+
}
218+
} else {
219+
locker.Lock()
220+
countFail++
221+
locker.Unlock()
222+
}
223+
}
224+
}(i)
225+
}
226+
227+
wg.Wait()
228+
229+
t.Log("success:", countSuccess, "fail:", countFail, "qps:", int(float64(countSuccess+countFail)/time.Since(beforeTime).Seconds()))
230+
}

pool.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package gofcgi
22

33
import (
44
"errors"
5+
"log"
56
"sync"
67
"time"
78
)
@@ -40,9 +41,17 @@ func SharedPool(network string, address string, size uint) *Pool {
4041

4142
// 第一个同步连接供使用,其余的可以异步建立连接
4243
if i == 0 {
43-
client.Connect()
44+
err := client.Connect()
45+
if err != nil {
46+
log.Println("[gofcgi]" + err.Error())
47+
}
4448
} else {
45-
go client.Connect()
49+
go func() {
50+
err := client.Connect()
51+
if err != nil {
52+
log.Println("[gofcgi]" + err.Error())
53+
}
54+
}()
4655
}
4756
pool.clients = append(pool.clients, client)
4857
}

request.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"errors"
88
"fmt"
99
"io"
10+
"log"
1011
"math"
1112
"net"
1213
"net/http"
@@ -28,7 +29,7 @@ var contentLengthRegexp = regexp.MustCompile("^\\d+$")
2829
type Request struct {
2930
id uint16
3031
keepAlive bool
31-
timeout time.Duration // @TODO 待实现
32+
timeout time.Duration
3233
params map[string]string
3334
body io.Reader
3435
bodyLength uint32
@@ -131,7 +132,7 @@ func (this *Request) writeParams(conn net.Conn) error {
131132
func (this *Request) writeStdin(conn net.Conn) error {
132133
if this.body != nil {
133134
// 分解body
134-
buf := make([]byte, 65535)
135+
buf := make([]byte, 60000)
135136
for {
136137
n, err := this.body.Read(buf)
137138

@@ -205,6 +206,7 @@ func (this *Request) readStdout(conn net.Conn) (*http.Response, error) {
205206
b := make([]byte, respHeader.ContentLength+uint16(respHeader.PaddingLength))
206207
err = binary.Read(conn, binary.BigEndian, &b)
207208
if err != nil {
209+
log.Println("err:", err.Error())
208210
return nil, ErrClientDisconnect
209211
}
210212

0 commit comments

Comments
 (0)