一个人实现一套app系统(1.2: 架构)
2015-11-15李万胜风铃草科技
第 2 节 架构
内容摘要
本节将讲解系统的组成单元,我们从不同维度来解释模块的功能以及模块与模块之间的关系。所谓架构,就是抹去细节之后的大骨架,就如同还没有粘上纸的风筝,本节要做的就是梳理一下“风筝”这套系统的骨架。
系统架构分解图
客户端架构
业务服务架构
图片服务架构
聊天服务架构
推送服务架构
系统架构分解图
客户端
首先我们要把系统做粗粒度的分解,我们需要给用户展示我们系统的功能,并且需要与用户进行交互,这就是客户端的职责了。
维护这个客户端的除了客户端开发人员,还需要2个角色,一个是产品经理,一个是UI设计师。
产品经理负责把你的APP的功能用按钮和布局的形式表达出来,而设计师的责任是把这些按钮和布局做的美观大方,并且给客户端开发人员提供图片资料,而开发人员,除了实现布局之外,还需要与服务端进行交互,以实现按钮和布局的功能。大部分业务逻辑是在服务端实现的。
服务端
服务端是对用户不可见的,我们在第一节里面讲过了本系统涉及的功能,因此我将系统需要的服务分成了4个单元。
业务服务:这本应该是一个系统最重要,最繁忙的服务,但是鉴于我们系统并不涉及具体业务,因此这个服务是我们系统最简单的服务。它的任务就是实现你创业的核心功能。
推送服务:这个就是在你手机上方显示的小提示,当有新的业务数据或者用户关心的信息时,服务器可以在用户没有打开我们APP的情况下,发送消息到客户的手机端。
图片服务:我们系统需要存储用户的头像,因此需要一个单独的图片服务器来支持此功能,我们把业务服务和图片服务分离,这样彼此都可以专注于自己的功能实现,而不是混在一起。这也是为了当您的用户迅速增长时,可以简单容易的对系统进行扩容。
聊天服务:这个是用户进行在线交流的重要系统,我们将采用websocket技术,实现一个简易的聊天系统。但是该实现简易但并不简单,他可以很容易的扩容,并且可以很大程度的降低网络损耗和提高性能。
存储:
除了处理各种客户端的请求,我们还需要存储很多的信息。我把存储与服务分开,是为了从另一个维度来解释一个完备的系统应该具有的能力。客户端提供的是交换能力,服务端提供的是计算能力,而存储提供的是存储能力。
Memcached:这是一个开源的内存存储模块,他的特点是访问速度快,但是断电后数据会丢失。它的作用是存储一些变更频繁却又不重要的信息,比如我们的推送消息,可以缓存在 Memcached,如果你的业务中有类似于“点赞”这样的功能,也适合使用该模块。
Disk:磁盘存储,我们的图片就存储在磁盘上。如果你的业务需要用户提交文档和图片,建议你也存储在服务器的硬盘上。他的特点是存取比较慢,但是数据安全。
Mysql:数据库存储,这是一款开源的数据库软件。我们将用它来存储用户的个人信息和好友关系。他是结构化的磁盘存储,其本质还是磁盘存取,但是性能比简单粗暴的磁盘存储要好。
Redis:这是一款既能支持内存存储,又可以进行磁盘持久化存储的开源软件。我们用Redis来存储用户的聊天记录,因为聊天记录的频繁程度和重要性介于“点赞”功能和“用户数据”两者之间,因此我们的技术选型也选择Redis这样的模块,它的技术特点介于Memcached与Mysql之间。
本部分内容,我们首先从粗粒度讲解了一个系统比较大的组成单元,就好像我们扎风筝的第一步,先把风筝轮廓做出来。不知道我这个比喻对于那些没有做过风筝的同学来说,是不是比理解我的技术术语还困难。接下来我们要做的是讲解局部的细节,就像是对风筝的局部再细化。
为了便于理解,我还是给大家展示一副风筝的骨架吧。
客户端架构
客户端就是我们平常所说的APP,他被安装在用户的手机上,每次你打开APP,首先展示的就是APP的界面。其实用户能看到的也只有用户的界面和APP的设置。
UI界面:这个就是手机的展示界面,无需多解释。
数据:我们的UI界面上会展示一些数据,比如支付宝您那华丽丽的金额,微信的聊天记录。这些都是数据,但是这些数据在被展示到界面之前,我们的手机需要一些数据结构和模块来存储这些数据。
设置:这个指的是和手机操作系统相关的部分,比如是否允许接受推送消息,是否允许读取通讯录,是否允许获取您的地理位置。
控制逻辑:我们的数据展现在UI界面上的方式,是需要控制的;在不同的设置下,APP的行为也是不一样的,这些都需要通过控制逻辑来处理。控制逻辑相当于一个小指挥官。
SDK:这里指的是手机开发平台提供给我们的一些工具,这些工具都由你APP的需求决定的。这里比较难理解,为了简便起见,对于Android,这里指的是Android studio这个开发工具,对于IOS,这里指的是XCode这个开发工具。
开源软件:这里指的是第三方提供的一些模块化工具,比如网络请求用的AFNetworking,图片加载工具Imageloader。我们会在后面的章节详细介绍所有的第三方开源组件。
IOS/Android操作系统:这个不需要多解释,就像你玩游戏需要在widows或者mac上安装一样,我们的APP也需要安装在IOS或者Android操作系统上才能运行。
本小节主要从更细粒度解释手机APP的结构,清晰的结构有利于编码时的分类组织,也有利于出现问题时的bug定位。往往我们看到APP行为不正确的时候,无从下手解决,如果有清晰的系统结构,那么我们可以在每一个环节设置观察点,这样可以清晰快速的定位问题所在。
业务服务架构
业务服务本来是留白的功能,因此我们只简单解释系统的运行环境。
我们将介绍2种语言来实现我们的业务模块,这样做的目的是为了更多的同学能够可以使用本书的代码进行扩展。对于学习Android的同学,使用JAVA来开发后台逻辑相对来说容易些;对于掌握IOS语言的同学,使用PHP实现业务逻辑,可能读起来更容易一些。同时对于掌握JAVA和PHP的同学,也增大了他们使用本书代码的机会。
nginx:本书会多次使用该开源软件,它还是非常强大的,建议有时间的时候,同学们多学习一下。该软件在本架构中的作用是反向代理。即Nginx后面可以跟多个php-fpm或者多个tomcat。
PHP-fpm/Tomcat:是用来解析nginx传输过来的http请求并且管理处理请求的进程。它们滤去了http协议的细节,将APP客户端传输来的数据交给业务层面处理,同时能够将业务层的处理数据,封装成http数据返回给客户端。
PHP/Spring-mvc:这样划分其实有点不准确,PHP指的是解析并执行php脚本的环境;而Spring-mvc是一套J2EE的架构,他将业务数据处理流程又分成了controller/service/business这样的几个层面,他的作用与PHP是不一样的。
Mysql:存储业务数据,当然我们系统的业务数据很简单,所以只使用了mysql存储了基本的数据。如果您的业务很复杂,mysql不能完全支持您的业务
OS:操作系统,所有的这些软件都需要运行在操作系统之上,但是这里并不指某一台服务器。我们可以把nginx,mysql,php-fpm/tomcat 安装在不同的服务器上。
本部分主要讲解了业务服务器的技术构成单元,鉴于我们的系统对业务模块没有具体设计,因此此部分架构相对简单了一些,但是我们接下来的服务与此业务架构有很多相似的地方,因此对模块进行了详细的讲解。接下来的模块如果与本部分的构成单元有重复的地方,我们将略过。
图片服务架构
本系统中,图片服务器的功能只是用来存取头像,当然您也可以用来为业务服务提供其它的文件服务。
Nginx:此处nginx的作用是http协议解析器,他将滤去协议的细节,将数据展现给上层逻辑。
Nginx-lua:寄生在nginx环境下的脚本语言,是lua在nginx的应用,此模块可以不用对nginx进行二次开发的情况下,使用lua脚本语言实现业务逻辑,除了nginx,我们提到最多的就应该是Lua了。
nginx-upload-module:这是一个开源的模块,是对nginx的扩展,该模块滤去了数据流传输的细节,将得到的数据交给了Lua编写的业务层代码。下载地址:https://github.com/vkholodkov/nginx-upload-module
Lua Business code:该模块是将nginx得到的图片数据流进行处理,是业务逻辑处理模块,用Lua编写。在本系统中,该模块将图片数据进行hash计算,使用该hash值表示图片存储的目录,然后将图片存放在该目录下。这样可以做到根据文件内容进行分级存储
disk:存储图片的磁盘。
OS:略
本部分内容主要讲解了图片服务器的组成单元,因为我们主要用来存取用户的头像,所以模块比较简单,总的来说就是将图片接收下来,分层级的把图片保持到磁盘上,并且把存储图片的相对路径返回给客户端。
聊天服务架构
聊天系统是本系统比较复杂的部分,所谓的复杂是技术细节比较复杂,我们需要使用基于HTTP协议的Websocket的协议来传输聊天内容,同时又需要redis和lua来记录和存取聊天记录,同时还要管理好友关系,这应该是本系统的重中之重了。
Nginx:http协议解析器,功能略。
Nginx-lua:功能略。
lua-resty-websocket:一位大牛写的lua库函数,在nginx和nginx-lua的环境中,创建和管理websocket。我们通过该模块提供的库函数,监听客户端与管理服务器的websocket,我们的websocket服务器将为聊天的双方提供消息转发功能,下载地址:https://github.com/openresty/lua-resty-websocket。
lua-resty-redis:还是上面那位大牛写的lua库,我们使用该库提供的库函数,将聊天记录存储在Redis服务上。下载地址:https://github.com/openresty/lua-resty-redis。
Redis:用来存取聊天信息,缓存待转发的消息,缓存好友关系。
mysql:存取好友关系。
disk:为Redis提供数据的持久化能力,该部分可以算作Redis的一部分,但是又不能完全隶属于Redis。
Receiver:监听和接收客户端的websocekt消息,Lua脚本编写。
Sender:转发消息到客户端的websocekt通道上,Lua脚本编写。
Recorder:记录用户的历史聊天记录,由Lua脚本编写,该接口将聊天记录存储到Redis服务上。
OS:略。
dispatcher:根据好友关系和发送方向,将消息传送到不同的websocket通道上。
本部分内容属于比较复杂的模块,我们需要管理websocket的链接,又要对聊天数据进行转发,还要存取聊天历史记录,因此仅仅通过模块划分就讲清楚他的运行原理有点困难,我们会在后面的章节中重点讲解此功能的实现。总的来说,该模块就是将客户端的消息,转发给目标客户同时记录聊天记录。
推送服务架构
推送分为IOS和Android两种,虽然google也提供了类似IOS推送的功能,但是因为某些原因,我们不能使用Android提供的这项服务,必须重新搭建自己的推送服务器。
Nginx:此处nginx的作用是http协议解析器。功能略
Nginx-lua:功能略。
nginx-push-stream-module:这是一个开源的模块,是对nginx的扩展,该模块可以通过频道的方式 与用户进行通信,从而实现推送消息的功能,如果是简单的聊天功能,也可以通过该模块来实现。下载地址:https://github.com/wandenberg/nginx-push-stream-module
Lua Business code:这个模块是判断push消息的类型,如果是给android的用户发送消息,那么消息就发送到用户的监听频道上;如果是发送给IOS手机,那么该模块就将消息缓存到Memcached中,然后再由PHP的业务代码,把推送消息发出。
Memcached:该模块是缓存发送给IOS的推送消息,因为IOS有自己的推送方式,我们必须把推送消息发送给苹果的PUSH Server。因此在发送之前,我们先将消息缓存下来。
PHP:php脚本解析运行环境,略。
PHP Business code:该模块是从memcached中读取推送消息,然后建立与苹果的PUSH server的长连接,然后批量将数据发送到苹果服务器。
OS 略。
本部分主要介绍了消息推送服务的模块,其实我们需要从更细的粒度讲解该模块,但是鉴于IOS和Android属于不同的推送模式,我们为了给业务服务器提供统一的Push接口,不得不将这几个复杂的模块统一在一起,在后面的章节中,我们会将该架构再详细的展开。
本节我们先把系统从一个维度展开,由前端到后台这样纵向切开系统,将系统的细节展现给大家。然后又从更低的维度把每个模块横向展开,展示了每个模块的相对细节的部分。在后面的章节中,我们可以在更细的粒度展示技术模块的细节,但是在介绍更细粒度技术细节之前,我们将在下一节从较高维度的横向切面,展示系统的技术细节,横向展开侧重点,是某一个功能如何贯穿前后台各个技术模块的。
在本节结束之前,我将技术架构与人员角色的关系给大家讲解一下,这样做的目的是为了告诉那些正在创业,又不懂技术的CEO们,如何组建技术团队及组建技术团队的依据是什么。
这大概是你创业初期最小的完备的技术团队了,如果没有这些角色,你团队成员肯定会抱怨而且会相互推卸责任。而你作为创业的创始人,如果没有这些角色,自己的APP肯定漏洞百出,而你自己忙得要死但却毫无头绪。
这里面还忽略了2个角色,一个是测试,一个CTO,其实创业初期,所有的人都可以参与测试,而所谓的CTO只是协调一下各个角色和催促一下实现方案,如果没人胜任,就自己先担任吧。
你会发现我用不同的图像代表了不同模块的人员。是的,这是最形象的表达,你仔细观察一下你的开发团队,和我的图标表达的意思非常相近。