Web框架基本概念
现在再来写这篇文章显然有些马后炮的意思。不过正是因为已经学习了Flask框架, 并且未来计划学习更加体系化的Django框架,在学习过程中碰到的很多术语等等,非常有必要通过这样一篇看似都是空话但坚实地理论知识学习来填充自己
■ MVC体系的框架
Python发展到今天,已经有了数十种不同的Web框架。其中比较著名,被广泛的使用的有Django,Flask,Tornado,Twisted等等。作为Web框架,它们都向使用者提供了对于网络和线程的封装,借此可以实现HTTP请求-应答模型的定制。另外前三种还提供了关于HTML模板,数据库读写管理,HTTP栈等从前台到后端的一系列方便的封装。有这些封装的框架又被称为全栈框架,是开发人员的好助手。
MVC即Model,View和Controller。是在上世纪八十年代左右发展出来的一种软件架构模式。其中Model,模型封装与应用程序的业务逻辑相关的数据以及对数据的处理方法,是Web程序中用于处理应用程序的数据逻辑部分,Model只提供性能性的接口,通过这些接口可以调用Model来访问数据。有些模型还提供了事件通知机制,为在其上注册的View和Controller提供数据的实时更新。
View是视图,负责数据的显示和呈现,View是对用户的直接输出,在MVC中,一个model往往要给多个View提供服务,View需要尽早注册到Model中去
Controller是控制器,负责从用户端收集数据,可以看做是View的一个反向操作。也就是说Controller的作用是根据用户的旨意来改变View。当然这种改变可能不直接在两者之间进行,而是通过Model作为一个数据中心来联系两者。
因为MVC三者之间互相隔离,所以在程序的发展过程中,改进界面、用户交互流程等要素时不用重写整个程序的逻辑,只要替换其中相关的一部分即可。
■ Python虚拟环境的安装
很早以前,第一次学习python的时候就看到过virturalenv这个东西。当时没有重视虚拟环境这种设定,但是在做Web开发的时候最好还是用虚拟环境。因为在一台电脑上进行多个web项目的开发的时候,不同的项目可能对支持包的要求不同,版本也有可能不同。如果都用默认的python环境,A项目要求某个包版本是1.0,B项目要求是2.0时就冲突了。如果有了虚拟环境,我们可以为一个项目单独起一个虚拟环境来管理它需要的依赖包。
建立虚拟环境用的包叫virtualenv,可以通过pip install virtualenv 来安装。安装完成之后,在某个特定的目录下键入命令
virtualenv venv
即可在目录下生成一个名为venv的目录,目录中存放的是Python虚拟环境,主要包括Include,Lib,Scripts等几个目录,这些目录的功能和PythonHome中的同名目录是类似的。利用虚拟环境中的venv/Scripts/python.exe作为二进制文件来执行相关的脚本的话这个脚本的环境就是当前venv这个虚拟环境了。
如果觉得还要写路径很麻烦的话,那么可以运行venv/Scripts/activate.exe以把当前系统的Python上下文切换到虚拟环境中,此时直接运行python指向的就是虚拟环境的python了。如果想要退出虚拟环境的上下文,可以运行venv/Scripts/deactivate.exe来退出。(Linux上的话是source bin/activate进入环境。在虚拟环境中键入deactivate退出虚拟环境。)
■ Web服务器
虽然口头上我们常说,用web框架开发出一个服务器,但是需要明确知道的是,Web框架开发出来的只是服务端程序,而真正的Web服务器并不是我们开发的。Web服务器是连接用户浏览器和Python服务器端程序的中间节点,目前主流的Web服务器可以选择Nginx,Apache,lighthttpd,IIS等。这些服务器组件,其作用就是在服务器端开启一个httpd进程(当然不止是一个,也不一定叫httpd,总之是这么个意思)来接受外界的请求。
另外,要实现Web服务器和python程序之间的连接,还需要一层叫做WSGI的程序。WSGI的代表有uWSGI,Apache,mod_wsgi等。WSGI全称是Web Server Gateway Interface,其本质是一个接口层,一边联系了Web服务器,另一边联系了Pythonweb程序。与Web服务器连接的一端的接口的例子有uwsgi,fast cgi等,这些东西都是WSGI程序本身实现的,我们需要关注的是如何按照WSGI规定来对接我们自己的程序和WSGI之间的接口。
利用python自带的一些wsgi的实现包可以进行简单的WSGI到服务程序的接口设计。比如:
def application(env, start_reponse):
start_response('200 OK',[('Content-Type','text/html')])
return '<h1>Hello,World</h1>'
####这个application函数就可以视作是一个简单的服务端程序####
####下面就是在编写wsgi到服务端程序的接口了####
from wsgiref.simple_server import make_server
if __name__ == '__main__':
server = make_server('',8080,application)
server.serve_forever()
在这个程序开始运行之后,访问localhost:8080就可以看到固定的h1的Hello,World了。从中也可以看到,虽然之前说WSGI程序是作为接口层连接服务程序和Web服务器的,但是WSGI本身也可以作为Web服务器来运行。这个例子中我们没有设置任何一个类似于Nginx的服务器,但是也能让它运行了。不过因为性能方面的原因,没有生产环境的程序会直接拿WSGI程序来做服务器,所以WSGI作为Web服务器来监听外界请求大多只用在测试环境。
事实上,上面这个程序包装的WSGI接口层也不是很方便进行服务程序的开发,所以Web开发框架基本上都会把这层接口的开发封装完毕,我们只需要使用uWSGI等WSGI程序的实例和它进行一下对接,也不用怎么关心实现。这样一来我们就可以只关注服务程序的开发了。
■ 用Linux+Nginx+uWSGI作为依托发布Web应用
● 安装配置nginx
首先是通过默认yum(Redhat系列)或者apt-get(Ubuntu系列)获得的nginx,主要安装到了以下位置
/usr/sbin/nginx 二进制可执行文件的地方
/etc/nginx/nginx.conf 全局配置文件
/var/log/nginx/access.log 访问日志
/var/log/nginx/error.log 错误日志
之前那篇文章对于/etc/nginx/conf.d中的配置文件有过简单描述,但是对于全局配置文件/etc/nginx/nginx.conf一带而过了。现在先来看看这个nginx.conf的一些配置。:
user nginx; ##定义运行Nginx的用户
worker_process 4; ##Nginx的最大进程数,应该设置成和系统CPU个数相同比较合理
pid /var/run/nginx.pid;
worker_rlimit_nofile 65535; ##限制每个nginx进程最多可以打开多少文件
events{
worker_connections 768; ##每个Nginx进程允许连接的最大客户端数目
}
http{
#########
#一些基本设置
#########
sendfile on; #是否允许文件上传
clilent_header_buffer_size 32k; #上传文件大小限制
tcp_nopush on; #防止网络阻塞
tcp_nodelay on; #防止网络阻塞
keepalive_timeout 65; #允许客户端长连接的最大秒数
types_hash_max_size 2048; #Nginx散列表大小设置,这个值设置得越大,占用的内存空间就越大,但是路由速度也会越快
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
include /etc/nginx/conf.d/*.conf; #加载站点配置文件
include /etc/nginx/site-enabled/*; #加载站点配置文件
}
上面最后两行提到的站点配置文件的话,nginx开启服务之后可以在它身后配置很多个服务程序,每个服务程序都可以视作一个Web站点。把所有站点都配置在nginx.conf中显然不合理,所以在conf.d中写各种不同的站点的配置,然后再把它们include进来这样的做法更加合适。每个站点的conf文件都是配置了一个server代码块,比如下面这个例子:
server{
listen 80; ##指出监听的端口,一般而言一个站点监听一个端口。是事实上不同的server代码块中不允许有相同的listen端口出现,
否则会强行忽略其中的某一些站点。所以如果为了访问的友好性,输入url时不打入端口号(默认80端口)的话,
可能需要全局只能配置一个server代码块,然后不同站点的访问通过后续的路由设置来实现。
root /usr/share/nginx/html; ##配置HTTP根页面目录,nginx提供了一些默认的html文件用于访问一些基础页面时的默认显示。
index index.html index.htm ##配置HTTP根页面中的默认页面
server_name localhost;
location /users/ {
##此处配置的是http://server_name/users/的转发地址
proxy_pass http://127.0.0.1:8080/; ##把指向users的所有路由都转发到本机的8080端口的/界面
proxy_redirect default;
}
error_page 404 /404.html ##配置错误页面指向的模板
}
配置完成之后,可以nginx -t来测试配置文件是否在语法上没有问题,如果OK,就可以键入nginx启动nginx,nginx -s stop关闭,nginx -s reload重新加载配置文件启动。
*nginx的配置文件,里面的技巧和细节还是很多的,这里只是粗粗过了一下,以后如果有需要可能得专门学习一下怎么配置nginx。
● 安装配置uWSGI
uWSGI是在linux上一种对于WSGI程序的实现。可以直接通过pip install uwsgi(注意,是小写的哦)安装。启动方法可以是uwsgi --http:8080 --wsgi-file app.py。这个启动的意思就是说开启8080端口接受http请求,并且后端的服务程序由app.py提供。
除了这种命令行的启动方式,实际上用得更多的是一种基于ini配置文件来进行启动的办法。即键入命令uwsgi uwsgi.ini。关于配置文件uwsgi.ini的写法,在那篇发布flask的文章里详细提到过,这里再简单说明一下:
[uwsgi]
#http = 9090
socket = 127.0.0.1:9090
chdir = /opt/Flasky
uid = 500
wsgi-file = webapp.py
processes = 4
threads = 3
processes和threads就是指定了uwsgi程序开启的进程数和每个进程最大处理的线程数。也就是说,总的处理线程最大数目是processs * threads。chdir指定了uwsgi开启之后的当前目录,这样可以让wsgi-file等涉及路径的内容写相对路径即可。uid指定了运行uwsgi程序的用户是谁。
http指定直接通过http方法访问uwsgi程序时的端口号。socket指定了通过socket的方式来和uwsgi进行对接的地址,一般而言,这个socket接口就是和类似于Nginx等Web服务器进行对接的地址。如果确定是进行Nginx+uWSGI的方式部署web应用的话,那么可以不用写http,而写socket。
当uwsgi的配置文件里写下了socket的话,在nginx的配置文件里进行指向该socket的配置就可以让前端请求信息通达到后端的服务程序了。比如按照上面的uwsgi.ini中设置的socket,在nginx中设置:
server {
listen 80;
location / {
uwsgi_pass http://127.0.0.1:9090;
}
}
● 建立https网站
基于SSL协议进行传输的HTTP数据就是HTTPS的范畴了。下面简单介绍一下如何建立一个HTTPS站点。
针对每一个想要访问HTTPS站点的客户端,都需要经过这样的一些步骤:
1.在服务器中安装OpenSSL等相关的依赖包
2. 生成SSL秘钥和证书
3. 将证书配置到Web服务器
4. 在客户端安装CA证书
首先可以通过yum或者apt-get来安装openssl和openssl-devel两个包。安装完成之后,默认配置下,可执行文件/usr/bin/openssl 以及 相关配置文件 /usr/lib/ssl/* 会被安装到系统中。然后依次执行以下命令来生成CA证书,服务器秘钥以及服务器证书:
#生成CA密钥
openssl genrsa -out ca.key 2048
#生成CA证书,days参数以天数为单位设置证书的有效期,在本过程中会要求输入证书的所在地,公司名,站点名等等
openssl req -x509 -new -nodes -key ca.key -days 365 -out ca.crt
#生成服务器证书的RSA密钥对
openssl genrsa -out server.key 2048
#生成服务器端证书CSR,要求输入证书的所在地,公司名,站点名
openssl req -new -key server.key -out server.csr
#生成服务器端证书ca.crt
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365
把一些必要的东西都生成好之后,就可以再调整一下nginx的配置,使HTTPS站点生效了
server{
listen 443;
server_name 0.0.0.0;
ssl on;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
location / {
uwsgi_pass http://127.0.0.1:9090;
}
}
需要注意的是ssl_certificate和ssl_sertificate_key必须使用指向服务器证书和服务器密钥的绝对路径。