org.jetlinks.supports.event.InternalEventBus Maven / Gradle / Ivy
The newest version!
package org.jetlinks.supports.event;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.internal.ThreadLocalRandom;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hswebframework.web.dict.EnumDict;
import org.jetlinks.core.Payload;
import org.jetlinks.core.codec.Decoder;
import org.jetlinks.core.codec.Encoder;
import org.jetlinks.core.event.EventBus;
import org.jetlinks.core.event.Subscription;
import org.jetlinks.core.event.TopicPayload;
import org.jetlinks.core.topic.Topic;
import org.jetlinks.core.trace.TraceHolder;
import org.jetlinks.core.utils.Reactors;
import org.jetlinks.core.utils.SerializeUtils;
import org.reactivestreams.Publisher;
import reactor.core.Disposable;
import reactor.core.Disposables;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import reactor.function.Function4;
import reactor.util.context.ContextView;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
@Slf4j
public class InternalEventBus implements EventBus {
protected final Topic subscriptionTable = Topic.createRoot();
@Override
public Flux subscribe(Subscription subscription) {
return Flux
.create(sink -> sink
.onDispose(
subscribe(subscription, e -> {
sink.next(e);
return Mono.empty();
})
));
}
@Override
public Flux subscribe(Subscription subscription, Decoder decoder) {
return this
.subscribe(subscription)
.mapNotNull(payload -> {
try {
//收到消息后解码
return payload.decode(decoder, false);
} catch (Throwable e) {
//忽略解码错误,如果返回错误,可能会导致整个流中断
log.error("decode message [{}] error", payload.getTopic(), e);
} finally {
ReferenceCountUtil.safeRelease(payload);
}
return null;
});
}
@Override
public Flux subscribe(Subscription subscription, Class type) {
return this
.subscribe(subscription)
.mapNotNull(payload -> {
try {
return payload.decode(type);
} catch (Throwable e) {
log.error("decode message [{}] error", payload.getTopic(), e);
}
return null;
});
}
public Disposable subscribe(Subscription subscription,
Function> handler) {
Disposable.Composite disposable = Disposables.composite();
Function> lazyHandler = new LocalHandler(handler);
for (String topic : subscription.getTopics()) {
SubscriptionInfo subscriptionInfo = SubscriptionInfo
.of(subscription,
topic,
lazyHandler,
false);
log.debug("subscribe: {}", subscriptionInfo);
Topic tTopic = subscriptionTable.append(topic);
tTopic.subscribe(subscriptionInfo);
disposable.add(() -> {
log.debug("unsubscribe: {}", subscriptionInfo);
tTopic.unsubscribe(subscriptionInfo);
subscriptionInfo.dispose();
});
//订阅集群
if (subscriptionInfo.hasFeature(Subscription.Feature.broker)) {
disposable.add(subscribeToCluster(subscriptionInfo));
}
}
return disposable;
}
protected Disposable subscribeToCluster(SubscriptionInfo info) {
return Disposables.disposed();
}
@Override
public Mono publish(String topic, Publisher event) {
return doPublish(
topic,
event,
(t, e, p, f) -> Flux
.from(e)
.flatMap(val -> publishFromLocal(t, val, p, f))
.then(),
sub -> sub.isCluster() || sub.hasFeature(Subscription.Feature.local));
}
@Override
public Mono publish(String topic, T event, Scheduler scheduler) {
return this
.publish(topic, event)
.subscribeOn(scheduler);
}
@Override
public Mono publish(String topic, Encoder encoder, T event) {
return publish(topic, event);
}
@Override
public Mono publish(String topic, T event) {
return doPublish(topic,
event,
this::publishFromLocal,
sub -> sub.isCluster() || sub.hasFeature(Subscription.Feature.local));
}
protected Mono doPublish0(String topic, TopicPayload payload, List subs, ContextView ctx) {
int subSize = subs.size();
//只有一个订阅者时,不需要排序,减少内存占用
TreeMap>> priority = subSize == 1 ? null : new TreeMap<>();
Mono task = null;
Function> handler;
for (SubscriptionInfo sub : subs) {
log.trace("publish {} to {}", topic, sub);
handler = sub.handler;
task = handler.apply(payload);
if (subSize > 1) {
priority
.computeIfAbsent(sub.priority, ignore -> new ArrayList<>(subSize))
.add(task);
}
}
if (task == null) {
return Mono.empty();
}
if (subSize == 1) {
return task;
}
if (priority.size() == 1) {
return Flux
.merge(priority.get(subs.get(0).priority))
.then();
}
//不同优先级之间串行,同一个优先级并行
return Flux
.fromIterable(priority.values())
.concatMap(Flux::merge)
.then();
}
private Mono publishFromLocal(String topic, T value, List subs, ContextView ctx) {
TopicPayload payload = TopicPayload.of(topic, Payload.of(value, null));
TraceHolder.writeContextTo(ctx, payload, TopicPayload::addHeader);
return doPublish0(topic, payload, subs, ctx);
}
@Override
public Mono publish(String topic,
Encoder encoder,
Publisher extends T> eventStream) {
return publish(topic, eventStream);
}
@Override
public Mono publish(String topic,
Encoder encoder,
Publisher extends T> eventStream,
Scheduler scheduler) {
return publish(topic, eventStream);
}
private static final FastThreadLocal> PUB_HANDLERS = new FastThreadLocal>() {
@Override
protected Set initialValue() {
return new HashSet<>();
}
};
private static final FastThreadLocal> DISTINCT_HANDLERS = new FastThreadLocal>() {
@Override
protected Set