java案例如何分配不同场景的权重?

wen java案例 4

本文目录导读:

java案例如何分配不同场景的权重?

  1. 简单权重随机选择(基于随机数)
  2. 动态权重分配(基于配置文件)
  3. 基于策略模式的权重方案
  4. 生产环境常用方案(基于配置中心)
  5. 核心设计建议
  6. 性能优化建议

在Java中分配不同场景的权重,常见的有以下几种方案,我分别给出实战代码示例:

简单权重随机选择(基于随机数)

import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
public class WeightedRandomSelector {
    // 场景定义
    static class Scene {
        String name;
        int weight;
        Scene(String name, int weight) {
            this.name = name;
            this.weight = weight;
        }
    }
    public static String selectScene(List<Scene> scenes) {
        // 计算总权重
        int totalWeight = scenes.stream()
                .mapToInt(s -> s.weight)
                .sum();
        // 生成随机数
        int random = ThreadLocalRandom.current().nextInt(totalWeight);
        // 遍历选择
        for (Scene scene : scenes) {
            random -= scene.weight;
            if (random < 0) {
                return scene.name;
            }
        }
        return scenes.get(0).name; // 兜底
    }
    public static void main(String[] args) {
        List<Scene> scenes = Arrays.asList(
            new Scene("首页推荐", 50),
            new Scene("搜索结果", 30),
            new Scene("广告位", 15),
            new Scene("活动页", 5)
        );
        // 模拟100次选择,统计分布
        Map<String, Integer> stats = new HashMap<>();
        for (int i = 0; i < 10000; i++) {
            String selected = selectScene(scenes);
            stats.merge(selected, 1, Integer::sum);
        }
        System.out.println("实际分布情况:");
        stats.forEach((k, v) -> 
            System.out.println(k + ": " + (v / 100.0) + "%"));
    }
}

动态权重分配(基于配置文件)

import java.util.*;
import java.util.stream.Collectors;
public class DynamicWeightService {
    // 场景配置实体
    @lombok.Data
    public static class SceneConfig {
        private String sceneCode;     // 场景编码
        private String sceneName;     // 场景名称
        private int baseWeight;       // 基础权重
        private int boostWeight;      // 加成权重(动态变化)
        private Date effectiveDate;   // 生效时间
        private Date expireDate;      // 过期时间
        private List<String> conditions; // 触发条件
    }
    // 动态权重分配器
    public class WeightAllocator {
        private Map<String, SceneConfig> configMap;
        // 根据业务规则计算权重
        public int calculateWeight(SceneConfig config, UserContext context) {
            int finalWeight = config.getBaseWeight();
            // 业务规则1:用户等级加成
            if (context.getUserLevel() >= 5) {
                finalWeight += config.getBoostWeight() * 1.5;
            }
            // 业务规则2:时间段加成
            int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
            if (hour >= 20 && hour <= 23) { // 晚间高峰
                finalWeight *= 1.3;
            }
            // 业务规则3:节假日加成
            if (isHoliday()) {
                finalWeight *= 1.2;
            }
            // 业务规则4:用户历史行为影响
            if (context.getUserHistory().contains(config.getSceneCode())) {
                finalWeight *= 0.8; // 降低已展示过的权重
            }
            return Math.max(finalWeight, 1); // 最低1
        }
        // 动态选择场景
        public SceneConfig selectScene(UserContext context) {
            List<SceneConfig> availableScenes = new ArrayList<>();
            double totalWeight = 0;
            // 收集有效场景并计算权重
            Map<SceneConfig, Double> weightMap = new LinkedHashMap<>();
            for (SceneConfig config : configMap.values()) {
                if (isValid(config, context)) {
                    double weight = calculateWeight(config, context);
                    totalWeight += weight;
                    weightMap.put(config, weight);
                }
            }
            // 加权随机选择
            double random = Math.random() * totalWeight;
            for (Map.Entry<SceneConfig, Double> entry : weightMap.entrySet()) {
                random -= entry.getValue();
                if (random <= 0) {
                    return entry.getKey();
                }
            }
            return weightMap.keySet().iterator().next();
        }
        private boolean isValid(SceneConfig config, UserContext context) {
            Date now = new Date();
            return !(now.before(config.getEffectiveDate()) || 
                    now.after(config.getExpireDate()));
        }
        private boolean isHoliday() {
            // 判断是否节假日
            return false; // 简化实现
        }
    }
    @lombok.Data
    public static class UserContext {
        private int userLevel;      // 用户等级
        private List<String> userHistory; // 历史浏览
        private String deviceType;  // 设备类型
        // ... 其他上下文属性
    }
}

基于策略模式的权重方案

