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
-![image-20230322103222684](./images/image-20230322103222684.png)
27
-
28
-2.查看jvm加载的日志组件,命令:logger
29
-
30
-![image-20230322102008797](./images/image-20230322102008797.png)
31
-
32
-3.获取classLoaderHash,命令:sc -d com.sie.iiot.apps.alarm.model.AlarmManager
33
-
34
-![image-20230322102055902](./images/image-20230322102055902.png)
35
-
36
-4.查看类加载的logger信息,命令:ognl -c 10dba097 '@com.sie.iiot.apps.alarm.model.AlarmManager@logger'
37
-
38
-![image-20230322101945614](./images/image-20230322101945614.png)
39
-
40
-5.查看jvm加载的logger配置文件信息,命令:ognl -c 1be6f5c3 '#map1=@org.slf4j.LoggerFactory@getLogger("root").loggerContext.objectMap, #map1.get("CONFIGURATION_WATCH_LIST")'
41
-
42
-![image-20230322101850358](./images/image-20230322101850358.png)
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
+![image-20230322103222684](./images/image-20230322103222684.png)
27
+
28
+2.查看jvm加载的日志组件,命令:logger
29
+
30
+![image-20230322102008797](./images/image-20230322102008797.png)
31
+
32
+3.获取classLoaderHash,命令:sc -d com.sie.iiot.apps.alarm.model.AlarmManager
33
+
34
+![image-20230322102055902](./images/image-20230322102055902.png)
35
+
36
+4.查看类加载的logger信息,命令:ognl -c 10dba097 '@com.sie.iiot.apps.alarm.model.AlarmManager@logger'
37
+
38
+![image-20230322101945614](./images/image-20230322101945614.png)
39
+
40
+5.查看jvm加载的logger配置文件信息,命令:ognl -c 1be6f5c3 '#map1=@org.slf4j.LoggerFactory@getLogger("root").loggerContext.objectMap, #map1.get("CONFIGURATION_WATCH_LIST")'
41
+
42
+![image-20230322101850358](./images/image-20230322101850358.png)
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
+