21.app后端如何高效更新内容
在app的主页或通知栏,经常需要通过api取最新的数据。那么,怎么在这部分上做优化,使获取内容的效率更高呢?在本文中,通过推拉和增量更新,实现了一种高效获取数据的策略。
1.高效更新数据策略在app中的应用场景
如图所示,在app首页,经常会有这种瀑布流形式的内容,具体可参考新浪微博的app。
这种内容有以下的特点:
1. 用户访问的频次好(首页一般都经常访问)
2. 看上去数据量很大
那么,怎么获取才能高效获取这种首页数据呢?
高效的关键是两点:
1. 降低访问的频次
2. 减少数据的传输量
内容的推拉和增量更新就能实现高效的关键。
2.什么是内容的推拉?
在一般的app设计中,如果需要知道首页是否有内容更新,就需要通过一个轮询机制访问api,才能知道是否有内容更新。
但是,轮询的缺点也很明显:
1. 耗电
2. 耗流量
轮询是很典型的拉模式,每隔一段很短的时间,就从服务器上拉数据到app,不管实际上有没有数据更新。这样会耗费大量的网络流量,同时也增大了服务器的压力。
怎么才能减少轮询的次数。
答案就是通过推模式。在服务器上,每当知道有数据更新了,就通过推送系统,告诉相关的app,你有数据更新啦,快点来取啦。当app收到这个数据更新的通知后再调用api,获取相应的数据。
当然了,不能只用推模式,因为手机的网络环境的复杂性,不能保证数据更新的通知一定会到达app,所以也要采用轮询的方式来定期拉数据。当然了,使用推拉结合轮询的时候可以设置得比较长,因为主要是以防万一用的。
通过这种推拉结合的模式,就能大大减少访问的频次和更新的数据量。
3.什么是增量更新?
在新浪微博的app中,从别的页面进入主页,在没有网络的情况下,首页中的已经收到的微博还是能显示的,这显然是把相关的数据存储在app本地。
使用数据的app本地存储,能减少网络的流量,同时极大提高了用户的体验(想想,很多数据都能在app本地获取,显示的速度当然快)。使用了本地存储后,需要考虑的是数据的增量更新方案。
什么是数据的增量更新?假设,用户A的首页在数据表中是有40条数据,id1-40,app每次获取10条数据。第一次运行,app从数据表获取了id1-10条数据同时存储在本地。假设用户离开了这个页面再回到首页,这时app需要再次从数据库中获取数据,由于之前已经有10条数据(id1-10)存储在app本地了,那么现在需要从数据库中获取的10条数据就是从剩余的30条中数据获取(id11-40)后并保存在app本地。这个就是增量更新的典型例子。
增量更新的原理是在数据库中,每条数据都必须有update_time这个值,记录数据最后更新的时间,当app从服务器获取了一次数据后(返回的数据必须按时间排序,update_time最近的在第一条),记录下第一条数据的update_time,当再次获取数据就只需要获取上个时间点到访问服务器这刻为止所更新的数据即可。
因为分页机制的存在,这个算法实现起来是挺多需要注意的地方,下面我举一个简化的例子详细说明:
一些假设:
1. app每次请求都带4个参数
http://test/api/timeline?count=3&page=1&since=1100&max=1200
count: 每页的显示条数(默认为3)
page: 当前页码(默认为1)
since: 时间戳,若指定此参数,则返回时间戳大于等于since的结果(应该是上次获取的最新数据的update_time)
max: 时间戳,若指定此参数,则返回时间戳少于等于max的结果(应该是发送时的时间)
在sql的查询时,使用条件 since<=update_time<= max
2. api 返回的数据包含
{
"size": 10, //实际返回的数据量(因为分页获取的缘故,所以经常少于total值)
"total": 284, //应该返回的总数据量
"page": 1,
"count": 3,
"max": 0, //max为获取的最后一条数据的update_time
"since": 0
},
{ //返回的数据实体
data:.......
}
3. app存储的本地数据中的update_time是指服务器中的这条数据的更新时间,不是指app中这条数据的更新时间。
现在开始讨论:
(1)当app安装完毕后还没启动,服务器的数据表中的数据为3条,app存储的本地数据为空
服务器的数据表的数据
|
id |
update_time |
|
1 |
1100 |
|
2 |
1101 |
|
3 |
1101 |
app存储的本地数据
|
id |
update_time |
(2)当app第一次运行(时间为11:05),因为是第一次运行,since为0,max为现在的时间点1105,在服务器的数据表中获取所有数据。
发送的请求为:http://test/api/timeline?count=3&page=1&since=0&max=1105
(3)从(2)中发送请求后,api的返回数据,服务器的数据表中的数据,app存储的本地数据如下:
api返回的数据
{
"size": 3, //实际返回的数据量
"total": 3, //应该返回的总数据量
"page": 1,
"count": 3,
"max": 1101,
"since":0
},
{ //返回的数据实体
data:.......
}
服务器的数据表的数据
|
id |
update_time |
|
1 |
1100 |
|
2 |
1101 |
|
3 |
1101 |
app存储的本地数据
|
id |
update_time |
|
1 |
1100 |
|
2 |
1101 |
|
3 |
1101 |
这里是策略的重点(1): api返回数据中的max必须为最后一条数据的update_time
(4)现在的时间是11:20,用户点击了页面中“获取更多”的按钮,app应该从服务器的数据表中拉取数据,在发送请求前,服务器的数据表中的数据如下:
服务器的数据表的数据
|
id |
update_time |
|
1 |
1100 |
|
2 |
1101 |
|
3 |
1101 |
|
4 |
1118 |
|
5 |
1118 |
|
6 |
1119 |
|
7 |
1119 |
可看到,比起上次拉取数据的时候,服务器的数据表多了id为4,5,6,7的数据。
这时发送api请求,策略的重点(2):当api的返回数据size=total时,since值比上次获取大一点,因为这时数据已经获取完整了,没必要重复获取数据上次已经获取的数据(记得条件since<=update_time<= max 吗?)所以since值设置为1101+1=1102,max为现在的时间点:1120,请求的url如下:
http://test/api/timeline?count=3&page=1&since=1102&max=1120
发送请求后api的返回数据和app存储的本地数据如下:
api返回的数据
{
"size": 3, //实际返回的数据量(因为分页获取的缘故,所以经常少于total值)
"total": 4, //应该返回的总数据量
"page": 1,
"count": 3,
"max": 1119,
"since":1102
},
{ //返回的数据实体
data:.......
}
app的数据:
|
id |
update_time |
|
1 |
1100 |
|
2 |
1101 |
|
3 |
1101 |
|
4 |
1118 |
|
5 |
1118 |
|
6 |
1119 |
这里是策略的重点(3):在数据库中,update_time为1101~1120的数据有4条,但由于分页的缘故,只获取了3条(从size和total参数可以判定),这意味着1101~1120这段时间的数据没有获取完整,app所获取的最后一条数据的update_time是1119,服务器的数据表中剩下的没有被app获取的数据有两种情况:
a.update_time刚好是1119
b.update_time大于1119
由于我们没法判断属于哪种种情况,如果我们下次拉数据的时候 since大于1119,服务器的数据表中id为7的数据不会再获取,那么会造成app中丢失了id为7的数据,所以针对上次数据获取不完整的情况,下次获取数据时since必须是等于1119,虽然有可能会获取重复的数据。
(5)现在的时间是11:30,用户点击了页面中“获取更多”的按钮,app应该从服务器的数据表中拉取数据,在发送请求前,服务器的数据表中的数据如下:
服务器的数据表的数据
|
id |
update_time |
|
1 |
1100 |
|
2 |
1101 |
|
3 |
1101 |
|
4 |
1118 |
|
5 |
1118 |
|
6 |
1119 |
|
7 |
1119 |
|
8 |
1120 |
这时发送api请求,这里是策略的重点(4):当api的返回数据size少于total,为了避免有数据丢失,since为上次收到api的返回数据的max值:1119,max为现在的时间点:1130。关于策略重点(4),请结合策略的重点(3)一起理解。
请求的url如下:
http://test/api/timeline?count=3&page=1&since=1119&max=1130
发送请求后api的返回数据和app存储的本地数据如下:
api返回的数据
{
"size": 3, //实际返回的数据量(因为分页获取的缘故,所以经常少于total值)
"total": 3, //应该返回的总数据量
"page": 1,
"count": 3,
"max": 1120,
"since":1119
},
{ //返回的数据实体
data:.......
}
这是策略的重点(5):api中返回数据中id为6的数据,在app的本地数据中已经存在,对于这条数据,app端应该放弃重复插入。
最后app存储的本地数据如下:
app的数据:
|
id |
update_time |
|
1 |
1100 |
|
2 |
1101 |
|
3 |
1101 |
|
4 |
1118 |
|
5 |
1118 |
|
6 |
1119 |
|
7 |
1119 |
|
8 |
1120 |
ok,整个增量更新的策略已经分析完毕了。在这个策略中,page参数几乎没用,之所以要保留,是为了兼容分页不带since,max的情况。对于这个增量更新的策略,请仔细理解策略的重点(1)(2)(3)(4)(5)的分析。
增量更新的策略,还要处理一个删除数据的同步问题。假设,在服务器的数据表要删除一条数据,怎么通知app本地也删除这条数据。我们的解决方案是服务器的服务器的数据表中增加一个标识is_delete,当需要在业务逻辑上删除的时候,把这条数据的is_delete设为1,同时更新update_time。当app增量更新检测到这条is_delete为1的数据,就在app本地数据中把这条数据删除。为了避免在服务器保存太多的数据,在服务器设置一个crontab,定期把那些已经标识is_delete设为1的数据删除。
---------------------------------------------------------------------------------------------------------------------------
打开链接 app后端系列文章总目录 总目录 ,能查看本人发表过的所有原创“app后端”文章。
【作者】曾健生
【QQ】190678908
【app后端qq群】254659220
【微信公众号】 appbackend
【新浪微博】 @newjueqi
【博客】http://blog.csdn.net/newjueqi
版权声明:本文为博主原创文章,未经博主允许不得转载。
21.app后端如何高效更新内容的更多相关文章
- app后端设计--总目录 (转)
特此说明,我转载的!!! app后端设计(1)--api app后端设计(2)--xmpp的使用 app后端设计(3)--短信,邮件,推送服务 app后端设计(4)-- 通讯的安全性 app后端设计( ...
- app后端设计--总目录
做了3年app相关的系统架构,api设计,先后在3个创业公司中工作,经历过手机网页端,android客户端,iphone客户端,现就职于app云后端平台bmob(想了解bmob点击这里).其中的乐与苦 ...
- [置顶] app后端设计--总目录
版权声明:本文为博主原创文章,未经博主允许不得转载. 做了3年app相关的系统架构,api设计,先后在3个创业公司中工作,经历过手机网页端,Android客户端,iphone客户端,现就职于app云后 ...
- app后端设计(2)--xmpp的使用(2014.01.14更新)
在app中有时候是需要添加聊天服务,在这里谈谈曾经开发聊天服务的经验: (1)聊天服务端选的openfire,这是一个基于xmpp协议的聊天服务器(XMPP是一种基于XML的协议,它继承了在XML环境 ...
- **app后端设计(10)--数据增量更新(省流量)
在新浪微博的app中,从别的页面进入主页,在没有网络的情况下,首页中的已经收到的微博还是能显示的,这显然是把相关的数据存储在app本地. 使用数据的app本地存储,能减少网络的流量,同时极大提高了用户 ...
- app后端设计(11)-- 系统架构(2014.12.05更新)
个人认为,在小型的创业团队中,特别是以应用产品为主,在架构后台的时候,需要集中精力解决自身业务上的问题,不是花时间解决第三方已经解决的问题,简单点来说,就是能用第三方服务就使用第三方的服务.基于这个原 ...
- app后端设计(10)--数据增量更新
在新浪微博的app中,从别的页面进入主页,在没有网络的情况下,首页中的已经收到的微博还是能显示的,这显然是把相关的数据存储在app本地. 使用数据的app本地存储,能减少网络的流量,同时极大提高了用户 ...
- app后端设计(3)--短信,邮件,推送服务(2014.12.05更新)
在app的后端设计中,免不了消息的推送,短信,邮件等服务,下面就个人的开发经验谈谈这方面. (1)最重要的是,各种推送一定要放在队列系统中处理,不然会严重影响api的响应时间. (2)短信方面 以前我 ...
- app 后端技术
app 后端技术 一直以来工作的方向是web server,对app server没有什么了解.虽然没有接触过移动app开发,但对app后端技术还是挺有探索欲望的,app应用和web应用在前端的用户习 ...
随机推荐
- 链路层 - SLIP,PPP,
最常使用的封装格式是RFC 894定义的格式.图2 - 1显示了两种不同形式的封装格式.图中每个方框下面的数字是它们的字节长度. 两种帧格式都采用48 bit(6字节)的目的地址和源地址( 8 0 2 ...
- 属性动画基础之ValueAnimator
概述 属性动画是谷歌在android3.0(API level 11)时候给我们带来了属性动画,真正意义上带来了"动画",以前的帧动画也就4中效果的组合(旋转.淡入淡出.放大缩小. ...
- How to configure ODBC DSN to access local DB2 for Windows
How to configure ODBC DSN to access local DB2 for Windows MA Genfeng (GuangdongUnitoll Services inco ...
- ORACLE数据库部分面试题目
1. 解释冷备份和热备份的不同点以及各自的优点 解答:热备份针对归档模式的数据库,在数据库仍旧处于工作状态时进行备份.而冷备份指在数据库关闭后,进行备份,适用于所有模式的数据库.热备份的优点在于当备份 ...
- Mybatis 系列2
上篇文章 写了一个Demo简单体现了一下Mybatis的流程.本次,将简单介绍一下Mybatis的配置文件: 上次例子中,我们以 SqlSessionFactoryBuilder 去创建 SqlSes ...
- MySQL下载安装配置和Navicat for MySQL的安装配置
MySQL 一.下载 地址:MySQL :: Download MySQL Installer 选择那个几百M的msi文件下载 二.安装 第一步: 安装许可 双击安装文件,在如下图所示界面中勾选&qu ...
- 正则表达式re模块小结
re模块的常用方法 1.compile(pattern[,flags]) 创建模式对象,一般配合其他方法使用.例如: import re #导入re模块 text = 'All that doth f ...
- 网络-udp
1. 网络:把双方或者多方的设备(电脑,智能手机,ipad等)连接起来的一个工具 1.1 学习网络的目的: 通过网络把数据从一方传递到另外一方,完成数据的共享 2. ip地址 2.1: ...
- UML类图10分钟快速入门 - From 圣杰
虚线箭头指向依赖: 实线箭头指向关联: 虚线三角指向接口: 实线三角指向父类: 空心菱形能分离而独立存在,是聚合: 实心菱形精密关联不可分,是组合: 原文作者:圣杰 原文地址:http://www.j ...
- vue config.js配置生产环境和发布环境不同的接口地址问题
第一步,分别设置不同的接口地址 首先,我们分别找到下面的文件: /config/dev.env.js /config/prod.env.js 其实,这两个文件就是针对生产环境和发布环境设置不同参数的文 ...