1、场景描述

  比如说我们要做一款APP,需要通过api接口给app提供数据。假设我们是做商城,比如我们卖书的。我们可以想象下这个APP大概有哪些内容:

  1)首页:banner区域(可以是一些热门书籍的图片做推广)、本周热卖书籍区域、本月好评书籍区域、活动打折的书籍区域。。。

  2)排行榜:比如第一季度热销榜、新书版。。。

  3)书单:管理后台运营添加的书单,比如《程序员从入门到放弃》系列书单。。。

  4)用户相关的:比如用户个人信息设置、订单管理、消息管理、收藏的书籍。。。

  数据是保存在数据库中,考虑到高并发数据库的瓶颈,采用DB+缓存的服务器架构。

2、重要接口汇总

  看似简单的一个app,需要调用的api接口是非常多的,总结下大概有这几类接口:

1)列表接口:比如书单里面的书籍列表、排行榜的书籍列表;

2)详情接口:书籍的详细信息;

3)评论接口:书籍评论(这里可能要求购买了的才能评论)、星标;

4)点赞接口:给书籍点赞、给书单点赞;

5)收藏接口:收藏书籍、收藏书单;

6)“相关”接口:比如书籍《php从入门到放弃》相关的有哪些书籍;

7)关注接口:关注某本书或者书籍作者,一旦某本书有打折或者作者有新书,会推消息等等。或者是用户间互相关注;

8)发布接口:比如用户可以发布书单。A用户发布了书单,B用户可以关注A用户,A用户再发布新书单,会给B用户推消息等等;

9)搜索接口:查询书籍、查询书单、查询用户等等

3、后台管理系统

1)书籍信息的来源:爬虫抓取的数据、运营人员在管理后台添加;

2)书籍的价格、库存等信息,是可以在后台设置;

3)书籍的分类、书籍的标签,像爬虫如果抓取不到的,运营可以手动设置;

4)书籍的排序:在管理后台设置,用于在app上展示的先后顺序;

5)书单管理:运营可以添加、可以设置展示/隐藏;用户提交的书单,在后台进行审核;

6)用户的评论:在后台审核;

备注:管理系统还有很多功能,简单先写这几个。

4、api接口设计

接口设计要遵循的一个想法:可以先从缓存读取,读取不到,再去数据库读取,然后写回缓存。

1)banner数据: key-value类型(string类型)

redis key名: index:banners   这个key是首页上面的banner 可以设置一个缓存时间:1小时

里面保存的数据是从数据库获取出来的banner数据,json之后保存的字符串。

2)实时性问题

比如数据是在管理设置的,比如banner数据添加了一条,或者某一个banner数据被删除了或者改为不展示了,但是缓存里面还没变化,怎么处理?

【方案一】不做处理,等待缓存自己过期,然后再有请求获取的就是新的数据。缓存要有个过期时间,这个要看业务需要设置相应的缓存过期时间,比如5分钟。

【方案二】管理后台对banner有添加删除修改操作时,修改成功的同时把缓存中的数据也更新。这个方案不太赞成,因为这样子数据就有2个地方维护了,

一个是api接口访问的时候,如果缓存过期会回源写缓存,一个是管理后台;

【方案三】解决方案二的缺点,就是管理后台有数据增加删除修改的时候,把对应的缓存删掉,这样子api接口从缓存读取不到key就会重新生成缓存。这样也是比较实时的。

3)详情数据

详情信息可以放在缓存里面,key-value类型,设置一个过期时间

redis key名: book:detail:{book_id}

4)列表数据:key-value类型

【方案一】把每一页的数据缓存起来,这样api接口来获取的时候,直接把一页的数据从缓存取出来然后返回。

redis key名:index:book_list:{page}    缓存时间根据实际业务设置

假设每一页20条数据,这里保存的就是20条数据的json后的字符串,key名里面有页码,每一页的数据用不同的key名缓存起来。

这个方案有个问题,比如管理后台删除一条数据,刚好缓存重新生成了,那么第一页的20条数据肯定是最新的,但是第二页如果缓存没有重新生成的,

那么新的第一页和旧的第二页会有一条数据重复。当然,如果新增数据那么第一页的缓存和第二页的缓存数据中间有一条数据遗漏了。。。

【方案二】把id放入有序集合,每次获取列表,从集合中获取出20个id,然后去缓存中获取详情数据,然后返回。

管理后台如果有新增删除,那么就在集合里面新增或删除。 这里相比方案一,就是每次要去缓存获取20条详情信息,虽然缓存中获取很快但是也有一定的开销,但是数据最实时。

还有一个问题,感觉就是有点耦合,就是万一集合失效,那么要重建集合,假如数据很多,比如有几十万几百万个id,那么要去数据库获取,然后写入缓存,有这种隐患。

