本文目录导读:

在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));
}
}
// 使用布隆过滤器快速排除
// 使用并发集合避免竞争
// 热点数据使用本地缓存
选择合适的权重分配方案需要考虑:
- 静态/动态:配置是否经常变化
- 精确度:是否需要精确的百分比控制
- 可观测性:是否需要统计各场景的调用占比
- 分布式:是否需要跨节点一致
建议从简单方案开始,如第一个示例,然后根据业务增长逐步升级到方案2和3,对于生产环境,使用方案4(配置中心+动态调整)是较好的选择。