import java.util.*;
public class StrategyWeightAllocator {
    // 场景接口
    public interface SceneStrategy {
        String getSceneName();
        int getWeight(SceneContext context);
        boolean isSupport(SceneContext context);
    }
    // 具体场景策略
    public static class HomePageStrategy implements SceneStrategy {
        @Override
        public String getSceneName() { return "homepage"; }
        @Override
        public int getWeight(SceneContext context) {
            int weight = 40;
            if (context.isNewUser()) weight += 30;
            if (context.getHour() >= 20) weight *= 1.2;
            return weight;
        }
        @Override
        public boolean isSupport(SceneContext context) {
            return context.getSceneType().equals("browser");
        }
    }
    public static class SearchStrategy implements SceneStrategy {
        @Override
        public String getSceneName() { return "search"; }
        @Override
        public int getWeight(SceneContext context) {
            if (context.hasSearchIntent()) {
                return 80;
            }
            return 20;
        }
        @Override
        public boolean isSupport(SceneContext context) {
            return true;
        }
    }
    // 集中式权重管理
    public class WeightManager {
        private List<SceneStrategy> strategies;
        private Map<String, Integer> dynamicWeights; // 动态调整
        public String route(SceneContext context) {
            // 收集所有支持的策略
            List<SceneStrategy> supported = strategies.stream()
                    .filter(s -> s.isSupport(context))
                    .collect(Collectors.toList());
            // 计算总权重
            int totalWeight = supported.stream()
                    .mapToInt(s -> s.getWeight(context))
                    .sum();
            // 随机选择
            int random = new Random().nextInt(totalWeight);
            for (SceneStrategy strategy : supported) {
                random -= strategy.getWeight(context);
                if (random < 0) {
                    return strategy.getSceneName();
                }
            }
            return null;
        }
        // 实时调整权重
        public void adjustWeight(String sceneName, int adjustment) {
            dynamicWeights.merge(sceneName, adjustment, Integer::sum);
        }
    }
    // 上下文对象
    @lombok.Data
    public static class SceneContext {
        private String sceneType;    // 页面类型
        private boolean newUser;     // 是否新用户
        private int hour;            // 当前小时
        private boolean searchIntent; // 是否有搜索意图
        // getters...
    }
}

生产环境常用方案(基于配置中心)

import com.alibaba.nacos.api.config.ConfigService;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class RemoteWeightConfig {
    // 从配置中心获取权重配置
    public class WeightConfigClient {
        private ConfigService configService;
        private Map<String, Integer> weightCache = new ConcurrentHashMap<>();
        // 初始化加载配置
        public void init() {
            String config = configService.getConfig("scene-weight", 
                    "DEFAULT_GROUP", 5000);
            parseConfig(config);
            // 订阅变更
            configService.addListener("scene-weight", "DEFAULT_GROUP", 
                new WeightConfigListener());
        }
        private void parseConfig(String config) {
            // 解析 JSON: {"homepage": 0.4, "search": 0.3, ...}
            Map<String, Integer> parsed = JSON.parseObject(config);
            weightCache.clear();
            weightCache.putAll(parsed);
        }
        // 动态更新缓存
        class WeightConfigListener implements Listener {
            @Override
            public void receiveConfigInfo(String configInfo) {
                parseConfig(configInfo);
            }
        }
        // 获取各场景权重
        public int getWeight(String scene) {
            return weightCache.getOrDefault(scene, 10);
        }
    }
    // A/B 实验权重分配
    public class ABTestWeight {
        public static class Experiment {
            String experimentId;
            String groupAWeight;  // 40%
            String groupBWeight;  // 60%
        }
        public String dispatchUser(String userId) {
            int hash = Math.abs(userId.hashCode());
            int mod = hash % 100;
            if (mod < 40) {
                return "control_group";
            } else {
                return "treatment_group";
            }
        }
    }
}

核心设计建议

权重计算原则

// 统一权重计算接口
public interface WeightCalculator {
    // 计算基础权重
    int getBaseWeight();
    // 计算加成权重
    int getBoostWeight(SceneContext context);
    // 校验权重有效性
    boolean validateWeight(int weight);
    // 获取最终权重
    default int getFinalWeight(SceneContext context) {
        int finalWeight = getBaseWeight() + getBoostWeight(context);
        // 应用全局调整因子
        if (context.isPeakTime()) {
            finalWeight = (int)(finalWeight * 1.2);
        }
        // 最低限制
        return Math.max(finalWeight, 1);
    }
}

性能优化建议

// 使用缓存避免重复计算
public class WeightCacheDecorator implements WeightCalculator {
    private final WeightCalculator delegate;
    private final Map<String, Integer> cache = new ConcurrentHashMap<>();
    @Override
    public int getFinalWeight(SceneContext context) {
        String userId = context.getUserId();
        return cache.computeIfAbsent(userId, 
            key -> delegate.getFinalWeight(context));
    }
}
// 使用布隆过滤器快速排除
// 使用并发集合避免竞争
// 热点数据使用本地缓存

选择合适的权重分配方案需要考虑:

  1. 静态/动态:配置是否经常变化
  2. 精确度:是否需要精确的百分比控制
  3. 可观测性:是否需要统计各场景的调用占比
  4. 分布式:是否需要跨节点一致

建议从简单方案开始,如第一个示例,然后根据业务增长逐步升级到方案2和3,对于生产环境,使用方案4(配置中心+动态调整)是较好的选择。

抱歉,评论功能暂时关闭!