后端技术学习

后端技术学习&&总结

前端Vue.js,后端Flask+Uwsgi+Nginx+Redis+mysql,项目配了TF+Keras两个模型。对学习的后端技术进行总结,分为下面几部分

  • 安装&&踩坑
  • 配置&&踩坑
  • 作用&&解析

安装&&踩坑

Uwsgi安装

安装其实很简单的一条命令:pip install uwsgi,但通常ubuntu用户安装时编译会报错,如下:

1
fatal error: bytecode stream generated with LTO version x.0 instead of the expected x.x

首先建议使用conda的环境,避免是没有类似python-dev这样基础的环境包导致报错。

然后这个报错写着是LTO版本不对,但官方文档上说asd,可以看下这里,其实应该是gcc版本不对,太高了。

解决方法:降低gcc版本(我的16.04原来gcc版本是5.4)

1
2
3
4
5
6
7
8
#安装
sudo apt-get install gcc-4.8
#查看当前版本
ls /usr/bin/gcc*
#设置4.8为优先使用版本
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100
#重新安装uwsgi
pip install uwsgi / conda install uwsgi

Redis安装

首先在Ubuntu上安装Redis,安装完成后Redis服务会自动启动

1
2
sudo apt-get update
sudo apt-get install redis-server

手动启动则输入redis-server即可

Nginx安装

简单点,apt-get安装

1
2
3
apt-get install nginx
#查看是否成功
nginx -v

配置

概要:配置uwsgi.ini,启动uwsgi

Uwsgi配置

Uwigi最重要的配置就是uwsgi.ini了,配置首先第一步是touch创建uwsgi.ini后放在项目根目录。

几个基本参数

  • pythonpathpython环境目录。如果是conda的环境就不用配置这个了,shellsource activate YourEnv即可。如果是自己创的虚拟环境,则配置到python环境bin目录下的pythone.g:

    1
    2
    # 指定python环境
    pythonpath = /home/applex/conda/envs/myproject/bin

    注:还有两个参数home和一个指定虚拟环境env啥的,容易报一个Encoding,建议别用。

  • http参数,http协议对客户端开发的端口号,客户端通过此端口访问flask web 服务接口。如果设置了该参数其实等同与同时设置了

    1
    2
    protocol = http  #设置默认的通信协议(uwsgi,http,fastcgi)
    socket = 127.0.0.1:5000 #5000是举个例子
  • wsgi-filechdirwsgi-fileweb 应用python主程序。chdir是项目根目录,即当前工作台。我的项目根目录为/home/kavin/rjb/,主程序为app.pyshell开在项目根目录,所以这里该这么写:

    1
    2
    chdir = /home/kavin/rjb/
    wsgi-file = app.py #看shell位置设置打

    注:这里的设置不对很容易报错unable to load app 0......。还有个坑就是项目的module或者app都得有__init__.py这个文件,不然也会报这个错(这坑躺了挺久…)

  • master参数。启动主进程,来管理其他进程,其它的uwsgi进程都是这个master进程的子进程,如果kill这个master进程,相当于重启所有的uwsgi进程。

    下面说说我遇到的问题,通常博客给的设置里master=true,但是我做的项目里运行tf+keras模型,一设置该参数就报一个CUDA的错。所以行tf+keras模型时将该参数去掉。

  • buffer_size参数,设置用于uwsgi包解析的内部缓存区大小为64k。默认是4k,即4096Bit。但如果先项目运行tf+keras模型,则建议修改为:

    1
    buffer-size = 327680
  • 其他几个常用参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 使进程在后台运行,并将日志打到指定的日志文件或者udp服务器
    daemonize = /var/log/myapp_uwsgi.log
    #设置最大日志文件大小
    log-maxsize = 5000000
    #指定pid文件
    pidfile = /var/run/uwsgi.pid
    #当服务器退出的时候自动删除unix socket文件和pid文件
    vacuum = true
    #启动2个工作进程,生成指定数目的worker/进程
    processes = 2
    #启动10个工作线程
    threads = 10
    # 一般在主运行程序 run_app.py 里指定 app = Flask(__name__)
    callable = app
  • 下面给出我的基础配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [uwsgi]
    #master = true
    http = 127.0.0.1:5000 # 不开nginx代理时使用
    #socket = 127.0.0.1:5000 # 开nginx代理时使用
    chdir = /home/kavin/rjb/
    wsgi-file = app.py
    callable = app
    processes = 1
    threads = 3
    buffer-size = 327680

