errors
Failed to load YAML frontmatter: Tried to load unspecified class: Time

问题1:为什么配置视图文件总是不生效?

解答:我们系统视图分为默认视图,手动配置视图。如果是默认视图,我们是没有view.json文件的。如果是手动配置视图,那么在我们的工程项目中,建立views目录建立mode.json视图文件。menu.json菜单视图配置项目中,菜单view属性配置视图模型对应的key值。app.json文件中也一同配置访问视图地址。这个时候需要重新打包我们的工程,重新跑起来即可。

问题2:为什么linux配置日志级别不生效?

解答:logback日志文件默认读取顺序为logging.xml、application.properties、logging-spring.xml,检查项目application.properties是否指定了logback配置文件,检查logback配置文件名称是否对得上,将logback配置文件对上,重启即可。

问题3:如何排查linux中jvm加载的是哪个日志配置文件?

解答:使用arthas工具排查,通过打印类的相关命令:

1.启动arthas,监控jvm,命令:./as.sh

image-20230322103222684

2.查看jvm加载的日志组件,命令:logger

image-20230322102008797

3.获取classLoaderHash,命令:sc -d com.sie.iiot.apps.alarm.model.AlarmManager

image-20230322102055902

4.查看类加载的logger信息,命令:ognl -c 10dba097 '@com.sie.iiot.apps.alarm.model.AlarmManager@logger'

image-20230322101945614

5.查看jvm加载的logger配置文件信息,命令:ognl -c 1be6f5c3 '#map1=@org.slf4j.LoggerFactory@getLogger("root").loggerContext.objectMap, #map1.get("CONFIGURATION_WATCH_LIST")'

image-20230322101850358

可以看到加载的是这个配置文件/root/java-iiot/jar/sie-iiot-server-1.0-SNAPSHOT.jar!/BOOT-INF/classes!/logback.xml

问题4: 查询性能优化

如果模型中的字段有 ER 关系。引擎自动会做懒加载,但是在查询的时候,使用懒加载可能会导致性能比较差。

以下模型作为示例

@Model 
public class Staff extends BaseModel<Staff> {

    @ManyToOne(displayName = "部门")
    @JoinColumn
    private Dept dept;

    @OneToMany(displayName = "学习记录")
    private List<LearningRecord> learningRecords;

    @Property(displayName = "附件", dataType = DataType.FILE, multiple = true, length = 10)
    private List<String> file;

    public List<LearningRecord> getLearningRecords() {
        return (List<LearningRecord>) get("learningRecords");
    }
}
@Model
public class LearningRecord extends BaseModel<LearningRecord> {

    @ManyToOne(displayName = "员工")
    @JoinColumn
    private Staff staff;
    
    @Property(displayName = "内容")
    private String content;
}

4.1 不要在循环中使用懒加载

错误做法

for (Staff staff : staffList) {
    List<LearningRecord> learningRecords = staff.getLearningRecords();
}

如果 staffList 有 100 条数据,那么会执行 100 次查询 LearningRecord 的语句。

正确做法应该是一次性查询所有相关的 LearningRecord,然后再进行下一步处理。

Set<String> staffIds = staffList.stream()
.map(BaseModel::getId)
.collect(Collectors.toSet());

List<LearningRecord> learningRecords = new ArrayList<>();
if (CollectionUtil.isNotEmpty(staffIds)) {
    learningRecords = new LearningRecord().search(Filter.in("staff", staffIds), Collections.singletonList("*"), 0, 0, null);
}

for (Staff staff : staffList) {
    List<LearningRecord> records = learningRecords.stream()
    .filter(r -> staff.getId().equals(r.getOrDefault("staff", "")))
    .collect(Collectors.toList());
    // 其他处理逻辑
}

4.2 尽量不查询类型为 File 的字段

dataType = File 的字段跟 @OneToMany 类似。如果在做表格查询、导出等功能,那么不要去查询这些字段。

错误做法

new Staff().search(null, Collections.singletonList("*"), 0, 0, null);

引擎在处理上面的查询时,会发出大量的查询语句去查询 MetaAttachment 模型。

正确的做法

new Staff().search(null, Arrays.asList("id", "dept"), 0, 0, null);

5 常用Arthas命令

1.查看耗时>200ms的方法的参数和返回值
watch com.sie.snest.engine.model.property.SelectionProperty convertToRead '{params[0],params[3],returnObj}' '#cost>200' -n 100  -x 3 


2. 统计耗时
trace com.sie.snest.engine.data.access.BussModelDataAccess search 'params[0].getModel().getName()=="equip_lubrication_calibration"' -n 100 --skipJDKMethod false 

trace com.sie.mbm.edo.calibration.models.Calibration periodUnitList  -n 100 --skipJDKMethod false 

3.查看参数
watch com.sie.mi.ioc.attribution.strategy.models.AttributionStrategy calcAttribution  '{params,returnObj,throwExp}' -n 20  -x 3

watch com.sie.mi.ioc.attribution.strategy.models.AttributionStrategy calcAttribution  '{params,returnObj,throwExp}' 'params[0]=="04dkj8y9h3kny"'  -n 20  -x 3

4.其他示例

watch com.sie.snest.engine.api.response.ResponseHandle beforeBodyWrite  '{params,returnObj,throwExp}' -n 100 --skipJDKMethod false 

 ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.sie.snest.engine.container.EngineContainer@getBussinessAppGroupContainer().getAppDataInfoMap().keySet()'
 
 
 watch com.sie.snest
trace com.sie.snest.engine.api.distributed.RpcInvocation invoke -n 5 --skipJDKMethod false 

trace com.sie.snest.engine.api.distributed.RpcInvocation invoke 'ModelMeta == "mbm_mes_process_match_rule"'

stack com.sie.snest.engine.api.response.ResponseHandle beforeBodyWrite  -n 20 --skipJDKMethod false


trace org.springframework.web.servlet.DispatcherServlet doDispatch  -n 20 --skipJDKMethod false


watch com.sie.mi.ioc.attribution.strategy.models.AttributionStrategy calcAttribution '{params,returnObj.reportResult.candidateResult.dimList[0].dataList[0].reportData,throwExp}' 'params[0]=="04dkj8y9h3kny"' -n 20  -x 3 

watch com.sie.mi.ioc.attribution.strategy.models.AttributionStrategy calcAttribution '{params,returnObj,throwExp}' 'params[0]=="04dkj8y9h3kny"' -n 20  -x 3 

watch com.sie.snest.engine.api.RpcController service  '{params,returnObj,throwExp}' -n 20  -x 3

watch com.sie.snest.engine.api.response.ResponseHandle beforeBodyWrite '{params,returnObj,throwExp}'  -n 100  -x 3

trace com.sie.snest.engine.api.distributed.RpcInvocation invoke 'params[3]=="mbm_mes_process_match_rule"' -n 100 --skipJDKMethod false


trace com.sie.snest.engine.api.RpcController service  'params[1].getParameter("businessIndex")=="04dkj8y9h3kny"' -n 20 --skipJDKMethod false 

watch com.sie.snest.engine.api.RpcController service '{params,returnObj,throwExp}' 'params[1].getParameter("businessIndex")=="04dkj8y9h3kny"' -n 100  -x 3

trace com.sie.snest.engine.api.RpcController service  'params[1].getParameter("businessIndex")=="04dkj8y9h3kny"' -n 20 --skipJDKMethod false 


trace com.sie.snest.engine.api.response.ResponseHandle beforeBodyWrite  -n 20 --skipJDKMethod false