宁波作为长三角地区重要的旅游城市,每年吸引大量游客。但游客在规划行程时常常面临两个痛点:一是难以获取个性化的游玩路线推荐,二是不清楚在哪里购买正宗的本地特产。这个毕业设计项目正是针对这两个需求,构建了一个整合旅游推荐与周边商品选购的一站式平台。
从技术角度看,项目采用了Hadoop处理海量用户行为数据和景点信息,通过SpringBoot构建灵活易扩展的Web服务。这种组合既能应对旅游高峰期的大数据量处理,又能快速迭代推荐算法。我在实际开发中发现,宁波本地游客的消费数据具有明显的时空特征——上午偏好文化景点,下午转向购物商圈,这为个性化推荐提供了宝贵的数据基础。
选择Hadoop+HDFS作为数据存储层主要基于三个因素:首先,游客的点击流、购买记录等数据量可能达到TB级;其次,MapReduce适合批量处理用户历史行为数据;最后,考虑到未来可能接入景区实时人流量数据,Hadoop的扩展性更有保障。这里有个细节需要注意:我们为HDFS配置了3个副本,因为测试发现宁波电信机房的磁盘故障率较高。
SpringBoot版本选用2.3.5.RELEASE,这是当时最稳定的长期支持版本。特别要说明的是,我们放弃了MyBatis而采用Spring Data JPA,因为在商品分类这种多层级关系的数据操作上,JPA的级联查询能减少30%以上的代码量。
系统主要分为四个核心模块:
特别要强调的是用户画像模块的设计。我们不仅采集基础的性别、年龄信息,还通过cookie记录用户在页面停留时长,这对判断兴趣强度非常有效。例如,发现用户反复查看东钱湖相关内容但未下单,系统会在24小时后推送包含东钱湖门票的特惠套餐。
推荐算法采用改进的ItemCF模型,主要优化点在相似度计算。传统方法仅考虑共同点击次数,我们加入了时空权重因子:
code复制sim(i,j) = Σ[1/(1+α*|t_u,i - t_u,j|)] * log(1+β*d_geo)
其中α是时间衰减系数(设为0.5),β是地理衰减系数(设为0.3),d_geo是两个景点的直线距离。这个公式使得上午浏览天一阁的用户,更可能被推荐附近的城隍庙而非较远的象山影视城。
算法在YARN集群上的执行流程:
重要提示:在reduce阶段一定要设置combiner,我们的测试显示这能使shuffle数据量减少60%
商品搜索采用了Elasticsearch实现多维度过滤,特别是解决了特产商品的模糊匹配问题。比如用户搜索"年糕",系统能同时返回"慈城年糕"和"水磨年糕"两类商品。这里有个技巧:为商品名称字段配置ik_smart分词器后,还需要自定义同义词词典:
code复制宁波汤圆 => 猪油汤圆
奉化水蜜桃 => 奉化桃子
支付接口对接了支付宝和微信双渠道,遇到的一个坑是:微信沙箱环境返回的签名验证总是失败,后来发现是证书链不完整的问题。正确的验证代码应该是:
java复制// 正确的证书加载方式
X509Certificate cert = (X509Certificate) CertificateFactory
.getInstance("X.509")
.generateCertificate(new ByteArrayInputStream(certBytes));
cert.checkValidity();
使用ECharts构建的管理员看板包含三个关键指标:
我们发现一个有趣现象:每周五下午3点后,老外滩酒吧街的推荐权重会突然提升。据此调整了算法参数,使得happy hour时段的酒水特产推荐量增加40%。
在压力测试阶段,当并发用户超过500时,推荐接口响应时间从800ms飙升到5s。通过Arthas工具定位到瓶颈在于HBase的scan操作。最终采用三级缓存方案:
调整后性能对比:
| 方案 | QPS | 平均延迟 | 99分位延迟 |
|---|---|---|---|
| 纯HBase | 320 | 780ms | 2.1s |
| 三级缓存 | 2100 | 45ms | 130ms |
生产环境采用5节点Hadoop集群,配置建议:
我们在宁波电信机房部署时遇到机架感知问题,解决方案是在hdfs-site.xml中明确指定机架ID:
xml复制<property>
<name>net.topology.script.file.name</name>
<value>/etc/hadoop/conf/rack-awareness.sh</value>
</property>
使用Prometheus+Grafana监控体系,特别注意以下指标:
曾出现过因日志文件未轮转导致磁盘爆满的事故,现在通过logrotate每日切割:
code复制/var/log/travel-recommend/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
}
现象:用户反馈连续收到相同景点的推荐
排查过程:
现象:订单创建时库存充足,支付时提示缺货
原因分析:
java复制// 正确的分布式锁实现
String lockKey = "stock_" + skuId;
boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
try {
if (locked) {
// 业务逻辑
}
} finally {
redisTemplate.delete(lockKey);
}
在实际运营中,我们发现两个有价值的扩展点:
季节性推荐策略:宁波的旅游淡旺季明显,可以引入时间序列预测模型。例如通过ARIMA算法预测下个月象山影视城的游客量,提前调整推荐权重。
私域流量运营:为商家开发直播带货功能,当用户被推荐到某个景点时,可以实时观看该景点特产店铺的直播。技术上需要解决高并发直播流的分发问题,我们测试发现用SRS+WebRTC方案在500并发时延迟能控制在3秒内。