提升用户体验-巧用-Feed-Redis-成功微博-流配置-提升网站性能 (提升用户体验感)
一、背景
最近接到一个需求,用一句话来说就是:展现关注人颁布的灵活,这个触及到feed流系统的设计。本文关键引见一个普通企业可用的Feed流处置打算。
二、相关概念
上方先引见一下关于Feed流的便捷概念。
1.什么是feed流
2.feed流分类
Feed流经常出现的分类有两种:
三、设计
设计一个Feed流系统,两个关键步骤,一个是Feed流的初始化,一个是推送。关于Feed流的存储其实也是一个外围的点,然而笔掌管久化经常使用的还是,后续可以思索提升。
1.Feed流初始化
Feed流【关注页Feed流】的初始化指的是,当用户的Feed流还不存在的时刻,为该用户创立一个属于他自己的关注页Feed流,详细怎样做呢?其实很便捷,遍历一遍关注列表,取出一切关注用户的feed,将feedId寄存到的sortSet中即可。这外面有几个关键点:
2.推送
经过上方的初始化,曾经把feed流放在了redis缓存中了。接上去就是须要更新feed流了,在上方四种状况须要启动更新:
3.颁布/删除Feed流程
上方四步详细怎样操作,会在上方的成功步骤中详细形容,在这里先咱们重点讨论一下第一、二种状况。由于在处置大V【千万级别粉丝】的时刻,咱们是须要对大V的一切粉丝的feed流启动处置的,这时刻触及到的量就会十分渺小,须要多加琢磨。关于推送,普通有两种推/拉。
4.推拉联合形式
当用户颁布一条新的Feed时,处置流程如下:
当刷新自己的Feed流的时刻,处置流程如下:
至此,经常使用推拉联合形式的颁布,读取Feed流的流程都完结了。
5.推形式
假设只是用推形式了,则会变的比拟便捷:
6.两种形式总结
推拉联合存在一个弊病,就是刷新自己的Feed流时,大V的团体页Timeline的读压力会很大。
如何处置:
四、成功
笔主关键驳回纯推形式成功了一个普通企业基本可用的Feed流系统,上方引见一下详细的实现代码,关键包含3大个局部:
1.初始化Feed流
当用户第一出去刷新Feed流,且Feed流还不存在时,咱们须要启动初始化,初始化的详细代码如下:外围现实就是从数据库中load出feed信息,塞到zSet中,而后分页前往。
/***失掉关注的人的信息流*/publicList<FeedDto>listFocusFeed(LonguserId,Integerpage,Integersize){StringfocusFeedKey="focusFeedKey"+userId;//假设zset为空,先初始化if(!zSetRedisTemplate.exists(focusFeedKey)){initFocusIdeaSet(userId);}//假设zset存在,然而存在0值Doublezscore=zSetRedisTemplate.zscore(focusFeedKey,"0");if(zscore!=null&&zscore>0){returnnull;}//分页intoffset=(page-1)*size;longscore=System.currentTimeMillis();//按score值从大到小从zSet中取出FeedId汇合List<String>list=zSetRedisTemplate.zrevrangeByScore(focusFeedKey,score,0,offset,size);List<FeedDto>result=newArrayList<>();if(QlchatUtil.isNotEmpty(list)){for(Strings:list){//依据feedId从缓存中load出feedFeedDtofeedDto=this.loadCache(Long.valueOf(s));if(feedDto!=null){result.add(feedDto);}}}returnresult;}/***初始化关注的人的信息流zSet*/privatevoidinitFocusFeedSet(LonguserId){StringfocusFeedKey="focusFeedKey"+userId;zSetRedisTemplate.del(focusIdeaKey);//从数据库中加载以后用户关注的人颁布过的FeedList<Feed>list=this.feedMer.listFocusFeed(userId);if(QlchatUtil.isEmpty(list)){//保留0,防止空数据频繁查库zSetRedisTemplate.zadd(focusFeedKey,1,"0");zSetRedisTemplate.expire(focusFeedKey,RedisKeyConstants.ONE_MINUTE*5);return;}//遍历FeedList,把FeedId存到zSet中for(Feedfeed:list){zSetRedisTemplate.zadd(focusFeedKey,feed.getCreateTime().getTime(),feed.getId().toString());}zSetRedisTemplate.expire(focusFeedKey,60*60*60);}
2.关注的用户颁布/删除新的feed
每当用户颁布/删除新的feed,咱们须要更新该用户一切的粉丝的Feed流,该步骤普通比拟耗时,所以倡导异步处置,为了防止一次性性load出太多的粉丝数据,这里驳回循环分页查问。为了防止粉丝的Feed流过大,咱们会限度Feed流的长度为1000,当Feed流长度超越1000时,会移除最旧的Feed。
/***新增/删除feed时,处置粉丝feed流**@paramuserId新增/删除feed的用户id*@paramfeedId新增/删除的feedId*@paramtypefeed_add=新增feedfeed_sub=删除feed*/publicvoidhandleFeed(LonguserId,LongfeedId,Stringtype){IntegercurrentPage=1;Integersize=1000;List<FansDto>fansDtos;while(true){Pagepage=newPage();page.setSize(size);page.setPage(currentPage);fansDtos=this.fansService.listFans(userId,page);for(FansDtofansDto:fansDtos){StringfocusFeedKey="focusFeedKey"+userId;//假设粉丝zSet不存在,分开if(!this.zSetRedisTemplate.exists(focusFeedKey)){continue;}//新增Feedif("feed_add".equals(type)){this.removeOldestZset(focusFeedKey);zSetRedisTemplate.zadd(focusFeedKey,System.currentTimeMillis(),feedId);}//删除Feedelseif("feed_sub".equals(type)){zSetRedisTemplate.zrem(focusFeedKey,feedId);}}if(fansDtos.size()<size){break;}currentPage++;}}/***删除zSet中最旧的数据*/privatevoidremoveOldestZset(StringfocusFeedKey){//假设以后zSet大于1000,删除最旧的数据if(this.zSetRedisTemplate.zcard(focusFeedKey)>=1000){//失掉以后zSet中score值最小的List<String>zrevrange=this.zSetRedisTemplate.zrevrange(focusFeedKey,-1,-1,String.class);if(QlchatUtil.isNotEmpty(zrevrange)){this.zSetRedisTemplate.zrem(focusFeedKey,zrevrange.get(0));}}}
3.用户新增关注/敞开关注
这里比拟便捷,新增/敞开关注,把新关注的Feed往自己的Feed流中参与/删除即可,然而雷同须要异步处置。
/***关注/取关时,处置followerId的zSet**@paramfollowId被关注的人*@paramfollowerId以后用户*@paramtypefocus=关注unfocus=取关*/publicvoidhandleFocus(LongfollowId,LongfollowerId,Stringtype){StringfocusFeedKey="focusFeedKey"+userId;//假设粉丝zSet不存在,分开if(!this.zSetRedisTemplate.exists(focusFeedKey)){return;}List<FeedDto>FeedDtos=this.listFeedByFollowId(source,followId);for(FeedDtofeedDto:FeedDtos){//关注if("focus".equals(type)){this.removeOldestZset(focusFeedKey);this.zSetRedisTemplate.zadd(focusFeedKey,feedDto.getCreateTime().getTime(),feedDto.getId());}//取关elseif("unfocus".equals(type)){this.zSetRedisTemplate.zrem(focusFeedKey,feedDto.getId());}}}
上方展现的是外围代码,仅仅是为大家提供一个成功思绪,并不是间接可运转的代码,毕竟真正成功还会触及到很多其余的有关要紧的类。
关于新浪微博缓存的功能是怎么实现的
这个缓存其实说难也不难,我给你提供一个最简单的实现方式吧。 就是说每次更新数据之后都会留一个版本的信息好,如果有更新,那么这个版本的号也就更行了。 而你每次在tableview的datasource的值都是进行的一个追加的动作,也就是说,你原来有10条,数据。 现在刷新,发现有12条数据,那么这次更新的版本号就变了,然后你就取更行的两条数据过来(没变的数据不用管。 )然后再把这两条数据追加到你的datasource中,然后reload当前的tableview
诺基亚s60v3性能最强机型:提升用户体验的绝佳选择
随着移动终端市场的竞争加剧,各大手机厂商纷纷在技术创新方面下功夫,以迎合用户对于高性能、高效率、高质量的需求。 作为智能手机市场的先驱,诺基亚始终致力于研发具有领先性能的手机。 其中,s60v3系列的机型以其稳定可靠、适应性强等特点,成为广大用户追捧的对象。 诺基亚s60v3系列的机型在市场上拥有着广泛的用户群体,其颜值和实用性兼备,同时其强悍的性能和稳定的体验也代表了诺基亚在移动终端市场的技术实力。 但是,面对如此多的机型的选择,如何选出性能最强的机型成为了广大用户的关注焦点。 首先,我们可以从机型的处理器开始分析。 处理器是手机性能的重要组成部分,直接决定了手机的速度和用户的使用体验。 目前市面上使用最广泛的处理器是高通骁龙系列,而诺基亚s60v3系列的机型中,搭载骁龙系列的不多,但它却不失为一款出色的手机。 它采用了TI OMAP850处理器,虽然在处理速度上和骁龙系列的处理器相比稍稍逊色,但其多任务处理和电量消耗方面却有着突出的表现,这也是其成为性能最强的手机之一的原因。 系统内存也是手机性能的重要指标之一,诺基亚s60v3系列的机型在这方面也表现出色。 其采用了Symbian OS 9.1操作系统,内存大小通常在64MB到128MB之间,对于一般的用户使用已经足够了。 内存的支持直接影响了手机的运行速度、稳定性以及流畅度,s60v3系列的机型无疑在这方面提供了出色的性能保障。 在手机存储方面,诺基亚s60v3系列的机型可以支持外部TF卡的扩展,因此用户可以根据自己的需求来选择存储容量大小。 同时,s60v3系列的机型还支持蓝牙和无线局域网的应用和传输,满足了用户在数据传输和信息共享方面的需求,更加方便实用。 在外观设计方面,诺基亚s60v3系列的机型也有着很好的表现。 其机身轻便、便携,样式精美,色彩鲜艳,对于各类目标用户都有着一定的吸引力,同时它使用的彩屏技术也具备很高的清晰度和色彩还原度,出色地满足了用户在观赏性方面的需求。 总体而言,诺基亚s60v3系列的机型是一款非常实用的手机,无论是在性能方面还是外观设计方面都有着卓越的表现。 作为移动终端市场的佼佼者,它在稳定性和耐用性方面都有着很高的口碑,深受广大用户的喜爱。 在众多的s60v3系列机型中,虽然性能最强的机型并不好选择,但对于大多数用户而言,选择性价比最高的机型才是最重要的。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。