@zhangnian88123
2017-12-20T17:02:41.000000Z
字数 2797
阅读 5863
挠挠是一款个性化电商APP(https://itunes.apple.com/us/app/nao-nao/id1062732158),简单来说就是场景化电商加推荐。
在架构设计过程中,有几个设计考量指导整个设计与决策过程:
无状态
架构上各层应该设计为无状态的,这样的好处是可以方便的实现水平伸缩。对于API接口层,通过nginx配置反向代理的方式可以实现负载均衡;对于服务层,通过部署多个实例并注册到服务注册中心,客户端调用时从服务注册中心查询出服务地址,再根据负载均衡策略选择其中一个服务进行调用。
高可用
微服务本身就是一种高可用的方案,通过部署多个实例可以避免一个服务实例宕机导致整套服务不可用,另外,一些基础服务会提供冗余方案,比如短信服务本身支持多个短信通道,避免某个第三方通道出现问题时导致整个服务不可用。
异步化
引入消息队列主要带来以下几个好处:
从上图可以看到,整个APP后台是分层架构,每一层的职责如下:
系统的唯一入口,基于Nginx + OpenResty实现,利用Nginx的反向代理功能实现流量向后层服务转发,利用LUA脚本在HTTP请求响应生命周期的各处理阶段实现一些业务独立的逻辑,比如:
业务服务的入口,按照业务模型进行划分,比如用户API、商品API、订单API等,这一层本身不做具体的业务逻辑处理,而是调用下层的业务服务或通用服务进行组合去实现具体的业务逻辑,类似于设计模式中的Facade模式。比如订单API中的下单API,需要调用用户服务、商品服务、计价服务、优惠服务、购物车服务等共同协作完成一次下单操作。
这一层使用python flask框架实现,并且对其进行了一些定制开发从而进一步提高开发效率,比如统一异常处理逻辑、重写Response对象的处理方法以实现在视图函数中直接返回list和dict、在请求周期的hook函数内集成influxdb和sentry进行统计数据和异常数据上报等。
提供业务相关的小粒度API,由于存储层也根据业务模块进行了分库所以每个业务服务只会访问自己相关的数据库,不会跨数据库访问,如果需要访问其他业务模块的数据,就需要通过业务服务API去获取数据。比如订单服务就只能访问订单库,不能直接访问商品库,只能通过商品服务提供的API去获取商品数据。
服务层的框架使用Python ZeroRPC库,并对其进行了封装,利用其中间件机制增加了基于Zookeeper的服务注册于发现机制,从而实现高可用的多实例部署和客户端负载均衡。
提供一些公共和通用的基础服务,比如邮件服务、短信服务、推送服务等。 由于很多核心业务流程依赖于基础服务,所以需要提供更高的可用性和可靠性,比如短信服务需要调用第三方SDK进行短信发送,为了在某个第三方短信通道发生意外故障时也能保证短信服务可用,因此在服务设计上采用多通道的方式,当首选短信通道不可用时,自动在备用通道进行重试,通过冗余的方式实现服务的高可靠,避免SPoF。
这一层引入RabbitMQ和Beanstalkd消息队列实现,主要用于将一些可以异步化处理的操作从业务流程中独立出去,从而减短业务流程的调用链路以降低延时,同时利用消息队列进行削峰,避免突发大流量冲垮后端。比如提现申请接口将真正的提现操作放入队列中执行,这样用户的提现申请可以很快返回,然后后台的工作进程从队列中取出任务去真正执行提现业务逻辑。消息队列的另外一个作用就是并发,比如短信服务将短信任务放入队列即可返回,后台有多个工作进程并发的去处理短信任务。引入Beanstalkd的原因主要是利用它提供的delay消息功能,用于实现一些超时类逻辑,比如下单30min之后不进行支付,订单状态就需要更改为已取消,如果使用常规轮训DB的方式去判断订单是否超时,会增加DB压力和降低效率,而利用Beanstalkd就可以设计为:
1.下单服务插入订单表,返回订单ID
2.将订单ID和其他订单相关数据封装为一个消息放入beanstalkd,并设置delay时间为30min
3.另外的工作进程从beanstalkd获取消息,当delay msg超时后,就会被工作进程取出
4.工作进程从消息中取出订单ID,判读订单状态,如果是未支付,就更改订单状态为已取消
使用Redis实现,主要用于缓存一些用户数据,比如登录Session,缓存不开启持久化功能。
存储层用到了三种类型的数据库:
业务服务、基础服务提供两种API调用方式:
(1)HTTP
(2)RPC
直接使用PaSS平台提供的监控系统,比如腾讯云监控,阿里云监控。
通过sentry sdk以UDP的方式上报至sentry,利用sentry的webhook机制和BearyChat集成,实现异常信息的实时消息推送。
使用Influxdb + Grafana,业务代码通过在其请求处理生命周期各阶段的hooks将一系列运行指标通过UDP发送给Influxdb,Grafana定时从Influxdb拉取数据并展示到Web Dashboard中。