几个命令

然后启动uwsgi.ini

  • 启动

    1
    uwsgi --ini uwsgi.ini
  • 查看uwsgi是否运行

    1
    ps -aux | grep uwsgi
  • 查看端口号占用

    1
    netstat -anp | grep 7878 # 上一步查出来的
  • 结束uwsgi进程

    1
    pgrep uwsgi | xargs kill -s 9

Nginx配置

概要:配置nginx.conf,检测配置文件,启动。

首先简单看一下nginx的文件结构。

  • /usr/sbin/nginx:主程序
  • /etc/nginx:存放配置文件
  • /usr/share/nginx:存放静态文件
  • /var/log/nginx:存放日志

可以先使用sudo /etc/init.d/nginx start启动默认的配置试试~

然后在项目根目录创建nginx.conf,编辑如下(附带解释):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Flask dataV api
#server块:配置虚拟主机的相关参数,一个http中可以有多个server。
server {
listen 3389; #nginx代理的主机端口
server_name test; #server_name 可以是域名,也可以写 ip 地址
charset utf-8;
client_max_body_size 75M;
#location块:配置请求的路由,以及各种页面的处理情况。
location / {
#文件扩展名与文件类型映射表
include uwsgi_params;
#uwsgi_pass 是表明 Nginx 与 uwsgi 的交流方式。我这里选择的是制定的端口号进行和uwsgi的socket通信。
uwsgi_pass 127.0.0.1:5000;
#uwsgi的python环境
uwsgi_param UWSGI_PYTHON /home/kavin/anaconda3/envs/ocr/bin/python;
#项目的根目录
uwsgi_param UWSGI_CHDIR /home/kavin/rjb;
# 项目的主程序,比如你测试用run.py文件,文件中app = Flask(__name__),那么这里就填run:app 。我是app.py
uwsgi_param UWSGI_SCRIPT app:app;
#其他参数:deny 127.0.0.1; #拒绝的ip
#其他参数:allow 172.18.5.54; #允许的ip
}
}

注意!配置完毕后我们萌新常犯一个错误,直接这样启动nginx

1
2
3
sudo  nginx /home/kavin/rjb/nginx.conf
或者
sudo nginx /home/kavin/rjb/nginx.conf

会报错:

1
nginx: [emerg] "server" directive is not allowed here in /home/kavin/rjb/nginx.conf:1

按照英文提示,我们应该将配置存放在允许的地方。我推荐按照下面两部走:

  • 首先,删除默认配置并将项目配置文件软链接到目的地

    1
    2
    sudo rm /etc/nginx/sites-enabled/default
    sudo ln -s /home/kavin/rjb/nginx.conf /etc/nginx/conf.d/
  • 其次,检测配置文件是否正确

    1
    nginx -t -c /etc/nginx/nginx.conf

若无问题,则可以启动Nginx

我的一些习惯命令是:

1
2
3
sudo /etc/init.d/nginx start
sudo /etc/init.d/nginx stop
sudo /etc/init.d/nginx restart

Redis配置

