一、前言
本文基于 ThingsBoard 4.0.2 编写,对应提交Version set to 4.0.2(01c5ba7d37006e1f8a3492afbb3c67d017ca8dd3)。
由于写作经验有限,欢迎读者指出文中的错误与不足。
虽然很想快速的深入到各个功能模块,比如说专注于MQTT基础上的传输实现,规则引擎如何执行。但是ThingsBoard微服务架构的即使以单体应用运行,也是按照微服务的接口去传递消息,所以到最后还是得去看ThignsBoard抽象出的架构。
二、整体看
这是ThingsBoard官方提供的一个架构图,整体核心的逻辑都集中在
Transports,Core,Engine三个组件中。而他们三者之间使用的是Queue。将其与代码库进行对应。
三、消息发送
代码有省略
文件内搜索Ctrl+F,IDEA直接使用Ctrl+Shift+N可以进行文件名搜索,VSC使用Ctrl+P。
Transport->Core|RuleEngine
接口TransportService类,其定义了消息传递的方法。
void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback);
vo#id process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback);
void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TbMsgMetaData md, TransportServiceCallback<Void> callback);
void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback<Void> callback);
void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TbMsgMetaData md, TransportServiceCallback<Void> callback);
void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback);
void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback);
void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback);
而其传递的对象,发现源代码中找不到,因为他是使用了Protocol Buffers,因此我们需要去找结构定义文件,而其就在queue.proto中。ProtoclBuf会自动生成对应的类。如果进行的正确的配置,你应该就会看到生成2MB的一个JAVA源代码文件,IDEA会直接继续分析它。以至于你其他使用到ProtocolBuf对象的类中全部报错。但是编译却能通过。
其中消息的数据,回调,就暂且略过,只在具体业务逻辑的时候再看即可。
Core<->RuleEngine, Core|RuleEngine ->Transport
TbClusterService接口。
void pushMsgToCore(ToDeviceActorNotificationMsg msg, TbQueueCallback callback);
void pushMsgToRuleEngine(TopicPartitionInfo tpi, UUID msgId, ToRuleEngineMsg msg, TbQueueCallback callback);
void pushNotificationToTransport(String targetServiceId, ToTransportMsg response, TbQueueCallback callback);
2. 传递消息的实际实现
寻找源头
接下来关注实现TransportService接口的DefaultTransportService,以其中的process方法为例。
@Override
public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostTelemetryMsg msg, TbMsgMetaData md, TransportServiceCallback<Void> callback) {
// 大量省略
sendToRuleEngine(tenantId, deviceId, customerId,
}
private void sendToRuleEngine(TenantId tenantId, DeviceId deviceId, CustomerId customerId, TransportProtos.SessionInfoProto sessionInfo, JsonObject json,
TbMsgMetaData metaData, TbMsgType tbMsgType, TbQueueCallback callback) {
// 大量省略
ruleEngineProducerService.sendToRuleEngine(ruleEngineMsgProducer, tenantId, tbMsg, new StatsCallback(callback, ruleEngineProducerStats));
}
经过这段代码,我们能注意它委托给了ruleEngineProducerService,同时传递了一个ruleEngineMsgProducer
public void sendToRuleEngine(TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> producer,
TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) {
sendToRuleEngine(producer, tpis.get(0), tenantId, tbMsg, callback);
}
private void sendToRuleEngine(TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> producer, TopicPartitionInfo tpi,
TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) {
producer.send(tpi, new TbProtoQueueMsg<>(tbMsg.getId(), msg), callback);
}
@PostConstruct
public void init() {
ruleEngineMsgProducer = producerProvider.getRuleEngineMsgProducer();
tbCoreMsgProducer = producerProvider.getTbCoreMsgProducer();
}
private final TbQueueProducerProvider producerProvider;
protected TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> ruleEngineMsgProducer;
protected TbQueueProducer<TbProtoQueueMsg<ToCoreMsg>> tbCoreMsgProducer;
根据上述代码,这实际的实现又委托给了TbQuqueProducer,从命名上就能看出队列使用的风格,Producer提供数据,Consumer消耗数据。而其中能看到由producerProvider提供。再向下分析就越来越复杂了。Producer就这一被构造,就可以向队列中发送数据了。
看看TbQueueProducer
InMemory不难推测出它只是内存里的
BlockingQueue,TbKafka就是Kafka的队列。
四、消息接收
从Consumer和Produer我们可以推测出最后消息是Consumer接收的。
五、🕊
有些懒得分析了,不过本人更多就是从架构和接口上去分析,也没有需求进行二次开发就渐渐放下了。这一段还有一些关于分区的机制,还有关系具体怎么运行调度来发送到对应的Actor和其他对象也没有分析清楚。