Skip to content

Commit 6407272

Browse files
authored
Fix mop producer publish metric (#1553)
1 parent 4764775 commit 6407272

File tree

12 files changed

+121
-16
lines changed

12 files changed

+121
-16
lines changed

mqtt-broker/src/main/java/io/streamnative/pulsar/handlers/mqtt/broker/processor/MQTTBrokerProtocolMethodProcessor.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ public void doProcessConnect(MqttAdapterMessage adapterMsg, String userRole,
152152
.clientRestrictions(clientRestrictions)
153153
.serverRestrictions(serverRestrictions)
154154
.authData(authData)
155+
.serverCnx(serverCnx)
155156
.channel(channel)
156157
.connectMessage(msg)
157158
.connectionManager(connectionManager)
@@ -295,6 +296,7 @@ public void processDisconnect(MqttAdapterMessage adapterMsg) {
295296
if (log.isDebugEnabled()) {
296297
log.debug("[Disconnect] [{}] ", clientId);
297298
}
299+
qosPublishHandlers.qos0().closeProducer(connection);
298300
// If client update session timeout interval property.
299301
Optional<Integer> newSessionExpireInterval;
300302
if ((newSessionExpireInterval = MqttPropertyUtils
@@ -328,6 +330,7 @@ public void processConnectionLost() {
328330
if (log.isDebugEnabled()) {
329331
log.debug("[Connection Lost] [{}] ", clientId);
330332
}
333+
qosPublishHandlers.qos0().closeProducer(connection);
331334
metricsCollector.removeClient(NettyUtils.getAddress(channel));
332335
WillMessage willMessage = connection.getWillMessage();
333336
if (willMessage != null) {

mqtt-broker/src/main/java/io/streamnative/pulsar/handlers/mqtt/broker/qos/AbstractQosPublishHandler.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package io.streamnative.pulsar.handlers.mqtt.broker.qos;
1515

1616
import static io.streamnative.pulsar.handlers.mqtt.broker.impl.PulsarMessageConverter.toPulsarMsg;
17+
import io.netty.buffer.ByteBuf;
1718
import io.netty.handler.codec.mqtt.MqttProperties;
1819
import io.netty.handler.codec.mqtt.MqttPublishMessage;
1920
import io.streamnative.pulsar.handlers.mqtt.broker.MQTTServerConfiguration;
@@ -30,10 +31,12 @@
3031
import java.util.Optional;
3132
import java.util.concurrent.CompletableFuture;
3233
import java.util.concurrent.ConcurrentHashMap;
34+
import lombok.extern.slf4j.Slf4j;
3335
import org.apache.bookkeeper.mledger.Position;
3436
import org.apache.commons.lang3.StringUtils;
3537
import org.apache.pulsar.broker.PulsarService;
3638
import org.apache.pulsar.broker.service.BrokerServiceException;
39+
import org.apache.pulsar.broker.service.Producer;
3740
import org.apache.pulsar.broker.service.Topic;
3841
import org.apache.pulsar.broker.service.persistent.PersistentTopic;
3942
import org.apache.pulsar.client.impl.MessageImpl;
@@ -42,12 +45,14 @@
4245
/**
4346
* Abstract class for publish handler.
4447
*/
48+
@Slf4j
4549
public abstract class AbstractQosPublishHandler implements QosPublishHandler {
4650

4751
protected final PulsarService pulsarService;
4852
protected final RetainedMessageHandler retainedMessageHandler;
4953
protected final MQTTServerConfiguration configuration;
5054
private final ConcurrentHashMap<String, Long> sequenceIdMap = new ConcurrentHashMap<>();
55+
private final ConcurrentHashMap<String, Producer> producerMap = new ConcurrentHashMap<>();
5156

5257

5358
protected AbstractQosPublishHandler(MQTTService mqttService) {
@@ -109,6 +114,19 @@ protected CompletableFuture<Position> writeToPulsarTopic(Connection connection,
109114
}
110115
return getTopicReference(mqttTopicName).thenCompose(topicOp -> topicOp.map(topic -> {
111116
long lastPublishedSequenceId = -1;
117+
Producer producer = producerMap.compute(producerName, (k, v) -> {
118+
if (v == null) {
119+
v = MQTTProducer.create(topic, connection.getServerCnx(), producerName);
120+
final CompletableFuture<Optional<Long>> producerFuture =
121+
topic.addProducer(v, new CompletableFuture<>());
122+
producerFuture.whenComplete((r, e) -> {
123+
if (e != null) {
124+
log.error("Failed to add producer", e);
125+
}
126+
});
127+
}
128+
return v;
129+
});
112130
if (topic instanceof PersistentTopic) {
113131
final long lastPublishedId = ((PersistentTopic) topic).getLastPublishedSequenceId(producerName);
114132
lastPublishedSequenceId = sequenceIdMap.compute(producerName, (k, v) -> {
@@ -121,12 +139,14 @@ protected CompletableFuture<Position> writeToPulsarTopic(Connection connection,
121139
return id;
122140
});
123141
}
142+
final ByteBuf payload = msg.payload();
124143
MessageImpl<byte[]> message = toPulsarMsg(configuration, topic, msg.variableHeader().properties(),
125-
msg.payload().nioBuffer());
144+
payload.nioBuffer());
126145
CompletableFuture<Position> ret = MessagePublishContext.publishMessages(producerName, message,
127146
lastPublishedSequenceId, topic);
128147
message.recycle();
129148
return ret.thenApply(position -> {
149+
topic.incrementPublishCount(producer, 1, payload.readableBytes());
130150
if (checkSubscription && topic.getSubscriptions().isEmpty()) {
131151
throw new MQTTNoMatchingSubscriberException(mqttTopicName);
132152
}
@@ -135,4 +155,12 @@ protected CompletableFuture<Position> writeToPulsarTopic(Connection connection,
135155
}).orElseGet(() -> FutureUtil.failedFuture(
136156
new BrokerServiceException.TopicNotFoundException(mqttTopicName))));
137157
}
158+
159+
@Override
160+
public void closeProducer(Connection connection) {
161+
final Producer producer = producerMap.remove(connection.getClientId());
162+
if (producer != null) {
163+
producer.close(true);
164+
}
165+
}
138166
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package io.streamnative.pulsar.handlers.mqtt.broker.qos;
15+
16+
import java.util.Map;
17+
import java.util.Optional;
18+
import java.util.concurrent.atomic.AtomicLong;
19+
import org.apache.pulsar.broker.service.Producer;
20+
import org.apache.pulsar.broker.service.Topic;
21+
import org.apache.pulsar.broker.service.TransportCnx;
22+
import org.apache.pulsar.common.api.proto.ProducerAccessMode;
23+
import org.apache.pulsar.common.protocol.schema.SchemaVersion;
24+
25+
public class MQTTProducer extends Producer {
26+
27+
public static final AtomicLong PRODUCER_ID = new AtomicLong();
28+
29+
public MQTTProducer(Topic topic, TransportCnx cnx, long producerId, String producerName, String appId,
30+
boolean isEncrypted, Map<String, String> metadata, SchemaVersion schemaVersion, long epoch,
31+
boolean userProvidedProducerName, ProducerAccessMode accessMode, Optional<Long> topicEpoch,
32+
boolean supportsPartialProducer) {
33+
super(topic, cnx, producerId, producerName, appId, isEncrypted, metadata, schemaVersion, epoch,
34+
userProvidedProducerName, accessMode, topicEpoch, supportsPartialProducer);
35+
}
36+
37+
public static MQTTProducer create(Topic topic, TransportCnx cnx, String producerName) {
38+
return new MQTTProducer(topic, cnx, PRODUCER_ID.incrementAndGet(), producerName, "",
39+
false, null, SchemaVersion.Latest, 0, true,
40+
ProducerAccessMode.Shared, Optional.empty(), true);
41+
}
42+
}

mqtt-broker/src/main/java/io/streamnative/pulsar/handlers/mqtt/broker/qos/QosPublishHandler.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@
2323
public interface QosPublishHandler {
2424

2525
CompletableFuture<Void> publish(Connection connection, MqttAdapterMessage msg);
26+
27+
void closeProducer(Connection connection);
2628
}

mqtt-broker/src/main/java/io/streamnative/pulsar/handlers/mqtt/broker/qos/QosPublishHandlers.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
*/
1414
package io.streamnative.pulsar.handlers.mqtt.broker.qos;
1515

16-
1716
/**
1817
* Qos publish handlers for different Qos message publish.
1918
*/

mqtt-broker/src/main/java/io/streamnative/pulsar/handlers/mqtt/broker/qos/QosPublishHandlersImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,6 @@ public QosPublishHandler qos1() {
4444
public QosPublishHandler qos2() {
4545
return this.qos2Handler;
4646
}
47+
48+
4749
}

mqtt-common/src/main/java/io/streamnative/pulsar/handlers/mqtt/common/Connection.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import lombok.Setter;
5151
import lombok.extern.slf4j.Slf4j;
5252
import org.apache.pulsar.broker.authentication.AuthenticationDataSource;
53+
import org.apache.pulsar.broker.service.ServerCnx;
5354

5455
/**
5556
* Value object to maintain the information of single connection, like ClientID, Channel, and clean
@@ -90,6 +91,9 @@ public class Connection {
9091
@Getter
9192
private AuthenticationDataSource authData;
9293

94+
@Getter
95+
private ServerCnx serverCnx;
96+
9397
@Getter
9498
private final boolean fromProxy;
9599
private volatile ConnectionState connectionState = DISCONNECTED;
@@ -118,6 +122,7 @@ public class Connection {
118122
this.processor = builder.processor;
119123
this.fromProxy = builder.fromProxy;
120124
this.authData = builder.authData;
125+
this.serverCnx = builder.serverCnx;
121126
this.channel.attr(AUTH_DATA_ATTRIBUTE_KEY).set(authData);
122127
this.addIdleStateHandler();
123128
this.manager.addConnection(this);
@@ -309,6 +314,7 @@ public static class ConnectionBuilder {
309314
private boolean fromProxy;
310315

311316
private AuthenticationDataSource authData;
317+
private ServerCnx serverCnx;
312318

313319
public ConnectionBuilder protocolVersion(int protocolVersion) {
314320
this.protocolVersion = protocolVersion;
@@ -370,6 +376,11 @@ public ConnectionBuilder authData(AuthenticationDataSource authData) {
370376
return this;
371377
}
372378

379+
public ConnectionBuilder serverCnx(ServerCnx serverCnx) {
380+
this.serverCnx = serverCnx;
381+
return this;
382+
}
383+
373384
public Connection build() {
374385
return new Connection(this);
375386
}

mqtt-proxy/src/main/java/io/streamnative/pulsar/handlers/mqtt/proxy/channel/AdapterChannel.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import static com.google.common.base.Preconditions.checkArgument;
1717
import io.netty.channel.Channel;
18+
import io.netty.handler.codec.mqtt.MqttConnectMessage;
1819
import io.streamnative.pulsar.handlers.mqtt.common.Connection;
1920
import io.streamnative.pulsar.handlers.mqtt.common.adapter.MqttAdapterMessage;
2021
import io.streamnative.pulsar.handlers.mqtt.common.utils.FutureUtils;
@@ -39,14 +40,18 @@ public AdapterChannel(MQTTProxyAdapter adapter,
3940
this.channelFuture = channelFuture;
4041
}
4142

42-
public CompletableFuture<Void> writeAndFlush(final MqttAdapterMessage adapterMsg) {
43+
public CompletableFuture<Void> writeAndFlush(final Connection connection, final MqttAdapterMessage adapterMsg) {
4344
checkArgument(StringUtils.isNotBlank(adapterMsg.getClientId()), "clientId is blank");
4445
final String clientId = adapterMsg.getClientId();
4546
adapterMsg.setEncodeType(MqttAdapterMessage.EncodeType.ADAPTER_MESSAGE);
4647
CompletableFuture<Void> future = channelFuture.thenCompose(channel -> {
4748
if (!channel.isActive()) {
4849
channelFuture = adapter.getChannel(broker);
49-
return writeAndFlush(adapterMsg);
50+
if (log.isDebugEnabled()) {
51+
log.debug("channel is inactive, re-create channel to broker : {}", broker);
52+
}
53+
return writeConnectMessage(connection)
54+
.thenCompose(__ -> writeAndFlush(connection, adapterMsg));
5055
}
5156
return FutureUtils.completableFuture(channel.writeAndFlush(adapterMsg));
5257
});
@@ -58,6 +63,11 @@ public CompletableFuture<Void> writeAndFlush(final MqttAdapterMessage adapterMsg
5863
return future;
5964
}
6065

66+
private CompletableFuture<Void> writeConnectMessage(final Connection connection) {
67+
final MqttConnectMessage connectMessage = connection.getConnectMessage();
68+
return writeAndFlush(connection, new MqttAdapterMessage(connection.getClientId(), connectMessage));
69+
}
70+
6171
/**
6272
* When client subscribes, the adapter channel maybe close in exception, so register listener to close the
6373
* related client channel and trigger reconnection.

mqtt-proxy/src/main/java/io/streamnative/pulsar/handlers/mqtt/proxy/impl/MQTTProxyProtocolMethodProcessor.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ public void processPingReq(final MqttAdapterMessage msg) {
245245
topicBrokers.values().forEach(adapterChannel -> {
246246
adapterChannel.thenAccept(channel -> {
247247
msg.setClientId(clientId);
248-
channel.writeAndFlush(msg);
248+
channel.writeAndFlush(connection, msg);
249249
});
250250
});
251251
}
@@ -269,7 +269,7 @@ public void processDisconnect(final MqttAdapterMessage msg) {
269269
.filter(future -> !future.isCompletedExceptionally())
270270
.map(CompletableFuture::join)
271271
.collect(Collectors.toSet())
272-
.forEach(channel -> channel.writeAndFlush(msg)));
272+
.forEach(channel -> channel.writeAndFlush(connection, msg)));
273273
} else {
274274
if (log.isDebugEnabled()) {
275275
log.debug("Disconnect is already triggered, ignore");
@@ -464,7 +464,7 @@ public void processUnSubscribe(final MqttAdapterMessage adapter) {
464464

465465
private CompletableFuture<Void> writeToBroker(final String topic, final MqttAdapterMessage msg) {
466466
CompletableFuture<AdapterChannel> proxyExchanger = connectToBroker(topic);
467-
return proxyExchanger.thenCompose(exchanger -> exchanger.writeAndFlush(msg));
467+
return proxyExchanger.thenCompose(exchanger -> exchanger.writeAndFlush(connection, msg));
468468
}
469469

470470
private CompletableFuture<AdapterChannel> connectToBroker(final String topic) {
@@ -473,8 +473,7 @@ private CompletableFuture<AdapterChannel> connectToBroker(final String topic) {
473473
adapterChannels.computeIfAbsent(mqttBroker, key1 -> {
474474
AdapterChannel adapterChannel = proxyAdapter.getAdapterChannel(mqttBroker);
475475
final MqttConnectMessage connectMessage = connection.getConnectMessage();
476-
477-
adapterChannel.writeAndFlush(new MqttAdapterMessage(connection.getClientId(),
476+
adapterChannel.writeAndFlush(connection, new MqttAdapterMessage(connection.getClientId(),
478477
connectMessage));
479478
return adapterChannel;
480479
})

tests/src/test/java/io/streamnative/pulsar/handlers/mqtt/broker/AdapterChannelTest.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@
2121
import io.netty.handler.codec.mqtt.MqttConnectMessage;
2222
import io.netty.handler.codec.mqtt.MqttMessageBuilders;
2323
import io.streamnative.pulsar.handlers.mqtt.base.MQTTTestBase;
24+
import io.streamnative.pulsar.handlers.mqtt.common.Connection;
2425
import io.streamnative.pulsar.handlers.mqtt.common.MQTTCommonConfiguration;
26+
import io.streamnative.pulsar.handlers.mqtt.common.MQTTConnectionManager;
2527
import io.streamnative.pulsar.handlers.mqtt.common.adapter.MqttAdapterMessage;
28+
import io.streamnative.pulsar.handlers.mqtt.common.mqtt5.restrictions.ClientRestrictions;
2629
import io.streamnative.pulsar.handlers.mqtt.proxy.MQTTProxyConfiguration;
2730
import io.streamnative.pulsar.handlers.mqtt.proxy.MQTTProxyService;
2831
import io.streamnative.pulsar.handlers.mqtt.proxy.channel.AdapterChannel;
@@ -38,7 +41,6 @@
3841
import org.testng.annotations.Test;
3942

4043

41-
4244
public class AdapterChannelTest extends MQTTTestBase {
4345

4446
@Override
@@ -74,7 +76,13 @@ public void testAdapterChannelAutoGetConnection() throws InterruptedException {
7476
MqttConnectMessage fakeConnectMessage = MqttMessageBuilders.connect()
7577
.clientId(clientId).build();
7678
MqttAdapterMessage mqttAdapterMessage = new MqttAdapterMessage(clientId, fakeConnectMessage);
77-
adapterChannel.writeAndFlush(mqttAdapterMessage).join();
79+
Connection connection = Connection.builder()
80+
.channel(channel)
81+
.clientRestrictions(ClientRestrictions.builder().keepAliveTime(10).build())
82+
.connectionManager(Mockito.mock(MQTTConnectionManager.class))
83+
.clientId(clientId)
84+
.connectMessage(fakeConnectMessage).build();
85+
adapterChannel.writeAndFlush(connection, mqttAdapterMessage).join();
7886
CompletableFuture<Channel> channelFutureAfterSend = brokerChannels.get(key);
7987
Channel channelAfterSend = channelFutureAfterSend.join();
8088
assertNotEquals(channelAfterSend.id(), previousChannelId);

0 commit comments

Comments
 (0)