简介
前面研究了HTTP以及MQTT设备属性消息的处理流程,这次主要研究设备消息告警是如何产生的,告警的配置有两处,一个是设备配置上创建一个是规则链,这里先说一下设备配置创建告警的流程
准备工作
创建设备
创建规则
模拟数据发送
curl -v -X POST http://localhost:8080/api/v1/htonk4pd5tdfdq98rnog/attributes --header Content-Type:application/json --data "{temperature:120}"
流程分析
流程图
TbActorMailbox
这个类是各种Actor用来接收消息的,跟名字一样,mailbox,消息会按优先级放入队列中,然后等待处理
private void enqueue(TbActorMsg msg, boolean highPriority) {
if (!destroyInProgress.get()) {
if (highPriority) {
highPriorityMsgs.add(msg);
} else {
normalPriorityMsgs.add(msg);
}
tryProcessQueue(true);
} else {
if (highPriority && msg.getMsgType().equals(MsgType.RULE_NODE_UPDATED_MSG)) {
synchronized (this) {
if (stopReason == TbActorStopReason.INIT_FAILED) {
destroyInProgress.set(false);
stopReason = null;
initActor();
} else {
msg.onTbActorStopped(stopReason);
}
}
} else {
msg.onTbActorStopped(stopReason);
}
}
}
private void tryProcessQueue(boolean newMsg) {
if (ready.get() == READY) {
if (newMsg || !highPriorityMsgs.isEmpty() || !normalPriorityMsgs.isEmpty()) {
if (busy.compareAndSet(FREE, BUSY)) {
dispatcher.getExecutor().execute(this::processMailbox);
} else {
log.trace("[{}] MessageBox is busy, new msg: {}", selfId, newMsg);
}
} else {
log.trace("[{}] MessageBox is empty, new msg: {}", selfId, newMsg);
}
} else {
log.trace("[{}] MessageBox is not ready, new msg: {}", selfId, newMsg);
}
}
RuleNodeActor
判断消息类型,提取对应的消息类型,因为这里研究告警所以主要看告警流程,消息类型为RULE_TO_SELF_MSG
protected boolean doProcess(TbActorMsg msg) {
switch (msg.getMsgType()) {
case COMPONENT_LIFE_CYCLE_MSG:
case RULE_NODE_UPDATED_MSG:
onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
break;
case RULE_CHAIN_TO_RULE_MSG:
onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg);
break;
case RULE_TO_SELF_MSG:
onRuleNodeToSelfMsg((RuleNodeToSelfMsg) msg);
break;
case STATS_PERSIST_TICK_MSG:
onStatsPersistTick(id);
break;
case PARTITION_CHANGE_MSG:
onClusterEventMsg((PartitionChangeMsg) msg);
break;
default:
return false;
}
return true;
}
RuleNodeActorMessageProcessor
规则消息处理封装类
public void onRuleToSelfMsg(RuleNodeToSelfMsg msg) throws Exception {
checkComponentStateActive(msg.getMsg());
TbMsg tbMsg = msg.getMsg();
int ruleNodeCount = tbMsg.getAndIncrementRuleNodeCounter();
int maxRuleNodeExecutionsPerMessage = getTenantProfileConfiguration().getMaxRuleNodeExecsPerMessage();
if (maxRuleNodeExecutionsPerMessage == 0 || ruleNodeCount < maxRuleNodeExecutionsPerMessage) {
apiUsageClient.report(tenantId, tbMsg.getCustomerId(), ApiUsageRecordKey.RE_EXEC_COUNT);
if (ruleNode.isDebugMode()) {
systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), "Self");
}
try {
tbNode.onMsg(defaultCtx, msg.getMsg());
} catch (Exception e) {
defaultCtx.tellFailure(msg.getMsg(), e);
}
} else {
tbMsg.getCallback().onFailure(new RuleNodeException("Message is processed by more then " + maxRuleNodeExecutionsPerMessage + " rule nodes!", ruleChainName, ruleNode));
}
}
TbDeviceProfileNode
获取到设备配置节点,读取设备规则
public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException {
EntityType originatorType = msg.getOriginator().getEntityType();
if (msg.isTypeOf(TbMsgType.DEVICE_PROFILE_PERIODIC_SELF_MSG)) {
scheduleAlarmHarvesting(ctx, msg);
harvestAlarms(ctx, System.currentTimeMillis());
} else if (msg.isTypeOf(TbMsgType.DEVICE_PROFILE_UPDATE_SELF_MSG)) {
updateProfile(ctx, new DeviceProfileId(UUID.fromString(msg.getData())));
} else if (msg.isTypeOf(TbMsgType.DEVICE_UPDATE_SELF_MSG)) {
JsonNode data = JacksonUtil.toJsonNode(msg.getData());
DeviceId deviceId = new DeviceId(UUID.fromString(data.get("deviceId").asText()));
if (data.has("profileId")) {
invalidateDeviceProfileCache(deviceId, new DeviceProfileId(UUID.fromString(data.get("deviceProfileId").asText())));
} else {
removeDeviceState(deviceId);
}
} else {
if (EntityType.DEVICE.equals(originatorType)) {
DeviceId deviceId = new DeviceId(msg.getOriginator().getId());
if (msg.isTypeOf(TbMsgType.ENTITY_UPDATED)) {
invalidateDeviceProfileCache(deviceId, msg.getData());
ctx.tellSuccess(msg);
} else if (msg.isTypeOf(TbMsgType.ENTITY_DELETED)) {
removeDeviceState(deviceId);
ctx.tellSuccess(msg);
} else {
DeviceState deviceState = getOrCreateDeviceState(ctx, deviceId, null, false);
if (deviceState != null) {
deviceState.process(ctx, msg);
} else {
log.info("Device was not found! Most probably device [" + deviceId + "] has been removed from the database. Acknowledging msg.");
ctx.ack(msg);
}
}
} else {
ctx.tellSuccess(msg);
}
}
}
DeviceState
设备状态处理类,接收到设备告警处理消息,处理并更改设备状态
public void harvestAlarms(TbContext ctx, long ts) throws ExecutionException, InterruptedException {
log.debug("[{}] Going to harvest alarms: {}", ctx.getSelfId(), ts);
boolean stateChanged = false;
for (AlarmState state : alarmStates.values()) {
stateChanged |= state.process(ctx, ts);
}
if (persistState && stateChanged) {
state.setStateData(JacksonUtil.toString(pds));
state = ctx.saveRuleNodeState(state);
}
}
AlarmState
告警处理,获取设备告警规则,形成告警消息,这是最关键的一处业务处理逻辑
public <T> boolean createOrClearAlarms(TbContext ctx, TbMsg msg, T data, SnapshotUpdate update, BiFunction<AlarmRuleState, T, AlarmEvalResult> evalFunction) {
boolean stateUpdate = false;
AlarmRuleState resultState = null;
log.debug("[{}] processing update: {}", alarmDefinition.getId(), data);
for (AlarmRuleState state : createRulesSortedBySeverityDesc) {
if (!validateUpdate(update, state)) {
log.debug("[{}][{}] Update is not valid for current rule state", alarmDefinition.getId(), state.getSeverity());
continue;
}
AlarmEvalResult evalResult = evalFunction.apply(state, data);
stateUpdate |= state.checkUpdate();
if (AlarmEvalResult.TRUE.equals(evalResult)) {
resultState = state;
break;
} else if (AlarmEvalResult.FALSE.equals(evalResult)) {
stateUpdate = clearAlarmState(stateUpdate, state);
}
}
if (resultState != null) {
TbAlarmResult result = calculateAlarmResult(ctx, resultState);
if (result != null) {
pushMsg(ctx, msg, result, resultState);
}
stateUpdate = clearAlarmState(stateUpdate, clearState);
} else if (currentAlarm != null && clearState != null) {
if (!validateUpdate(update, clearState)) {
log.debug("[{}] Update is not valid for current clear state", alarmDefinition.getId());
return stateUpdate;
}
AlarmEvalResult evalResult = evalFunction.apply(clearState, data);
if (AlarmEvalResult.TRUE.equals(evalResult)) {
stateUpdate = clearAlarmState(stateUpdate, clearState);
for (AlarmRuleState state : createRulesSortedBySeverityDesc) {
stateUpdate = clearAlarmState(stateUpdate, state);
}
AlarmApiCallResult result = ctx.getAlarmService().clearAlarm(
ctx.getTenantId(), currentAlarm.getId(), System.currentTimeMillis(), createDetails(clearState)
);
if (result.isCleared()) {
pushMsg(ctx, msg, new TbAlarmResult(false, false, true, result.getAlarm()), clearState);
}
currentAlarm = null;
} else if (AlarmEvalResult.FALSE.equals(evalResult)) {
stateUpdate = clearAlarmState(stateUpdate, clearState);
}
}
return stateUpdate;
}
BaseAlarmService
告警消息处理
public AlarmApiCallResult createAlarm(AlarmCreateOrUpdateActiveRequest request, boolean alarmCreationEnabled) {
validateAlarmRequest(request);
CustomerId customerId = entityService.fetchEntityCustomerId(request.getTenantId(), request.getOriginator()).orElse(null);
if (customerId == null && request.getCustomerId() != null) {
throw new DataValidationException("Can't assign alarm to customer. Originator is not assigned to customer!");
} else if (customerId != null && request.getCustomerId() != null && !customerId.equals(request.getCustomerId())) {
throw new DataValidationException("Can't assign alarm to customer. Originator belongs to different customer!");
}
request.setCustomerId(customerId);
AlarmApiCallResult result = alarmDao.createOrUpdateActiveAlarm(request, alarmCreationEnabled);
if (!result.isSuccessful() && !alarmCreationEnabled) {
throw new ApiUsageLimitsExceededException("Alarms creation is disabled");
}
if (result.getAlarm() != null) {
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(result.getAlarm().getTenantId())
.entityId(result.getAlarm().getId()).added(true).build());
publishEvictEvent(new AlarmTypesCacheEvictEvent(request.getTenantId()));
}
return withPropagated(result);
}
AlarmDao,AlarmRepository
到这一个类,基本上已经结束了,业务的拼接结束后只剩数据库操作了,原本以为就是一个表的读写,后面发现最终是用数据库函数完成的数据写入
@Query(value = "SELECT create_or_update_active_alarm(:t_id, :c_id, :a_id, :a_created_ts, :a_o_id, :a_o_type, :a_type, :a_severity, " +
":a_start_ts, :a_end_ts, :a_details, :a_propagate, :a_propagate_to_owner, " +
":a_propagate_to_tenant, :a_propagation_types, :a_creation_enabled)", nativeQuery = true)
String createOrUpdateActiveAlarm(@Param("t_id") UUID tenantId, @Param("c_id") UUID customerId,
@Param("a_id") UUID alarmId, @Param("a_created_ts") long createdTime,
@Param("a_o_id") UUID originatorId, @Param("a_o_type") int originatorType,
@Param("a_type") String type, @Param("a_severity") String severity,
@Param("a_start_ts") long startTs, @Param("a_end_ts") long endTs, @Param("a_details") String detailsAsString,
@Param("a_propagate") boolean propagate, @Param("a_propagate_to_owner") boolean propagateToOwner,
@Param("a_propagate_to_tenant") boolean propagateToTenant, @Param("a_propagation_types") String propagationTypes,
@Param("a_creation_enabled") boolean alarmCreationEnabled);
函数
总结
告警处理逻辑还是比较复杂的,最后的数据写入还是用函数做的,如果不细看,很多细节会遗漏,跟了这次源码,同时也发现了其他数据库函数