@yanglfyangl
2018-08-29T11:04:43.000000Z
字数 2729
阅读 540
搜索接口示例
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() {@Overridpublic 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或灰度等,是不需要重启集群的,只需要进行相应的配置就可以。
什么情况之下需要改动代码升级:
- 目前的模型算法中公式需要增加(一般这是由算法团队主导的)
外部依赖有哪些
- 模型文件和配置需要放到一个高可用的地方。
- 配置中心需要高可用(最好强一致性,根据业务需要)