Storm 实时推荐系统实践用户行为流、特征计算与模型打分实时推荐系统已成为现代互联网应用的核心组件能够根据用户实时行为快速调整推荐策略。Apache Storm作为一款开源的分布式实时计算系统以其低延迟、高可靠的特性成为构建实时推荐系统的理想选择。本文将详细介绍如何利用Storm实现用户行为流处理、实时特征计算和模型打分构建高效的实时推荐系统。1. 用户行为流处理实时推荐系统的第一步是高效处理用户行为数据流。在Storm中我们通过Spout组件收集用户行为数据然后通过Bolt组件进行实时处理。1.1 行为数据收集首先设计一个KafkaSpout从Kafka消息队列中实时获取用户行为数据如浏览、点击、购买等行为。代码如下public class BehaviorKafkaSpout extends BaseRichSpout { private SpoutOutputCollector collector; private KafkaSpoutConfigString, String spoutConfig; Override public void open(MapString, Object conf, TopologyContext context, SpoutOutputCollector collector) { this.collector collector; // 创建KafkaSpout配置 spoutConfig KafkaSpoutConfig.builder(localhost:9092, behavior-topic) .setProp(Serde.STRING_SERDE, Serde.STRING_SERDE) .setStartingOffsetsFromEnd() .build(); } Override public void nextTuple() { // 从Kafka中获取数据并发射 collector.emit(spoutConfig); } }1.2 行为数据清洗与规范化接下来设计一个BehaviorParseBolt对收集到的行为数据进行清洗和规范化处理。public class BehaviorParseBolt extends BaseRichBolt { private OutputCollector collector; Override public void prepare(MapString, Object conf, TopologyContext context, OutputCollector collector) { this.collector collector; } Override public void execute(Tuple tuple) { String behaviorData (String) tuple.getValue(0); // 解析JSON格式行为数据 JSONObject behavior JSON.parseObject(behaviorData); String userId behavior.getString(user_id); String itemId behavior.getString(item_id); String behaviorType behavior.getString(behavior_type); long timestamp behavior.getLong(timestamp); // 过滤无效数据 if (userId ! null itemId ! null behaviorType ! null) { // 发射清洗后的行为数据 collector.emit(new Values(userId, itemId, behaviorType, timestamp)); } collector.ack(tuple); } }1.3 行为流聚合为了实时计算用户兴趣需要对用户行为流进行实时聚合。设计一个BehaviorAggregateBolt按时间窗口聚合用户行为。public class BehaviorAggregateBolt extends BaseRichBolt { private OutputCollector collector; private MapStateString, MapString, Integer userBehaviorMap; Override public void prepare(MapString, Object conf, TopologyContext context, OutputCollector collector) { this.collector collector; // 初始化状态存储 userBehaviorMap MapState.create(new MapState.Deserializer()); } Override public void execute(Tuple tuple) { String userId tuple.getString(0); String itemId tuple.getString(1); String behaviorType tuple.getString(2); long timestamp tuple.getLong(3); // 获取用户当前行为统计 MapString, Integer behaviorStats userBehaviorMap.get(userId); if (behaviorStats null) { behaviorStats new HashMap(); } // 更新行为统计 int count behaviorStats.getOrDefault(behaviorType, 0) 1; behaviorStats.put(behaviorType, count); userBehaviorMap.put(userId, behaviorStats); // 发射聚合结果 collector.emit(new Values(userId, behaviorStats)); collector.ack(tuple); } }用户行为流处理架构展示从数据收集到行为聚合的完整流程Kafka消息队列KafkaSpoutBehaviorParseBoltBehaviorAggregateBolt实时特征计算用户兴趣模型推送行为数据原始数据清洗后数据行为数据聚合统计特征向量模型更新该架构图展示了从Kafka消息队列到实时特征计算的完整流程包括数据收集、清洗、聚合和特征生成等关键步骤。2. 特征计算模块特征计算是实时推荐系统的核心环节直接影响推荐效果。在Storm中我们需要设计高效的实时特征计算流程。2.1 特征工程基于用户行为流我们可以计算出多种特征如用户兴趣特征、物品流行度特征、上下文特征等。public class FeatureComputeBolt extends BaseRichBolt { private OutputCollector collector; private MapStateString, UserFeature userFeatureMap; private MapStateString, ItemFeature itemFeatureMap; Override public void prepare(MapString, Object conf, TopologyContext context, OutputCollector collector) { this.collector collector; // 初始化用户特征和物品特征存储 userFeatureMap MapState.create(new MapState.Deserializer()); itemFeatureMap MapState.create(new MapState.Deserializer()); } Override public void execute(Tuple tuple) { String userId tuple.getString(0); MapString, Integer behaviorStats (MapString, Integer) tuple.getValue(1); // 计算用户兴趣特征 UserFeature userFeature computeUserFeature(userId, behaviorStats); userFeatureMap.put(userId, userFeature); // 发射特征向量 collector.emit(new Values(userId, userFeature.toVector())); collector.ack(tuple); } private UserFeature computeUserFeature(String userId, MapString, Integer behaviorStats) { UserFeature feature new UserFeature(userId); // 计算各类行为的权重 int totalBehaviors behaviorStats.values().stream().mapToInt(Integer::intValue).sum(); // 浏览行为权重 int browseCount behaviorStats.getOrDefault(browse, 0); feature.setBrowseWeight((double)browseCount / totalBehaviors); // 点击行为权重 int clickCount behaviorStats.getOrDefault(click, 0); feature.setClickWeight((double)clickCount / totalBehaviors); // 购买行为权重 int buyCount behaviorStats.getOrDefault(buy, 0); feature.setBuyWeight((double)buyCount / totalBehaviors); return feature; } }2.2 实时特征更新为了实现特征的实时更新我们需要使用Storm的状态管理功能。public class FeatureUpdateBolt extends BaseRichBolt { private OutputCollector collector; private MapStateString, UserFeature userFeatureMap; Override public void prepare(MapString, Object conf, TopologyContext context, OutputCollector collector) { this.collector collector; // 初始化用户特征存储 userFeatureMap MapState.create(new MapState.Deserializer()); } Override public void execute(Tuple tuple) { String userId tuple.getString(0); UserFeature newFeature (UserFeature) tuple.getValue(1); // 获取当前用户特征 UserFeature currentFeature userFeatureMap.get(userId); if (currentFeature null) { currentFeature new UserFeature(userId); } // 更新用户特征使用指数平滑算法 double alpha 0.3; // 平滑因子 currentFeature updateFeature(currentFeature, newFeature, alpha); // 存储更新后的特征 userFeatureMap.put(userId, currentFeature); // 发射更新后的特征 collector.emit(new Values(userId, currentFeature.toVector())); collector.ack(tuple); } private UserFeature updateFeature(UserFeature oldFeature, UserFeature newFeature, double alpha) { UserFeature updated new UserFeature(oldFeature.getUserId()); // 应用指数平滑更新各类特征 updated.setBrowseWeight(alpha * newFeature.getBrowseWeight() (1 - alpha) * oldFeature.getBrowseWeight()); updated.setClickWeight(alpha * newFeature.getClickWeight() (1 - alpha) * oldFeature.getClickWeight()); updated.setBuyWeight(alpha * newFeature.getBuyWeight() (1 - alpha) * oldFeature.getBuyWeight()); return updated; } }2.3 特征存储与检索为了支持实时推荐我们需要将计算的特征存储在高效检索的存储系统中如Redis。public class FeatureStoreBolt extends BaseRichBolt { private OutputCollector collector; private JedisPool jedisPool; private String redisHost; private int redisPort; Override public void prepare(MapString, Object conf, TopologyContext context, OutputCollector collector) { this.collector collector; this.redisHost (String) conf.get(redis.host); this.redisPort (int) conf.get(redis.port); this.jedisPool new JedisPool(redisHost, redisPort); } Override public void execute(Tuple tuple) { String userId tuple.getString(0); double[] featureVector (double[]) tuple.getValue(1); try (Jedis jedis jedisPool.getResource()) { // 将用户特征存储到Redis String featureKey user:feature: userId; jedis.del(featureKey); // 删除旧特征 // 存储特征向量 for (int i 0; i featureVector.length; i) { jedis.hset(featureKey, feature_ i, String.valueOf(featureVector[i])); } // 设置过期时间如24小时 jedis.expire(featureKey, 86400); } collector.ack(tuple); } }特征计算模块架构展示实时特征计算的核心组件与流程行为数据流FeatureComputeBolt用户兴趣特征物品流行度特征上下文特征FeatureUpdateBoltFeatureStoreBoltRedis特征存储行为数据计算特征原始特征指数平滑存储特征持久化浏览/点击/购买历史行为统计点击率/转化率时间/位置/设备指数平滑更新高效检索该架构图展示了特征计算模块的核心组件包括基础特征计算、特征更新和特征存储等关键步骤。3. 模型打分与推荐特征计算完成后我们需要将特征输入到推荐模型中进行打分生成实时推荐结果。3.1 模型打分在实时推荐系统中通常使用简单的机器学习模型进行快速打分如逻辑回归、矩阵分解或深度学习模型。public class ModelScoreBolt extends BaseRichBolt { private OutputCollector collector; private LRModel model; // 加载预训练的逻辑回归模型 Override public void prepare(MapString, Object conf, TopologyContext context, OutputCollector collector) { this.collector collector; // 加载预训练的模型 this.model loadModel(model/lr_model.bin); } Override public void execute(Tuple tuple) { String userId tuple.getString(0); double[] userFeature (double[]) tuple.getValue(1); // 从Redis获取用户特征 double[] fullFeature getFullFeature(userId, userFeature); // 使用模型进行打分 double score model.predict(fullFeature); // 发射用户与预测得分 collector.emit(new Values(userId, score)); collector.ack(tuple); } private double[] getFullFeature(String userId, double[] userFeature) { try (Jedis jedis new Jedis(localhost, 6379)) { // 合并用户特征和物品特征 double[] fullFeature new double[userFeature.length ITEM_FEATURE_DIM]; // 复制用户特征 System.arraycopy(userFeature, 0, fullFeature, 0, userFeature.length); // 获取物品特征示例 String itemFeatureKey item:feature:12345; // 实际中从消息获取 MapString, String itemFeatureMap jedis.hgetAll(itemFeatureKey); int offset userFeature.length; for (int i 0; i ITEM_FEATURE_DIM; i) { String value itemFeatureMap.get(feature_ i); fullFeature[offset i] value ! null ? Double.parseDouble(value) : 0.0; } return fullFeature; } } private LRModel loadModel(String modelPath) { // 实际实现中应从文件加载预训练模型 return new LRModel(); } }3.2 推荐结果生成根据模型打分结果生成最终的推荐列表。public class RecommendationBolt extends BaseRichBolt { private OutputCollector collector; private MapStateString, PriorityQueueItemScore userRecommendations; Override public void prepare(MapString, Object conf, TopologyContext context, OutputCollector collector) { this.collector collector; // 初始化用户推荐结果存储 userRecommendations MapState.create(new MapState.Deserializer()); } Override public void execute(Tuple tuple) { String userId tuple.getString(0); double score (double) tuple.getValue(1); // 获取候选物品ID实际中应从候选池获取 String itemId item_12345; // 示例值 long timestamp System.currentTimeMillis(); // 获取用户当前推荐队列 PriorityQueueItemScore recommendations userRecommendations.get(userId); if (recommendations null) { recommendations new PriorityQueue(10, Comparator.comparingDouble(ItemScore::getScore)); } // 添加新的推荐候选 recommendations.add(new ItemScore(itemId, score, timestamp)); // 保持推荐列表大小 if (recommendations.size() 10) { recommendations.poll(); // 移除得分最低的物品 } // 更新用户推荐 userRecommendations.put(userId, recommendations); // 生成推荐结果 ListString topItems getTopItems(recommendations); collector.emit(new Values(userId, topItems)); collector.ack(tuple); } private ListString getTopItems(PriorityQueueItemScore recommendations) { ListString topItems new ArrayList(); PriorityQueueItemScore temp new PriorityQueue(recommendations); while (!temp.isEmpty()) { topItems.add(temp.poll().getItemId()); } // 反转得到降序排列 Collections.reverse(topItems); return topItems; } private static class ItemScore { private String itemId; private double score; private long timestamp; public ItemScore(String itemId, double score, long timestamp) { this.itemId itemId; this.score score; this.timestamp timestamp; } public String getItemId() { return itemId; } public double getScore() { return score; } public long getTimestamp() { return timestamp; } } }3.3 推荐结果分发最后将推荐结果分发到不同的应用场景如Web端、移动端等。public class RecommendationDispatchBolt extends BaseRichBolt { private OutputCollector collector; private KafkaProducerString, String producer; Override public void prepare(MapString, Object conf, TopologyContext context, OutputCollector collector) { this.collector collector; // 配置Kafka生产者 Properties props new Properties(); props.put(bootstrap.servers, localhost:9092); props.put(key.serializer, org.apache.kafka.common.serialization.StringSerializer); props.put(value.serializer, org.apache.kafka.common.serialization.StringSerializer); this.producer new KafkaProducer(props); } Override public void execute(Tuple tuple) { String userId tuple.getString(0); ListString recommendations (ListString) tuple.getValue(1); // 构建推荐结果消息 JSONObject recommendationMsg new JSONObject(); recommendationMsg.put(user_id, userId); recommendationMsg.put(recommendations, recommendations); recommendationMsg.put(timestamp, System.currentTimeMillis()); // 发送到不同的Kafka主题 String topic recommendation_result; producer.send(new ProducerRecord(topic, userId, recommendationMsg.toJSONString())); collector.ack(tuple); } Override public void cleanup() { // 关闭Kafka生产者 if (producer ! null) { producer.close(); } } }模型打分与推荐决策树展示推荐结果生成的关键决策流程模型打分准备否是获取用户特征获取物品特征模型打分计算过滤敏感内容生成推荐列表分发推荐结果返回推荐列表Redis查询用户特征Redis查询物品特征LR/矩阵分解/深度学习过滤违规内容排序取TopN该决策树图展示了从模型打分准备到推荐结果生成的完整决策流程包括特征获取、模型计算、内容过滤和结果分发等关键步骤。4. 系统优化与性能对比实时推荐系统的性能优化是确保系统稳定运行的关键。本节将介绍系统优化的关键策略和不同方案的性能对比。4.1 性能优化策略针对Storm实时推荐系统可以从以下几个方面进行优化并行度调整根据数据量和处理能力调整Bolt的并行度合理利用集群资源。状态管理优化使用Storm的分布式状态管理避免状态不一致问题。缓存策略对热点数据实施缓存减少重复计算。批处理优化对于非实时性要求高的任务采用批处理模式。资源隔离关键组件部署到专用资源组避免相互影响。// 示例并行度配置 TopologyBuilder builder new TopologyBuilder(); // 设置Spout并行度 builder.setSpout(kafka-spout, new BehaviorKafkaSpout(), 2); // 设置Bolt并行度 builder.setBolt(parse-bolt, new BehaviorParseBolt(), 4) .shuffleGrouping(kafka-spout); builder.setBolt(aggregate-bolt, new BehaviorAggregateBolt(), 8) .fieldsGrouping(parse-bolt, new Fields(user_id)); builder.setBolt(feature-bolt, new FeatureComputeBolt(), 6) .fieldsGrouping(aggregate-bolt, new Fields(user_id)); builder.setBolt(model-bolt, new ModelScoreBolt(), 4) .shuffleGrouping(feature-bolt); builder.setBolt(recommend-bolt, new RecommendationBolt(), 3) .fieldsGrouping(model-bolt, new Fields(user_id));4.2 不同方案性能对比下面对比不同推荐系统的性能指标方案延迟(ms)吞吐量(条/秒)CPU使用率内存占用(GB)可扩展性批处理推荐50001000040%4差实时推荐(单机)100500080%8中实时推荐(Storm集群)505000060%12优系统性能占比分析展示各处理阶段的耗时占比处理阶段耗时占比数据收集与清洗 (25%)特征计算 (35%)模型打分 (20%)结果排序 (15%)结果分发 (5%)KafkaSpoutParseAggregateFeatureModelSortDispatch特征计算是整个系统的主要性能瓶颈该图表展示了实时推荐系统中各处理阶段的耗时占比可以看出特征计算是整个系统的主要性能瓶颈。5. 完整示例与注意事项5.1 最小示例代码下面是一个完整的Storm实时推荐系统拓扑示例public class RealTimeRecommendationTopology { public static void main(String[] args) throws Exception { TopologyBuilder builder new TopologyBuilder(); // 数据采集Spout builder.setSpout(behavior-spout, new BehaviorKafkaSpout(), 2); // 数据清洗Bolt builder.setBolt(parse-bolt, new BehaviorParseBolt(), 4) .shuffleGrouping(behavior-spout); // 行为聚合Bolt builder.setBolt(aggregate-bolt, new BehaviorAggregateBolt(), 8) .fieldsGrouping(parse-bolt, new Fields(user_id)); // 特征计算Bolt builder.setBolt(feature-bolt, new FeatureComputeBolt(), 6) .fieldsGrouping(aggregate-bolt, new Fields(user_id)); // 特征更新Bolt builder.setBolt(update-bolt, new FeatureUpdateBolt(), 4) .fieldsGrouping(feature-bolt, new Fields(user_id)); // 特征存储Bolt builder.setBolt(store-bolt, new FeatureStoreBolt(), 3) .fieldsGrouping(update-bolt, new Fields(user_id)); // 模型打分Bolt builder.setBolt(model-bolt, new ModelScoreBolt(), 4) .shuffleGrouping(store-bolt); // 推荐生成Bolt builder.setBolt(recommend-bolt, new RecommendationBolt(), 3) .fieldsGrouping(model-bolt, new Fields(user_id)); // 推荐分发Bolt builder.setBolt(dispatch-bolt, new RecommendationDispatchBolt(), 2) .fieldsGrouping(recommend-bolt, new Fields(user_id)); // 配置拓扑 Config config new Config(); config.setNumWorkers(6); config.setMaxSpoutPending(1000); // 提交拓扑 if (args ! null args.length 0) { StormSubmitter.submitTopology(args[0], config, builder.createTopology()); } else { LocalCluster cluster new LocalCluster(); cluster.submitTopology(realtime-recommendation, config, builder.createTopology()); Utils.sleep(60000); cluster.shutdown(); } } }5.2 注意事项状态一致性确保分布式状态的一致性避免数据不一致问题。资源管理合理配置JVM内存和并行度避免资源竞争。容错机制实现完善的容错机制确保系统异常时的数据不丢失。监控告警建立完善的监控和告警机制及时发现系统异常。容量规划提前规划系统容量应对流量高峰。实时推荐系统整体架构展示完整的实时推荐系统架构与数据流向数据采集层结果分发层数据处理层结果生成层存储层Storm实时计算集群实时计算层行为数据推荐结果数据清洗结果分发特征计算模型打分数据处理模型训练Kafka/LogAPI/Web/App特征提取推荐生成ZooKeeper/Redis/HBase该架构图展示了完整的实时推荐系统架构包括数据采集层、实时计算层、数据处理层、结果生成层、存储层和结果分发层等关键组件。
企业数字化 ERP 产品动态
相关推荐
3步搞定怀柔区地图实战项目,面试原理不再慌 3步搞定怀柔区地图实战项目,面试原理不再慌 面试时被问到GIS数据加载原理,你支支吾吾答不上来? 别慌,这通常是把复杂概念想得太深了。 今天用【怀柔区地图】做个 实战项目 ,让你彻底搞懂原理。 概念速懂:地图不是图片,是数据… · 2026/9/23 12:42:43
Storm 分布式 RPC:实现高性能同步查询与实时计算服务化 Storm 分布式 RPC:实现高性能同步查询与实时计算服务化本文深入探讨 Storm 分布式远程过程调用(DRPC)模式,详解其核心架构与工作原理。通过分析同步查询机制与实时计算服务化的实现方法,展示如何在分布式环境中实现高效可靠的功能调用服务。文… · 2026/9/23 12:42:43
kustomize 实战:用 configMapGenerator 实现 ConfigMap 生成与滚动更新 CLI开发工具云原生 【免费下载链接】kustomize Customization of kubernetes YAML configurations 项目地址: https://gitcode.com/gh_mirrors/ku/kustomize 点击查看 免费下载 本指南以 kustomize 仓库中的 examples/zh/configGeneration.md 为骨架,结… · 2026/9/23 12:42:36
LLM+HTN:大型语言模型与任务规划的深度融合 一、引子:当语言遇见规划
2030年的某个下午,NASA的任务规划工程师面对一个棘手的问题:火星探测器传回了一段模糊的自然语言描述,“如果前面的岩石看起来不太稳,就绕到左边拍张全景,然后分析一下土壤成分”。… · 2026/9/23 13:22:02
深入解析 xxhash:wandb core 中 Go 实现的 XXH64 哈希算法(vendored 包) 机器学习深度学习数据可视化可观测性 【免费下载链接】wandb The AI developer platform. Use Weights & Biases to train and fine-tune models, and manage models from experimentation to production. 项目地址: https://gitcode.com/gh_mirrors/wa/wandb 点… · 2026/9/23 13:21:56
2026开发者必备的6款AI编程工具实战指南 1. 这6款AI工具不是“锦上添花”,而是2026年开发者生存的硬性配置 你有没有过这种体验:凌晨两点,盯着一段遗留的Java微服务代码,接口文档缺失、注释为零、调用链像毛线团——你花了47分钟才搞清一个 Transactional 为什么没生效… · 2026/9/23 13:21:56
MCP协议与Git Worktree:AI编程助手的协同范式革命 1. 这场“AI编程助手”的胜负手,根本不在模型参数上2026年下半年再看 Codex vs Claude Code,胜负已经开始变了——这句话不是预测,而是我过去18个月在真实开发场景中反复验证后的结论。我带过三个团队,从金融风控系统重构到工业Io… · 2026/9/23 13:21:56
CLI驱动的Diff-Aware代码评审工作流:LLM Agent如何精准理解Git变更 1. 项目概述:这不是一个“工具”,而是一套可落地的开源代码评审工作流“open-code-review”这个名称乍看像某个具体软件包或GitHub仓库名,但结合当前开发者社区的真实语境——尤其是高频出现的open-code-review、LLM Agent、CLI、git diffs这… · 2026/9/23 13:21:55
5个estee底层坑点与完整示例解析 5个estee底层坑点与完整示例解析 面对满屏红色的 StackTrace,很多开发者第一反应是懵圈。报错信息里混杂着内存地址、堆栈层级和奇怪的变量名,像天书一样难以解读。其实,绝大多数 estee… · 2026/9/23 13:21:49
3招搞定手机怎么下载微信面试难题实战项目解析 3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29