【方案三】不用缓存,采用像spinx、es这些搜索引擎。通常有些业务需要很多排序规则,比如sort字段、时间字段、销量字段等等等,这个时候如果硬是要用方案二,

就必须是把排好序的数据保存如有序集合,比如id=1,序号=1;id=15,序号=2;id=8,序号=3,每次有新增、删除,就要重新生成集合,很不好维护。

使用搜索系统来辅助,可以应该解决方案一和方案二的问题。当然了,看业务,如果不要紧的,方案一即可。像新浪博客、今日头条,之前也看到他们有些分页数据有重复的。

5)点赞接口

根据不同的业务要求,有些业务是只有点赞,没有取消点赞,我们就讨论这个有取消点赞的。

首先,假设我们有个业务是对书籍点赞,点赞之后可以取消点赞。比如说APP首页的列表里面需要点赞数,而且要实时,怎么处理?

我们肯定有个数据表来存储点赞信息,比如book_like 表,里面记录了用户对书籍的点赞信息。

鉴于点赞数如果每次用户接口请求都去数据库请求,会给数据库增加压力,我们使用redis来减压。这里使用hash类型,下面会讲为何。

redis key名:  book:like:{hash_id}  内容: field =》 value

这里的hash_id是 book_id / 1000 的结果

field 的值是 book_id % 1000的结果

value的值是点赞数

为何这种设计:hash相比string类型有个优势,在满足2个条件的时候会进行压缩,就是内存的占用会小很多。

这两个条件是hash里面的field的数量小于指定数量(貌似默认是512还是1024),另外一个条件是value值的大小要小于64字节(这个也是可以调整的,配置信息里面改)。

举个例子:

book_id = 100, 那么hash_id就是0,field是100,值是点赞数

book_id = 1001, 那么hash_id就是1,field是1,值是点赞数

列表接口里面有点赞数,可以从hash中获取

多次redis的get请求(get、hget等),可以用管道(pipeline)来一次性获取,也会提高速度

点赞的时候,数据肯定是要写入数据库的,有2种方案处理:

【方案一】

书籍表里面比如有个点赞数字段,每次点赞/取消点赞,就是往里面加1减1。

点赞表就是往里面增加或删除数据(可以是软删除)。

像php的laravel的eloquent模型是支持直接给字段+-1这样,不需要先取出数据再加。

【方案二】

先写入缓存,然后定期把缓存的数据取出来然后写入数据库。这个方案的话有点耦合了,比如需要定时脚本去写回数据库。

【方案三】

不使用缓存,数据直接更新回book表,或者book表不需要这个字段,需要点赞数的时候去book_like表计算,如果是有搜索引擎系统,那么应该不成问题。

6)相关接口

比如根据分类关联书籍。《php从入门到放弃》和《php从入门到奔溃》关联。

数据库里面可以根据分类id查找。简单的业务可以通过集合存储分类id下有哪些书籍。

如果复杂的业务,比如书籍有多个分类,比如分类id=1  , 分类名称:php,分类id=2 ,分类名称:程序开发,假设这2个分类是同级的,

那么可能有一些书是同时属于这2个分类的。

又或者有些业务下,是根据标签来关联,一个书籍可以有多个标签,标签和书籍是多对多关系。

此时又要来一句搜索系统可以处理。。。缓存也不是不能处理,感觉有些场景缓存处理起来很费劲。

以上只是一些很肤浅的看法,肯定有更好的处理方案,待研究补充上。

5、api接口设计总结

  考虑到高并发时数据库的瓶颈,所以需要把请求的结果缓存起来。这里的策略是:

1)从缓存中获取数据,缓存中有数据则直接返回,或者做简单处理然后返回;

2)缓存没有数据,则从DB中查找数据,然后写回缓存;这种情况叫做“回源”。

3)缓存的key是需要设置过期时间的,避免数据一直占用内存;

4)过期时间的设计,最好是打乱,避免同一时间有大量的key过期导致请求集中去DB请求导致雪崩;

5)根据业务需求,“回源”的时候考虑申请到缓存锁的请求去数据库获取数据并更新缓存,其他请求则sleep一段时间(比如5毫秒10毫秒)然后再去缓存请求,如果请求还是没数据,可以继续等待或者直接返回空数据。

6)没有必要缓存起来的字段,不要缓存;

7)APP不需要用到的字段没有必要返回给APP,比如评论的审核时间、审核者,返回给APP是没用的,因为这些数据不需要展示出来,不会被用到;

8)减少api请求的次数,比如多次请求,要看下能否合并,比如首页,有好几个区域,每个区域都有几条数据。当然多次请求一样可以实现,但是请求次数少,则服务器可以接受更多客户端的请求。

