abf1dcd36fd0117a364468feace96af27fe53a8c
01.\345\274\200\345\217\221\346\211\213\345\206\214/06.\345\270\270\350\247\201\351\227\256\351\242\230QA/01.\345\270\270\350\247\201\351\227\256\351\242\230QA.md
| ... | ... | @@ -1,133 +1,192 @@ |
| 1 | ----
|
|
| 2 | -title: 常见问题QA
|
|
| 3 | -date: 2023-09-25 17:31:35
|
|
| 4 | -permalink: /pages/c7b051/
|
|
| 5 | ----
|
|
| 6 | -# 常见问题QA
|
|
| 7 | -
|
|
| 8 | -## 问题1:为什么配置视图文件总是不生效?
|
|
| 9 | -
|
|
| 10 | -解答:我们系统视图分为默认视图,手动配置视图。如果是默认视图,我们是没有view.json文件的。如果是手动配置视图,那么在我们的工程项目中,建立views目录建立mode.json视图文件。menu.json菜单视图配置项目中,菜单view属性配置视图模型对应的key值。app.json文件中也一同配置访问视图地址。这个时候需要重新打包我们的工程,重新跑起来即可。
|
|
| 11 | -
|
|
| 12 | -
|
|
| 13 | -
|
|
| 14 | -## 问题2:为什么linux配置日志级别不生效?
|
|
| 15 | -
|
|
| 16 | -解答:logback日志文件默认读取顺序为logging.xml、application.properties、logging-spring.xml,检查项目application.properties是否指定了logback配置文件,检查logback配置文件名称是否对得上,将logback配置文件对上,重启即可。
|
|
| 17 | -
|
|
| 18 | -
|
|
| 19 | -
|
|
| 20 | -## 问题3:如何排查linux中jvm加载的是哪个日志配置文件?
|
|
| 21 | -
|
|
| 22 | -解答:使用arthas工具排查,通过打印类的相关命令:
|
|
| 23 | -
|
|
| 24 | -1.启动arthas,监控jvm,命令:./as.sh
|
|
| 25 | -
|
|
| 26 | -
|
|
| 27 | -
|
|
| 28 | -2.查看jvm加载的日志组件,命令:logger
|
|
| 29 | -
|
|
| 30 | -
|
|
| 31 | -
|
|
| 32 | -3.获取classLoaderHash,命令:sc -d com.sie.iiot.apps.alarm.model.AlarmManager
|
|
| 33 | -
|
|
| 34 | -
|
|
| 35 | -
|
|
| 36 | -4.查看类加载的logger信息,命令:ognl -c 10dba097 '@com.sie.iiot.apps.alarm.model.AlarmManager@logger'
|
|
| 37 | -
|
|
| 38 | -
|
|
| 39 | -
|
|
| 40 | -5.查看jvm加载的logger配置文件信息,命令:ognl -c 1be6f5c3 '#map1=@org.slf4j.LoggerFactory@getLogger("root").loggerContext.objectMap, #map1.get("CONFIGURATION_WATCH_LIST")'
|
|
| 41 | -
|
|
| 42 | -
|
|
| 43 | -
|
|
| 44 | -可以看到加载的是这个配置文件/root/java-iiot/jar/sie-iiot-server-1.0-SNAPSHOT.jar!/BOOT-INF/classes!/logback.xml
|
|
| 45 | -
|
|
| 46 | -## 问题4: 查询性能优化
|
|
| 47 | -
|
|
| 48 | -如果模型中的字段有 ER 关系。引擎自动会做懒加载,但是在查询的时候,使用懒加载可能会导致性能比较差。
|
|
| 49 | -
|
|
| 50 | -以下模型作为示例
|
|
| 51 | -
|
|
| 52 | -```java
|
|
| 53 | -@Model
|
|
| 54 | -public class Staff extends BaseModel<Staff> {
|
|
| 55 | -
|
|
| 56 | - @ManyToOne(displayName = "部门")
|
|
| 57 | - @JoinColumn
|
|
| 58 | - private Dept dept;
|
|
| 59 | -
|
|
| 60 | - @OneToMany(displayName = "学习记录")
|
|
| 61 | - private List<LearningRecord> learningRecords;
|
|
| 62 | -
|
|
| 63 | - @Property(displayName = "附件", dataType = DataType.FILE, multiple = true, length = 10)
|
|
| 64 | - private List<String> file;
|
|
| 65 | -
|
|
| 66 | - public List<LearningRecord> getLearningRecords() {
|
|
| 67 | - return (List<LearningRecord>) get("learningRecords");
|
|
| 68 | - }
|
|
| 69 | -}
|
|
| 70 | -```
|
|
| 71 | -
|
|
| 72 | -```java
|
|
| 73 | -@Model
|
|
| 74 | -public class LearningRecord extends BaseModel<LearningRecord> {
|
|
| 75 | -
|
|
| 76 | - @ManyToOne(displayName = "员工")
|
|
| 77 | - @JoinColumn
|
|
| 78 | - private Staff staff;
|
|
| 79 | -
|
|
| 80 | - @Property(displayName = "内容")
|
|
| 81 | - private String content;
|
|
| 82 | -}
|
|
| 83 | -```
|
|
| 84 | -
|
|
| 85 | -### 4.1 不要在循环中使用懒加载
|
|
| 86 | -
|
|
| 87 | -错误做法
|
|
| 88 | -
|
|
| 89 | -```java
|
|
| 90 | -for (Staff staff : staffList) {
|
|
| 91 | - List<LearningRecord> learningRecords = staff.getLearningRecords();
|
|
| 92 | -}
|
|
| 93 | -```
|
|
| 94 | -
|
|
| 95 | -如果 staffList 有 100 条数据,那么会执行 100 次查询 LearningRecord 的语句。
|
|
| 96 | -
|
|
| 97 | -正确做法应该是一次性查询所有相关的 LearningRecord,然后再进行下一步处理。
|
|
| 98 | -
|
|
| 99 | -```java
|
|
| 100 | -Set<String> staffIds = staffList.stream()
|
|
| 101 | -.map(BaseModel::getId)
|
|
| 102 | -.collect(Collectors.toSet());
|
|
| 103 | -
|
|
| 104 | -List<LearningRecord> learningRecords = new ArrayList<>();
|
|
| 105 | -if (CollectionUtil.isNotEmpty(staffIds)) {
|
|
| 106 | - learningRecords = new LearningRecord().search(Filter.in("staff", staffIds), Collections.singletonList("*"), 0, 0, null);
|
|
| 107 | -}
|
|
| 108 | -
|
|
| 109 | -for (Staff staff : staffList) {
|
|
| 110 | - List<LearningRecord> records = learningRecords.stream()
|
|
| 111 | - .filter(r -> staff.getId().equals(r.getOrDefault("staff", "")))
|
|
| 112 | - .collect(Collectors.toList());
|
|
| 113 | - // 其他处理逻辑
|
|
| 114 | -}
|
|
| 115 | -```
|
|
| 116 | -
|
|
| 117 | -### 4.2 尽量不查询类型为 File 的字段
|
|
| 118 | -
|
|
| 119 | -dataType = File 的字段跟 @OneToMany 类似。如果在做表格查询、导出等功能,那么不要去查询这些字段。
|
|
| 120 | -
|
|
| 121 | -错误做法
|
|
| 122 | -
|
|
| 123 | -```java
|
|
| 124 | -new Staff().search(null, Collections.singletonList("*"), 0, 0, null);
|
|
| 125 | -```
|
|
| 126 | -
|
|
| 127 | -引擎在处理上面的查询时,会发出大量的查询语句去查询 MetaAttachment 模型。
|
|
| 128 | -
|
|
| 129 | -正确的做法
|
|
| 130 | -
|
|
| 131 | -```java
|
|
| 132 | -new Staff().search(null, Arrays.asList("id", "dept"), 0, 0, null);
|
|
| 133 | -```
|
|
| 1 | +--- |
|
| 2 | +title: 常见问题QA |
|
| 3 | +date: 2023-09-25 17:31:35 |
|
| 4 | +permalink: /pages/c7b051/ |
|
| 5 | +--- |
|
| 6 | +# 常见问题QA |
|
| 7 | + |
|
| 8 | +## 问题1:为什么配置视图文件总是不生效? |
|
| 9 | + |
|
| 10 | +解答:我们系统视图分为默认视图,手动配置视图。如果是默认视图,我们是没有view.json文件的。如果是手动配置视图,那么在我们的工程项目中,建立views目录建立mode.json视图文件。menu.json菜单视图配置项目中,菜单view属性配置视图模型对应的key值。app.json文件中也一同配置访问视图地址。这个时候需要重新打包我们的工程,重新跑起来即可。 |
|
| 11 | + |
|
| 12 | + |
|
| 13 | + |
|
| 14 | +## 问题2:为什么linux配置日志级别不生效? |
|
| 15 | + |
|
| 16 | +解答:logback日志文件默认读取顺序为logging.xml、application.properties、logging-spring.xml,检查项目application.properties是否指定了logback配置文件,检查logback配置文件名称是否对得上,将logback配置文件对上,重启即可。 |
|
| 17 | + |
|
| 18 | + |
|
| 19 | + |
|
| 20 | +## 问题3:如何排查linux中jvm加载的是哪个日志配置文件? |
|
| 21 | + |
|
| 22 | +解答:使用arthas工具排查,通过打印类的相关命令: |
|
| 23 | + |
|
| 24 | +1.启动arthas,监控jvm,命令:./as.sh |
|
| 25 | + |
|
| 26 | + |
|
| 27 | + |
|
| 28 | +2.查看jvm加载的日志组件,命令:logger |
|
| 29 | + |
|
| 30 | + |
|
| 31 | + |
|
| 32 | +3.获取classLoaderHash,命令:sc -d com.sie.iiot.apps.alarm.model.AlarmManager |
|
| 33 | + |
|
| 34 | + |
|
| 35 | + |
|
| 36 | +4.查看类加载的logger信息,命令:ognl -c 10dba097 '@com.sie.iiot.apps.alarm.model.AlarmManager@logger' |
|
| 37 | + |
|
| 38 | + |
|
| 39 | + |
|
| 40 | +5.查看jvm加载的logger配置文件信息,命令:ognl -c 1be6f5c3 '#map1=@org.slf4j.LoggerFactory@getLogger("root").loggerContext.objectMap, #map1.get("CONFIGURATION_WATCH_LIST")' |
|
| 41 | + |
|
| 42 | + |
|
| 43 | + |
|
| 44 | +可以看到加载的是这个配置文件/root/java-iiot/jar/sie-iiot-server-1.0-SNAPSHOT.jar!/BOOT-INF/classes!/logback.xml |
|
| 45 | + |
|
| 46 | +## 问题4: 查询性能优化 |
|
| 47 | + |
|
| 48 | +如果模型中的字段有 ER 关系。引擎自动会做懒加载,但是在查询的时候,使用懒加载可能会导致性能比较差。 |
|
| 49 | + |
|
| 50 | +以下模型作为示例 |
|
| 51 | + |
|
| 52 | +```java |
|
| 53 | +@Model |
|
| 54 | +public class Staff extends BaseModel<Staff> { |
|
| 55 | + |
|
| 56 | + @ManyToOne(displayName = "部门") |
|
| 57 | + @JoinColumn |
|
| 58 | + private Dept dept; |
|
| 59 | + |
|
| 60 | + @OneToMany(displayName = "学习记录") |
|
| 61 | + private List<LearningRecord> learningRecords; |
|
| 62 | + |
|
| 63 | + @Property(displayName = "附件", dataType = DataType.FILE, multiple = true, length = 10) |
|
| 64 | + private List<String> file; |
|
| 65 | + |
|
| 66 | + public List<LearningRecord> getLearningRecords() { |
|
| 67 | + return (List<LearningRecord>) get("learningRecords"); |
|
| 68 | + } |
|
| 69 | +} |
|
| 70 | +``` |
|
| 71 | + |
|
| 72 | +```java |
|
| 73 | +@Model |
|
| 74 | +public class LearningRecord extends BaseModel<LearningRecord> { |
|
| 75 | + |
|
| 76 | + @ManyToOne(displayName = "员工") |
|
| 77 | + @JoinColumn |
|
| 78 | + private Staff staff; |
|
| 79 | + |
|
| 80 | + @Property(displayName = "内容") |
|
| 81 | + private String content; |
|
| 82 | +} |
|
| 83 | +``` |
|
| 84 | + |
|
| 85 | +### 4.1 不要在循环中使用懒加载 |
|
| 86 | + |
|
| 87 | +错误做法 |
|
| 88 | + |
|
| 89 | +```java |
|
| 90 | +for (Staff staff : staffList) { |
|
| 91 | + List<LearningRecord> learningRecords = staff.getLearningRecords(); |
|
| 92 | +} |
|
| 93 | +``` |
|
| 94 | + |
|
| 95 | +如果 staffList 有 100 条数据,那么会执行 100 次查询 LearningRecord 的语句。 |
|
| 96 | + |
|
| 97 | +正确做法应该是一次性查询所有相关的 LearningRecord,然后再进行下一步处理。 |
|
| 98 | + |
|
| 99 | +```java |
|
| 100 | +Set<String> staffIds = staffList.stream() |
|
| 101 | +.map(BaseModel::getId) |
|
| 102 | +.collect(Collectors.toSet()); |
|
| 103 | + |
|
| 104 | +List<LearningRecord> learningRecords = new ArrayList<>(); |
|
| 105 | +if (CollectionUtil.isNotEmpty(staffIds)) { |
|
| 106 | + learningRecords = new LearningRecord().search(Filter.in("staff", staffIds), Collections.singletonList("*"), 0, 0, null); |
|
| 107 | +} |
|
| 108 | + |
|
| 109 | +for (Staff staff : staffList) { |
|
| 110 | + List<LearningRecord> records = learningRecords.stream() |
|
| 111 | + .filter(r -> staff.getId().equals(r.getOrDefault("staff", ""))) |
|
| 112 | + .collect(Collectors.toList()); |
|
| 113 | + // 其他处理逻辑 |
|
| 114 | +} |
|
| 115 | +``` |
|
| 116 | + |
|
| 117 | +### 4.2 尽量不查询类型为 File 的字段 |
|
| 118 | + |
|
| 119 | +dataType = File 的字段跟 @OneToMany 类似。如果在做表格查询、导出等功能,那么不要去查询这些字段。 |
|
| 120 | + |
|
| 121 | +错误做法 |
|
| 122 | + |
|
| 123 | +```java |
|
| 124 | +new Staff().search(null, Collections.singletonList("*"), 0, 0, null); |
|
| 125 | +``` |
|
| 126 | + |
|
| 127 | +引擎在处理上面的查询时,会发出大量的查询语句去查询 MetaAttachment 模型。 |
|
| 128 | + |
|
| 129 | +正确的做法 |
|
| 130 | + |
|
| 131 | +```java |
|
| 132 | +new Staff().search(null, Arrays.asList("id", "dept"), 0, 0, null); |
|
| 133 | +``` |
|
| 134 | + |
|
| 135 | +### 5 常用Arthas命令 |
|
| 136 | + |
|
| 137 | +``` |
|
| 138 | +1.查看耗时>200ms的方法的参数和返回值 |
|
| 139 | +watch com.sie.snest.engine.model.property.SelectionProperty convertToRead '{params[0],params[3],returnObj}' '#cost>200' -n 100 -x 3 |
|
| 140 | + |
|
| 141 | + |
|
| 142 | +2. 统计耗时 |
|
| 143 | +trace com.sie.snest.engine.data.access.BussModelDataAccess search 'params[0].getModel().getName()=="equip_lubrication_calibration"' -n 100 --skipJDKMethod false |
|
| 144 | + |
|
| 145 | +trace com.sie.mbm.edo.calibration.models.Calibration periodUnitList -n 100 --skipJDKMethod false |
|
| 146 | + |
|
| 147 | +3.查看参数 |
|
| 148 | +watch com.sie.mi.ioc.attribution.strategy.models.AttributionStrategy calcAttribution '{params,returnObj,throwExp}' -n 20 -x 3 |
|
| 149 | + |
|
| 150 | +watch com.sie.mi.ioc.attribution.strategy.models.AttributionStrategy calcAttribution '{params,returnObj,throwExp}' 'params[0]=="04dkj8y9h3kny"' -n 20 -x 3 |
|
| 151 | + |
|
| 152 | +4.其他示例 |
|
| 153 | + |
|
| 154 | +watch com.sie.snest.engine.api.response.ResponseHandle beforeBodyWrite '{params,returnObj,throwExp}' -n 100 --skipJDKMethod false |
|
| 155 | + |
|
| 156 | + ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.sie.snest.engine.container.EngineContainer@getBussinessAppGroupContainer().getAppDataInfoMap().keySet()' |
|
| 157 | + |
|
| 158 | + |
|
| 159 | + watch com.sie.snest |
|
| 160 | +trace com.sie.snest.engine.api.distributed.RpcInvocation invoke -n 5 --skipJDKMethod false |
|
| 161 | + |
|
| 162 | +trace com.sie.snest.engine.api.distributed.RpcInvocation invoke 'ModelMeta == "mbm_mes_process_match_rule"' |
|
| 163 | + |
|
| 164 | +stack com.sie.snest.engine.api.response.ResponseHandle beforeBodyWrite -n 20 --skipJDKMethod false |
|
| 165 | + |
|
| 166 | + |
|
| 167 | +trace org.springframework.web.servlet.DispatcherServlet doDispatch -n 20 --skipJDKMethod false |
|
| 168 | + |
|
| 169 | + |
|
| 170 | +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 |
|
| 171 | + |
|
| 172 | +watch com.sie.mi.ioc.attribution.strategy.models.AttributionStrategy calcAttribution '{params,returnObj,throwExp}' 'params[0]=="04dkj8y9h3kny"' -n 20 -x 3 |
|
| 173 | + |
|
| 174 | +watch com.sie.snest.engine.api.RpcController service '{params,returnObj,throwExp}' -n 20 -x 3 |
|
| 175 | + |
|
| 176 | +watch com.sie.snest.engine.api.response.ResponseHandle beforeBodyWrite '{params,returnObj,throwExp}' -n 100 -x 3 |
|
| 177 | + |
|
| 178 | +trace com.sie.snest.engine.api.distributed.RpcInvocation invoke 'params[3]=="mbm_mes_process_match_rule"' -n 100 --skipJDKMethod false |
|
| 179 | + |
|
| 180 | + |
|
| 181 | +trace com.sie.snest.engine.api.RpcController service 'params[1].getParameter("businessIndex")=="04dkj8y9h3kny"' -n 20 --skipJDKMethod false |
|
| 182 | + |
|
| 183 | +watch com.sie.snest.engine.api.RpcController service '{params,returnObj,throwExp}' 'params[1].getParameter("businessIndex")=="04dkj8y9h3kny"' -n 100 -x 3 |
|
| 184 | + |
|
| 185 | +trace com.sie.snest.engine.api.RpcController service 'params[1].getParameter("businessIndex")=="04dkj8y9h3kny"' -n 20 --skipJDKMethod false |
|
| 186 | + |
|
| 187 | + |
|
| 188 | +trace com.sie.snest.engine.api.response.ResponseHandle beforeBodyWrite -n 20 --skipJDKMethod false |
|
| 189 | + |
|
| 190 | + |
|
| 191 | +``` |
|
| 192 | + |