ttrpc: implement Close and Shutdown
This apples logic to correctly Close a server, as well as implements graceful shutdown. This ensures that inflight requests are not interrupted and works similar to the functionality in `net/http`. This required a fair bit of refactoring around how the connection is managed. The connection now has an explicit wrapper object, ensuring that shutdown happens in a coordinated fashion, whether or not a forceful close or graceful shutdown is called. In addition to the above, hardening around the accept loop has been added. We now correctly exit on non-temporary errors and debounce the accept call when encountering repeated errors. This should address some issues where `SIGTERM` was not honored when dropping into the accept spin. Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
31
client.go
31
client.go
@@ -40,22 +40,30 @@ func NewClient(conn net.Conn) *Client {
|
||||
}
|
||||
|
||||
func (c *Client) Call(ctx context.Context, service, method string, req, resp interface{}) error {
|
||||
requestID := atomic.AddUint32(&c.requestID, 2)
|
||||
if err := c.sendRequest(ctx, requestID, service, method, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.recvResponse(ctx, requestID, resp)
|
||||
}
|
||||
|
||||
func (c *Client) sendRequest(ctx context.Context, requestID uint32, service, method string, req interface{}) error {
|
||||
payload, err := c.codec.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
requestID := atomic.AddUint32(&c.requestID, 2)
|
||||
request := Request{
|
||||
Service: service,
|
||||
Method: method,
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
if err := c.send(ctx, requestID, &request); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.send(ctx, requestID, &request)
|
||||
}
|
||||
|
||||
func (c *Client) recvResponse(ctx context.Context, requestID uint32, resp interface{}) error {
|
||||
var response Response
|
||||
if err := c.recv(ctx, requestID, &response); err != nil {
|
||||
return err
|
||||
@@ -160,6 +168,10 @@ func (c *Client) run() {
|
||||
// start one more goroutine to recv messages without blocking.
|
||||
for {
|
||||
var p [messageLengthMax]byte
|
||||
// TODO(stevvooe): Something still isn't quite right with error
|
||||
// handling on the client-side, causing EOFs to come through. We
|
||||
// need other fixes in this changeset, so we'll address this
|
||||
// correctly later.
|
||||
mh, err := c.channel.recv(context.TODO(), p[:])
|
||||
select {
|
||||
case incoming <- received{
|
||||
@@ -187,13 +199,12 @@ func (c *Client) run() {
|
||||
}
|
||||
waiters[req.id] = req
|
||||
case r := <-incoming:
|
||||
if r.err != nil {
|
||||
c.err = r.err
|
||||
return
|
||||
}
|
||||
|
||||
if waiter, ok := waiters[r.mh.StreamID]; ok {
|
||||
waiter.err <- proto.Unmarshal(r.p, waiter.msg.(proto.Message))
|
||||
if r.err != nil {
|
||||
waiter.err <- r.err
|
||||
} else {
|
||||
waiter.err <- proto.Unmarshal(r.p, waiter.msg.(proto.Message))
|
||||
}
|
||||
} else {
|
||||
queued[r.mh.StreamID] = r
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user