概要:修改程序主文件,启动Redis
Redis是高性能的key-value数据库.
flask也提供了缓存模块flask-cache,该模块可以连接Redis数据库进行缓存。具体来说,可以缓存以下几个方面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1、缓存视图函数
缓存视图函数**@cache.cached**必须在路由之后
@cache.cached 装饰器可以判断是否已经存在缓存,如果有缓存,使用,如果没有缓存,查询然后缓存,再使用
2.缓存Jinja2片段
使用cache标签在模板中设置缓存,单位默认是秒
{%cache 3600%}
<div>detail...</div>
{%endcache%}
3.缓存api
- 设置缓存:cache.set(key,value,timeout)
- 获取缓存:cache.get(key)
#e.g.
# cache.get('view//article/list')
# cache.get('blog_cache/view//acticle/list')
- 删除缓存:cache.delete(key)
- 清空缓存:cache.clear()

以下是参考的博客的经验总结:

1
2
3
4
5
6
7
8
9
一般在项目中,因为业务的复杂性,一般使用缓存api自定义缓存,这样更加灵活。比如电商的首页的商品分类和模块栏,一般都是不变的,这样的内容可以缓存起来。

#那么什么时候要更新缓存的?
假设商品分类修改了,这时候需要首先删除原来的缓存,然后查询数据库得到新的数据,再放入缓存中一份。下次再访问的时候又可以使用缓存了。
设置一个函数方法,当我们修改缓存的东西时,调用该方法删除缓存
def add(request):
print('add user success')
cache.delete('users')
return HttpResponse('add')

我们项目这里采用了Redis结合Flask-cache来缓存视图,具体如下(附解释):

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
26
27
28
29
from flask_cache import Cache
#初始化
cache = Cache()
#配置redis链接的config
config = {
'CACHE_TYPE': 'redis', #缓存到redis数据库
# CACHE_TYPE = 'simple' #缓存到内存
'CACHE_KEY_PREFIX': 'bank/',
'CACHE_REDIS_HOST': '127.0.0.1',
'CACHE_REDIS_PORT': 6379,
'CACHE_REDIS_DB': '',
'CACHE_REDIS_PASSWORD': ''
}

...
#初始化
app = Flask(__name__)
app.config.from_object(config)
cache.init_app(app,config)

#装饰器调用
#e.g.
@app.route('/index2')
@cache.cached(timeout=20)
def index2():
if(session['username'] and session['password'] and session['permission']==2):
return render_template('index2.html')
else:
return render_template('index.html')

几个基本命令

  • 执行命令访问Redis客户端:\redis-cli**
  • 设置键值对命令:\set key value**
  • 取出键值对命令:\get key**
  • 启动命令:\redis-server**

然后启动Redis,界面怪好看的~


作用&&解析

我的项目生产环境配置是Flask+Uwsgi+Nginx+Redis+mysql,前端找大哥用Vue写的,项目内调用了tf+keras的模型。

项目一开始成型的时候其实只有Flask+mysql,在本地也能跑的挺好哒,那为什么需要配置这些呢?下面是我在配置中的学习记录和理解。

Uwsgi理解

若直接使用Flask自带的WSGI(Web服务器):Werkzeug,会提示Not in preducting enviroment...,可能会觉得没啥问题。

但是对于大项目来说,flask自带的开发Web服务器表现很差,响应慢,而且直接通过nginx进行反向代理,经常无法响应请求。有两个可以在生产环境中使用、性能良好且支持Flask程序的服务器,分别是GunicornuWSGI,但是这两个模块不提供对windows的支持。

uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议。NginxHttpUwsgiModule的作用是与uWSGI服务器进行交换。

uWSGI的主要特点是:

  • 超快的性能
  • 低内存占用
  • app管理
  • 详尽的日志功能(可以用来分析app的性能和瓶颈)
  • 高度可定制(内存大小限制,服务一定次数后重启等)

要注意WSGI / uwsgi / uWSGI这三个概念的区分

  • WSGI是一种通信协议。
  • uwsgi是一种线路协议而不是通信协议,在此常用于在uWSGI服务器与其他网络服务器的数据通信。
  • uWSGI是实现了uwsgiWSGI两种协议的Web服务器。

