|
|
@@ -2,32 +2,34 @@ package kafka
|
|
|
|
|
|
import (
|
|
|
"encoding/binary"
|
|
|
+ "io"
|
|
|
"math"
|
|
|
"net"
|
|
|
)
|
|
|
|
|
|
type broker struct {
|
|
|
- nodeId int32
|
|
|
- host *string
|
|
|
- port int32
|
|
|
+ id int32
|
|
|
+ host *string
|
|
|
+ port int32
|
|
|
|
|
|
correlation_id int32
|
|
|
|
|
|
conn net.Conn
|
|
|
addr net.TCPAddr
|
|
|
|
|
|
- requests chan reqResPair
|
|
|
- responses chan reqResPair
|
|
|
+ requests chan responsePromise
|
|
|
+ responses chan responsePromise
|
|
|
}
|
|
|
|
|
|
-type reqResPair struct {
|
|
|
+type responsePromise struct {
|
|
|
correlation_id int32
|
|
|
packets chan []byte
|
|
|
+ errors chan error
|
|
|
}
|
|
|
|
|
|
func newBroker(host string, port int32) (b *broker, err error) {
|
|
|
b = new(broker)
|
|
|
- b.nodeId = -1 // don't know it yet
|
|
|
+ b.id = -1 // don't know it yet
|
|
|
b.host = &host
|
|
|
b.port = port
|
|
|
err = b.connect()
|
|
|
@@ -53,30 +55,30 @@ func (b *broker) connect() (err error) {
|
|
|
}
|
|
|
|
|
|
go b.sendRequestLoop()
|
|
|
- go b.rcvResponseLoop()
|
|
|
+ go b.rcvresponsePromiseLoop()
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (b *broker) disconnect() {
|
|
|
- close(b.requests)
|
|
|
- b.requests = nil
|
|
|
+func (b *broker) forceDisconnect(reqRes *responsePromise, err error) {
|
|
|
+ reqRes.errors <- err
|
|
|
+ close(reqRes.errors)
|
|
|
+ close(reqRes.packets)
|
|
|
|
|
|
+ close(b.requests)
|
|
|
close(b.responses)
|
|
|
- b.responses = nil
|
|
|
|
|
|
b.conn.Close()
|
|
|
- b.conn = nil
|
|
|
}
|
|
|
|
|
|
func (b *broker) encode(pe packetEncoder) {
|
|
|
- pe.putInt32(b.nodeId)
|
|
|
+ pe.putInt32(b.id)
|
|
|
pe.putString(b.host)
|
|
|
pe.putInt32(b.port)
|
|
|
}
|
|
|
|
|
|
func (b *broker) decode(pd packetDecoder) (err error) {
|
|
|
- b.nodeId, err = pd.getInt32()
|
|
|
+ b.id, err = pd.getInt32()
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
@@ -100,61 +102,56 @@ func (b *broker) decode(pd packetDecoder) (err error) {
|
|
|
}
|
|
|
|
|
|
func (b *broker) sendRequestLoop() {
|
|
|
- var n int
|
|
|
- var err error
|
|
|
- var buf []byte
|
|
|
for request := range b.requests {
|
|
|
- buf = <-request.packets
|
|
|
- n, err = b.conn.Write(buf)
|
|
|
- if err != nil || n != len(buf) {
|
|
|
- b.disconnect()
|
|
|
+ buf := <-request.packets
|
|
|
+ _, err := b.conn.Write(buf)
|
|
|
+ if err != nil {
|
|
|
+ b.forceDisconnect(&request, err)
|
|
|
return
|
|
|
}
|
|
|
b.responses <- request
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (b *broker) rcvResponseLoop() {
|
|
|
- var n int
|
|
|
- var length int32
|
|
|
- var err error
|
|
|
- var buf []byte
|
|
|
+func (b *broker) rcvresponsePromiseLoop() {
|
|
|
header := make([]byte, 4)
|
|
|
for response := range b.responses {
|
|
|
- n, err = b.conn.Read(header)
|
|
|
- if err != nil || n != 4 {
|
|
|
- b.disconnect()
|
|
|
+ _, err := io.ReadFull(b.conn, header)
|
|
|
+ if err != nil {
|
|
|
+ b.forceDisconnect(&response, err)
|
|
|
return
|
|
|
}
|
|
|
- length = int32(binary.BigEndian.Uint32(header))
|
|
|
+
|
|
|
+ length := int32(binary.BigEndian.Uint32(header))
|
|
|
if length <= 4 || length > 2*math.MaxUint16 {
|
|
|
- b.disconnect()
|
|
|
+ b.forceDisconnect(&response, DecodingError{})
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- n, err = b.conn.Read(header)
|
|
|
- if err != nil || n != 4 {
|
|
|
- b.disconnect()
|
|
|
+ _, err = io.ReadFull(b.conn, header)
|
|
|
+ if err != nil {
|
|
|
+ b.forceDisconnect(&response, err)
|
|
|
return
|
|
|
}
|
|
|
if response.correlation_id != int32(binary.BigEndian.Uint32(header)) {
|
|
|
- b.disconnect()
|
|
|
+ b.forceDisconnect(&response, DecodingError{})
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- buf = make([]byte, length-4)
|
|
|
- n, err = b.conn.Read(buf)
|
|
|
- if err != nil || n != int(length-4) {
|
|
|
- b.disconnect()
|
|
|
+ buf := make([]byte, length-4)
|
|
|
+ _, err = io.ReadFull(b.conn, buf)
|
|
|
+ if err != nil {
|
|
|
+ b.forceDisconnect(&response, err)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
response.packets <- buf
|
|
|
close(response.packets)
|
|
|
+ close(response.errors)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (b *broker) sendRequest(clientID *string, body encoder) (chan []byte, error) {
|
|
|
+func (b *broker) sendRequest(clientID *string, body encoder) (*responsePromise, error) {
|
|
|
var prepEnc prepEncoder
|
|
|
var realEnc realEncoder
|
|
|
var api API
|
|
|
@@ -177,12 +174,13 @@ func (b *broker) sendRequest(clientID *string, body encoder) (chan []byte, error
|
|
|
realEnc.putInt32(int32(prepEnc.length))
|
|
|
req.encode(&realEnc)
|
|
|
|
|
|
- // we buffer one packet so that we can send our packet to the request queue without
|
|
|
- // blocking, and so that the responses can be sent to us async if we want them
|
|
|
- request := reqResPair{b.correlation_id, make(chan []byte, 1)}
|
|
|
+ // we buffer one packet and one error so that all this can work async if the
|
|
|
+ // caller so desires. we also cheat and use the same responsePromise object for both the
|
|
|
+ // request and the response, as things are much simpler that way
|
|
|
+ request := responsePromise{b.correlation_id, make(chan []byte, 1), make(chan error, 1)}
|
|
|
|
|
|
request.packets <- realEnc.raw
|
|
|
b.requests <- request
|
|
|
b.correlation_id++
|
|
|
- return request.packets, nil
|
|
|
+ return &request, nil
|
|
|
}
|