SQL Server推送数据到MQ字段丢失的生产问题排查
背景
从SQL Server推数据到MQ。
SQL查询语句(省略若干查询字段):
select
CallStartDt,
SeqNum,
CallId,
Service_Id,
UMID
from CallDetail
where CallInsertDt >= CONVERT(varchar(10),DATEADD(MINUTE,-510,GETDATE()),120)+' '+
cast(DATEPART(HOUR,DATEADD(MINUTE,-510,GETDATE())) as varchar(10))+':'+
right('0'+cast((DATEPART(MINUTE,DATEADD(MINUTE,-510,GETDATE()))/10)*10 as varchar(10)),2)+':00'
and CallInsertDt < CONVERT(varchar(10),DATEADD(MINUTE,-480,GETDATE()),120)+' '+cast(DATEPART(HOUR,DATEADD(MINUTE,-480,GETDATE())) as varchar(10)) + ':'+ right('0'+cast(DATEPART(MINUTE,DATEADD(MINUTE,-480,GETDATE()))- DATEPART(MINUTE,DATEADD(MINUTE,-480,GETDATE()))%10 as varchar(10)),2)+':00'
String topic = query.get(TOPIC) + "";
String[] columnArr = (query.get(COLUMN) + "").replace("\\n", "").replace(" ", "").split(",");
String childName = query.get(CHILD_NAME) + "";
String[] childColumnArr = (query.get(CHILD_COLUMN) + "").split(",");
// 调整: 未填写字段名不发送MQ(旧版==null条件始终不满足)
if (query.get(COLUMN) == null || StringUtils.isEmpty(query.get(COLUMN).toString())) {
jsonObjectReturn.put("msg", "未指定字段名,不发送MQ");
jsonObjectReturn.put("code", 1);
return jsonObjectReturn;
}
log.info("execAutoWorkTo autoWordConfigId id:{}, query:{}", jobId, query);
// 批量消息发送
List<String> msgList = new ArrayList<>();
for (Map<String, Object> map : dataList) {
JSONObject mq = new JSONObject();
JSONObject childMq = new JSONObject();
for (String s : columnArr) {
String dbKey = s.split(":")[0];
String mqKey = "";
if (s.contains(":")) {
mqKey = s.split(":")[1];
}
if (StringUtils.isBlank(mqKey)) {
mq.put(dbKey, map.get(dbKey));
} else {
mq.put(mqKey, map.get(dbKey));
}
}
if (StringUtils.isNotBlank(childName) && !"null".equals(childName)) {
for (String s : childColumnArr) {
String childDbKey = s.split(":")[0];
String childMqKey = "";
if (s.contains(":")) {
childMqKey = s.split(":")[1];
}
if (StringUtils.isBlank(childMqKey)) {
childMq.put(childDbKey, map.get(childDbKey));
} else {
childMq.put(childMqKey, map.get(childDbKey));
}
}
mq.put(childName, childMq);
}
msgList.add(JSONObject.toJSONString(mq));
// 批量发送
if (msgList.size() == batchNum) {
List<String> paramList = new ArrayList<>(msgList);
mqThreadService.execute(() -> {
try {
mqMessageService.sendBatchMsg(topic, paramList);
} catch (Exception e) {
e.printStackTrace();
}
});
msgList.clear();
}
}
select id, json_extract(datajson, '$.queryTo.column') from auto_word_config where isactive = 1
and cron_exp_status = 1 and to_type='mq' and json_extract(datajson, '$.queryTo.column') !='';
本地开发环境复现
反思
为什么一模一样的程序,一模一样的数据表column字段,旧版没有问题,而新版有问题?
旧版代码和新版一模一样,除了一行:
String columnTmp = (query.get(COLUMN) + "").replaceAll("\\n", "").replaceAll(" ", "");
恍然大悟:这不就是Java最最基础的String工具类replace与replaceAll的区别吗?
String.replace vs replaceAll
-
replace的参数是char和CharSequence,即可以支持字符的替换,也支持字符串的替换(CharSequence,字符串序列,即字符串);
-
replaceAll的参数是regex,即基于规则表达式的替换,比如:可通过
replaceAll("\\d", "*")
把一个字符串所有的数字字符都换成星号; -
相同点:都是全部替换,即把源字符串中的某一字符或字符串全部换成指定的字符或字符串;
-
不同点:replaceAll支持正则表达式,因此会对参数进行解析(两个参数均是),如
replaceAll("\\d", "*")
,而replace则不会,replace("\\d","*")
就是替换"\d"的字符串,而不会解析为正则。
另一个不同点:“\”在java中是一个转义字符,所以需要用两个代表一个。例如System.out.println( “\” ) ;只打印出一个""。但是“\”也是正则表达式中的转义字符,需要用两个代表一个。所以:\\被java转换成\,\又被正则表达式转换成\,因此用replaceAll替换“\”为"\",就要用replaceAll("\\","\\\\"),而replace则replace("\","\\")。
如果只想替换第一次出现的,可以使用replaceFirst(),这个方法也是基于规则表达式的替换。