来tn后,一直在写业务代码,感觉不会遇到难点。然而最近的需求虽然仍是业务,但坑颇多,记下。
背景:CEO&董事长想在首页产品列表看到精华点评,好久之前就和产品说过。产品一直没有提上日程,老总心血来潮,让上这个功能。产品终于放在了心上,提了一个需求——苦了研发(me)
讲一下最初的情况:点评系统支持查询金华点评,但不支持批量(根据产品ID)查(因为以前点评都是在详情页,用户需要先点进产品详情页,再点击点评,才会看到点评,并发量不是很大,没有批量的必要)。然而,首页的产品列表是用户一打开app就会直接加载的,而且是产品列表,不是单个产品。另外,点评系统不稳定,性能不好,对方说,瓶颈在DB,不在IO,对方还说,只要老板加机器,就没有瓶颈。。。
然后我只能在for循环里一次调用点评系统。
研发经理知道这个需求很坑,但是是老大的需求,创造条件也得上!让我异步的调用后台,并在用户请求第一页的时候,异步调用下一页的点评。这里,会用到缓存(redis)。说道缓存,让人又爱又恨——它即提高了服务端响应速度(不用每次都查库||调接口),又增加了逻辑、程序复杂度(后面再讲)
首先讲经理的思路:
方案一:服务端每页十条数据,并发调用点评接口
方案二:客户端异步调用点评信息,但是app拒绝(很有道理,如果app自己调用,那还要我们干什么?另外,那一天老总明白过来,不想要点评了,已经发出去的app是收不回来的,服务端则可以自由的上功能、下功能)
最终选择了方案一,并决定v906才开始支持点评,v905及之前不用支持(坑1)。指定城市支持点评,其余不支持(坑2)
缓存方案:1 以十条产品为一个单位,存储下他们的所有点评
2 这十条产品分别存入缓存(因为1种的不同单位可能含有相同产品)
3 请求第一页时,异步请求下一页(该页放入缓存,该页每条产品也放入缓存)
我的解决方式:先从缓存里取1单位,如果有,皆大欢喜。如果没有,依次从缓存里取,并把缓存未命中的存到list里,最后异步从后台获取,并按缓存方案1,2设置缓存。
缓存是redis,异步方案采用spring rest + http client + hystrix(熔断、并发、降级)
异步下一页的方案是,构造一个事件,扔到阻塞队列里,开一个线程池,从队列里消费事件。消费的逻辑:判断缓存key是否存在,存在什么都不做,不存在,按“我的解决方式”来。
其实,还是有更好的方案的,点评后台提了两个:
1 他们把精华点评塞入redis,每天更新。缺点是万一缓存地址变了,我们这边除了该配置,重启,没有别的方法更改缓存地址(其实,可以使用jmx)
2 他们把点评塞入mq,及时性高。
但最终,点评后台还是选择了在规定时间内,提供批量查询的接口给我
关于点评,有关联线路点评这一说法,详情页都会取关联点评(即,点评不一定是该产品的点评。。。)。关联线路点评的productId与产品本身的productId不一样(坑3)
先说坑3:刚开始不支持批量时,我会从后台获取产品的第一条精华点评(该产品、关联线路,都会取),由于可能会是关联线路的,由于productId不一样,会让程序(其实是程序员-我)以为该产品没取到点评。在经理的提醒下,我手动把关联线路点评的productId设为该产品的productId。
后台提供批量查询接口的时候,也犯了这个错,他门也手动改了id(我这边就不改了)
坑2:由于预订城市是我这边可以得到的,但是是在 fun1(request) 函数里得到的,而我在fun2里做点评,调用fun1时,它并没有把cityCode返回,所以我把cityCode放入request中。由于先读缓存,缓存里有,就不会调用fun1,从而request.cityCode==0,即该城市不支持点评。最后,我把cityCode也存入了缓存(这个坑的缘由的发现也是经理,我发现了现象,但是本地写死了是1602(南京),所以本地始终没问题。)
坑3:版本控制。在预发布测时,由于是906(还未推向市场)的包,没有问题。但是到了线上,大量版本<906的请求过来,导致缓存里全是没有点评的产品信息,从而906版本读缓存时,没有点评!最后,按照是否需要点评分别缓存(这个坑的缘由的发现是我师傅达先生,写代码的我2个钟头没想到,他3分钟内想到了,姜还是老的辣)
由于今天是周五,想上线只能等到下周一,或者紧急上线。app今天封包,下周1,2,3回归,周3下班发包。想紧急上线,同事建议拖到周一晚上上线,因为紧急上线是记名的(你懂得)。但睡完午觉后,同事又提醒,下周一是会员日(每月16号), 不准上线,所以只能紧急。