前后端分离

在前后端没有分离时,双方开发职责不清,合作效率低。前后端分离后,可以分清职责,也有利于PC、APP、PAD多端适应。RESTful api目前是前后端分离的最佳实践,其特点为:

  • 轻量,直接通过http,不需要额外协议,post/get/put/delete操作
  • 面向资源,一目了然,具有自解释性
  • 数据描述简单,一般通过json或者xml做数据通信

RESTful架构的主要定义:

  1. 每一个URI代表一种资源;所谓"资源”,就是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。
  2. 客户端和服务器之间,传递这种资源的某种表现层;互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化”。
  3. 客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化”。客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

本地开发

在本地的测试中,简单地采用Django+Mongodb的方案。

数据库

我们使用mongodb,在安装好mongodb的环境以后,启动mongodb环境,并用pip安装pymongo库来操作。

我们参考了普通商城的数据表,将数据表(集合)设计如下:

1
2
3
4
5
6
7
8
9
商品表:
(编号, 图片地址,商品名称, 价格区间, 运费, 重量, SPU销量, 店铺名)
(101, "TB2fX.jpg", "腮红", ["80","100"], 6, 1.0, ["4230","件"],"彩妆套装专营店" )
(120, "Lu6Dg.jpg", "唇膏", ["99","120"], 8, 0.2, ["1233","件"],"睫毛刷专营店" )

购物车集合(示例):
{ "user" : "user0", "cart" : [ 
	{ "id" : 102, "num" : 3, "sel" : false }, 	{ "id" : 108, "num" : 4, "sel" : true }, 
	{ "id" : "101", "num" : 3, "sel" : true }, { "id" : 111, "num" : 11, "sel" : false } ] }

我们从网上爬取了一组商品数据,并做了脱敏处理,不涉及真实的商品信息。完成初始数据文档的建立后,我们针对前端的应用需求,设计了一组数据库操纵的函数,

Django REST

我们将使用下述的模板,编写一个单文件Django REST应用main.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from django.conf import settings
from django.http import HttpResponse, JsonResponse
from django.conf.urls import url
from django.views.decorators.http import require_POST, require_GET
from django.views.decorators.csrf import csrf_exempt

setting = { 'DEBUG':True, 'ROOT_URLCONF':__name__, }
settings.configure(**setting)

@csrf_exempt
@require_POST
def api(request):
    data = request.POST.get('data')
    output = data
    return JsonResponse(data={'output':output})

urlpatterns = [url('^api/$',api,name='api')]

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

if __name__ == '__main__':
    import sys
    from django.core.management import execute_from_command_line
    execute_from_command_line(sys.argv)

上述模板提供了一个使用post方法请求并返回json的可跨域api,在此基础上我们将数据库的功能包装成了API,完成的基本功能如下:

方法地址参数说明
GET/getCart/None获取购物车的初始信息,为前端渲染做准备
POST/addCart/pid传入商品编号,购物车表加入中该商品,库存不足返回错误
POST/delCart/pid传入商品编号,购物车表减少中该商品,无该物品返回错误

人脸识别

在算法同学的努力下,我们封装了一个python的算法接口face_json(),可以对输入的人脸照片进行检测,输出主要肤色成分,痘痘数量,黑眼圈程度。因此,我们包装了下面两个API。

方法地址参数说明
POST/upload/File上传人脸照片,保存到服务器,返回文件临时编号fn。示例:{‘fn’:‘img_0EL3JpH’}
POST/getFace/fn传入人脸临时编号,调用face_json()算法,返回肤质信息。示例:{‘color’:"[120,98,61]", ‘pimple’:23, ‘darkeye’:0.317}

其中,处理upload请求时,使用了Django的FileSystemStorage库对上传的图片进行管理,并产生编号。

测试

此处建立了本地的html测试页面,通过ajax发送相应的请求,对以上5个接口进行测试。

当然也可利用其他工具,例如在线接口测试工具,或者代码编辑器的插件等。在本地测试完成后,将代码上传到git仓库,以备部署。

云端部署

在服务器端部署时,采用了Django+Mongodb+gunicorn+nginx的方案。其中:

  1. Gunicorn : 一个Python WSGI UNIX的HTTP服务器,可以并发多线程,取代django自带的server。
  2. Nginx:高性能的HTTP和反向代理服务器,主要功能为:
    • 缓冲请求:直到收完整个请求,再转发给Gunicorn,避免Gunicorn一直占用进程等待接收完成。
    • 负载均衡:由nginx占用80端口,而gunicorn同时运行多个程序占用不同的端口。Nginx根据客户端发来的不同的url请求,把请求分发给不同的程序,达到一台服务器运行多个网站的目的。
    • 访问控制:限制流量,限制ip,限制连接数量。
    • 处理静态文件:对图片、css之类的静态文件请求直接由Nginx处理,速度更快。

将本地项目上传到git仓库后,再克隆到服务器中,此处目录指代为myproject/

Python环境配置

服务器内置的python环境有python2.7和python3.4,经常发生冲突。此处需要安装python3.6,在没有管理员权限的情况下,解决方案如下。

1
2
3
4
5
6
pip3 install --user virtualenv
export PATH=$PATH:/home/username/.local/bin

cd myproject
virtualenv venv --python=python3.6
source venv/bin/activate

先在用户目录下安装virtualenv,再将其安装目录加入环境变量,这样即可使用virtualenv来管理我们的python环境。之后进入项目目录,创建名为venv的python3.6虚拟环境,激活之,则进入了虚拟环境。

之后为pip设置国内镜像源,创建配置文件,

1
2
mkdir ~/.pip
vim ~/.pip/pip.conf

加入如下内容:

1
2
3
4
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
[install]
trusted-host=mirrors.aliyun.com

设置完镜像源后,在虚拟环境下安装所需包,其中opencv-pythonscikit-learnmatplotlib为人脸识别所需库。

1
2
3
4
5
pip install django
pip install pymongo
pip install opencv-python
pip install -U scikit-learn
pip install matplotlib

nginx配置

使用服务器中安装好的nginx,直接编辑default配置文件:

1
sudo vim /etc/nginx/sites-available/default

在配置中加入如下内容:

1
2
3
4
5
6
7
8
9
server
{
    listen       80;
    server_name x.x.x.x; #server ip
    location / {
        proxy_pass http://127.0.0.1:8000;
        include     /etc/nginx/uwsgi_params;
    }
}

这样即可把外部80端口的请求转发到django默认的服务端口8000。此时在django项目下开启默认服务器,可以通过浏览器访问到该django应用。

gunicorn配置

在虚拟环境下安装gunicorn,用which python, which gunicorn来确定如下命令,运行即可

1
/home/username/myproject/venv/bin/gunicorn --chdir /home/username/myproject --pythonpath /home/username/myproject/venv/bin/python -w4 -b127.0.0.1:8000 main:application

此时若断开远程连接,django也会后台继续提供服务,如果想杀掉gunicorn服务,则可以运行:

1
2
lsof -i:8000
killall gunicorn

测试

将接口信息告知前端的同学,完成前后端联合测试。

参考

[1] 商城 商品模块 数据库 表设计

[2] MongoDB 教程

[3] Python MongoDB 教程

[4] MongoDB 数组元素增删改

[5] Django RESTful 系列教程(一)

[6] 阿里云部署 django gunicorn nginx mysql ubuntu python3

[7] PIP源使用国内镜像

[8] 直接使用uWSGI来运行Django