31
31
import com .jun .mqttx .utils .JsonSerializer ;
32
32
import com .jun .mqttx .utils .RateLimiter ;
33
33
import com .jun .mqttx .utils .Serializer ;
34
- import com .jun .mqttx .utils .TopicUtils ;
35
34
import io .netty .buffer .Unpooled ;
36
35
import io .netty .channel .ChannelHandlerContext ;
37
36
import io .netty .handler .codec .mqtt .*;
42
41
import org .springframework .util .ObjectUtils ;
43
42
import org .springframework .util .StringUtils ;
44
43
import reactor .core .publisher .Flux ;
44
+ import reactor .core .publisher .GroupedFlux ;
45
45
import reactor .core .publisher .Mono ;
46
+ import reactor .core .scheduler .Schedulers ;
46
47
47
48
import java .time .Instant ;
48
49
import java .util .*;
@@ -69,7 +70,7 @@ public class PublishHandler extends AbstractMqttTopicSecureHandler implements Wa
69
70
private final IPublishMessageService publishMessageService ;
70
71
private final IPubRelMessageService pubRelMessageService ;
71
72
private final String brokerId ;
72
- private final boolean enableTopicSubPubSecure , enableShareTopic , enableRateLimiter , ignoreClientSelfPub ;
73
+ private final boolean enableTopicSubPubSecure , enableRateLimiter , ignoreClientSelfPub ;
73
74
/** 共享主题轮询策略 */
74
75
private final ShareStrategy shareStrategy ;
75
76
/** 消息桥接开关 */
@@ -86,20 +87,25 @@ public class PublishHandler extends AbstractMqttTopicSecureHandler implements Wa
86
87
87
88
//@formatter:on
88
89
89
- public PublishHandler (IPublishMessageService publishMessageService , IRetainMessageService retainMessageService ,
90
- ISubscriptionService subscriptionService , IPubRelMessageService pubRelMessageService , ISessionService sessionService ,
91
- @ Nullable IInternalMessagePublishService internalMessagePublishService , MqttxConfig config ,
92
- @ Nullable KafkaTemplate <String , byte []> kafkaTemplate , Serializer serializer ) {
90
+ public PublishHandler (IPublishMessageService publishMessageService ,
91
+ IRetainMessageService retainMessageService ,
92
+ ISubscriptionService subscriptionService ,
93
+ IPubRelMessageService pubRelMessageService ,
94
+ ISessionService sessionService ,
95
+ @ Nullable IInternalMessagePublishService internalMessagePublishService ,
96
+ MqttxConfig config ,
97
+ @ Nullable KafkaTemplate <String , byte []> kafkaTemplate ,
98
+ Serializer serializer ) {
93
99
super (config .getCluster ().getEnable ());
94
100
Assert .notNull (publishMessageService , "publishMessageService can't be null" );
95
101
Assert .notNull (retainMessageService , "retainMessageService can't be null" );
96
102
Assert .notNull (subscriptionService , "publishMessageService can't be null" );
97
103
Assert .notNull (pubRelMessageService , "publishMessageService can't be null" );
98
104
Assert .notNull (config , "mqttxConfig can't be null" );
99
105
100
- MqttxConfig . ShareTopic shareTopic = config .getShareTopic ();
101
- MqttxConfig . MessageBridge messageBridge = config .getMessageBridge ();
102
- MqttxConfig . RateLimiter rateLimiter = config .getRateLimiter ();
106
+ var shareTopic = config .getShareTopic ();
107
+ var messageBridge = config .getMessageBridge ();
108
+ var rateLimiter = config .getRateLimiter ();
103
109
this .sessionService = sessionService ;
104
110
this .serializer = serializer ;
105
111
this .publishMessageService = publishMessageService ;
@@ -109,7 +115,6 @@ public PublishHandler(IPublishMessageService publishMessageService, IRetainMessa
109
115
this .brokerId = config .getBrokerId ();
110
116
this .enableTopicSubPubSecure = config .getEnableTopicSubPubSecure ();
111
117
this .ignoreClientSelfPub = config .getIgnoreClientSelfPub ();
112
- this .enableShareTopic = shareTopic .getEnable ();
113
118
if (!CollectionUtils .isEmpty (rateLimiter .getTopicRateLimits ()) && rateLimiter .getEnable ()) {
114
119
enableRateLimiter = true ;
115
120
rateLimiter .getTopicRateLimits ()
@@ -196,13 +201,16 @@ public void process(ChannelHandlerContext ctx, MqttMessage msg) {
196
201
197
202
// 响应
198
203
switch (qos ) {
199
- case AT_MOST_ONCE -> publish (pubMsg , ctx , false ).doOnSuccess (unused -> {
200
- if (retain ) {
201
- handleRetainMsg (pubMsg ).subscribe ();
202
- }
203
- }).subscribe ();
204
+ case AT_MOST_ONCE -> publish (pubMsg , ctx , false )
205
+ .publishOn (Schedulers .boundedElastic ())
206
+ .doOnSuccess (unused -> {
207
+ if (retain ) {
208
+ handleRetainMsg (pubMsg ).subscribe ();
209
+ }
210
+ }).subscribe ();
204
211
case AT_LEAST_ONCE -> {
205
212
publish (pubMsg , ctx , false )
213
+ .publishOn (Schedulers .boundedElastic ())
206
214
.doOnSuccess (unused -> {
207
215
MqttMessage pubAck = MqttMessageFactory .newMessage (
208
216
new MqttFixedHeader (MqttMessageType .PUBACK , false , MqttQoS .AT_MOST_ONCE , false , 0 ),
@@ -223,6 +231,7 @@ public void process(ChannelHandlerContext ctx, MqttMessage msg) {
223
231
Session session = getSession (ctx );
224
232
if (!session .isDupMsg (packetId )) {
225
233
publish (pubMsg , ctx , false )
234
+ .publishOn (Schedulers .boundedElastic ())
226
235
.doOnSuccess (unused -> {
227
236
// 保存 pub
228
237
session .savePubRelInMsg (packetId );
@@ -261,9 +270,11 @@ public void process(ChannelHandlerContext ctx, MqttMessage msg) {
261
270
return Mono .empty ();
262
271
} else {
263
272
return publish (pubMsg , ctx , false )
273
+ .publishOn (Schedulers .boundedElastic ())
264
274
.doOnSuccess (unused -> pubRelMessageService .saveIn (clientId (ctx ), packetId ).subscribe ());
265
275
}
266
276
})
277
+ .publishOn (Schedulers .boundedElastic ())
267
278
.doOnSuccess (unused -> {
268
279
var pubRec = MqttMessageFactory .newMessage (
269
280
new MqttFixedHeader (MqttMessageType .PUBREC , false , MqttQoS .AT_MOST_ONCE , false , 0 ),
@@ -313,7 +324,7 @@ public Mono<Void> publish(final PubMsg pubMsg, ChannelHandlerContext ctx, boolea
313
324
}
314
325
315
326
// 获取 topic 订阅者 id 列表
316
- String topic = pubMsg .getTopic ();
327
+ final var topic = pubMsg .getTopic ();
317
328
Flux <ClientSub > clientSubFlux = subscriptionService .searchSubscribeClientList (topic )
318
329
.filter (clientSub -> {
319
330
if (ignoreClientSelfPub ) {
@@ -325,27 +336,28 @@ public Mono<Void> publish(final PubMsg pubMsg, ChannelHandlerContext ctx, boolea
325
336
});
326
337
327
338
// 共享订阅
328
- if (enableShareTopic && TopicUtils .isShare (topic )) {
329
- return clientSubFlux .collectList ()
330
- .map (e -> chooseClient (e , topic ))
331
- .flatMap (clientSub -> {
332
- pubMsg .setAppointedClientId (clientSub .getClientId ());
333
- return publish0 (clientSub , pubMsg , isClusterMessage ).doOnSuccess (unused -> {
334
- // 满足如下条件,则发送消息给集群
335
- // 1 集群模式开启
336
- // 2 订阅的客户端连接在其它实例上
337
- if (isClusterMode () && !ConnectHandler .CLIENT_MAP .containsKey (clientSub .getClientId ())) {
338
- internalMessagePublish (pubMsg );
339
- }
340
- });
341
- })
342
- .then ();
343
- }
339
+ var f1 = clientSubFlux .filter (ClientSub ::isShareSub )
340
+ .groupBy (ClientSub ::getShareName )
341
+ .flatMap (GroupedFlux ::collectList )
342
+ .map (t -> chooseClient (t , topic ))
343
+ .flatMap (clientSub -> {
344
+ pubMsg .setAppointedClientId (clientSub .getClientId ());
345
+ return publish0 (clientSub , pubMsg , isClusterMessage ).doOnSuccess (unused -> {
346
+ // 满足如下条件,则发送消息给集群
347
+ // 1 集群模式开启
348
+ // 2 订阅的客户端连接在其它实例上
349
+ if (isClusterMode () && !ConnectHandler .CLIENT_MAP .containsKey (clientSub .getClientId ())) {
350
+ internalMessagePublish (pubMsg );
351
+ }
352
+ });
353
+ });
344
354
345
- return clientSubFlux
355
+ // 普通订阅
356
+ var f2 = clientSubFlux
357
+ .filter (ClientSub ::notShareSub )
346
358
.collectList ()
347
359
.doOnSuccess (lst -> {
348
- // 将消息推送给集群中的broker
360
+ // 将消息推送给集群中的 broker
349
361
if (isClusterMode () && !isClusterMessage ) {
350
362
// 判断是否需要进行集群消息分发
351
363
boolean flag = false ;
@@ -361,8 +373,9 @@ public Mono<Void> publish(final PubMsg pubMsg, ChannelHandlerContext ctx, boolea
361
373
}
362
374
})
363
375
.flatMapIterable (Function .identity ())
364
- .flatMap (clientSub -> publish0 (clientSub , pubMsg , isClusterMessage ))
365
- .then ();
376
+ .flatMap (clientSub -> publish0 (clientSub , pubMsg , isClusterMessage ));
377
+
378
+ return Mono .when (f1 , f2 );
366
379
}
367
380
368
381
/**
@@ -544,7 +557,6 @@ public boolean support(String channel) {
544
557
* 共享订阅选择客户端, 支持的策略如下:
545
558
* <ol>
546
559
* <li>随机: {@link ShareStrategy#random}</li>
547
- * <li>哈希: {@link ShareStrategy#hash}</li>
548
560
* <li>轮询: {@link ShareStrategy#round}</li>
549
561
* </ol>
550
562
*
@@ -554,15 +566,14 @@ public boolean support(String channel) {
554
566
private ClientSub chooseClient (List <ClientSub > clientSubList , String topic ) {
555
567
// 集合排序
556
568
clientSubList .sort (ClientSub ::compareTo );
569
+ final var size = clientSubList .size ();
557
570
558
- if (hash == shareStrategy ) {
559
- return clientSubList .get (topic .hashCode () % clientSubList .size ());
560
- } else if (random == shareStrategy ) {
561
- int key = ThreadLocalRandom .current ().nextInt (0 , clientSubList .size ());
562
- return clientSubList .get (key % clientSubList .size ());
571
+ if (random == shareStrategy ) {
572
+ int key = ThreadLocalRandom .current ().nextInt (0 , size );
573
+ return clientSubList .get (key % size );
563
574
} else if (round == shareStrategy ) {
564
575
int i = roundMap .computeIfAbsent (topic , s -> new AtomicInteger (0 )).getAndIncrement ();
565
- return clientSubList .get (i % clientSubList . size () );
576
+ return clientSubList .get (i % size );
566
577
}
567
578
568
579
throw new IllegalArgumentException ("不可能到达的代码, strategy:" + shareStrategy );
0 commit comments