close gracefully

wait all resp-client connections closed
This commit is contained in:
siddontang 2014-11-11 15:21:41 +08:00
parent c6576990ef
commit 6f39cb17a9
7 changed files with 85 additions and 42 deletions

View File

@ -88,17 +88,15 @@ func main() {
syscall.SIGTERM, syscall.SIGTERM,
syscall.SIGQUIT) syscall.SIGQUIT)
go func() {
<-sc
app.Close()
}()
if *usePprof { if *usePprof {
go func() { go func() {
log.Println(http.ListenAndServe(fmt.Sprintf(":%d", *pprofPort), nil)) log.Println(http.ListenAndServe(fmt.Sprintf(":%d", *pprofPort), nil))
}() }()
} }
app.Run() go app.Run()
<-sc
app.Close()
} }

View File

@ -202,7 +202,7 @@ func (l *Ledis) ReadLogsTo(startLogID uint64, w io.Writer) (n int, nextLogID uin
} }
// try to read events, if no events read, try to wait the new event singal until timeout seconds // try to read events, if no events read, try to wait the new event singal until timeout seconds
func (l *Ledis) ReadLogsToTimeout(startLogID uint64, w io.Writer, timeout int) (n int, nextLogID uint64, err error) { func (l *Ledis) ReadLogsToTimeout(startLogID uint64, w io.Writer, timeout int, quitCh chan struct{}) (n int, nextLogID uint64, err error) {
n, nextLogID, err = l.ReadLogsTo(startLogID, w) n, nextLogID, err = l.ReadLogsTo(startLogID, w)
if err != nil { if err != nil {
return return
@ -213,6 +213,8 @@ func (l *Ledis) ReadLogsToTimeout(startLogID uint64, w io.Writer, timeout int) (
select { select {
case <-l.r.WaitLog(): case <-l.r.WaitLog():
case <-time.After(time.Duration(timeout) * time.Second): case <-time.After(time.Duration(timeout) * time.Second):
case <-quitCh:
return
} }
return l.ReadLogsTo(startLogID, w) return l.ReadLogsTo(startLogID, w)
} }

View File

@ -37,6 +37,11 @@ type App struct {
slaveSyncAck chan uint64 slaveSyncAck chan uint64
snap *snapshotStore snap *snapshotStore
connWait sync.WaitGroup
rcm sync.Mutex
rcs map[*respClient]struct{}
} }
func netType(s string) string { func netType(s string) string {
@ -64,6 +69,8 @@ func NewApp(cfg *config.Config) (*App, error) {
app.slaves = make(map[string]*client) app.slaves = make(map[string]*client)
app.slaveSyncAck = make(chan uint64) app.slaveSyncAck = make(chan uint64)
app.rcs = make(map[*respClient]struct{})
var err error var err error
if app.info, err = newInfo(app); err != nil { if app.info, err = newInfo(app); err != nil {
@ -129,6 +136,11 @@ func (app *App) Close() {
app.httpListener.Close() app.httpListener.Close()
} }
app.closeAllRespClients()
//wait all connection closed
app.connWait.Wait()
app.closeScript() app.closeScript()
app.m.Close() app.m.Close()

View File

@ -40,15 +40,18 @@ type httpWriter struct {
} }
func newClientHTTP(app *App, w http.ResponseWriter, r *http.Request) { func newClientHTTP(app *App, w http.ResponseWriter, r *http.Request) {
app.connWait.Add(1)
defer app.connWait.Done()
var err error var err error
c := new(httpClient) c := new(httpClient)
c.client = newClient(app)
err = c.makeRequest(app, r, w) err = c.makeRequest(app, r, w)
if err != nil { if err != nil {
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))
return return
} }
c.client = newClient(app)
c.perform() c.perform()
c.client.close() c.client.close()
} }

View File

@ -16,6 +16,7 @@ import (
) )
var errReadRequest = errors.New("invalid request protocol") var errReadRequest = errors.New("invalid request protocol")
var errClientQuit = errors.New("remote client quit")
type respClient struct { type respClient struct {
*client *client
@ -24,18 +25,51 @@ type respClient struct {
rb *bufio.Reader rb *bufio.Reader
ar *arena.Arena ar *arena.Arena
activeQuit bool
} }
type respWriter struct { type respWriter struct {
buff *bufio.Writer buff *bufio.Writer
} }
func (app *App) addRespClient(c *respClient) {
app.rcm.Lock()
app.rcs[c] = struct{}{}
app.rcm.Unlock()
}
func (app *App) delRespClient(c *respClient) {
app.rcm.Lock()
delete(app.rcs, c)
app.rcm.Unlock()
}
func (app *App) closeAllRespClients() {
app.rcm.Lock()
for c := range app.rcs {
c.conn.Close()
}
app.rcm.Unlock()
}
func (app *App) respClientNum() int {
app.rcm.Lock()
n := len(app.rcs)
app.rcm.Unlock()
return n
}
func newClientRESP(conn net.Conn, app *App) { func newClientRESP(conn net.Conn, app *App) {
c := new(respClient) c := new(respClient)
c.client = newClient(app) c.client = newClient(app)
c.conn = conn c.conn = conn
c.activeQuit = false
if tcpConn, ok := conn.(*net.TCPConn); ok { if tcpConn, ok := conn.(*net.TCPConn); ok {
tcpConn.SetReadBuffer(app.cfg.ConnReadBufferSize) tcpConn.SetReadBuffer(app.cfg.ConnReadBufferSize)
tcpConn.SetWriteBuffer(app.cfg.ConnWriteBufferSize) tcpConn.SetWriteBuffer(app.cfg.ConnWriteBufferSize)
@ -49,17 +83,15 @@ func newClientRESP(conn net.Conn, app *App) {
//maybe another config? //maybe another config?
c.ar = arena.NewArena(app.cfg.ConnReadBufferSize) c.ar = arena.NewArena(app.cfg.ConnReadBufferSize)
app.connWait.Add(1)
app.addRespClient(c)
go c.run() go c.run()
} }
func (c *respClient) run() { func (c *respClient) run() {
c.app.info.addClients(1)
defer func() { defer func() {
c.client.close()
c.app.info.addClients(-1)
if e := recover(); e != nil { if e := recover(); e != nil {
buf := make([]byte, 4096) buf := make([]byte, 4096)
n := runtime.Stack(buf, false) n := runtime.Stack(buf, false)
@ -68,21 +100,30 @@ func (c *respClient) run() {
log.Fatal("client run panic %s:%v", buf, e) log.Fatal("client run panic %s:%v", buf, e)
} }
handleQuit := true c.client.close()
if c.conn != nil {
//if handle quit command before, conn is nil
handleQuit = false
c.conn.Close() c.conn.Close()
}
if c.tx != nil { if c.tx != nil {
c.tx.Rollback() c.tx.Rollback()
c.tx = nil c.tx = nil
} }
c.app.removeSlave(c.client, handleQuit) c.app.removeSlave(c.client, c.activeQuit)
c.app.delRespClient(c)
c.app.connWait.Done()
}() }()
select {
case <-c.app.quit:
//check app closed
return
default:
break
}
kc := time.Duration(c.app.cfg.ConnKeepaliveInterval) * time.Second kc := time.Duration(c.app.cfg.ConnKeepaliveInterval) * time.Second
for { for {
if kc > 0 { if kc > 0 {
@ -91,16 +132,12 @@ func (c *respClient) run() {
reqData, err := c.readRequest() reqData, err := c.readRequest()
if err == nil { if err == nil {
c.handleRequest(reqData) err = c.handleRequest(reqData)
} }
if err != nil { if err != nil {
return return
} }
if c.conn == nil {
return
}
} }
} }
@ -108,7 +145,7 @@ func (c *respClient) readRequest() ([][]byte, error) {
return ReadRequest(c.rb, c.ar) return ReadRequest(c.rb, c.ar)
} }
func (c *respClient) handleRequest(reqData [][]byte) { func (c *respClient) handleRequest(reqData [][]byte) error {
if len(reqData) == 0 { if len(reqData) == 0 {
c.cmd = "" c.cmd = ""
c.args = reqData[0:0] c.args = reqData[0:0]
@ -117,11 +154,11 @@ func (c *respClient) handleRequest(reqData [][]byte) {
c.args = reqData[1:] c.args = reqData[1:]
} }
if c.cmd == "quit" { if c.cmd == "quit" {
c.activeQuit = true
c.resp.writeStatus(OK) c.resp.writeStatus(OK)
c.resp.flush() c.resp.flush()
c.conn.Close() c.conn.Close()
c.conn = nil return errClientQuit
return
} }
c.perform() c.perform()
@ -131,7 +168,7 @@ func (c *respClient) handleRequest(reqData [][]byte) {
c.ar.Reset() c.ar.Reset()
return return nil
} }
// response writer // response writer

View File

@ -131,7 +131,7 @@ func syncCommand(c *client) error {
c.syncBuf.Write(dummyBuf) c.syncBuf.Write(dummyBuf)
if _, _, err := c.app.ldb.ReadLogsToTimeout(logId, &c.syncBuf, 30); err != nil { if _, _, err := c.app.ldb.ReadLogsToTimeout(logId, &c.syncBuf, 30, c.app.quit); err != nil {
return err return err
} else { } else {
buf := c.syncBuf.Bytes() buf := c.syncBuf.Bytes()

View File

@ -9,7 +9,6 @@ import (
"runtime/debug" "runtime/debug"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
) )
@ -23,10 +22,6 @@ type info struct {
ProceessId int ProceessId int
} }
Clients struct {
ConnectedClients int64
}
Replication struct { Replication struct {
PubLogNum sync2.AtomicInt64 PubLogNum sync2.AtomicInt64
PubLogAckNum sync2.AtomicInt64 PubLogAckNum sync2.AtomicInt64
@ -47,10 +42,6 @@ func newInfo(app *App) (i *info, err error) {
return i, nil return i, nil
} }
func (i *info) addClients(delta int64) {
atomic.AddInt64(&i.Clients.ConnectedClients, delta)
}
func (i *info) Close() { func (i *info) Close() {
} }
@ -116,7 +107,7 @@ func (i *info) dumpServer(buf *bytes.Buffer) {
infoPair{"readonly", i.app.cfg.Readonly}, infoPair{"readonly", i.app.cfg.Readonly},
infoPair{"goroutine_num", runtime.NumGoroutine()}, infoPair{"goroutine_num", runtime.NumGoroutine()},
infoPair{"cgo_call_num", runtime.NumCgoCall()}, infoPair{"cgo_call_num", runtime.NumCgoCall()},
infoPair{"client_num", i.Clients.ConnectedClients}, infoPair{"resp_client_num", i.app.respClientNum()},
) )
} }