API接口设计的更多相关文章

  1. Web API接口设计经验总结

    在Web API接口的开发过程中,我们可能会碰到各种各样的问题,我在前面两篇随笔<Web API应用架构在Winform混合框架中的应用(1)>.<Web API应用架构在Winfo ...

  2. atitit.基于http json api 接口设计 最佳实践 总结o7

    atitit.基于http  json  api 接口设计 最佳实践 总结o7 1. 需求:::服务器and android 端接口通讯 2 2. 接口开发的要点 2 2.1. 普通参数 meth,p ...

  3. Web API接口设计(学习)

    1.在接口定义中确定MVC的GET或者POST方式 由于我们整个Web API平台是基于MVC的基础上进行的API开发,因此整个Web API的接口,在定义的时候,一般需要显示来声明接口是[HttpG ...

  4. Java生鲜电商平台-API接口设计之token、timestamp、sign 具体架构与实现(APP/小程序,传输安全)

    Java生鲜电商平台-API接口设计之token.timestamp.sign 具体设计与实现 说明:在实际的业务中,难免会跟第三方系统进行数据的交互与传递,那么如何保证数据在传输过程中的安全呢(防窃 ...

  5. API接口设计之token、timestamp、sign 具体架构与实现(APP/小程序,传输安全)

    Java生鲜电商平台-API接口设计之token.timestamp.sign 具体设计与实现 说明:在实际的业务中,难免会跟第三方系统进行数据的交互与传递,那么如何保证数据在传输过程中的安全呢(防窃 ...

  6. 优秀的API接口设计原则及方法(转)

    一旦API发生变化,就可能对相关的调用者带来巨大的代价,用户需要排查所有调用的代码,需要调整所有与之相关的部分,这些工作对他们来说都是额外的.如果辛辛苦苦完成这些以后,还发现了相关的bug,那对用户的 ...

  7. 微信小程序的Web API接口设计及常见接口实现

    微信小程序给我们提供了一个很好的开发平台,可以用于展现各种数据和实现丰富的功能,通过小程序的请求Web API 平台获取JSON数据后,可以在小程序界面上进行数据的动态展示.在数据的关键 一环中,我们 ...

  8. php后台对接ios,安卓,API接口设计和实践完全攻略,涨薪必备技能

    2016年12月29日13:45:27    关于接口设计要说的东西很多,可能写一个系列都可以,vsd图都得画很多张,但是由于个人时间和精力有限,所有有些东西后面再补充   说道接口设计第一反应就是r ...

  9. API 接口设计中 Token 类型的分类与设计

    在实际的网站设计中我们经常会遇到用户数据的验证和加密的问题,如果实现单点,如果保证数据准确,如何放着重放,如何防止CSRF等等 其中,在所有的服务设计中,都不可避免的涉及到Token的设计. 目前,基 ...

随机推荐

  1. ZROI 19.08.07模拟赛

    传送门 写在前面:为了保护正睿题目版权,这里不放题面,只写题解. "正睿从来没有保证,模拟赛的题目必须原创." "文案不是我写的,有问题找喵老师去."--蔡老师 ...

  2. maven项目解决pom.xml头部 http://maven.apache.org/xsd/maven-4.0.0.xsd报错的问题

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/qq_36611526/article/d ...

  3. POJ 1236 学校传数据 强连通+缩点+DAG

    题意描述: 网络中有一些学校,每个学校可以分发软件给其他学校.可以向哪个分发取决于他们各自维护的一个清单. 两个问题 1:至少要copy多少份新软件给那些学校, 才能使得每个学校都能得到. 2:要在所 ...

  4. 算法设计与分析 1.2 不一样的fibonacci数列 (矩阵快速幂思想)

    题目描述 Winder 最近在学习 fibonacci 数列的相关知识.我们都知道 fibonacci 数列的递推公式是F(n) = F(n - 1) + F(n - 2)(n >= 2 且 n ...

  5. git强行覆盖master分支

    目录 我遇到的场景 需要注意的预备操作 操作步骤 我遇到的场景 1.master分支只为护较早的版本 2.由于业务不稳定,新业务和功能都在dev 分支上,dev的开发周期很长,一直变更迭代 3.从de ...

  6. Array Stack Implement using C

  7. Python的sys.argv用法

    import sys a = sys.argv[:] print("输入的参数为:", a) def train_start(start_time, end_time, selec ...

  8. WPF 自定义按钮 Style

    <Style TargetType="{x:Type Button}" x:Key="DefaultButton"> <Setter Prop ...

  9. Java定时器Timer

    Java定时器Timer在JDK库中,Timer类主要负责计划任务的功能,也就是在指定的时开始执行某一个任务.Timer类的主要作用就是设置计划任务,但封装任务的类却是TimerTask类,执行计划任 ...

  10. Delphi XE2 之 FireMonkey 入门(45Finally) - 结题与问题

    Delphi XE2 之 FireMonkey 入门(45Finally) - 结题与问题 很喜欢 FMX 的一些新控件, 如: TExpander.TArcDial.TComboTrackBar.T ...