影票比价,中国早期观影平台
一、背景
银行各大信用卡app的崛起,需要各种生活服务类场景入驻,丰富app的内容。
公开市场的影票app不计其数,主要包括:猫眼、爱奇艺(前身糯米)、淘票票、网票、万达、单车、影托邦、蜘蛛、格瓦拉(后来倒闭)、卖座等,若能将这些平台的能力整合到一起,形成比价的产品形态输出给用户,势必能给信用卡app带来一定的流量转换。
2016年之前,各大信用卡app的影票平台大致分为三类:
自主研发,对接主流影票平台接口,如掌上生活(招行)(截至目前为止,依然是行业的标杆);第三方外包,如买单吧(交行)、浦大喜奔(浦发)、动卡空间(中信)等,都是第三方公司以api或者h5的形式接入;主流购票平台外链,如工银生活(工行)、口袋银行(平安)等,通过外链跳转到猫眼、格瓦拉的开放平台;结合现有的产品形态,影票比价的概念尚未映入用户的眼帘。同时,购票平台的营销活动还停留在第三方渠道发码,短信引导用户到指定app使用的阶段,整个链路没有形成闭环,没有充分利用这些用户资源,提高app用户的粘性。
各银行在影票营销经费这块有很大的投入,比如浦发一年有3000万的营销经费,如何最大程度地将优惠补贴给用户成了亟需解决的问题。
二、历史成绩和现状2.1、成绩影票比价平台一经推出,便冲击到多家合作商的业务,如格瓦拉(此前跟银行最大的影票平台合作商),网票、蜘蛛、卖座之类的票务商直接或间接的受到了影响。
两年的时间,便拿下了工行、农行、建行、交行、浦发、民生、平安、光大等主流银行的业务,在行业内树立了良好的口碑。(四大行:工、农、中、建)
平台累计用户3000万,年流水1.5亿,单日票房高峰150万,占整个大盘的1%。
平台日常qps也就几十,周末高峰500左右,活动期间并发较高,通常在1-5万。
记忆深刻的是2018年4月份速度与激情8的抢10元观影券的活动,多家银行同时开启活动,瞬时并发达到了20万qps,抛开非人为因素,实际的用户请求达到了5万qps,不幸的是,我们未能成功应对那次突发情况,web服务和数据库一下午都没能拉起来。
2.2、现状随着时间的推移,市场和用户的消费习惯在不断地发生着变化,加之公司的重心往其他产品转移,比价观影平台日渐没落,长时间没有推出新的模式,使得平台失去了核心的竞争力,银行对我们逐渐失去耐心,相继引进了其他的合作商。
2.3、特色特色:
各种吸引用户的营销手段,原价基础上的立减(根据梯度10-8-5-2),支付随机减(1-5),X元观影(1-5-10)多票务商之间的比价,用户能直观的选择最低价的场次每周至少一次的抢券活动(1-5-10),提供了几十种的形式,如:猜拳、拼图、助力、答题、VR找宝箱等,用户享受优惠的同时,也增加了乐趣合作层面,我方能提供快速灵活的解决方案,这种能力是大公司所不具备的,政府、银行、国企类的企业客户存在很多变化多端的诉求除了线上的活动,我们也支持线上线下相结合的方案,如:包场活动、明星见面会活动、VR人脸购票等三、演进成熟阶段研发部人员配置:9后端 5前端 3测试 2产品 1UI。
回顾过去,创业公司首要追求的是快,上线快别人一步,就会赢得更多的生存机遇。当然,“省”紧跟其后,除了人员、房租、经营等方面的支出,IT资源也要物尽其用,恰到好处。
很多解决方案不能按照大厂的教条主义,如:2C4G的服务器只能部署一个服务实例,CPU使用率不能超过30%,主从延迟不能超过N秒,线程池参数要按照经典公式来设置等。
举几个例子:
促销活动的时候,高并发持续的时间可能也就5分钟,流量洪峰更是在活动开始的前30秒,这种场景无法依赖服务器动态伸缩(伸缩时长决定),所以我们根据经验值,提前扩容机器,压测5分钟,假如按5万qps压测,这五分钟内,服务器CPU在100%左右徘徊,但是接口响应时间稳定,且cpu load稳定在某范围,我们认为这种配置是安全稳定的。我们尝试引入golang,将打车平台进行重构,最终拿掉了60%的服务器,给公司节约了不少的成本支出。后话,最终又改回了java,技术选型也要结合市场行情,要提前考虑招聘候选人的难度。结论,公司体量小,主力语言最好统一集中,其他语言和工具只能作为提效的辅助作用。3.1、小米加步枪2016年5月,主打比价购票和多样化营销的旗号,谈成了和浦发信用卡中心的合作。一期的产品比较简单,包括网票、蜘蛛和卖座三家票务平台的比价,新用户立减10元的优惠。
形态:传统单体web项目,后端:JFinal(国产web框架,类似ruby on rails) tomcat,前端:jquery frozenui(腾讯移动端ui框架),人力:1人30天(开发、测试到上线)。
一期的项目十分粗糙,为了快速上线,满足基本的购票功能即可。其部署架构很简单,C端请求直接打到服务器(弹性IP),主服务器的nginx充当网关,将流量分发到各个服务器,主服务器还充当着缓存、DB的作用。
存在的问题:
主题
描述
影响
改进措施
数据库设计不合理
数据库设计不规范,主键的设计,字段的类型,索引的设计,混乱不堪
sql查询无索引sql查询索引不生效主键问题为后续隐患埋下了伏笔(超限,业务聚合)针对查询条件,增加了索引平台基础数据存在很多错误
票务商的数据合并不严谨,影院的合并完全依赖人工(由于各票务商数据无统一码),影片的合并依赖于名称的判断(相同的名字可能不是一部影片,相同的影片可能不是同一个名字)
买的A影院的票,结果出来的是B影院买的A电影,结果出来的是B电影影院问题似乎没有好的解决方案,各个平台的字段内容各不相同将标点符号过滤之后再比较,若出现:复仇者联盟4、终结-复仇者联盟4的情况,程序不做合并,根据相似度报告给运营、测试人员接口安全性差
接口安全性问题,无token机制,有些敏感字段没有做脱敏处理
攻击者可以伪造用户身份请求接口A用户可以看到B用户的数据敏感信息可能被中间人利用增加jwtToken严格区分匿名接口和需登录接口敏感字段全部脱敏处理异常机制不完善
异常问题,没有对异常进行分类处理,有些runtime类型的异常甚至没有捕获,导致出现各种出票异常和支付异常
接口报未知的错误信息业务流程异常中断,有些场景甚至没有保护现场,隐患巨大对业务代码进行异常分类,区分捕获全局异常兜底订单设计简陋
订单模块设计的过于简单,无法区分状态的流转,没有考虑任务补偿和最终一致性
订单表提供的信息量太少,不便于问题的追踪和排查将订单状态细化增加订单日志表物理部署不合理
系统性能更是一塌糊涂,没有保持网络的就近原则(比如内网配外网的mysql地址,内网连外网的redis),没有区分缓存的辨识度,将经纬度作为key的一部分保存到ehcahe,服务阶段性崩溃
redis虽快,若网络不在同一VPC内,特别耗时,还不如不用缓存key区分度低,导致内存溢出将redis和mysql部署在同一网段,程序用内网连接由于经纬度查询是依赖mysql的,本期缓存key区分度低的问题没有好的解决方案营销规则漏洞
营销规则各种漏洞,没有单人的限制,没有总体库存的限制,也没有票价的限制
容易被黄牛薅羊毛增加各种优惠的限制,如单人每月2笔,票价不超过60,每日系统总优惠不超过1000笔等等单体web项目的弊端
前后端在一起部署,服务器承受很大的流量冲击,10来个人同时访问,搞不好页面就白屏了
用户体验差,页面等待时间长将js、css、图片等资源放到cdn,服务器只保留html文件(虽有很大提升,但html体积比起api接口数据,依然很大)传统机房的弊端
部署在传统idc机房,带宽峰值2mbps,调整带宽异常麻烦,需提前申请,流程通常需要好几天
无法应对银行灵活多变的需求上云,浦发银行涉及到防火墙上云3.2、入坑SpringCloud随着银行的逐渐增加,我们引入了spring cloud框架,开始对项目进行微服务的改造。
形态:前后端分离,后端:springcloud springboot,前端:vue,人力:研发6个人(持续优化)
好的方面:
主题
描述
收益
项目拆分
根据银行进行项目拆分,各自独立迭代,独立部署
一个同学负责多家银行的形式,便于管理(银行业务需要持续跟进)拆分之后,降低了项目的复杂程度增加限速模块
能对同一ip进行简单的限速
过滤了大量重复的请求前后端分离
将单体web改为前后端分离的项目,各司其职
加快前端迭代速度提高前端的产品质量服务上云
将idc部署全量搬到阿里云,资源的申请十分便利,当时阿里云也在发展阶段,给与用户较大的支持力度,解决问题效率高
资源申请便利服务易于伸缩能有效地应对高并发、大流量场景存在的问题:
主题
描述
影响
改进措施
微服务划分不合理
微服务划分不够清晰、粒度不够细,只将票务商的数据查询能力抽出了单独的服务,后续的下单出票环节,各自银行项目依然独立处理
虽然引入了微服务,但没有对业务进行拆分,体现不出微服务的优势依然存在需求重复开发、bug重复修的问题细分业务,可以分业务接入层、crm层、领域服务层运维难度增加
增加了运维上的难度,基于传统服务器的方式部署微服务,全靠人肉记忆网络关系、服务器关系、IP关系等
由于不是专业的运维人员,开发同学linux操作水平参差不齐,经常发生人为误操作、漏操作该阶段只能依靠培训和规范来降低问题发生的概率盲目使用组件
盲目的使用hystrix降级组件,经常出现某些接口异常或者缓慢,直接引发整个系统雪崩的问题
营销活动期间,某接口熔断,进而引发其他的接口熔断,且不受人为控制去除hystrix组件,使用人工降级,编程式的业务兜底数据库、缓存分散
数据库和缓存没有做聚合设计,业务上依然是彼此分离
浪费资源无法集中收口申请redis集群,规划好key空间数据库表重新设计,将各银行的业务以渠道号作区分这个阶段发生了公司创立至今最大的一次事故,过年期间,由于购票量的剧增,任务系统出票模块的bug直接导致70万的亏损,具体表现为,猫眼票务商出票,我方认为失败,给用户退款,也就是说,猫眼出了70万的票,我们都给用户退款了。
那次事故对创业公司来说,绝对称得上是致命的打击,老板最终抗下了所有,有悲有喜,借着此事,我们在业界的口碑进一步提升,因为我们是一家敢于担当的公司。
业务正直飞速发展阶段,写出该bug的同学最终被扣除了1500块钱的绩效,也算是对研发团队的一个警示吧。
我深刻的意识到,交易链路极为重要,不能再出大的问题了,公司可承受不起第二次的大面积资损。
自那以后,我主动承担起了review代码的工作,对待其他同事的提交,极为严苛,命名不规范、标点符号滥用、代码格式不对,在我这里通通会被打回去修改。起初,同事很不高兴,久而久之,我也不用做到那么细的review,因为他们的能力已经起来。
3.3、高并发改造上文提到了2018年4月份流量洪峰的事情,树大招风,每次做活动,系统都要经受严苛的考研:
吸引众多的黄牛,配合针对性的软件,发起大规模的羊毛群体抢券吸引竞对的恶意攻击,他们不惜花大价钱用成百上千万的ip来攻击我们票务商爬取我们的数据,给我们的服务造成了很大的压力猫眼、爱奇艺、淘票票也会爬取我们的数据,由于比价的特性,在我们平台拿到竞对的数据相对来讲成本很小。
比如猫眼爬我们的价格,根据其他票务商的价格进而修改自己的价格,始终比别人低一分钱,新的定价不光分发给我们,也会分发给其他跟他们合作的企业,价格低一分真的会发生很多奇妙的事情。
针对这次事故,我们对整个系统进行了全方位的重构和优化:
前端项目部署到阿里云oss,开启cdn,这样做的目的是解决带宽的瓶颈,后端输出的api接口数据包并不大,日常带宽50mbps足矣(按3K的接口大小,足矣支撑50*1024/8/3=2133的并发)接口缓存化改造,之前是利用spring的cache注解,鉴于我们的瞬时高并发的场景,很容易发生缓存穿透,详见下面章节的“缓存改造”不能缓存的耗时接口异步化改造,主要集中在实时座位图、锁座、下单、经纬度换城市信息这些接口,前端请求携带requestId,后端丢到消息队列,前端进行轮询操作提升系统防攻击的能力,如:ip限流、防ddos攻击和防爬虫3.4、DevOps架构层面的进一步探索:
redis集群的演进,单机redis->多节点redis->codis(豌豆荚自研redis proxy模式)->redis cluster项目构建的演进,本地构建->shell脚本拉代码服务器构建->jenkins构建项目部署的演进,基于xshell多屏终端部署ECS->docker部署->k8s部署物理架构的演进,多机多实例->多机docker容器->docker swarm编排->k8s编排。最终我们全面使用云k8s,拿掉了大部分的springcloud组件,研发人员几乎不需要进服务器运维操作。
四、技术专项4.1、缓存改造4.1.1、老版缓存存在的问题:
高并发的瞬间,存在缓存击穿的问题,诸如影院列表按距离排序的查询,若流量冲到数据库,系统将面临宕机的风险基于spring注解的缓存模式,参数的样本存在不确定性,如参数带有经纬度的时候,可能会将redis内存打爆4.1.2、改造之后4.2、防攻击和爬虫4.2.1、防ip攻击如上文介绍,slb做tcp端口的监听,对nginx log进行定时扫描,将恶意ip拉黑到防火墙名单,同时利用netstat命令,定时扫描ip连接的情况(slb做http监听的话,客户端ip跟服务器非直连,过来的是代理ip,无法配合防火墙拉黑)web服务进一步根据userId进行限速拦截,同理,将恶意用户的ip拉黑到防火墙,同时将其拉黑到redis黑名单,双重拦截(java代码调用系统命令,将ip添加到iptables)拉黑防火墙的目的,大量恶意攻击的情况下,应该从服务器网卡层面直接杜绝ip请求,否则严重影响网络带宽和连接4.2.2、防爬虫接口增加验签手段,前端代码混淆(增加加解密算法的破解难度),定期更换验签的算法(根据经验来看,破解加签的js代码,通常一个星期之内能完成)增加接口承前启后的标志,如,场次列表接口需依赖影院详情接口返回的标识,此标识可能仅仅作为识别爬虫的标识识别为爬虫的时候,不返回异常,不返回空,返回假的数据,避免打草惊蛇猫眼之前爬我们的数据,来回博弈几个月,最终他们用几百万的ip,严格控制访问的频率,接口参数全部正常模拟,一度让我们陷入僵局。
最终我们找到了破绽,由于爬虫的数据有时效性的要求,他们不能爬的太慢,也就是说,爬虫期间,系统整体的qps比平时高一些。
我们按接口分类,按时段分类,评估了各个接口分钟级的qps,一旦发现qps超过阈值,则触发验证码机制,某ip的访问,当天只需输入正确一次验证码即可。
同时,我们将验证码输错三次的ip,或者漏传验证码的ip,拉入黑名单。一个月下来,我们拉黑了一百多万的ip,也许爬虫方觉得ip成本太大,最终放弃了爬虫。
4.2.3、正向爬虫为了丰富系统的数据,我们从第三方平台爬取一些电影相关的信息,如短视频、评价、评分、海报、演员等信息,这类信息属于公开市场的信息,爬虫比较简单,使用第三方ip代理池,控制速率即可。
为了丰富院线的购票需求,我们也会爬虫一些其他票务商的数据,当然包括代理购票和代理支付,正如上面防爬虫章节提到的步骤,我们对js加解密的代码进行了破解,对接口参数进行了模拟,控制请求的速率,大部分时候这些手段足够应付。当这些手段无效的时候,我们采取了终极手段,java配合webdriver真实模拟请求。(打车场景,滴滴的接口也是这么被破掉的)
4.3、搜索改造系统主要支持两大类搜索:
影片搜索,总共影片不超过1万部,有场次的电影不超过500部,需根据影片名称、演员、导演等维度进行搜搜影院搜索,总共影院在1万家左右,按城市划分,最多的城市有300多家,需根据影院名称、地址等维度进行搜索关键阶段:
阶段
方案
缺点
mysql查询
基于mysql文本的like查询
无法利用索引,查询效率低下无法按字段进行权重排序es查询
将影片和影院的数据同步到es,调用es进行查询
云es有免费额度限制,qps大于100或者每日调用次数超过限额则收费查询效率较低redis search查询
引入redis search中间件,将数据同步到中间件
需要自行维护中间件的部署中间件依赖的jedis版本较低,和项目代码不兼容,需单独抽出一个服务权重搜索支持的不够友好lucene搜索
考虑到数据量不大,在项目启动的时候,将数据同步到lucene引擎,走本地lucene搜索
增加项目的复杂程度额外增加机器资源的消耗该场景特点:数据量不大,有权重搜索的要求,且对并发和性能有一定要求。在这种场景下,最优解还需探索。
五、进一步思考5.1、系统层面主题
描述
思考
缺乏打点的能力
系统没有打点监控的机制,业务指标全靠日志统计
借鉴cat,给系统提供全方位的监控能力,可以用micro meter influxdb grafana的组合实现
缺乏链路追踪的能力
无法将多个服务之间来回请求的链路串到一起,目前依靠时间和userId来区分,量大的时候靠猜
借鉴mtrace,可以利用市面现有的技术,如:skywalking或zipkin,给系统提供分布式链路追踪的能力
告警能力时效性差
系统告警的方式停留在发邮件和发短信的阶段,关注者彼此独立,管理者无法知晓该告警是否被处理
借鉴大象告警能力,可以结合钉钉开放api实现,告警可以按照个人和群组进行发送,提高关注面,给与实时反馈
加大code review力度
目前的代码审核机制比较简单,也比较随意,取决于组长的严格程度,无小组和团队范围的code review
code review机制,互相code review,分组code review
缓存版本号
目前的缓存没有版本号的概念,只有覆盖操作
菜品任务同步和票务数据任务同步的异同,缓存版本号值得借鉴,菜品:全量 增量,票务:全量(无法增量,无通知),系统会基于基础数据计算聚合缓存,若引入版本号的概念,聚合缓存该如何处理?
影院列表优化
目前的做法是,在任务阶段,将各个城市的影院列表的缓存计算出来,存到一个key。使用的时候,整个取出来,然后根据用户位置进行距离排序,进而分页
存在大value的问题,好在这些列表的数量是确定的,使用了二级缓存来缓解redis的压力。可以参考菜品分页的做法,增加simple列表,分页之后,用multi get命令取detail信息。
数据同步任务优化
目前的任务是一台4C16G的服务器来执行,将数据全量加载到内存,然后计算各种缓存,大概占据9G的内存
票务数据任务同步进一步优化,分城市、分影院、分机器,同时执行,且对一些异常中断进行任务补偿
接口异步化存在的风险
接口异步化也会带来负面影响,当mq队列大量积压消息的时候,轮询时间可能呈现倍数增长,比如用户请求轮询10秒、20秒才能拿到结果,根据实际情况,5秒拿不到结果,用户可能就会刷新页面,等待的时候只会越久。
目前的处理办法是,mq集群化,提高消费速率,代码层面,丢弃超过一定时长的消息,如,消费者拿到消息,判断时间,若是5秒之前的,则直接结束处理,前端轮询也设置5秒的上限。
应该寻求更好的方式。
5.2、业务层面主题
描述
思考
防黄牛
黄牛组织特定群定薅羊毛,然后将优惠券转销,使得平台补贴不能给到真实的用户黄牛钻优惠券空子,如限价60的10元观影券,黄牛专挑59.9的场次购买,导致我方亏损黄牛跟影院勾结,开通虚拟场次,利用优惠券购票,也会造成资金亏损如何有效地打击黄牛,是一个长期的话题。目前的做法只能在事后通过数据分析,发现可疑的影院,然后将其下架。但这么做不能从根本上解决问题,下一次又会出现新的一批影院。(偏远地区的小影院)
盈利模式
技术服务费票务商佣金银行支付通道抽成优惠券集采,通过采购价和限价进行博弈其中优惠券集采的收入占大头,但是小银行几乎无营销费用,所以不存在这种收入。国内小银行的数量不计其数,是否可以按年费的形式进行合作,同时系统增加saas能力,能快速接入一家小银行?
用户导流
目前访问过系统的总用户量有3000万,大部分都是持卡用户,用户质量相对来讲还不错
能否将这些用户进行分类,比如按照下单、参加活动进行区分,然后定向营销,目的性地导流到其他场景,进一步提高公司的流水?
评论
- 评论加载中...