|
@@ -2,6 +2,7 @@ package org.jetlinks.community.network.mqtt.server.vertx;
|
|
|
|
|
|
import io.netty.buffer.ByteBuf;
|
|
import io.netty.buffer.ByteBuf;
|
|
import io.netty.buffer.Unpooled;
|
|
import io.netty.buffer.Unpooled;
|
|
|
|
+import io.netty.handler.codec.DecoderException;
|
|
import io.netty.handler.codec.mqtt.MqttConnectReturnCode;
|
|
import io.netty.handler.codec.mqtt.MqttConnectReturnCode;
|
|
import io.netty.handler.codec.mqtt.MqttQoS;
|
|
import io.netty.handler.codec.mqtt.MqttQoS;
|
|
import io.vertx.core.buffer.Buffer;
|
|
import io.vertx.core.buffer.Buffer;
|
|
@@ -18,17 +19,16 @@ import org.jetlinks.community.network.mqtt.server.MqttConnection;
|
|
import org.jetlinks.community.network.mqtt.server.MqttPublishing;
|
|
import org.jetlinks.community.network.mqtt.server.MqttPublishing;
|
|
import org.jetlinks.community.network.mqtt.server.MqttSubscription;
|
|
import org.jetlinks.community.network.mqtt.server.MqttSubscription;
|
|
import org.jetlinks.community.network.mqtt.server.MqttUnSubscription;
|
|
import org.jetlinks.community.network.mqtt.server.MqttUnSubscription;
|
|
|
|
+import org.jetlinks.core.message.codec.EncodedMessage;
|
|
import org.jetlinks.core.message.codec.MqttMessage;
|
|
import org.jetlinks.core.message.codec.MqttMessage;
|
|
import org.jetlinks.core.message.codec.SimpleMqttMessage;
|
|
import org.jetlinks.core.message.codec.SimpleMqttMessage;
|
|
import org.jetlinks.core.server.mqtt.MqttAuth;
|
|
import org.jetlinks.core.server.mqtt.MqttAuth;
|
|
-import reactor.core.publisher.EmitterProcessor;
|
|
|
|
-import reactor.core.publisher.Flux;
|
|
|
|
-import reactor.core.publisher.FluxSink;
|
|
|
|
-import reactor.core.publisher.Mono;
|
|
|
|
|
|
+import reactor.core.publisher.*;
|
|
|
|
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nonnull;
|
|
import java.net.InetSocketAddress;
|
|
import java.net.InetSocketAddress;
|
|
import java.time.Duration;
|
|
import java.time.Duration;
|
|
|
|
+import java.util.Objects;
|
|
import java.util.Optional;
|
|
import java.util.Optional;
|
|
import java.util.function.Consumer;
|
|
import java.util.function.Consumer;
|
|
import java.util.function.Function;
|
|
import java.util.function.Function;
|
|
@@ -42,14 +42,6 @@ class VertxMqttConnection implements MqttConnection {
|
|
@Getter
|
|
@Getter
|
|
private long lastPingTime = System.currentTimeMillis();
|
|
private long lastPingTime = System.currentTimeMillis();
|
|
private volatile boolean closed = false, accepted = false, autoAckSub = true, autoAckUnSub = true, autoAckMsg = true;
|
|
private volatile boolean closed = false, accepted = false, autoAckSub = true, autoAckUnSub = true, autoAckMsg = true;
|
|
-
|
|
|
|
- private final EmitterProcessor<MqttPublishing> messageProcessor = EmitterProcessor.create(false);
|
|
|
|
-
|
|
|
|
- private final FluxSink<MqttPublishing> publishingFluxSink = messageProcessor.sink(FluxSink.OverflowStrategy.BUFFER);
|
|
|
|
-
|
|
|
|
- private final EmitterProcessor<MqttSubscription> subscription = EmitterProcessor.create(false);
|
|
|
|
- private final EmitterProcessor<MqttUnSubscription> unsubscription = EmitterProcessor.create(false);
|
|
|
|
-
|
|
|
|
private static final MqttAuth emptyAuth = new MqttAuth() {
|
|
private static final MqttAuth emptyAuth = new MqttAuth() {
|
|
@Override
|
|
@Override
|
|
public String getUsername() {
|
|
public String getUsername() {
|
|
@@ -61,6 +53,20 @@ class VertxMqttConnection implements MqttConnection {
|
|
return "";
|
|
return "";
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
+ private final Sinks.Many<MqttPublishing> messageProcessor = Sinks
|
|
|
|
+ .many()
|
|
|
|
+ .multicast()
|
|
|
|
+ .onBackpressureBuffer(Integer.MAX_VALUE);
|
|
|
|
+
|
|
|
|
+ private final Sinks.Many<MqttSubscription> subscription = Sinks
|
|
|
|
+ .many()
|
|
|
|
+ .multicast()
|
|
|
|
+ .onBackpressureBuffer(Integer.MAX_VALUE);
|
|
|
|
+ private final Sinks.Many<MqttUnSubscription> unsubscription = Sinks
|
|
|
|
+ .many()
|
|
|
|
+ .multicast()
|
|
|
|
+ .onBackpressureBuffer(Integer.MAX_VALUE);
|
|
|
|
+
|
|
|
|
|
|
public VertxMqttConnection(MqttEndpoint endpoint) {
|
|
public VertxMqttConnection(MqttEndpoint endpoint) {
|
|
this.endpoint = endpoint;
|
|
this.endpoint = endpoint;
|
|
@@ -68,10 +74,10 @@ class VertxMqttConnection implements MqttConnection {
|
|
}
|
|
}
|
|
|
|
|
|
private final Consumer<MqttConnection> defaultListener = mqttConnection -> {
|
|
private final Consumer<MqttConnection> defaultListener = mqttConnection -> {
|
|
- log.debug("mqtt client [{}] disconnected", getClientId());
|
|
|
|
- subscription.onComplete();
|
|
|
|
- unsubscription.onComplete();
|
|
|
|
- messageProcessor.onComplete();
|
|
|
|
|
|
+ VertxMqttConnection.log.debug("mqtt client [{}] disconnected", getClientId());
|
|
|
|
+ subscription.tryEmitComplete();
|
|
|
|
+ unsubscription.tryEmitComplete();
|
|
|
|
+ messageProcessor.tryEmitComplete();
|
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
@@ -92,20 +98,28 @@ class VertxMqttConnection implements MqttConnection {
|
|
if (closed) {
|
|
if (closed) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- endpoint.reject(code);
|
|
|
|
- complete();
|
|
|
|
|
|
+ try {
|
|
|
|
+ endpoint.reject(code);
|
|
|
|
+ } catch (Throwable ignore) {
|
|
|
|
+ }
|
|
|
|
+ try {
|
|
|
|
+ complete();
|
|
|
|
+ } catch (Throwable ignore) {
|
|
|
|
+
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public Optional<MqttMessage> getWillMessage() {
|
|
public Optional<MqttMessage> getWillMessage() {
|
|
return Optional.ofNullable(endpoint.will())
|
|
return Optional.ofNullable(endpoint.will())
|
|
- .filter(will -> will.getWillMessageBytes() != null)
|
|
|
|
- .map(will -> SimpleMqttMessage.builder()
|
|
|
|
- .will(true)
|
|
|
|
- .payload(Unpooled.wrappedBuffer(will.getWillMessageBytes()))
|
|
|
|
- .topic(will.getWillTopic())
|
|
|
|
- .qosLevel(will.getWillQos())
|
|
|
|
- .build());
|
|
|
|
|
|
+ .filter(will -> will.getWillMessageBytes() != null)
|
|
|
|
+ .map(will -> SimpleMqttMessage
|
|
|
|
+ .builder()
|
|
|
|
+ .will(true)
|
|
|
|
+ .payload(Unpooled.wrappedBuffer(will.getWillMessageBytes()))
|
|
|
|
+ .topic(will.getWillTopic())
|
|
|
|
+ .qosLevel(will.getWillQos())
|
|
|
|
+ .build());
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -141,6 +155,15 @@ class VertxMqttConnection implements MqttConnection {
|
|
this.endpoint
|
|
this.endpoint
|
|
.disconnectHandler(ignore -> this.complete())
|
|
.disconnectHandler(ignore -> this.complete())
|
|
.closeHandler(ignore -> this.complete())
|
|
.closeHandler(ignore -> this.complete())
|
|
|
|
+ .exceptionHandler(error -> {
|
|
|
|
+ if (error instanceof DecoderException) {
|
|
|
|
+ if (error.getMessage().contains("too large message")) {
|
|
|
|
+ log.error("MQTT消息过大,请在网络组件中设置[最大消息长度].", error);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ log.error(error.getMessage(), error);
|
|
|
|
+ })
|
|
.pingHandler(ignore -> {
|
|
.pingHandler(ignore -> {
|
|
this.ping();
|
|
this.ping();
|
|
if (!endpoint.isAutoKeepAlive()) {
|
|
if (!endpoint.isAutoKeepAlive()) {
|
|
@@ -150,12 +173,12 @@ class VertxMqttConnection implements MqttConnection {
|
|
.publishHandler(msg -> {
|
|
.publishHandler(msg -> {
|
|
ping();
|
|
ping();
|
|
VertxMqttPublishing publishing = new VertxMqttPublishing(msg, false);
|
|
VertxMqttPublishing publishing = new VertxMqttPublishing(msg, false);
|
|
- boolean hasDownstream = this.messageProcessor.hasDownstreams();
|
|
|
|
- if (autoAckMsg || !hasDownstream) {
|
|
|
|
|
|
+ boolean hasDownstream = this.messageProcessor.currentSubscriberCount() > 0;
|
|
|
|
+ if (autoAckMsg && hasDownstream) {
|
|
publishing.acknowledge();
|
|
publishing.acknowledge();
|
|
}
|
|
}
|
|
if (hasDownstream) {
|
|
if (hasDownstream) {
|
|
- this.publishingFluxSink.next(publishing);
|
|
|
|
|
|
+ this.messageProcessor.tryEmitNext(publishing);
|
|
}
|
|
}
|
|
})
|
|
})
|
|
//QoS 1 PUBACK
|
|
//QoS 1 PUBACK
|
|
@@ -183,23 +206,23 @@ class VertxMqttConnection implements MqttConnection {
|
|
.subscribeHandler(msg -> {
|
|
.subscribeHandler(msg -> {
|
|
ping();
|
|
ping();
|
|
VertxMqttSubscription subscription = new VertxMqttSubscription(msg, false);
|
|
VertxMqttSubscription subscription = new VertxMqttSubscription(msg, false);
|
|
- boolean hasDownstream = this.subscription.hasDownstreams();
|
|
|
|
|
|
+ boolean hasDownstream = this.subscription.currentSubscriberCount() > 0;
|
|
if (autoAckSub || !hasDownstream) {
|
|
if (autoAckSub || !hasDownstream) {
|
|
subscription.acknowledge();
|
|
subscription.acknowledge();
|
|
}
|
|
}
|
|
if (hasDownstream) {
|
|
if (hasDownstream) {
|
|
- this.subscription.onNext(subscription);
|
|
|
|
|
|
+ this.subscription.tryEmitNext(subscription);
|
|
}
|
|
}
|
|
})
|
|
})
|
|
.unsubscribeHandler(msg -> {
|
|
.unsubscribeHandler(msg -> {
|
|
ping();
|
|
ping();
|
|
VertxMqttMqttUnSubscription unSubscription = new VertxMqttMqttUnSubscription(msg, false);
|
|
VertxMqttMqttUnSubscription unSubscription = new VertxMqttMqttUnSubscription(msg, false);
|
|
- boolean hasDownstream = this.unsubscription.hasDownstreams();
|
|
|
|
|
|
+ boolean hasDownstream = this.unsubscription.currentSubscriberCount() > 0;
|
|
if (autoAckUnSub || !hasDownstream) {
|
|
if (autoAckUnSub || !hasDownstream) {
|
|
unSubscription.acknowledge();
|
|
unSubscription.acknowledge();
|
|
}
|
|
}
|
|
if (hasDownstream) {
|
|
if (hasDownstream) {
|
|
- this.unsubscription.onNext(unSubscription);
|
|
|
|
|
|
+ this.unsubscription.tryEmitNext(unSubscription);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
@@ -220,7 +243,7 @@ class VertxMqttConnection implements MqttConnection {
|
|
clientAddress = new InetSocketAddress(address.host(), address.port());
|
|
clientAddress = new InetSocketAddress(address.host(), address.port());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- }catch (Throwable ignore){
|
|
|
|
|
|
+ } catch (Throwable ignore) {
|
|
|
|
|
|
}
|
|
}
|
|
return clientAddress;
|
|
return clientAddress;
|
|
@@ -233,11 +256,7 @@ class VertxMqttConnection implements MqttConnection {
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public Flux<MqttPublishing> handleMessage() {
|
|
public Flux<MqttPublishing> handleMessage() {
|
|
- if (messageProcessor.isCancelled()) {
|
|
|
|
- return Flux.empty();
|
|
|
|
- }
|
|
|
|
- return messageProcessor
|
|
|
|
- .map(Function.identity());
|
|
|
|
|
|
+ return messageProcessor.asFlux();
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -247,17 +266,17 @@ class VertxMqttConnection implements MqttConnection {
|
|
.<Void>create(sink -> {
|
|
.<Void>create(sink -> {
|
|
Buffer buffer = Buffer.buffer(message.getPayload());
|
|
Buffer buffer = Buffer.buffer(message.getPayload());
|
|
endpoint.publish(message.getTopic(),
|
|
endpoint.publish(message.getTopic(),
|
|
- buffer,
|
|
|
|
- MqttQoS.valueOf(message.getQosLevel()),
|
|
|
|
- message.isDup(),
|
|
|
|
- message.isRetain(),
|
|
|
|
- result -> {
|
|
|
|
- if (result.succeeded()) {
|
|
|
|
- sink.success();
|
|
|
|
- } else {
|
|
|
|
- sink.error(result.cause());
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ buffer,
|
|
|
|
+ MqttQoS.valueOf(message.getQosLevel()),
|
|
|
|
+ message.isDup(),
|
|
|
|
+ message.isRetain(),
|
|
|
|
+ result -> {
|
|
|
|
+ if (result.succeeded()) {
|
|
|
|
+ sink.success();
|
|
|
|
+ } else {
|
|
|
|
+ sink.error(result.cause());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
);
|
|
);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
@@ -266,13 +285,13 @@ class VertxMqttConnection implements MqttConnection {
|
|
public Flux<MqttSubscription> handleSubscribe(boolean autoAck) {
|
|
public Flux<MqttSubscription> handleSubscribe(boolean autoAck) {
|
|
|
|
|
|
autoAckSub = autoAck;
|
|
autoAckSub = autoAck;
|
|
- return subscription.map(Function.identity());
|
|
|
|
|
|
+ return subscription.asFlux();
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public Flux<MqttUnSubscription> handleUnSubscribe(boolean autoAck) {
|
|
public Flux<MqttUnSubscription> handleUnSubscribe(boolean autoAck) {
|
|
autoAckUnSub = autoAck;
|
|
autoAckUnSub = autoAck;
|
|
- return unsubscription.map(Function.identity());
|
|
|
|
|
|
+ return unsubscription.asFlux();
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -282,11 +301,19 @@ class VertxMqttConnection implements MqttConnection {
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public Mono<Void> close() {
|
|
public Mono<Void> close() {
|
|
|
|
+ if (closed) {
|
|
|
|
+ return Mono.empty();
|
|
|
|
+ }
|
|
return Mono.<Void>fromRunnable(() -> {
|
|
return Mono.<Void>fromRunnable(() -> {
|
|
- if (endpoint.isConnected()) {
|
|
|
|
- endpoint.close();
|
|
|
|
|
|
+ try {
|
|
|
|
+ if (endpoint.isConnected()) {
|
|
|
|
+ endpoint.close();
|
|
|
|
+ } else {
|
|
|
|
+ complete();
|
|
|
|
+ }
|
|
|
|
+ } catch (Throwable ignore) {
|
|
}
|
|
}
|
|
- }).doFinally(s -> this.complete());
|
|
|
|
|
|
+ });
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -296,12 +323,15 @@ class VertxMqttConnection implements MqttConnection {
|
|
}
|
|
}
|
|
closed = true;
|
|
closed = true;
|
|
disconnectConsumer.accept(this);
|
|
disconnectConsumer.accept(this);
|
|
- disconnectConsumer = defaultListener;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
@AllArgsConstructor
|
|
@AllArgsConstructor
|
|
- class VertxMqttMessage implements MqttMessage {
|
|
|
|
- MqttPublishMessage message;
|
|
|
|
|
|
+ class VertxMqttPublishing implements MqttPublishing {
|
|
|
|
+
|
|
|
|
+ private final MqttPublishMessage message;
|
|
|
|
+
|
|
|
|
+ private volatile boolean acknowledged;
|
|
|
|
|
|
@Nonnull
|
|
@Nonnull
|
|
@Override
|
|
@Override
|
|
@@ -349,18 +379,10 @@ class VertxMqttConnection implements MqttConnection {
|
|
public String toString() {
|
|
public String toString() {
|
|
return print();
|
|
return print();
|
|
}
|
|
}
|
|
- }
|
|
|
|
-
|
|
|
|
- @AllArgsConstructor
|
|
|
|
- class VertxMqttPublishing implements MqttPublishing {
|
|
|
|
-
|
|
|
|
- private final MqttPublishMessage message;
|
|
|
|
-
|
|
|
|
- private volatile boolean acknowledged;
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public MqttMessage getMessage() {
|
|
public MqttMessage getMessage() {
|
|
- return new VertxMqttMessage(message);
|
|
|
|
|
|
+ return this;
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -397,8 +419,11 @@ class VertxMqttConnection implements MqttConnection {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
acknowledged = true;
|
|
acknowledged = true;
|
|
- endpoint.subscribeAcknowledge(message.messageId(), message.topicSubscriptions().stream()
|
|
|
|
- .map(MqttTopicSubscription::qualityOfService).collect(Collectors.toList()));
|
|
|
|
|
|
+ endpoint.subscribeAcknowledge(message.messageId(), message
|
|
|
|
+ .topicSubscriptions()
|
|
|
|
+ .stream()
|
|
|
|
+ .map(MqttTopicSubscription::qualityOfService)
|
|
|
|
+ .collect(Collectors.toList()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -438,4 +463,17 @@ class VertxMqttConnection implements MqttConnection {
|
|
return endpoint.auth().getPassword();
|
|
return endpoint.auth().getPassword();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean equals(Object o) {
|
|
|
|
+ if (this == o) return true;
|
|
|
|
+ if (o == null || getClass() != o.getClass()) return false;
|
|
|
|
+ VertxMqttConnection that = (VertxMqttConnection) o;
|
|
|
|
+ return Objects.equals(endpoint, that.endpoint);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public int hashCode() {
|
|
|
|
+ return Objects.hash(endpoint);
|
|
|
|
+ }
|
|
}
|
|
}
|