智能推荐

通用推荐引擎的一种设计方案

2019-05-01  本文已影响39人  晴天哥_王志

推荐引擎场景方案的设计

说明:

// 推荐引擎的核心服务
public class RecomServiceImpl  {
    private Map<String, SceneImpl> Scenes

    public RecomResult recommend(RecomParam param) {
        // 省略相关代码
    }
}

// 推荐引擎的场景(scene)服务
public class SceneImpl implements RecommendHandler {
    private final String sceneName;
    private Map<String, SolutionImpl> solutions;
    private SolutionSelector selector;

    public SceneImpl(EngineContext context, RecomEngineConfig
                 recomEngineConfig, String sceneName) {
        this.sceneName = sceneName;
        this.solutions = ImmutableMap.copyOf(new HashMap<String, Solution>());
    }
}

// 推荐引擎的方案(solution)服务
public class SolutionImpl implements RecommendHandler {
    private String sceneName;
    private String solutionName;
    private RecommendHandler handler;
    private URLClassLoader classLoader;
    private RecomEngineConfig recomEngineConfig;

    private SolutionImpl(RecomEngineConfig recomEngineConfig, String sceneName, String solutionName)
    {
        this.sceneName = sceneName;
        this.solutionName = solutionName;
        this.recomEngineConfig = recomEngineConfig;
        this.solutionJarPath = File.separator + solutionName + ".jar";

        try {
            File jarFile = new File(solutionJarPath);
            classLoader = new URLClassLoader(new URL[] { new File(solutionJarPath).toURI().toURL() },
                    Thread.currentThread().getContextClassLoader());
            Class<?> clazz = classLoader.loadClass(SolutionUtils.getClassName(sceneName, solutionName));
            handler = (RecommendHandler) (clazz.newInstance());
            handler.init(context);
        } catch (Throwable t) {
          // 省略相关代码
        }
    }

    public static SolutionImpl build(final EngineContext context, final RecomEngineConfig recomEngineConfig,
            String sceneName, String solutionName) {
        Solution solution = null;
        try {
            solution = new SolutionImpl(context, recomEngineConfig, sceneName, solutionName);
            
            return solution;
        } catch (Throwable t) {
        }
    }
}

推荐引擎推荐流程

说明:

public class RecomServiceImpl  {
    public RecomResult recommend(RecomParam param) {
        
        RecomResult result = null;
        try{
            String sceneId = param.getSceneId();
            Scene scene = this.Scenes.get(sceneId);
            result = scene.recommend(reqId, condition);
        }finally {
            // 省略相关代码
        }   
        return result;
    }
}


public class SceneImpl implements RecommendHandler {

    public RecomResult recommend(RecomParam param) {
        return doRecommand(param);
    }
    
    private RecomResult doRecommand(RecomParam param) {
        try {
            String solutionName = selector.selectSolution(condition);
            SolutionImpl solution = solutions.get(solutionName);
            return solution.recommend(reqId, condition);
        } catch (Throwable t) {
            // 省略相关代码
        }
    }
}


public class SolutionImpl implements RecommendHandler {

    private RecommendHandler handler;
    private URLClassLoader classLoader;

    private SolutionImpl(RecomEngineConfig recomEngineConfig, String sceneName, String solutionName)
    {
        this.sceneName = sceneName;
        this.solutionName = solutionName;
        this.recomEngineConfig = recomEngineConfig;
        this.solutionJarPath = File.separator + solutionName + ".jar";

        try {
            File jarFile = new File(solutionJarPath);
            classLoader = new URLClassLoader(new URL[] { new File(solutionJarPath).toURI().toURL() },
                    Thread.currentThread().getContextClassLoader());
            Class<?> clazz = classLoader.loadClass(SolutionUtils.getClassName(sceneName, solutionName));
            handler = (RecommendHandler) (clazz.newInstance());
            handler.init(context);
        } catch (Throwable t) {
            // 省略相关代码
        }
    }

