21
21
import java .util .concurrent .CompletableFuture ;
22
22
import java .util .concurrent .ConcurrentHashMap ;
23
23
import java .util .concurrent .atomic .AtomicBoolean ;
24
+ import java .util .regex .Matcher ;
24
25
import javax .annotation .Nullable ;
25
- import lombok .NonNull ;
26
26
import lombok .extern .slf4j .Slf4j ;
27
27
import org .apache .commons .lang3 .StringUtils ;
28
28
import org .apache .pulsar .broker .PulsarService ;
38
38
@ Slf4j
39
39
public class KopBrokerLookupManager {
40
40
41
- private final String advertisedListeners ;
42
41
private final LookupClient lookupClient ;
43
42
private final MetadataStoreCacheLoader metadataStoreCacheLoader ;
44
43
45
44
private final AtomicBoolean closed = new AtomicBoolean (false );
46
45
47
- public static final ConcurrentHashMap <String , ConcurrentHashMap < String , CompletableFuture <InetSocketAddress > >>
46
+ public static final ConcurrentHashMap <String , CompletableFuture <InetSocketAddress >>
48
47
LOOKUP_CACHE = new ConcurrentHashMap <>();
49
48
50
- public static final ConcurrentHashMap <String , CompletableFuture <Optional <String >>>
51
- KOP_ADDRESS_CACHE = new ConcurrentHashMap <>();
52
-
53
49
public KopBrokerLookupManager (KafkaServiceConfiguration conf , PulsarService pulsarService ) throws Exception {
54
- this .advertisedListeners = conf .getKafkaAdvertisedListeners ();
55
50
this .lookupClient = KafkaProtocolHandler .getLookupClient (pulsarService );
56
51
this .metadataStoreCacheLoader = new MetadataStoreCacheLoader (pulsarService .getPulsarResources (),
57
52
conf .getBrokerLookupTimeoutMs ());
58
53
}
59
54
60
- public CompletableFuture <Optional <InetSocketAddress >> findBroker (@ NonNull TopicName topic ,
55
+ public CompletableFuture <Optional <InetSocketAddress >> findBroker (String topic ,
61
56
@ Nullable EndPoint advertisedEndPoint ) {
62
- if (log .isDebugEnabled ()) {
63
- log .debug ("Handle Lookup for topic {}" , topic );
64
- }
65
- CompletableFuture <Optional <InetSocketAddress >> returnFuture = new CompletableFuture <>();
66
-
67
- getTopicBroker (topic .toString (),
68
- advertisedEndPoint != null && advertisedEndPoint .isValidInProtocolMap ()
69
- ? advertisedEndPoint .getListenerName () : null )
70
- .thenApply (address -> getProtocolDataToAdvertise (address , topic , advertisedEndPoint ))
71
- .thenAccept (kopAddressFuture -> kopAddressFuture .thenAccept (listenersOptional -> {
72
- if (!listenersOptional .isPresent ()) {
73
- log .error ("Not get advertise data for Kafka topic:{}." , topic );
74
- removeTopicManagerCache (topic .toString ());
75
- returnFuture .complete (Optional .empty ());
76
- return ;
57
+ return getTopicBroker (topic )
58
+ .thenApply (internalListenerAddress -> {
59
+ if (internalListenerAddress == null ) {
60
+ log .error ("[{}] failed get pulsar address, returned null." , topic );
61
+ removeTopicManagerCache (topic );
62
+ return Optional .empty ();
63
+ } else if (log .isDebugEnabled ()) {
64
+ log .debug ("[{}] Found broker's internal listener address: {}" ,
65
+ topic , internalListenerAddress );
77
66
}
78
67
79
- // It's the `kafkaAdvertisedListeners` config that's written to ZK
80
- final String listeners = listenersOptional .get ();
81
- final EndPoint endPoint =
82
- (advertisedEndPoint != null && advertisedEndPoint .isTlsEnabled ()
83
- ? EndPoint .getSslEndPoint (listeners ) : EndPoint .getPlainTextEndPoint (listeners ));
84
-
85
- if (log .isDebugEnabled ()) {
86
- log .debug ("Found broker localListeners: {} for topicName: {}, "
87
- + "localListeners: {}, found Listeners: {}" ,
88
- listeners , topic , advertisedListeners , listeners );
68
+ try {
69
+ final String listener = getAdvertisedListener (
70
+ internalListenerAddress , topic , advertisedEndPoint );
71
+ if (log .isDebugEnabled ()) {
72
+ log .debug ("Found listener {} for topic {}" , listener , topic );
73
+ }
74
+ final Matcher matcher = EndPoint .matcherListener (listener ,
75
+ listener + " cannot be split into 3 parts" );
76
+ return Optional .of (new InetSocketAddress (matcher .group (2 ), Integer .parseInt (matcher .group (3 ))));
77
+ } catch (IllegalStateException | NumberFormatException e ) {
78
+ log .error ("Failed to find the advertised listener: {}" , e .getMessage ());
79
+ removeTopicManagerCache (topic );
80
+ return Optional .empty ();
89
81
}
90
-
91
- // here we found topic broker: broker2, but this is in broker1,
92
- // how to clean the lookup cache?
93
- if (!advertisedListeners .contains (endPoint .getOriginalListener ())) {
94
- removeTopicManagerCache (topic .toString ());
95
- }
96
- returnFuture .complete (Optional .of (endPoint .getInetAddress ()));
97
- })).exceptionally (throwable -> {
98
- log .error ("Not get advertise data for Kafka topic:{}. throwable: [{}]" ,
99
- topic , throwable .getMessage ());
100
- removeTopicManagerCache (topic .toString ());
101
- returnFuture .complete (Optional .empty ());
102
- return null ;
103
82
});
104
- return returnFuture ;
105
83
}
106
84
107
- // call pulsarclient.lookup.getbroker to get and own a topic.
108
- // when error happens, the returned future will complete with null.
109
- public CompletableFuture <InetSocketAddress > getTopicBroker (String topicName , String listenerName ) {
85
+ public CompletableFuture <InetSocketAddress > getTopicBroker (String topicName ) {
110
86
if (closed .get ()) {
111
87
if (log .isDebugEnabled ()) {
112
88
log .debug ("Return null for getTopicBroker({}) since channel closing" , topicName );
113
89
}
114
90
return CompletableFuture .completedFuture (null );
115
91
}
116
92
117
- ConcurrentHashMap <String , CompletableFuture <InetSocketAddress >> topicLookupCache =
118
- LOOKUP_CACHE .computeIfAbsent (topicName , t -> {
119
- if (log .isDebugEnabled ()) {
120
- log .debug ("Topic {} not in Lookup_cache, call lookupBroker" , topicName );
121
- }
122
- ConcurrentHashMap <String , CompletableFuture <InetSocketAddress >> cache = new ConcurrentHashMap <>();
123
- cache .put (listenerName == null ? "" : listenerName , lookupBroker (topicName , listenerName ));
124
- return cache ;
125
- });
126
-
127
- return topicLookupCache .computeIfAbsent (listenerName == null ? "" : listenerName , t -> {
128
- if (log .isDebugEnabled ()) {
129
- log .debug ("Topic {} not in Lookup_cache, call lookupBroker" , topicName );
130
- }
131
- return lookupBroker (topicName , listenerName );
132
- });
93
+ if (log .isDebugEnabled ()) {
94
+ log .debug ("Handle Lookup for topic {}" , topicName );
95
+ }
96
+ return LOOKUP_CACHE .computeIfAbsent (topicName , this ::lookupBroker );
133
97
}
134
98
135
- private CompletableFuture <InetSocketAddress > lookupBroker (final String topic , String listenerName ) {
99
+ private CompletableFuture <InetSocketAddress > lookupBroker (final String topic ) {
136
100
if (closed .get ()) {
137
101
if (log .isDebugEnabled ()) {
138
102
log .debug ("Return null for getTopic({}) since channel closing" , topic );
139
103
}
140
104
return CompletableFuture .completedFuture (null );
141
105
}
142
- return lookupClient .getBrokerAddress (TopicName .get (topic ), listenerName );
106
+ return lookupClient .getBrokerAddress (TopicName .get (topic ));
143
107
}
144
108
145
- private CompletableFuture <Optional <String >> getProtocolDataToAdvertise (
146
- InetSocketAddress pulsarAddress , TopicName topic , @ Nullable EndPoint advertisedEndPoint ) {
147
- CompletableFuture <Optional <String >> returnFuture = new CompletableFuture <>();
148
-
149
- if (pulsarAddress == null ) {
150
- log .error ("[{}] failed get pulsar address, returned null." , topic .toString ());
151
-
152
- // getTopicBroker returns null. topic should be removed from LookupCache.
153
- removeTopicManagerCache (topic .toString ());
154
-
155
- returnFuture .complete (Optional .empty ());
156
- return returnFuture ;
157
- }
109
+ private String getAdvertisedListener (InetSocketAddress internalListenerAddress ,
110
+ String topic ,
111
+ @ Nullable EndPoint advertisedEndPoint ) {
158
112
159
- if (log .isDebugEnabled ()) {
160
- log .debug ("Found broker for topic {} puslarAddress: {}" ,
161
- topic , pulsarAddress );
162
- }
163
-
164
- // get kop address from cache to prevent query zk each time.
165
- final CompletableFuture <Optional <String >> future = KOP_ADDRESS_CACHE .get (topic .toString ());
166
- if (future != null ) {
167
- return future ;
168
- }
169
-
170
- if (advertisedEndPoint != null && advertisedEndPoint .isValidInProtocolMap ()) {
171
- // if kafkaProtocolMap is set, the lookup result is the advertised address
172
- String kafkaAdvertisedAddress = String .format ("%s://%s:%s" , advertisedEndPoint .getSecurityProtocol ().name ,
173
- pulsarAddress .getHostName (), pulsarAddress .getPort ());
174
- KOP_ADDRESS_CACHE .put (topic .toString (), returnFuture );
175
- returnFuture .complete (Optional .ofNullable (kafkaAdvertisedAddress ));
176
- if (log .isDebugEnabled ()) {
177
- log .debug ("{} get kafka Advertised Address through kafkaListenerName: {}" ,
178
- topic , pulsarAddress );
179
- }
180
- return returnFuture ;
181
- }
182
-
183
- List <LoadManagerReport > availableBrokers = metadataStoreCacheLoader .getAvailableBrokers ();
113
+ final List <LoadManagerReport > availableBrokers = metadataStoreCacheLoader .getAvailableBrokers ();
184
114
if (log .isDebugEnabled ()) {
185
115
availableBrokers .forEach (loadManagerReport ->
186
116
log .debug ("Handle getProtocolDataToAdvertise for {}, pulsarUrl: {}, "
@@ -193,18 +123,20 @@ private CompletableFuture<Optional<String>> getProtocolDataToAdvertise(
193
123
loadManagerReport .getProtocol (KafkaProtocolHandler .PROTOCOL_NAME )));
194
124
}
195
125
196
- String hostAndPort = pulsarAddress .getHostName () + ":" + pulsarAddress .getPort ();
197
- Optional <LoadManagerReport > serviceLookupData = availableBrokers .stream ()
126
+ final String hostAndPort = internalListenerAddress .getHostName () + ":" + internalListenerAddress .getPort ();
127
+ final Optional <LoadManagerReport > serviceLookupData = availableBrokers .stream ()
198
128
.filter (loadManagerReport -> lookupDataContainsAddress (loadManagerReport , hostAndPort )).findAny ();
199
- if (serviceLookupData .isPresent ()) {
200
- KOP_ADDRESS_CACHE .put (topic .toString (), returnFuture );
201
- returnFuture .complete (serviceLookupData .get ().getProtocol (KafkaProtocolHandler .PROTOCOL_NAME ));
202
- } else {
203
- log .error ("No node for broker {} under loadBalance" , pulsarAddress );
204
- removeTopicManagerCache (topic .toString ());
205
- returnFuture .complete (Optional .empty ());
129
+ if (!serviceLookupData .isPresent ()) {
130
+ log .error ("No node for broker {} under loadBalance" , internalListenerAddress );
131
+ return null ;
206
132
}
207
- return returnFuture ;
133
+
134
+ return serviceLookupData .get ().getProtocol (KafkaProtocolHandler .PROTOCOL_NAME ).map (kafkaAdvertisedListeners ->
135
+ Optional .ofNullable (advertisedEndPoint )
136
+ .map (endPoint -> EndPoint .findListener (kafkaAdvertisedListeners , endPoint .getListenerName ()))
137
+ .orElse (EndPoint .findFirstListener (kafkaAdvertisedListeners ))
138
+ ).orElseThrow (() -> new IllegalStateException (
139
+ "No kafkaAdvertisedListeners found in broker " + internalListenerAddress ));
208
140
}
209
141
210
142
// whether a ServiceLookupData contains wanted address.
@@ -215,12 +147,10 @@ private static boolean lookupDataContainsAddress(ServiceLookupData data, String
215
147
216
148
public static void removeTopicManagerCache (String topicName ) {
217
149
LOOKUP_CACHE .remove (topicName );
218
- KOP_ADDRESS_CACHE .remove (topicName );
219
150
}
220
151
221
152
public static void clear () {
222
153
LOOKUP_CACHE .clear ();
223
- KOP_ADDRESS_CACHE .clear ();
224
154
}
225
155
226
156
public void close () {
0 commit comments