@yanglfyangl
2018-08-29T11:04:43.000000Z
字数 2729
阅读 491
搜索接口示例
rq={!zlrerank reRankQuery=$reRankQuery reRankDocs=10000 reRankWeight=1}
&reRankQuery=_val_:"ff(kpn({!edismax qf=SOU_POSITION_NAME_QUERY v='\\\"java工程师\\\"'},'10-9.5-4'),'c1')"
&thirdQuery=_val_:"pedict(TestGroup, usrid, queryString, timeout)"
1. TestGroup: 预测模型的分组,未来上线多少个预测模型,就会有多少分组,业务端可以根据不同的需求调用不同分组。
2. queryString: 在API层将solor请求的query封装,传给pedict函数。
3. usrID:用户ID。
业务端可以通过选择高效模型来提高性能。(当然在某些场景下也可以使用精度高的模型来提高用户体验)
内部设计逻辑
增加Parser
addParser("pedict", new ValueSourceParser() {
@Overrid
public ValueSource parse(FunctionQParser fp)
ValueSource v = fp.parseValueSource();
...
return new PedictFunction(v, group, usrid, queryString);
}
});
PedictFunctionclass 的逻辑
public class PedictFunction extends ValueSource {
private static final MLEngineGroup engineGroup = ...
private String iGroup;
public PedictFunction(...) {
存下变量。
}
public FunctionValues getValues(final Map context, final LeafReaderContext readerContext) throws IOException {
。。。
engineGroup.pedict(iGroup, ...);
。。。
}
}
注:Solr cloud 启动时,需要设置一下配置中心地址到环境变量中。(配置中心最好是Zk,强一致性)
MLEngineGroup:{
private static final private static final Map<String, MLEngine> engines = new HashMap<>();
MLEngineGroup engineGroup = ...
private ConfigMonitor configMonitor;
private FeatureReader featureReader;
private getMLEngineByGroupName(String groupName){
从map中获取。
如果不存在,则单例创建
...
{
new MLEngine(featureReader,...)
}
}
double pedict(...) {
return getMLEngineByGroupName().pedict(...)
}
void onConfigChanged(String group){
如果对应的Group配置文件发生变化,就remove掉之前的
engines.remove(group)
不在这里新建,后面会从
}
}
// 用来读取特征。
class FeatureReader:{
private LocalCache localCache;
private Solr solrReader;
private Redis redis;
//特征的读取分三个层次
// 1. 本地缓存。(内存+磁盘)
// 2. 本地Solr中 (离线特征会根据要求存在Solr中)
// 3. Redis中
T read(targetRedis, key){
data = localCache.get(key);
if(data) return data;
if (!data && dataStoredInSolr) {
data = solrReader... 从solor来获取
}
if (!data) {
data = redis... 从redis来获取
}
if(data){
localCache.push();
}
return data;
}
}
class MLEngine:{
void init(){
if(!initlizedModel) {
synchronized ...
try{
从zk或配置中心获取本group的配置文件,包括:
{
模型所在的Redis地址。
模型所在的文件夹子(包含模型文件和配置文件)
}
读取模型配置文件。
读取模型
初始化 OnlineFeatureBuilder
}
}
initlizedModel = true;
if(!initlizedModelConfig) {
synchronized ...
try{
解析模型对应的配置文件。
}
}
initlizedModelConfig = true;
if(!initlizedQueryStringMapping) {
synchronized ...
try{
解析 query mapping的对应关系
用来解析 queryString 。
}
}
initlizedQueryStringMapping = true;
}
double pedict(){
double ret =0.0;
try {
String[] features = readFeatures(JDTYPE|CVTYPE, ...);
if(JDTYPE){
return OnlineFeatureBuilder.build(...)
} else {
return OnlineFeatureBuilder.build(...)
}
}
final{
...
}
return ret;
}
....
}
目前方案中性能的优化点
- 增加了本地缓存(本地内存+本地磁盘)
- 如果特征存入了Solr,则从Solr中取对应的特征。
- 支持小批量查询(比如,20个为一组进行计算),如果超时,则返回。
- 支持快速模型切换。(如果对性能有更高的要求,可以切换成高速模型)
目前方案中灵活性的体现
- 模型上线只需要在配置中心增加配置文件,就可以按名字调用。
- 模型中的特征数据并不依赖redis或solr,只要有就可以用。
- 如果发现模型有问题,只需要更新配置,就可以全部替换,下一次查询时生效。
- 类似A/B test或灰度等,是不需要重启集群的,只需要进行相应的配置就可以。
什么情况之下需要改动代码升级:
- 目前的模型算法中公式需要增加(一般这是由算法团队主导的)
外部依赖有哪些
- 模型文件和配置需要放到一个高可用的地方。
- 配置中心需要高可用(最好强一致性,根据业务需要)