- 在之前启动服务的时候,可以发现,是先启动solr服务,再启动faq服务的,
- 从百度AnyQ之四中可以知道,FAQ数据集部分是完全由solr去控制的,
- 所以anyq中,数据和模型(逻辑控制)这两个部分,非常松耦合。
所以这部分,考虑看一下逻辑部分
1. run_server 1.1 锁定文件启动faq服务,其实是在./build/run_server部分,查看其文件类型,使用ls -l或者ll查看文件详细信息。
- 关于linux文件系统,如果有不清楚的,可以去看我另一个文章:linux中文件类型说明
- 其中run_server是一个普通文件,而且是一个可执行文件,使用vi ./run_server打开是乱码,所以无法查看其执行逻辑。
- 考虑到这个run_server位于build这个文件夹,而这个文件夹来自于cmake && make,故查看CMaKeLists.txt文件(cmake的执行入口),看到最后,发现以下内容:
add_executable(demo_anyq_multi ${CMAKE_SOURCE_DIR}/demo/demo_anyq_multi.cpp) add_executable(demo_anyq ${CMAKE_SOURCE_DIR}/demo/demo_anyq.cpp) add_executable(run_server ${CMAKE_SOURCE_DIR}/demo/run_server.cpp) add_executable(annoy_index_build_tool ${CMAKE_SOURCE_DIR}/demo/annoy_index_build.cpp) add_executable(feature_dump_tool ${CMAKE_SOURCE_DIR}/demo/feature_dump.cpp) target_link_libraries(demo_anyq_multi ${LIBS_LIST}) target_link_libraries(demo_anyq ${LIBS_LIST}) target_link_libraries(run_server ${LIBS_LIST}) target_link_libraries(annoy_index_build_tool ${LIBS_LIST}) target_link_libraries(feature_dump_tool ${LIBS_LIST}) - 所以其实有对应的cpp文件的,同时${LIBS_LIST}这个变量在CMaKeLists.txt文件中也有定义,是一些.a和.so文件,
- 其中.a 是好多个.o合在一起,用于静态连接 ,即STATIC mode,多个.a可以链接生成一个exe的可执行文件。
- .so 是shared object,用于动态连接的,和windows的dll差不多,使用时才载入。
首先查看run_server,代码如下:
#include#include "server/http_server.h" #include "common/utils.h" #include "common/plugin_header.h" int main(int argc, char* argv[]) { google::InitGoogleLogging(argv[0]); FLAGS_stderrthreshold = google::INFO; anyq::HttpServer server; std::string anyq_brpc_conf = "./example/conf/anyq_brpc.conf"; if (server.init(anyq_brpc_conf) != 0) { FATAL_LOG("server init failed"); return -1; } if (server.always_run() != 0) { FATAL_LOG("server run failed"); return -1; } return 0; }
- 第一点,声明了anyq::HttpServer server一个类的示例,这个类来源于server/http_server.h这个文件
- 第二点,指明配置文件的位置"./example/conf/anyq_brpc.conf",将其传递给刚刚的server进行初始化。查看这个文件的内容,如下:
idle_timeout_sec : -1
max_concurrency : 8
port : 8999
server_conf_dir : "./example/conf/"
log_conf_file : "log.conf"
anyq_dict_conf_dir : "./example/conf/"
anyq_conf_dir: "./example/conf/"
preproc_plugin {
name : "default_preproc"
type : "AnyqPreprocessor"
}
postproc_plugin {
name : "default_postproc"
type : "AnyqPostprocessor"
}
- 继续去查看server/http_server.h文件,其中主要内容就是在any的命名空间中定义HttpServer为一个public class。
- 其主要功能就是进行初始化,将刚刚传入的配置信息分别输入到对应的server_conf_dir、log_conf_file、anyq_dict_conf_dir、anyq_conf_dir、server_config等更明确的配置项目中。
- 这个文件中还引入了#include "server/http_service_impl.h"这个文件
- 继续去查看#include "server/http_service_impl.h"这个文件。文件名称的含义就是:服务端实施,内容也非常具体了
namespace anyq {
class HttpServiceImpl : public anyq::HttpService {
public:
HttpServiceImpl();
~HttpServiceImpl();
int init(const ServerConfig& server_config);
int destroy();
int normalize_input(brpc::Controller* cntl, Json::Value& parameters);
// 问答语义检索
void anyq(google::protobuf::RpcController* cntl_base,
const HttpRequest*,
HttpResponse*,
google::protobuf::Closure* done);
// solr 数据操纵接口--增加数据
void solr_insert(google::protobuf::RpcController* cntl_base,
const HttpRequest*,
HttpResponse*,
google::protobuf::Closure* done);
// solr 数据操纵接口--更新数据
void solr_update(google::protobuf::RpcController* cntl_base,
const HttpRequest*,
HttpResponse*,
google::protobuf::Closure* done);
// solr 数据操纵接口--删除数据
void solr_delete(google::protobuf::RpcController* cntl_base,
const HttpRequest*,
HttpResponse*,
google::protobuf::Closure* done);
// solr 数据操纵接口--清空索引库, 需要密码验证
void solr_clear(google::protobuf::RpcController* cntl_base,
const HttpRequest*,
HttpResponse*,
google::protobuf::Closure* done);
private:
// 前处理,将server接收到的数据(get/post)处理成anyq的输入格式
ReqPreprocInterface* _preproc_plugin;
// 后处理,将anyq的输出结果定制输出
ReqPostprocInterface* _postproc_plugin;
DISALLOW_COPY_AND_ASSIGN(HttpServiceImpl);
};
} // namespace anyq
#endif // BAIDU_NLP_ANYQ_HTTP_SERVICE_IMPL_H
- 这个文件引用的内容就更多了,其中有一个brpc,是之前通过github下载并编译的内容,参考BRPC详解(一)——概述
- 另外,还有这两个文件,没有找到,所以使用find命令去搜索
#include "http_service.pb.h" #include "anyq.pb.h"
- 可以看到,找到了,但是位于inclue文件夹中,不和上面的.h一样,虽然都位于include文件夹,但是默认的any这个repo的include文件夹中没有config这个文件夹,这个应该是编译过程或者什么时候另外产生的。查看CMaKeLists.txt文件夹,确实在其中看到了
[root@567b3aed2b1c AnyQ-master]$ find . -name "http_service.pb.h" -print ./include/config/http_service.pb.h SET(PROTO_INC ${CMAKE_SOURCE_DIR}/include/config) # 30行 ${CMAKE_SOURCE_DIR}/include/config # 60行 - 随后,在./include/config文件夹中,确实看到了那两个没有找到的头文件
[root@567b3aed2b1c config]# ls anyq.pb.h http_service.pb.h
- 以anyq.pb.h文件为例,其文件内容如下:
// Generated by the protocol buffer compiler. DO NOT EDIT! // source: anyq.proto #ifndef PROTOBUF_anyq_2eproto__INCLUDED #define PROTOBUF_anyq_2eproto__INCLUDED #include
#include #if GOOGLE_PROTOBUF_VERSION < 3001000 #error This file was generated by a newer version of protoc which is #error incompatible with your Protocol Buffer headers. Please update #error your headers. #endif #if 3001000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION #error This file was generated by an older version of protoc which is #error incompatible with your Protocol Buffer headers. Please #error regenerate this file with a newer version of protoc. #endif #include #include #include #include #include #include #include #include // @@protoc_insertion_point(includes) namespace anyq { // Internal implementation detail -- do not call these. void protobuf_AddDesc_anyq_2eproto(); void protobuf_InitDefaults_anyq_2eproto(); void protobuf_AssignDesc_anyq_2eproto(); void protobuf_ShutdownFile_anyq_2eproto(); - 可以看到,前几行的重点提示,这个是由protocol buffer compiler自动生成的,源文件是anyq.proto
- 类似的,对于http_service.pb.h来说,也是这样的。
- 这两个proto文件,anyq这个repo是有的,不是编译之后产生的。
- 以http_service.proto为例:有点看不懂。。。放弃
package anyq; option cc_generic_services = true; message HttpRequest { }; message HttpResponse { }; service HttpService { rpc anyq(HttpRequest) returns (HttpResponse); rpc solr_insert(HttpRequest) returns (HttpResponse); rpc solr_update(HttpRequest) returns (HttpResponse); rpc solr_delete(HttpRequest) returns (HttpResponse); rpc solr_clear(HttpRequest) returns (HttpResponse); };
相关内容参考另一篇文章:使用strace追踪程序调用情况,
整体过程大概总结如下:
(docker run的时候不添加一些参数,无法使用strace,故重新启动一个容器,仅用来进行strace的追踪)
# 加入--privileged参数,就可以使用strace了 $ docker run -itd --privileged --name anyq-trace -p 0.0.0.0:8876:8999 -p 0.0.0.0:8700:8900 anyq/base $ docker exec -it anyq-trace /bin/bash $ cd /home/AnyQ-master/build/ # 执行solr服务 $ sh solr_script/anyq_solr.sh solr_script/sample_docs # 执行faq服务 $ ./run_server # 验证一下是否正确 # 然后开始追踪 $ strace ./run_server
显示很多内容,例如:
我所搜索到的工具似乎无法满足我的需求
在CMaKeLists文件中,与run_server相关的有两行内容,如下
add_executable(run_server ${CMAKE_SOURCE_DIR}/demo/run_server.cpp)
target_link_libraries(run_server ${LIBS_LIST})
# 这个就是将后面的待执行文件,
参考:
- Cmake命令之add_executable介绍
可知,这里的add_executable的作用是通过指定的源文件列表构建出可执行目标文件。
还是去查看执行.run_server后打印出的log信息
2. 执行时打印的log信息-
./example/conf/./rank_weights这个文件的内容如下:
jaccard_sim 0.2 fluid_simnet_feature 0.8
-
./wordseg_utf8文件夹中包含
确实是一些词典,比如:strong_punc.dic中包含的内容是一些标点符号,如下:! 。 ! ; ;
word.dic中包含有26个英语字母大小写,数字,标点符号等。
-
./simnet中有一个term2id.dict词典,其内容形式如下
赫尔曼·黑塞 1 weifeng 2 苗山 3 棍子 4 水平角 5 粘米粉 6 电脑投影仪 7 中国光大国际有限公司 8 爱程旅游网 9 知字 10 亿亩 11 耳鼻喉科 12 卫生计生局 13 集水器 14 内管 15 LUXURY 16 废钢破碎机 17 潍坊市人民医院 18 思南公馆 19 复华 20 雅思考试网 21
-
关于term_retrieval.cpp:77] RAW: create solr q builder equal_solr_q_1 success这部分输出信息,定位到term_retrieval.cpp文件中,进一步定位到plugin_factory.h。其中有一句注释:// 根据组件类型生成一个组件实例, 自己创建的实例自己销毁,工厂不负责,这里的组件其实就是配置项,所以去查看所有的配置项。
-
整理/build/example/conf文件中所有conf文件内如下:
analysis.confname: "analysis_conf" analysis_method { name: "method_wordseg" type: "AnalysisWordseg" using_dict_name: "lac" }anyq_brpc.conf
idle_timeout_sec : -1 max_concurrency : 8 port : 8999 server_conf_dir : "./example/conf/" log_conf_file : "log.conf" anyq_dict_conf_dir : "./example/conf/" anyq_conf_dir: "./example/conf/" preproc_plugin { name : "default_preproc" type : "AnyqPreprocessor" } postproc_plugin { name : "default_postproc" type : "AnyqPostprocessor" }anyq.conf
analysis_config: "analysis.conf" retrieval_config: "retrieval.conf" rank_config: "rank.conf"
dict.conf
name: "example_dict_conf" dict_config { name: "rank_weights" type: "String2FloatAdapter" path: "./rank_weights" } dict_config { name: "lac" type: "WordsegAdapter" path: "./wordseg_utf8" } dict_config{ name: "fluid_simnet" type: "PaddleSimAdapter" path: "./simnet" } -
rank.conf文件中,可以看到,最终只需要一个top-one作为最终的结果,其中,threshold:0.5,仔细查看语义匹配阶段的输出,其输出的内容不只是概率最大的n个,而是概率大于0.5的都进行了输出。
rank.conf
name : "test_rank" top_result: 1 matching_config { name : "wordseg_process" type : "WordsegProcessor" using_dict_name: "lac" output_num : 0 rough : false } matching_config { name: "fluid_simnet_feature" type: "PaddleSimilarity" using_dict_name: "fluid_simnet" output_num : 1 rough : false query_feed_name: "left" cand_feed_name: "right" score_fetch_name: "cos_sim_0.tmp" } matching_config { name : "jaccard_sim" type : "JaccardSimilarity" output_num : 1 rough : false } rank_predictor { type: "PredictLinearModel" using_dict_name: "rank_weights" } threshold : 0.5 -
粗排阶段,数据检索,返回15个包含query中term的question,是在retrieval.conf配置文件中进行规定的。同时,可以看到,使用的engine_name : "collection1"也是在这里进行指定。这就是为什么
百度AnyQ之四——solr添加数据试验中即便替换了mask_core的数据,但是检索依然是从collection1中进行的原因。
retrieval.confretrieval_plugin { name : "term_recall_1" type : "TermRetrievalPlugin" search_host : "127.0.0.1" search_port : 8900 engine_name : "collection1" solr_result_fl : "id,question,answer" solr_q : { type : "EqualSolrQBuilder" name : "equal_solr_q_1" solr_field : "question" source_name : "question" } num_result : 15 }