    public RecomResult recommend(RecomParam param) {

        RecomResult result = null;
        try {
            if (null != engineContext) {
                result = handler.recommend(param);
            }
            return result;
        } catch (Throwable t) {
           // 省略相关代码
        } 
    }
}

推荐引擎场景方案管理方案

说明:

public class RecomServiceImpl  {

    private Map<String, Scene> currentScenes;

    protected void syncSceneSolutionsFromZK() {
        try {

            Map<String, String> scenesConfigs = new HashMap<String, String>();
            synchronized (mutex) {
                // 获取所有场景的配置信息
                List<String> scenes = zkGetChildren(zkSceneDir);
                Map<String, Scene> newScenes = new HashMap<String, Scene>();
                newScenes.putAll(currentScenes);
                // 遍历所有场景获取场景的配置信息,并挨个进行处理。
                for (String sceneName : scenes) {

                    String zkScenePath = recomConfig.getSceneZKPath(sceneName);
                    // 读取场景的配置信息
                    String data = zkReadData(zkScenePath);
                    // 保存场景的配置信息
                    scenesConfigs.put(sceneName, data);

                    try {
                        SceneImpl scene = currentScenes.get(sceneName);
                        // 根据场景是否为空创建场景对象
                        if (scene == null) {
                            scene = new SceneImpl(context, recomConfig, sceneName);
                        }
                        // 根据配置生成场景对象
                        if (!scene.update(data)) {
                            continue;
                        }

                        newScenes.put(sceneName, scene);
                    } catch (Throwable se) {
                    }
                }

                runningScenes = ImmutableMap.copyOf(newScenes);
            }
        } catch (Throwable t) {
        }
    }



    public boolean update(String data) {

        try {
            SceneConfig newSceneConfig = JSON.parseObject(data, SceneConfig.class);

            Map<String, SolutionImpl> newSolutions = new HashMap<String, SolutionImpl>();
            for (String solutionName : newSceneConfig.allSolutions()) {                
                SolutionImplsolution = SolutionImpl.build(context, recomEngineConfig, sceneName, solutionName);
                newSolutions.put(solutionName, solution);
            }
            // 生成场景所有方案的对象
            solutions = ImmutableMap.copyOf(newSolutions);
            // 生成分流选择器
            selector = SceneSolutionSelector.build(newSceneConfig);
            // 保存最新的场景配置信息
            sceneConfig = newSceneConfig;
            return true;
        } catch (Throwable t) {
        }
        return false;
    }
}

推荐引擎方案场景分流方案

说明:

public class SceneSolutionSelector {

    private SolutionSelectorStrategy strategy = null;

    public String selectSolution(final RecomParam param) {
        return this.strategy.selectSolution(param);
    }
    
    public static SceneSolutionSelector build(final SceneConfig conf) {
        SceneSolutionSelector selector = new SceneSolutionSelector();
        // 默认分流策略
        String defaultSolution = conf.getDefaultSolution();
        DefaultStrategy ds = new DefaultStrategy(defaultSolution);
        selector.strategy = ds;
        
        // 按比例分流策略
        Map<String, Integer> ap = conf.getAccessPortion();
        if (ap != null && ap.size() > 0) {
            SpecifyRateStrategy srs = new SpecifyRateStrategy(ap, conf.isAccessByUid(), conf.getStrategyMap());
            srs.setNext(selector.strategy);
            selector.strategy = srs;
        }
        
        // 白名单策略
        Map<Long, String> dau = conf.getDirectAccessUser();
        if (dau != null && dau.size() > 0) {
            DirectAccessUserStrategy daus = new DirectAccessUserStrategy(dau);
            daus.setNext(selector.strategy);
            selector.strategy = daus;
        }
        
        
        return selector;
    }
}


public abstract class SolutionSelectorStrategy {

    private SolutionSelectorStrategy nextStrategy = null;
    
    public void setNext(SolutionSelectorStrategy strategy) {
        this.nextStrategy = strategy;
    }
    
    public abstract String takeSolution(RecomParam condition);
    
    public String selectSolution(RecomParam condition) {
        String solutionName = takeSolution(condition);
        if (solutionName != null) {
            return solutionName;
        }

        if (nextStrategy  != null) {
            return nextStrategy.selectSolution(condition);
        }

        return null;
    }
}



