@@ -10,6 +10,7 @@ import (
10
10
"net"
11
11
"runtime/pprof"
12
12
"sort"
13
+ "strconv"
13
14
"strings"
14
15
"sync"
15
16
"sync/atomic"
@@ -93,6 +94,25 @@ type Transport struct {
93
94
// SASL configures the Transfer to use SASL authentication.
94
95
SASL sasl.Mechanism
95
96
97
+ // An optional resolver used to translate broker host names into network
98
+ // addresses.
99
+ //
100
+ // The resolver will be called for every request (not every connection),
101
+ // making it possible to implement ACL policies by validating that the
102
+ // program is allowed to connect to the kafka broker. This also means that
103
+ // the resolver should probably provide a caching layer to avoid storming
104
+ // the service discovery backend with requests.
105
+ //
106
+ // When set, the Dial function is not responsible for performing name
107
+ // resolution, and is always called with a pre-resolved address.
108
+ Resolver BrokerResolver
109
+
110
+ // The background context used to control goroutines started internally by
111
+ // the transport.
112
+ //
113
+ // If nil, context.Background() is used instead.
114
+ Context context.Context
115
+
96
116
mutex sync.RWMutex
97
117
pools map [networkAddress ]* connPool
98
118
}
@@ -210,7 +230,7 @@ func (t *Transport) grabPool(addr net.Addr) *connPool {
210
230
return p
211
231
}
212
232
213
- ctx , cancel := context .WithCancel (context . Background ())
233
+ ctx , cancel := context .WithCancel (t . context ())
214
234
215
235
p = & connPool {
216
236
refc : 2 ,
@@ -222,6 +242,7 @@ func (t *Transport) grabPool(addr net.Addr) *connPool {
222
242
clientID : t .ClientID ,
223
243
tls : t .TLS ,
224
244
sasl : t .SASL ,
245
+ resolver : t .Resolver ,
225
246
226
247
ready : make (event ),
227
248
wake : make (chan event ),
@@ -239,6 +260,13 @@ func (t *Transport) grabPool(addr net.Addr) *connPool {
239
260
return p
240
261
}
241
262
263
+ func (t * Transport ) context () context.Context {
264
+ if t .Context != nil {
265
+ return t .Context
266
+ }
267
+ return context .Background ()
268
+ }
269
+
242
270
type event chan struct {}
243
271
244
272
func (e event ) trigger () { close (e ) }
@@ -255,6 +283,7 @@ type connPool struct {
255
283
clientID string
256
284
tls * tls.Config
257
285
sasl sasl.Mechanism
286
+ resolver BrokerResolver
258
287
// Signaling mechanisms to orchestrate communications between the pool and
259
288
// the rest of the program.
260
289
once sync.Once // ensure that `ready` is triggered only once
@@ -491,9 +520,11 @@ func (p *connPool) update(ctx context.Context, metadata *meta.Response, err erro
491
520
492
521
for id := range addBrokers {
493
522
broker := layout .Brokers [id ]
494
- p .conns [id ] = p .newConnGroup (& networkAddress {
495
- network : "tcp" ,
496
- address : broker .String (),
523
+ p .conns [id ] = p .newBrokerConnGroup (Broker {
524
+ Rack : broker .Rack ,
525
+ Host : broker .Host ,
526
+ Port : broker .Port ,
527
+ ID : broker .ID ,
497
528
})
498
529
}
499
530
}
@@ -559,12 +590,12 @@ func (p *connPool) discover(ctx context.Context, wake <-chan event) {
559
590
// returned.
560
591
func (p * connPool ) grabBrokerConn (ctx context.Context , brokerID int ) (* conn , error ) {
561
592
p .mutex .RLock ()
562
- c := p .conns [brokerID ]
593
+ g := p .conns [brokerID ]
563
594
p .mutex .RUnlock ()
564
- if c == nil {
595
+ if g == nil {
565
596
return nil , BrokerNotAvailable
566
597
}
567
- return c .grabConnOrConnect (ctx )
598
+ return g .grabConnOrConnect (ctx )
568
599
}
569
600
570
601
// grabClusterConn returns the connection to the kafka cluster that the pool is
@@ -754,6 +785,20 @@ func (p *connPool) newConnGroup(a net.Addr) *connGroup {
754
785
return & connGroup {
755
786
addr : a ,
756
787
pool : p ,
788
+ broker : Broker {
789
+ ID : - 1 ,
790
+ },
791
+ }
792
+ }
793
+
794
+ func (p * connPool ) newBrokerConnGroup (broker Broker ) * connGroup {
795
+ return & connGroup {
796
+ addr : & networkAddress {
797
+ network : "tcp" ,
798
+ address : net .JoinHostPort (broker .Host , strconv .Itoa (broker .Port )),
799
+ },
800
+ pool : p ,
801
+ broker : broker ,
757
802
}
758
803
}
759
804
@@ -849,7 +894,8 @@ var defaultDialer = net.Dialer{
849
894
// actual network connections are lazily open before sending requests, and
850
895
// closed if they are unused for longer than the idle timeout.
851
896
type connGroup struct {
852
- addr net.Addr
897
+ addr net.Addr
898
+ broker Broker
853
899
// Immutable state of the connection.
854
900
pool * connPool
855
901
// Shared state of the connection, this is synchronized on the mutex through
@@ -873,14 +919,50 @@ func (g *connGroup) closeIdleConns() {
873
919
}
874
920
875
921
func (g * connGroup ) grabConnOrConnect (ctx context.Context ) (* conn , error ) {
876
- c := g .grabConn ()
922
+ var rslv = g .pool .resolver
923
+ var addr = g .addr
924
+ var c * conn
925
+
926
+ if rslv == nil {
927
+ c = g .grabConn ()
928
+ } else {
929
+ var err error
930
+ var broker = g .broker
931
+
932
+ if broker .ID < 0 {
933
+ host , port , err := net .SplitHostPort (addr .String ())
934
+ if err != nil {
935
+ return nil , fmt .Errorf ("%s: %w" , addr , err )
936
+ }
937
+ portNumber , err := strconv .Atoi (port )
938
+ if err != nil {
939
+ return nil , fmt .Errorf ("%s: %w" , addr , err )
940
+ }
941
+ broker .Host = host
942
+ broker .Port = portNumber
943
+ }
944
+
945
+ ipAddrs , err := rslv .LookupBrokerIPAddr (ctx , broker )
946
+ if err != nil {
947
+ return nil , err
948
+ }
949
+
950
+ for _ , ipAddr := range ipAddrs {
951
+ network := addr .Network ()
952
+ address := net .JoinHostPort (ipAddr .String (), strconv .Itoa (broker .Port ))
953
+
954
+ if c = g .grabConnTo (network , address ); c != nil {
955
+ break
956
+ }
957
+ }
958
+ }
877
959
878
960
if c == nil {
879
961
connChan := make (chan * conn )
880
962
errChan := make (chan error )
881
963
882
964
go func () {
883
- c , err := g .connect (ctx )
965
+ c , err := g .connect (ctx , addr )
884
966
if err != nil {
885
967
select {
886
968
case errChan <- err :
@@ -909,6 +991,30 @@ func (g *connGroup) grabConnOrConnect(ctx context.Context) (*conn, error) {
909
991
return c , nil
910
992
}
911
993
994
+ func (g * connGroup ) grabConnTo (network , address string ) * conn {
995
+ g .mutex .Lock ()
996
+ defer g .mutex .Unlock ()
997
+
998
+ for i := len (g .idleConns ) - 1 ; i >= 0 ; i -- {
999
+ c := g .idleConns [i ]
1000
+
1001
+ if c .network == network && c .address == address {
1002
+ copy (g .idleConns [i :], g .idleConns [i + 1 :])
1003
+ n := len (g .idleConns ) - 1
1004
+ g .idleConns [n ] = nil
1005
+ g .idleConns = g .idleConns [:n ]
1006
+
1007
+ if c .timer != nil {
1008
+ c .timer .Stop ()
1009
+ }
1010
+
1011
+ return c
1012
+ }
1013
+ }
1014
+
1015
+ return nil
1016
+ }
1017
+
912
1018
func (g * connGroup ) grabConn () * conn {
913
1019
g .mutex .Lock ()
914
1020
defer g .mutex .Unlock ()
@@ -974,14 +1080,14 @@ func (g *connGroup) releaseConn(c *conn) bool {
974
1080
return true
975
1081
}
976
1082
977
- func (g * connGroup ) connect (ctx context.Context ) (* conn , error ) {
1083
+ func (g * connGroup ) connect (ctx context.Context , addr net. Addr ) (* conn , error ) {
978
1084
deadline := time .Now ().Add (g .pool .dialTimeout )
979
1085
980
1086
ctx , cancel := context .WithDeadline (ctx , deadline )
981
1087
defer cancel ()
982
1088
983
- var network = strings .Split (g . addr .Network (), "," )
984
- var address = strings .Split (g . addr .String (), "," )
1089
+ var network = strings .Split (addr .Network (), "," )
1090
+ var address = strings .Split (addr .String (), "," )
985
1091
var netConn net.Conn
986
1092
var netAddr net.Addr
987
1093
var err error
@@ -1055,18 +1161,25 @@ func (g *connGroup) connect(ctx context.Context) (*conn, error) {
1055
1161
pc .SetDeadline (time.Time {})
1056
1162
1057
1163
reqs := make (chan connRequest )
1058
- c := & conn {reqs : reqs , group : g }
1164
+ c := & conn {
1165
+ network : netAddr .Network (),
1166
+ address : netAddr .String (),
1167
+ reqs : reqs ,
1168
+ group : g ,
1169
+ }
1059
1170
go c .run (pc , reqs )
1060
1171
1061
1172
netConn = nil
1062
1173
return c , nil
1063
1174
}
1064
1175
1065
1176
type conn struct {
1066
- reqs chan <- connRequest
1067
- once sync.Once
1068
- group * connGroup
1069
- timer * time.Timer
1177
+ reqs chan <- connRequest
1178
+ network string
1179
+ address string
1180
+ once sync.Once
1181
+ group * connGroup
1182
+ timer * time.Timer
1070
1183
}
1071
1184
1072
1185
func (c * conn ) close () {
0 commit comments