bot-sdk-java的bug导致了自定义技能无法实现数据统计功能
刘****烁 · 刘****烁 发布于2019-07-09 00:42 浏览:1517 回复:9

如果你在论坛搜索过为什么基于bot-sdk-java开发的自定义技能无法实现数据统计,你会一无所获。因为这些帖子要么无人回复,要么回复的办法没有效果。(比如asass2 发布的 关于技能数据统计的问题)。废话少说,先上结论——bot-sdk-java中的数据统计有bug。说得再细点是sdk中引用的bot-monitor有bug,导致数据统计功能根本就没有执行过。下面讲为什么。

技能之所以能够对数据统计是因为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<HttpResponse>() {
@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<HttpResponse>() {
@Override
public void completed(HttpResponse result) {
}

@Override
public void failed(Exception ex) {
}

@Override
public void cancelled() {
}
});
} catch (Exception e) {
e.printStackTrace();
}

原因是httpClient大量创建,长期没有关闭,导致资源耗尽。在completed方法里加上close后,这个问题不再出现。

 

眼看基于node.js和python的自定义技能都有数据统计功能,你却连优质奖励计划的门都进不去,你说你急不急。最初寄希望于官方解决这个问题呢,后来在论坛和QQ群都没有获得任何实质性帮助。看来小度音箱这块的资源,不论是人手还是其他资源,都远不能满足自身的发展。小度想做生态就要有良好的社区和广泛的开发者。如果问题总是拖延或无法解决,大家就会失去热情。

 

点赞  ( 1 )
收藏
评论(9)
共9条回复 最后由1****2回复于2019-07-25 10:28
#21****4回复于2019-07-15 09:45:42

赞👍。

0
#3刘****烁回复于2019-07-15 14:50:41

棒棒哒

0
#4l****n回复于2019-07-15 18:31:06

棒棒哒

0
#5刘****烁回复于2019-07-15 21:03:41
#4 l****n回复
棒棒哒

因为bot-sdk-java的pom里bot-monitor.jar没变啊。我是复制了botmonitor的内容自己改了个类。

0
#61****2回复于2019-07-24 10:34:22

是不是去掉request和response的校验就可以了?  私钥是psk里面的内容吗?要带行首行尾吗?

0
#71****2回复于2019-07-24 11:00:56

去掉那两个校验后有了。感谢!

顺便psk私钥的头尾我去掉了,因为debug的时候抛异常提示我太长了。

0
#8o****3回复于2019-07-24 14:49:18
#7 1****2回复
去掉那两个校验后有了。感谢! 顺便psk私钥的头尾我去掉了,因为debug的时候抛异常提示我太长了。

求教是怎么解决的

0
#9刘****烁回复于2019-07-25 10:11:01
#8 o****3回复
求教是怎么解决的

棒棒哒

0
#101****2回复于2019-07-25 10:28:36
#8 o****3回复
求教是怎么解决的

棒棒哒

0
TOP