-
Notifications
You must be signed in to change notification settings - Fork 51
Description
详见https://dueros.baidu.com/forum/topic/show/307433
技能之所以能够对数据统计是因为web服务在将response返回给音箱之前,把response也发给了百度数据统计服务器。见BaseBot源代码中的run方法。
public String run() throws Exception {
// 请求参数不合法
if (verify() == false) {
return this.illegalRequest();
}
this.dispatch();
String responseStr = this.build(response);
this.botMonitor.setResponse(responseStr);//将response注入
this.botMonitor.uploadData();//提交给统计服务器
return responseStr;
}
我开始以为uploadData()方法出了问题,后来发现第一个问题出现在setResponse里。见BotMonitor的setResponse方法。
public void setResponse(String responseData) {
if (StringUtils.isBlank(responseData)|| this.isShouldDisable()) {
return;
}
this.requestEndTime = this.getMillisecond();
this.response = new Response(responseData);
}
这个本该为monitor中response属性赋值的方法执行后,response总是为空,因为isShouldDisable()只会返回true,具体说是this.response == null这个条件永远是true。
public boolean isShouldDisable() {
if (StringUtils.isBlank(this.privateKey)
|| this.request == null
|| this.response == null
|| !this.enabled) {
return true;
}
return false;
}
monitor从BaseBot构造方法创建后response默认为空,除了setResponse方法没有其他地方赋值。setResponse方法中的赋值语句却因为调用isShouldDisable返回true,永远没有机会执行。参照nodejs和python中的isShouldDisable方法,你会发现只有Java版的isShouldDisable方法增加了request和response两个条件。
nodejs版
isShouldDisable() {
if (this._privateKey == null || this._privateKey.length === 0
|| !Number.isInteger(this._environment) || !this._enabled) {
return true;
}
return false;
}
python版
def is_should_disable(self):
'''
判断Monitor是否可用
:return:
'''
if not self.enabled:
logging.warning('未开启数据统计功能, 如果使用统计功能需要调用set_monitor_enabled(True)')
return True
if self.enabled:
if not self.private_key or len(self.private_key) == 0:
logging.warning('未配置私钥, 请调用set_environment_info(prikey)')
return True
return False
这两个isShouldDisable方法只检查privateKey和enabled,nodejs还检查了environment,但二者均没有检查request和response。显然,检查request和response应该放在uploadData方法中,而不是这里。
注释掉这两个判断条件,果然monitor的response有值了,uploadData也执行了,但是依然没有统计数据。
uploadData方法片段
httpClient.execute(httpPost, new FutureCallback() {
@OverRide
public void completed(HttpResponse result) {//原文这里就是result,很奇特。
}
@OverRide
public void failed(Exception ex) {
}
@OverRide
public void cancelled() {
}
});
在completed方法中添加调试信息发现,返回结果是“bot pubkey not found.”(通过一个上线但发布新版本的技能访问)或“get bot pub key error.”(通过一个从未上线的技能访问。顺便说下这个问题在论坛也有人问过,结果是无人回复。zeali 发布的 get bot pub key error.)。以上结果是我的environment为0,即调试模式的前提下。我将environment改为1后返回“Failed to verify the signature.”(通过一个上线但发布新版本的技能访问)或“get bot pub key error.”(通过一个从未上线的技能访问。因为截至目前技能上线还没通过,所以不排除上线后能得到正确结果的可能性。
我开始怀疑是公钥私钥不匹配的问题,所以还验证了一下。签名是通过monitor自带的Certificate类的rsaSign方法,验证是网上找的一段方法。结论是公钥和私钥都不能包含第一行和最后一行的说明。bot-sdk-java在示例里强调了这一点,但技能后台配置界面上不论是否去掉收尾两行都不正确。
至此,我认为我能做的已经结束了,虽然知道问题出在数据统计服务器那头,但个人注定是无法解决了。希望sdk开发人员早日解决,提交新版本。
然而,事情却没有结束。隔一段时间我收到短信,告诉我技能稳定性指标不达标。上服务器一看,每次请求都会报异常,too many open files,Failure opening selector createDefault等等。异常指向uploadData方法。
CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault();
httpClient.start();
HttpPost httpPost = new HttpPost(Config.getUploadUrl());
try {
Map<String, String> contentMap = new HashMap<>();
contentMap.put("data", base64Str);
JSONObject jsonContent = new JSONObject(contentMap);
httpPost.setEntity(new StringEntity(base64Str.toString()));
httpPost.setHeader("SIGNATURE", signRet);
httpPost.setHeader("botId", botId);
httpPost.setHeader("timestamp", timestamp.toString());
httpPost.setHeader("pkversion", pkversion);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
try {
httpClient.execute(httpPost, new FutureCallback() {
@OverRide
public void completed(HttpResponse result) {
}
@OverRide
public void failed(Exception ex) {
}
@OverRide
public void cancelled() {
}
});
} catch (Exception e) {
e.printStackTrace();
}
原因是httpClient大量创建,长期没有关闭,导致资源耗尽。在completed方法里加上close后,这个问题不再出现。