ThingsBoard 二次开发之源码分析 3-启动分析 2
ThingsKit物联网平台
ThingsKit物联网平台可提供跨不同设备和数据源的通用PaaS服务,在整个物联网架构中起到承上启下的中介作用,联动感知层及应用层之间的所有交互——向下连接、管理物联网设备端并完成感知数据的归集与存储,向上为应用开发商与系统集成商提供应用开发的统一数据接口及共性模块工具。在实现“物联”的基础之上,感知层与应用层频繁交互过程中,产生的数据具有体量大、种类多、动态滚动的特征,物联网平台作为产业链中的核心枢纽,更是应用融合以及数据价值孵化的土壤,除提供基础设施服务支撑设备间的数据交换外,通过对平台数据的处理、分析和可视化,将数据赋能过程大幅前置,充分发挥规模效应,实现数据即生产即处理,便于数据快速应用落地,简化物联网解决方案的复杂度并降低方案成本,充当“加速层”,推进各层在应用场景的落地速度与进程。
ThingsKit物联网平台:基于ThingsBoard开发,面向中小型企业开箱即用的低代码物联网平台 :ThingsKit物联网平台
ThingsKit物联网平台演示环境 :https://docs.thingskit.com/thingskit-link/getting-started
欢迎加入ThingsBoard技术交流群
这里可复制Q群号:69998183
关注“云腾五洲”:获取二开ThingsBoard物联网平台演示
TK物联网平台:ThingsKit物联网平台
#ThingsBoard源码分析3-启动分析2
***以下的分析环境基于内存消息队列和默认配置***
## 1. DefaultTransportService
![DefaultTransportService启动](https://pic.downk.cc/item/5f50c674160a154a67352e0a.png)
分析初始化方法:
```java
@PostConstruct
public void init() {
//根据配置判断是否创建限流
if (rateLimitEnabled) {
//Just checking the configuration parameters
new TbRateLimits(perTenantLimitsConf);
new TbRateLimits(perDevicesLimitsConf);
}
this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("transport-scheduler"));
this.transportCallbackExecutor = Executors.newWorkStealingPool(20);
this.schedulerExecutor.scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) sessionReportTimeout), sessionReportTimeout, TimeUnit.MILLISECONDS);
//transportApiRequestTemplate的创建见下分析①,transportApiRequestTemplate中包含了
//一个生产者producerTemplate(requestTemplate) topic:tb_transport.api.responses ②
//和一个消费者consumerTemplate (responseTemplate) topic:tb_transport.api.responses.localHostName ③
transportApiRequestTemplate = queueProvider.createTransportApiRequestTemplate();
//此处的producerProvider bean的创建是按照配置文件的值创建的,TbQueueProducerProvider有三个实现类,使用ConditionalOnExpression注解,读取service.type的值(默认monolith),所以该Bean的实现类是TbCoreQueueProducerProvider,此类的@PostConstruct标记的init()方法初始化的,该类TbCoreQueueProducerProvider初始化了一下变量:
// 1.toTbCore topic:tb_core
// 2.toTransport topic:tb_transport.notifications
// 3.toRuleEngine topic:tb_rule_engine
// 4.toRuleEngineNotifications topic:tb_rule_engine
// 5.toTbCoreNotifications topic:tb_core
ruleEngineMsgProducer = producerProvider.getRuleEngineMsgProducer();
tbCoreMsgProducer = producerProvider.getTbCoreMsgProducer();
transportNotificationsConsumer = queueProvider.createTransportNotificationsConsumer();
//fullTopic = topic:tb_transport.notifications.localHostName
TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_TRANSPORT, serviceInfoProvider.getServiceId());
transportNotificationsConsumer.subscribe(Collections.singleton(tpi));
//见④分析
transportApiRequestTemplate.init();
mainConsumerExecutor.execute(() -> {
while (!stopped) {
try {
List<TbProtoQueueMsg<ToTransportMsg>> records = transportNotificationsConsumer.poll(notificationsPollDuration);
if (records.size() == 0) {
continue;
}
records.forEach(record -> {
try {
processToTransportMsg(record.getValue());
} catch (Throwable e) {
log.warn("Failed to process the notification.", e);
}
});
transportNotificationsConsumer.commit();
} catch (Exception e) {
if (!stopped) {
log.warn("Failed to obtain messages from queue.", e);
try {
Thread.sleep(notificationsPollDuration);
} catch (InterruptedException e2) {
log.trace("Failed to wait until the server has capacity to handle new requests", e2);
}
}
}
}
});
}
① createTransportApiRequestTemplate
In InMemoryTbTransportQueueFactory
,因为我们没有启用相应的消息队列中间件,我们分析InMemoryTbTransportQueueFactory
:
public TbQueueRequestTemplate<TbProtoQueueMsg<TransportApiRequestMsg>, TbProtoQueueMsg<TransportApiResponseMsg>> createTransportApiRequestTemplate() {
//根据配置文件值queue.transport_api.requests_topic获取到的topic是tb_transport.api.requests创建了生产者
InMemoryTbQueueProducer<TbProtoQueueMsg<TransportApiRequestMsg>> producerTemplate =
new InMemoryTbQueueProducer<>(transportApiSettings.getRequestsTopic());
//根据配置文件值queue.transport_api.responses_topic获取到的topic是tb_transport.api.responses
//加上serviceId(我们在第二篇分析中提到,本机的HostName作为serviceId,其topic就是tb_transport.api.responses.localHostName
InMemoryTbQueueConsumer<TbProtoQueueMsg<TransportApiResponseMsg>> consumerTemplate =
new InMemoryTbQueueConsumer<>(transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId());
//使用建造者模式返回了TbQueueRequestTemplate实例,其中包含了一个消费者和一个生产者
DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder
<TbProtoQueueMsg<TransportApiRequestMsg>, TbProtoQueueMsg<TransportApiResponseMsg>> templateBuilder = DefaultTbQueueRequestTemplate.builder();
templateBuilder.queueAdmin(new TbQueueAdmin() {
@Override
public void createTopicIfNotExists(String topic) {}
@Override
public void destroy() {}
});
templateBuilder.requestTemplate(producerTemplate);
templateBuilder.responseTemplate(consumerTemplate);
templateBuilder.maxPendingRequests(transportApiSettings.getMaxPendingRequests());
templateBuilder.maxRequestTimeout(transportApiSettings.getMaxRequestsTimeout());
templateBuilder.pollInterval(transportApiSettings.getResponsePollInterval());
return templateBuilder.build();
}
④init()
in DefaultTbQueueRequestTemplate
:
public void init() {
queueAdmin.createTopicIfNotExists(responseTemplate.getTopic());
//按照是使用的中间件,实现不同的初始化方法,Inmemory该方法体为空
this.requestTemplate.init();
tickTs = System.currentTimeMillis();
//见③,订阅主题为 tb_transport.api.responses.localHostName
responseTemplate.subscribe();
executor.submit(() -> {
long nextCleanupMs = 0L;
while (!stopped) {
try {
//从消息队列里面获取消息
List<Response> responses = responseTemplate.poll(pollInterval);
...........
2.TbCoreTransportApiService
-
PostConstruct
注解方法:
@PostConstruct
public void init() {
this.transportCallbackExecutor = Executors.newWorkStealingPool(maxCallbackThreads);
//topic是配置文件queue.transport_api.responses_topic的值默认为:tb_transport.api.responses ⑤
TbQueueProducer<TbProtoQueueMsg
//topic是配置文件queue.transport_api.requests_topic的值,默认为:tb_transport.api.requests ⑥
TbQueueConsumer<TbProtoQueueMsg
DefaultTbQueueResponseTemplate.DefaultTbQueueResponseTemplateBuilder
<TbProtoQueueMsg<TransportApiRequestMsg>, TbProtoQueueMsg<TransportApiResponseMsg>> builder = DefaultTbQueueResponseTemplate.builder();
builder.requestTemplate(consumer);
builder.responseTemplate(producer);
builder.maxPendingRequests(maxPendingRequests);
builder.requestTimeout(requestTimeout);
builder.pollInterval(responsePollDuration);
builder.executor(transportCallbackExecutor);
builder.handler(transportApiService);
transportApiTemplate = builder.build();
- `@EventListener(ApplicationReadyEvent.class)`注解方法,调用了`transportApiTemplate.init(transportApiService);``transportApiTemplate`即上一步创建的`DefaultTbQueueResponseTemplate`对象`init()`方法为:
```java
@Override
public void init(TbQueueHandler<Request, Response> handler) {
//按照是使用的中间件,实现不同的初始化方法,Inmemory该方法体为空
this.responseTemplate.init();
//见⑥,订阅主题为tb_transport.api.requests
requestTemplate.subscribe();
loopExecutor.submit(() -> {
while (!stopped) {
try {
while (pendingRequestCount.get() >= maxPendingRequests) {
try {
Thread.sleep(pollInterval);
} catch (InterruptedException e) {
log.trace("Failed to wait until the server has capacity to handle new requests", e);
}
}
List<Request> requests = requestTemplate.poll(pollInterval);
...........
总结
DefaultTransportService
和TbCoreTransportApiService
方法的启动并不是很复杂,我们需要将主要的关注点放在两个Bean初始化消费者和生产者的topic上面,thingsboard将使用中间件将消息解耦,如果按照传统的调试方法很容易找不到消息的流向,此时我们将topic作为关键的切入点,方便后面整个数据流的分析。