瞄星架构设计说明


关键技术:

nginx
java
tomcat7
Restful api
maven
spring
hibernate
mysql
redis
npm
angularjs
yeoman
bower

grunt

动静分离

动静分离的优点

动静分离是让动态网站里的动态网页根据一定规则把不变的资源(html,css,js,静态文件)和经常变的资源(数据资源)区分开来。

  • 提高静态资源访问速度,后端不用再将模板渲染为html返回给用户端,且静态服务器可以采用更为专业的技术提高静态资源的访问速度。
  • 后端应用更为服务化,只需要通过提供api接口即可,可以为多个功能模块甚至是多个平台的功能使用,可以有效的节省后端人力,更便于功能维护。
  • 前后端可以并行开发。
  • 修改前端页面后,不再需要重新部署整个服务,只需要替换相关文件即可。

瞄星中动静分离的实现手段

前端开发

此项目中直接使用SPA(Single Page Application),整个站点只有index.html一个路由,前端的状态依靠js来操作,和后端用ajax传送json交互的数据

使用nginx serve本地静态资源(包括前端页面以及其他图片等静态资源)

后端开发

后端提供RestFul API ,处理前端请求以json形式返回。
此项目中采用tomcat7服务器,并使用nginx反向代理

跨域处理

动静分离因为静态资源和应用服务分别部署在不同的服务器上,因此会面临域名策略的选择。

此项目采用nginx反向代理将前后端映射到相同的域名下,暂时避免了跨域请求。

更新

由于网站需要多域名访问,如无论是否包含www的前缀都可以访问,跨域请求难以避免。

使用nginx add_header方式建立CORS的api服务


前端框架

前端MVC、模块化、数据绑定

angularjs

  • dom指令

    以命令式编程的思想拓展html词汇表,高度封装页面组件(如导航条,聊天窗等),达到更具可读性的组件复用。

    将dom操作交给directive!

  • 模块化

    主要以功能为划分标准,如分为作者、作品、订单等,交叉部分依赖注入。

  • 前端mvc

    单个功能模块内,将代码以mvc为标准解耦,以接口隔离的思想:

    • view层主要考虑页面显示;
    • controller层只考虑页面的基础动作,如菜单的打开关闭、输入框的激活失焦等;
    • model层主要负责真正的业务数据(而不是页面动作的临时数据),通常表现为需要与服务器接口交互的数据,以及将该模型本身业务逻辑的行为定义为其原型链上的方法;
    • 将可复用的基础服务(如身份验证、文件处理等)抽象为angular的service(provider)
    • 在实现每一层的时候,只实现自己的功能,依赖低层的地方需抽象为接口(以这种方式思考,js不需要显示定义抽象接口),即依赖接口而不依赖实现;
  • 数据绑定

    双向绑定,便于更敏捷地开发。

项目骨架生成

yeoman

yeoman首页上的Slogan深深地吸引了我,The web’s scaffolding tool for modern webapps。yeoman的魅力在于提供了丰富的generator,可谓无所不有且十分傻瓜,恨不能不会写前端的人拿了yeoman也会依葫芦画瓢写出个网站。

此项目我选用了AngularJs generator,一键安装基于angularjs的SPA应用

包管理工具

bower

类似于java世界的maven或gradle,以模块化思想管理包及其之间的依赖关系(传递依赖、版本冲突等),将包名与版本号加入bower.json中,可自动去github中央仓库下载所需文件

自动化

grunt

javascript世界的构建工具,众多功能细小可组合的开源Task,且可由yeoman自动生成gruntfile,包含了监测文件变化自动热部署,内置server,js检错jshint,代码风格管理,压缩、连接、丑化源代码等等功能,使用了grunt自动化之后的webapp简直就是重机枪与大刀长矛的区别。

路由管理

AngularUI Router,以层叠的状态树为模型构建单页应用。

有限状态机思想管理页面状态

解决了angular自带ngRouter的两个痛点:嵌套视图与单状态多视图

ui框架

angular.material

google官方出品的基于angular的material design实现,提供了丰富的基础组件,快速构建应用。


后端框架

Restful api

REST: Representational State Transfer,不知道怎么翻译成人类的语言。

我对REST的理解比较浅显:

  • 一切网络信息都被看做是资源,并且有一个id,对应于调用它的URI
  • 对资源的访问操作,对应于请求的操作动词:get/post/put/delete
  • 难以对应于资源的操作,也应该名词化,如修改我的密码,我的endpoint设计是put /mine/password(这只是我比较能接受的设计,stackoverflow上对这个问题有激烈争论)
  • 状态码status code也是响应的一部分,应该恰当地表达响应的状态(不能无脑200)
  • 应该建立易读易懂的错误信息体系告诉客户端错误
  • 返回结果应与请求一致,且不言自明

TODO 幂等性,https,版本管理

此处强烈推荐阮一峰大神的网络开发日志-RESTful API 设计指南

Web MVC framework

spring mvc4.0,基于注解的访问路由、参数处理,利用org.springframework.http.ResponseEntity快速实例化包含http status code和response body的响应。

另外,大部分java开发者对spring都非常熟悉

Maven

必须承认,最开始的设计,是没有准备引入maven来管理项目的,一是团队其他开发人员对maven并不熟悉,二是微信书项目也没有使用maven。

但是在开发过程中,有几个痛点:

  1. jar包下载繁琐、版本冲突,并且把jar包放在git里实在是太蠢了
  2. 跟微信书几乎完全类似的代码,由于没有合理的控制依赖意识(本质还是开发者解耦意识较差),造成移植十分困难。希望引入maven来从硬件上约束开发者。
  3. BaseDao,BaseController,BaseEntity,BaseVo希望能够抽离,并限制为经验比较丰富的开发者才能修改
  4. 一直重复的微信支付、支付宝支付没有写出自己的SDK

于是在前端使用了bower后,一不做二不休,引入maven来管理项目

其他常规java框架

spring ioc管理java bean

hibernate 做orm

spring transaction管理数据库事务

数据库

mysql

1
// 我是井底之蛙,只用过oracle和mysql

redis

主要保存一下数据:

  • 身份校验token
  • 手机验证码
  • 用户密码输错次数

不难看出,这些数据或对持久化到磁盘的要求很低,或对读写性能要求较高,或需要失效机制,将其保存在业务数据的关系型数据库中不是很合适,所以将其转移到NoSQL中。

开始阶段这些数据直接保存在一个单例模式的变量中,本质是保存在方法区(静态区)内,这种保存方式读写更快,但是缺少失效机制,无法管理内存大小,在数据变多后迁移至redis中。

与常常与之对比的memcached相比:

  • redis具有更丰富的数据结构,如hash,sorted set,list等。
  • redis可以持久化到磁盘
  • 单个value值大小上限为512m(memcached为1m)
  • 3.0版本的redis可以集群
  • Memcached和redis都能当做内存键值缓存使用,但redis更能表现为一个数据结构的服务器。

其他

api doc

以识别代码注释的方式生成api文档,样式美观,使用简单,支持java,强烈推荐

http://apidocjs.com/