uwsgi协议是一个uWSGI服务器自有的协议,它用于定义传输信息的类型(type of information),每一个uwsgi packet前4byte为传输信息类型描述,它与WSGI相比是两样东西。

为什么使用uWSGIuWSGI的好处?

一下摘自知乎

1
2
3
4
5
通常最好在与主Web服务器分开的过程中运行Python。这样,Web服务器可以有很多细小的线程,它们可以非常快速地为静态内容提供服务,而单独的Python进程将会很大并且很重量级,并且每个都会运行他们自己的Python解释器。所以普通WSGI是不好的,因为它用一个大的Python解释器膨胀了你的每一个nginx线程。

使用flup或gunicorn或uWSGI后面nginx要好得多,因为腾出的nginx简单地提供内容,并让您选择多少个微小的光nginx的线程运行,独立您选择的您带来多少重量级的Python线程最多提供动态内容的。目前人们似乎很高兴gunicorn,但这三种选择中的任何一种都可以正常工作。

展望未来,当负载开始变得严重时,它还可以将Python移动到另一台服务器。

uWSGI服务器自己实现了基于uwsgi协议的server部分,我们只需要在uwsgi的配置文件中指定application的地址,uWSGI就能直接和应用框架中的WSGI application通信。

Nginx理解

搬运自博客

1
2
3
4
5
6
Nginx 是高效的 Web 服务器和反向代理服务器,可以用作负载均衡(当有 n 个用户访问服务器时,可以实现分流,分担服务器的压力),与 Apache 相比,Nginx 支持高并发,可以支持百万级的 TCP 连接,十万级别的并发连接,部署简单,内存消耗少,成本低,但 Nginx 的模块没有 Apache 丰富。Nginx 支持 uWSGI 的 uwsgi 协议,因此我们可以将 Nginx 与 uWSGI 结合起来,Nginx 通过 uwsgi_pass 将动态内容交给 uWSGI 处理。

官方文档在这
#https://www.nginx.com/resources/wiki/
最好的 Nginx 教程在这
#http://openresty.org/download/agentzh-nginx-tutorials-zhcn.html

Redis理解

下面是对Redis简单的介绍。

  • Redis是一个高性能的key-value数据库;将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用;
  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储;
  • Redis支持数据的备份,即master-slave模式的数据备份。

而类似Django框架,Flask提供了flask-cache模块去链接Redis数据库,来提高访问速度和减少服务器负载,具体来说Flask使用Redis可以缓存:

1
2
3
1、缓存视图函数
2、缓存Jinja2片段
3、缓存api

在前面配置部分已经给出,请见前。

感受

写的挺浅滴,是为了项目所用的,之后有空继续深入。附一下运行我的项目所有步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#先开一个shell用于redis
redis-server
#再开一个shell,并激活conda环境
source activate rjb
#进入工作台
cd /home/kavin/rjb
#启动uwsgi
uwsgi --ini uwsgi.ini
#再开一个shell,启动nginx(sudo键入的主机密码是我生日)
sudo /etc/init.d/nginx start
#======================
#分布式项目,登录训练用服务器
source activate fuck
cd rjb
python use.py
#点击一键生成训练数据,一键训练模型测试

参考

1
2
3
4
5
6
7
8
9
10
11
12
#uwsgi
https://www.cnblogs.com/vsignsoft/p/9300160.html
https://www.cnblogs.com/applex007/p/10287351.html
https://blog.csdn.net/lmz_lmz/article/details/92583636
https://www.jianshu.com/p/6ae98e93d595
https://blog.csdn.net/Liu_Jack/article/details/53643245
https://www.jianshu.com/p/679dee0a4193
#nginx
https://www.cnblogs.com/leiziv5/p/7137277.html
坑:软链接
#redis
https://blog.csdn.net/panjunxiao/article/details/100926667

TODO

深度解析:

  • WSGIuWSGI原理层面。
  • Nginx参数配置和解析
  • Redis使用和解析

——Kevin