fix(tunasync): connection leakage caused by http keep-alive

This commit is contained in:
bigeagle 2016-04-29 08:57:14 +08:00
parent d1981379a4
commit 2268eb3b0f
No known key found for this signature in database
GPG Key ID: 9171A4571C27920A
3 changed files with 68 additions and 49 deletions

View File

@ -8,6 +8,7 @@ import (
"errors" "errors"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"time"
) )
// GetTLSConfig generate tls.Config from CAFile // GetTLSConfig generate tls.Config from CAFile
@ -28,20 +29,34 @@ func GetTLSConfig(CAFile string) (*tls.Config, error) {
return tlsConfig, nil return tlsConfig, nil
} }
// PostJSON posts json object to url // CreateHTTPClient returns a http.Client
func PostJSON(url string, obj interface{}, tlsConfig *tls.Config) (*http.Response, error) { func CreateHTTPClient(CAFile string) (*http.Client, error) {
var client *http.Client var tlsConfig *tls.Config
if tlsConfig == nil { var err error
client = &http.Client{}
} else { if CAFile != "" {
tr := &http.Transport{ tlsConfig, err = GetTLSConfig(CAFile)
TLSClientConfig: tlsConfig, if err != nil {
} return nil, err
client = &http.Client{
Transport: tr,
} }
} }
tr := &http.Transport{
MaxIdleConnsPerHost: 20,
TLSClientConfig: tlsConfig,
}
return &http.Client{
Transport: tr,
Timeout: 5 * time.Second,
}, nil
}
// PostJSON posts json object to url
func PostJSON(url string, obj interface{}, client *http.Client) (*http.Response, error) {
if client == nil {
client, _ = CreateHTTPClient("")
}
b := new(bytes.Buffer) b := new(bytes.Buffer)
if err := json.NewEncoder(b).Encode(obj); err != nil { if err := json.NewEncoder(b).Encode(obj); err != nil {
return nil, err return nil, err
@ -50,17 +65,9 @@ func PostJSON(url string, obj interface{}, tlsConfig *tls.Config) (*http.Respons
} }
// GetJSON gets a json response from url // GetJSON gets a json response from url
func GetJSON(url string, obj interface{}, tlsConfig *tls.Config) (*http.Response, error) { func GetJSON(url string, obj interface{}, client *http.Client) (*http.Response, error) {
var client *http.Client if client == nil {
if tlsConfig == nil { client, _ = CreateHTTPClient("")
client = &http.Client{}
} else {
tr := &http.Transport{
TLSClientConfig: tlsConfig,
}
client = &http.Client{
Transport: tr,
}
} }
resp, err := client.Get(url) resp, err := client.Get(url)

View File

@ -1,7 +1,6 @@
package manager package manager
import ( import (
"crypto/tls"
"fmt" "fmt"
"net/http" "net/http"
"time" "time"
@ -20,10 +19,10 @@ var manager *Manager
// A Manager represents a manager server // A Manager represents a manager server
type Manager struct { type Manager struct {
cfg *Config cfg *Config
engine *gin.Engine engine *gin.Engine
adapter dbAdapter adapter dbAdapter
tlsConfig *tls.Config httpClient *http.Client
} }
// GetTUNASyncManager returns the manager from config // GetTUNASyncManager returns the manager from config
@ -37,19 +36,18 @@ func GetTUNASyncManager(cfg *Config) *Manager {
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
} }
s := &Manager{ s := &Manager{
cfg: cfg, cfg: cfg,
engine: gin.Default(), engine: gin.Default(),
adapter: nil, adapter: nil,
tlsConfig: nil,
} }
if cfg.Files.CACert != "" { if cfg.Files.CACert != "" {
tlsConfig, err := GetTLSConfig(cfg.Files.CACert) httpClient, err := CreateHTTPClient(cfg.Files.CACert)
if err != nil { if err != nil {
logger.Error("Error initializing TLS config: %s", err.Error()) logger.Error("Error initializing HTTP client: %s", err.Error())
return nil return nil
} }
s.tlsConfig = tlsConfig s.httpClient = httpClient
} }
if cfg.Files.DBFile != "" { if cfg.Files.DBFile != "" {
@ -96,12 +94,20 @@ func (s *Manager) setDBAdapter(adapter dbAdapter) {
// Run runs the manager server forever // Run runs the manager server forever
func (s *Manager) Run() { func (s *Manager) Run() {
addr := fmt.Sprintf("%s:%d", s.cfg.Server.Addr, s.cfg.Server.Port) addr := fmt.Sprintf("%s:%d", s.cfg.Server.Addr, s.cfg.Server.Port)
httpServer := &http.Server{
Addr: addr,
Handler: s.engine,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
if s.cfg.Server.SSLCert == "" && s.cfg.Server.SSLKey == "" { if s.cfg.Server.SSLCert == "" && s.cfg.Server.SSLKey == "" {
if err := s.engine.Run(addr); err != nil { if err := httpServer.ListenAndServe(); err != nil {
panic(err) panic(err)
} }
} else { } else {
if err := s.engine.RunTLS(addr, s.cfg.Server.SSLCert, s.cfg.Server.SSLKey); err != nil { if err := httpServer.ListenAndServeTLS(s.cfg.Server.SSLCert, s.cfg.Server.SSLKey); err != nil {
panic(err) panic(err)
} }
} }
@ -258,7 +264,7 @@ func (s *Manager) handleClientCmd(c *gin.Context) {
} }
// post command to worker // post command to worker
_, err = PostJSON(workerURL, workerCmd, s.tlsConfig) _, err = PostJSON(workerURL, workerCmd, s.httpClient)
if err != nil { if err != nil {
err := fmt.Errorf("post command to worker %s(%s) fail: %s", workerID, workerURL, err.Error()) err := fmt.Errorf("post command to worker %s(%s) fail: %s", workerID, workerURL, err.Error())
c.Error(err) c.Error(err)

View File

@ -2,7 +2,6 @@ package worker
import ( import (
"bytes" "bytes"
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
"html/template" "html/template"
@ -26,8 +25,8 @@ type Worker struct {
semaphore chan empty semaphore chan empty
schedule *scheduleQueue schedule *scheduleQueue
httpServer *gin.Engine httpEngine *gin.Engine
tlsConfig *tls.Config httpClient *http.Client
} }
// GetTUNASyncWorker returns a singalton worker // GetTUNASyncWorker returns a singalton worker
@ -48,12 +47,12 @@ func GetTUNASyncWorker(cfg *Config) *Worker {
} }
if cfg.Manager.CACert != "" { if cfg.Manager.CACert != "" {
tlsConfig, err := GetTLSConfig(cfg.Manager.CACert) httpClient, err := CreateHTTPClient(cfg.Manager.CACert)
if err != nil { if err != nil {
logger.Error("Failed to init TLS config: %s", err.Error()) logger.Error("Error initializing HTTP client: %s", err.Error())
return nil return nil
} }
w.tlsConfig = tlsConfig w.httpClient = httpClient
} }
w.initJobs() w.initJobs()
@ -227,18 +226,25 @@ func (w *Worker) makeHTTPServer() {
c.JSON(http.StatusOK, gin.H{"msg": "OK"}) c.JSON(http.StatusOK, gin.H{"msg": "OK"})
}) })
w.httpServer = s w.httpEngine = s
} }
func (w *Worker) runHTTPServer() { func (w *Worker) runHTTPServer() {
addr := fmt.Sprintf("%s:%d", w.cfg.Server.Addr, w.cfg.Server.Port) addr := fmt.Sprintf("%s:%d", w.cfg.Server.Addr, w.cfg.Server.Port)
httpServer := &http.Server{
Addr: addr,
Handler: w.httpEngine,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
if w.cfg.Server.SSLCert == "" && w.cfg.Server.SSLKey == "" { if w.cfg.Server.SSLCert == "" && w.cfg.Server.SSLKey == "" {
if err := w.httpServer.Run(addr); err != nil { if err := httpServer.ListenAndServe(); err != nil {
panic(err) panic(err)
} }
} else { } else {
if err := w.httpServer.RunTLS(addr, w.cfg.Server.SSLCert, w.cfg.Server.SSLKey); err != nil { if err := httpServer.ListenAndServeTLS(w.cfg.Server.SSLCert, w.cfg.Server.SSLKey); err != nil {
panic(err) panic(err)
} }
} }
@ -345,7 +351,7 @@ func (w *Worker) registorWorker() {
URL: w.URL(), URL: w.URL(),
} }
if _, err := PostJSON(url, msg, w.tlsConfig); err != nil { if _, err := PostJSON(url, msg, w.httpClient); err != nil {
logger.Error("Failed to register worker") logger.Error("Failed to register worker")
} }
} }
@ -368,7 +374,7 @@ func (w *Worker) updateStatus(jobMsg jobMessage) {
ErrorMsg: jobMsg.msg, ErrorMsg: jobMsg.msg,
} }
if _, err := PostJSON(url, smsg, w.tlsConfig); err != nil { if _, err := PostJSON(url, smsg, w.httpClient); err != nil {
logger.Error("Failed to update mirror(%s) status: %s", jobMsg.name, err.Error()) logger.Error("Failed to update mirror(%s) status: %s", jobMsg.name, err.Error())
} }
} }
@ -382,7 +388,7 @@ func (w *Worker) fetchJobStatus() []MirrorStatus {
w.Name(), w.Name(),
) )
if _, err := GetJSON(url, &mirrorList, w.tlsConfig); err != nil { if _, err := GetJSON(url, &mirrorList, w.httpClient); err != nil {
logger.Error("Failed to fetch job status: %s", err.Error()) logger.Error("Failed to fetch job status: %s", err.Error())
} }