|
15 | 15 | package phpfpm
|
16 | 16 |
|
17 | 17 | import (
|
| 18 | + "context" |
18 | 19 | "encoding/json"
|
19 | 20 | "fmt"
|
20 |
| - "io/ioutil" |
| 21 | + "io" |
| 22 | + "net/http" |
21 | 23 | "net/url"
|
22 | 24 | "regexp"
|
23 | 25 | "strconv"
|
@@ -163,36 +165,63 @@ func (p *Pool) Update() (err error) {
|
163 | 165 |
|
164 | 166 | defer fcgi.Close()
|
165 | 167 |
|
166 |
| - env := map[string]string{ |
167 |
| - "SCRIPT_FILENAME": path, |
168 |
| - "SCRIPT_NAME": path, |
169 |
| - "SERVER_SOFTWARE": "go / php-fpm_exporter", |
170 |
| - "REMOTE_ADDR": "127.0.0.1", |
171 |
| - "QUERY_STRING": "json&full", |
172 |
| - } |
| 168 | + // Process might hang on execution indefinitely there, and block other requests. |
| 169 | + // This still allows for goroutine to live forever, if either of the requests will not finish. |
| 170 | + done := make(chan *http.Response, 1) |
| 171 | + errCh := make(chan error, 1) |
| 172 | + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) |
| 173 | + defer cancel() |
| 174 | + |
| 175 | + go func(ctx context.Context, ch chan *http.Response, errCh chan error) { |
| 176 | + env := map[string]string{ |
| 177 | + "SCRIPT_FILENAME": path, |
| 178 | + "SCRIPT_NAME": path, |
| 179 | + "SERVER_SOFTWARE": "go / php-fpm_exporter", |
| 180 | + "REMOTE_ADDR": "127.0.0.1", |
| 181 | + "QUERY_STRING": "json&full", |
| 182 | + } |
173 | 183 |
|
174 |
| - resp, err := fcgi.Get(env) |
175 |
| - if err != nil { |
176 |
| - return p.error(err) |
177 |
| - } |
| 184 | + res, err := fcgi.Get(env) |
| 185 | + if err != nil { |
| 186 | + errCh <- p.error(err) |
| 187 | + return |
| 188 | + } |
178 | 189 |
|
179 |
| - defer resp.Body.Close() |
| 190 | + select { |
| 191 | + case <-ctx.Done(): |
| 192 | + // request is already done, let's just release response resources |
| 193 | + res.Body.Close() |
| 194 | + default: |
| 195 | + // |
| 196 | + ch <- res |
| 197 | + } |
180 | 198 |
|
181 |
| - content, err := ioutil.ReadAll(resp.Body) |
182 |
| - if err != nil { |
183 |
| - return p.error(err) |
184 |
| - } |
| 199 | + }(ctx, done, errCh) |
185 | 200 |
|
186 |
| - content = JSONResponseFixer(content) |
| 201 | + select { |
| 202 | + case err = <-errCh: |
| 203 | + return err |
| 204 | + case <-ctx.Done(): |
| 205 | + return ctx.Err() |
| 206 | + case resp := <-done: |
| 207 | + defer resp.Body.Close() |
| 208 | + |
| 209 | + content, err := io.ReadAll(resp.Body) |
| 210 | + if err != nil { |
| 211 | + return p.error(err) |
| 212 | + } |
187 | 213 |
|
188 |
| - log.Debugf("Pool[%v]: %v", p.Address, string(content)) |
| 214 | + content = JSONResponseFixer(content) |
189 | 215 |
|
190 |
| - if err = json.Unmarshal(content, &p); err != nil { |
191 |
| - log.Errorf("Pool[%v]: %v", p.Address, string(content)) |
192 |
| - return p.error(err) |
193 |
| - } |
| 216 | + log.Debugf("Pool[%v]: %v", p.Address, string(content)) |
194 | 217 |
|
195 |
| - return nil |
| 218 | + if err = json.Unmarshal(content, &p); err != nil { |
| 219 | + log.Errorf("Pool[%v]: %v", p.Address, string(content)) |
| 220 | + return p.error(err) |
| 221 | + } |
| 222 | + |
| 223 | + return nil |
| 224 | + } |
196 | 225 | }
|
197 | 226 |
|
198 | 227 | func (p *Pool) error(err error) error {
|
|
0 commit comments