去年为某游戏平台实施推荐系统时,我们发现传统数据库已无法处理日均2TB的用户行为数据。这套基于Hadoop的热门游戏推荐系统,通过分布式架构解决了海量数据存储与实时分析的难题。系统核心包含三个关键模块:使用Flume+Kafka构建的数据管道实现毫秒级数据采集,基于Spark MLlib的混合推荐算法达到83%的点击转化率,以及通过ECharts实现的动态可视化大屏。
选择Hadoop生态系统主要基于三个考量:首先,游戏用户行为数据具有明显的非结构化特征(如点击流、会话日志),HDFS的块存储模式比传统RDBMS更适合存储这类数据;其次,MapReduce/Spark的分布式计算能力可处理高峰时段每分钟超过50万条的并发请求;最后,Hive的数据仓库特性便于后续进行OLAP分析。
实际部署中我们采用如下架构:
code复制[用户终端] -> [Flume Agent] -> [Kafka Cluster]
-> [Spark Streaming] -> [HDFS/HBase]
-> [Spark MLlib] -> [Redis Cache]
-> [Spring Boot API] -> [Vue.js前端]
注意:Hadoop集群最少需要5个节点(1个NameNode+4个DataNode),测试环境可使用伪分布式模式,但生产环境必须保证Zookeeper和JournalNode的高可用配置
我们测试了三种采集方案后最终选择Flume+Kafka组合:
| 方案 | 吞吐量 | 延迟 | 数据丢失风险 |
|---|---|---|---|
| Flume直写HDFS | 5MB/s | 高 | 中 |
| Kafka+Spark | 50MB/s | 低 | 低 |
| Logstash+ES | 20MB/s | 中 | 高 |
具体配置示例(flume-agent.conf):
properties复制agent.sources = http-source
agent.channels = mem-channel
agent.sinks = kafka-sink
agent.sources.http-source.type = http
agent.sources.http-source.port = 5140
agent.sources.http-source.channels = mem-channel
agent.channels.mem-channel.type = memory
agent.channels.mem-channel.capacity = 100000
agent.sinks.kafka-sink.type = org.apache.flume.sink.kafka.KafkaSink
agent.sinks.kafka-sink.kafka.bootstrap.servers = kafka01:9092,kafka02:9092
agent.sinks.kafka-sink.kafka.topic = game-logs
agent.sinks.kafka-sink.channel = mem-channel
原始数据需要经过以下处理流程:
sql复制SELECT *, ROW_NUMBER() OVER(PARTITION BY session_id, event_time ORDER BY ingest_time DESC) AS rn
FROM raw_events
WHERE rn = 1
采用协同过滤(CF)+内容特征(CB)的混合模型:
协同过滤部分:
python复制def time_decay_similarity(u1, u2):
alpha = 0.95 # 衰减系数
common_items = set(u1.items) & set(u2.items)
score = sum(alpha**(t_now - t_click) for t_click in common_items)
return score / (len(u1.items)*len(u2.items))**0.5
内容特征部分:
Spark Streaming处理流程:
scala复制val stream = KafkaUtils.createDirectStream[...](ssc, kafkaParams)
stream.foreachRDD { rdd =>
rdd.map(parseEvent)
.groupBy(_.userId)
.join(userProfiles) // 离线特征
.map { case (uid, (events, profile)) =>
val recentTags = analyzeRecentEvents(events)
val recs = blendRecommendations(profile, recentTags)
(uid, recs)
}
.saveToRedis()
}
实现地图热力图的三个关键点:
javascript复制option = {
bmap: {
center: [104.114129, 37.550339],
zoom: 5,
roam: true
},
series: [{
type: 'heatmap',
coordinateSystem: 'bmap',
data: convertToHeatmapData(cityLevelData),
pointSize: 10,
blurSize: 15
}]
}
关键配置(deployment.yaml):
yaml复制apiVersion: apps/v1
kind: Deployment
metadata:
name: recommender-service
spec:
replicas: 3
selector:
matchLabels:
app: recommender
template:
spec:
containers:
- name: recommender
image: registry.cn-hangzhou.aliyuncs.com/game-rec:1.2
resources:
limits:
cpu: "2"
memory: 4Gi
env:
- name: REDIS_HOST
value: "redis-master"
ports:
- containerPort: 8080
Grafana需要监控的核心指标:
| 指标类型 | 采集频率 | 告警阈值 |
|---|---|---|
| Kafka Lag | 15s | >1000消息延迟 |
| Spark任务堆积 | 1m | 持续5分钟>10任务 |
| API响应时间 | 30s | P99>500ms |
| Redis命中率 | 1m | <85%持续10分钟 |
问题1:Spark任务频繁OOM
spark.executor.memoryOverhead=2g问题2:推荐结果重复率高
问题3:大屏数据加载慢
实际部署时建议先进行小规模压力测试,我们使用Locust模拟并发请求时发现,当Kafka分区数不足时会出现明显的消费延迟。通过增加分区数并调整num.io.threads参数,吞吐量提升了3倍。