[ThingsBoard] 4. 微服务/队列分析

118 阅读3分钟

一、前言

本文基于 ThingsBoard 4.0.2 编写,对应提交Version set to 4.0.2(01c5ba7d37006e1f8a3492afbb3c67d017ca8dd3)
由于写作经验有限,欢迎读者指出文中的错误与不足。
虽然很想快速的深入到各个功能模块,比如说专注于MQTT基础上的传输实现,规则引擎如何执行。但是ThingsBoard微服务架构的即使以单体应用运行,也是按照微服务的接口去传递消息,所以到最后还是得去看ThignsBoard抽象出的架构。

二、整体看

参考:
ThingsBoard architecture
实际代码

image.png 这是ThingsBoard官方提供的一个架构图,整体核心的逻辑都集中在Transports,Core,Engine三个组件中。而他们三者之间使用的是Queue。将其与代码库进行对应。

image.png

三、消息发送

代码有省略
文件内搜索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

image.png InMemory不难推测出它只是内存里的BlockingQueue,TbKafka就是Kafka的队列。

四、消息接收

ConsumerProduer我们可以推测出最后消息是Consumer接收的。

五、🕊

有些懒得分析了,不过本人更多就是从架构和接口上去分析,也没有需求进行二次开发就渐渐放下了。这一段还有一些关于分区的机制,还有关系具体怎么运行调度来发送到对应的Actor和其他对象也没有分析清楚。