public class DefaultStrategy extends SolutionSelectorStrategy {
    private String defaultSolution = null;
    
    public DefaultStrategy(String defaultSolution) {
        this.defaultSolution = defaultSolution;
    }
    
    public String takeSolution(RecomParam condition) {
        return defaultSolution;
    }
}



public class DirectAccessUserStrategyextends SolutionSelectorStrategy {

    ImmutableMap<Long, String> directAccessUser = null;
    public DirectAccessUserStrategy(Map<Long, String> directAccessUser) {
        this.directAccessUser = ImmutableMap.copyOf(directAccessUser);
    }
    @Override
    public String takeSolution(RecomParam condition) {
        if (this.directAccessUser == null) {
            return null;
        }
        
        Long uid = condition.getUid();
        if (uid == null) {
            return null;
        }
        
        String solutionName = directAccessUser.get(uid);
        if (solutionName == null) {
            return null;
        }
        return solutionName;
    }

}


public class SpecifyRateStrategy extends SolutionSelectorStrategy {
    String[] solutionPortion = null;

    //是否按照uid进行分流
    private boolean accessByUid = false;
    
    //按照uid进行分流的分流策略
    Map<Integer, String> strategyMap = new HashMap<Integer,String>();
    
    public SpecifyRateStrategy(Map<String, Integer> solutionRates, boolean accessByUid, Map<Integer, String> strategyMap) {
        
        this.accessByUid = accessByUid;
        
        if (solutionRates != null && solutionRates.size() > 0) {
            solutionPortion = new String[SceneConfig.TOTAL_BUCKET_PORTION];
            int idx = 0;
            
            List<String> solutionList = new ArrayList<String>();
            for(String solutionName : solutionRates.keySet()){
                solutionList.add(solutionName);
            }
            Collections.sort(solutionList);
            
            for (String solutionName : solutionList) {
                int portion = solutionRates.get(solutionName);
                for (int i = 0; i < portion; i++) {
                    solutionPortion[idx++] = solutionName;
                    if (idx > solutionPortion.length) {
                        throw new IllegalArgumentException("Invalidate rate " + solutionRates);
                    }
                }
            }

            while (idx < solutionPortion.length) {
                solutionPortion[idx++] = null;
            }
        }
        
        if(null != strategyMap && strategyMap.size() > 0){
            this.strategyMap = strategyMap;
        }
    }

    public String takeSolution(RecomParam condition) {
        Long uid = condition.getUid();
        if (uid == null) {
            return null;
        }
        if(accessByUid){
            if(null == strategyMap){
                return null;
            }
            int hashCode = Math.abs(uid.toString().hashCode());
            int idx = (int) (hashCode % SceneConfig.UID_TOTAL_BUCKET_PORTION);
            return strategyMap.get(Integer.valueOf(idx));
            
        }else{
            
            String sceneId = condition.getSceneId();
            if(StringUtils.isEmpty(sceneId)){
                return null;
            }
            
            int sceneIdHashCode = sceneId.hashCode();
            if (sceneIdHashCode < 0){
                sceneIdHashCode = -sceneIdHashCode;
            }
    
            if (uid < 0) {
                uid = -uid;
            }
            
            long id = uid + sceneIdHashCode;
            
            int idx = (int) (id % SceneConfig.TOTAL_BUCKET_PORTION);
            return solutionPortion[idx];
        }
    }
}

推荐引擎方案场景同步方案

说明:

public class RecomPlatformService  {

    protected void watchTrigger() {
        client.subscribeDataChanges(recomEngineConfig.getZKTriggerPath(), new IZkDataListener() {
            @Override
            public void handleDataChange(String dataPath, Object data) throws Exception {
                syncSceneSolutionsFromZK();
            }

            @Override
            public void handleDataDeleted(String dataPath) throws Exception {
                syncSceneSolutionsFromZK();
            }
        });
    }
}
上一篇下一篇

猜你喜欢

热点阅读