[后台开发工程师总结系列] 9.Python,Nginx and Django

python

PEP8规范

  • 每一行使用四个空格
  • 每一行限制最大字符数为79
  • 推荐从( [ 换行, 与上一行的第一个(错一个字符。
  • 顶层函数和类定义换两行, 类里的方法一个换行
  • 命名、空格规则

如何理解Python

  • Python是一种解释性语言,与C语言及衍生语言不同,Python运行之前不需要编译
  • Python是动态类型的语言,声明变量时,不需要说明变量的类型。
  • 非常适合面向对象编程(OOP), 没有访问说明符
  • 函数是第一类对象,可以被指定给变量,返回函数类型
  • Python代码编写块,但是运行慢。Python允许编写C语言的扩展以加快速度

Python

  • Pyhton迭代器与生成器

迭代器是一个抽象的概念,任何对象,如果它有next方法和iter方法返回它本身

iter() 是Python的内置函数,iter()会返回一个定义了next()方法的迭代器对象,在容器中逐级的访问元素,next也是python的内置函数,没有后续元素时会抛异常

生成器是创建迭代器简单而强大的工具,他们写起来就像是其他的函数,只需要在返回数据时使用yeild语句。每次next调用时,他都会脱离当前的位置。

生成器能做迭代器做的所用事情,而且因为创建了iter() 和next 简介而高效

  • 装饰器的作用和功能

引入日志

函数执行时间统计

执行函数前预备处理

执行函数后的清理功能

权限校验等场景

缓存

  • GIL(Global INterpreter Lock 全局解释器锁)

Python代码执行有Python虚拟机控制。Python在设计之初就考虑到在解释器中只有一个线程在执行,即任意时刻只有一个线程在解释器中运行。对Python虚拟机的访问由全局解释器锁来控制。

在多线程环境中,Python虚拟机按照如下方式运行

  1. 设置GIL
  2. 切换到一个线程去运行
  3. 运行: 指定数量的字节码,线程主动让出控制
  4. 线程睡眠
  5. 解锁GIL
  6. 重复上述步骤
  • Python 的内存管理
  1. 垃圾回收:Python 不像C++ java 语言一样,不用事先声明变量而直接对变量进行赋值。对Python来讲,对象和内存都是在运行时确定的。这也是为什么Python被称为动态类型的原因(动态类型可以简单归结为对变量内存地址的分配是在运行时自动判断类型并赋值)。
  2. 引用计数:Pyhton采用了类似windows内核对象方式进行内存管理。每一个对象都有一个指针指向其引用计数。当变量绑定该对象时引用计数就是1
  3. 内存池机制:

Python内存池以金字塔行

0层是 malloc 和free 操作

1、2层是内存池,对象小于256K时直接由该层分配内存

3层是最上层,对Pythton对象直接操作

Python多继承(MRO)

C3算法最早提出用于Lisp,应用到Python中是为了解决原来深度优先搜索不满足本地优先级、单调性的问题

本地优先级:声明父类时的顺序,C(A,B),访问父类时应该根据声明顺序优先查找A类然后是B类

单调性:如果在C的解析顺序中,A在B前面,那C的所有子类都必须是这个顺序

经典的方法是深度优先搜索,而新方法(C3)是一种广度优先搜索的方法。

gevent

为Python提供了较为全面的协程支持。

gevent 是第三方库,通过greenlet实现协程,基本思想是:

当一个greenlet 遇到IO操作时,比如访问网络,就会切换到其他的greenlet,等到IO操作完成时再切换回来执行。由于IO操作非常耗时,常常使程序处于等待状态,greenlet使得总有其在运行而不是等待IO

1
2
3
4
5
6
7
8
9
10
11
12
13
from gevent import monkey
import gevent

def f(n):
for i in range(n):
print(gevent.getcurrent(), i)

g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g2.join()

协程

协程又称为微线程

协程的概念很早就出来了,但是近几年才得到广泛应用

由协程定义的过程可能会交替执行,有点像多线程,协程相比于多线程有以下优势

  1. 协程有极高的执行效率,子进程切换不会引起线程切换,没有线程的切换开销
  2. 不需要多线程的锁机制,也不存在资源的写变量冲突,执行效率较高

is 和 == 的区别

Python中的值有三个基本的要素:id(身份表示)、type(数据类型)、value(值)

is和==都是比较判断,但是内容不同

is比较的是id, 而== 比较的是值

xrange 和 range

range直接返回一个列表

xrange返回一个对象,显示调用list将其转换为一个列表

大数列的话xrange效率高不需要开辟较大的空间,内存空间使用较少

Python元类

元类是一个深奥的面向对象的概念,隐藏在激活所有的Python代码后

简单的说,元类就是用来创建类的东西。元类就是类的类

函数type实际上是一个元类。

MRO GIL GC

Nginx 概述

Nginx是一个高性能的HTTP和反向代理服务器,以及电子邮件代理服务器

  • 跨平台、配置简单
  • 非阻塞,高并发连接 如果作为web服务器 nginx能够支持高达50000的并发连接数
  • 内存消耗小, 10个nginx才占用150M内存,
  • 处理静态文件好,耗费内存少
  • 一个master进程生成多个worker进程

nginx 的负载均衡算法

nginx的upstream支持4种方式的分配

  1. 轮询(默认)

每个请求按照一定的时间顺序逐一分配到不同的后端服务器

  1. weight

指定轮询的几率, 和访问比例呈正比,用于性能不均的情况。

默认深搜分配权重

  1. IP_HASH

每个请求按照访问的ip地址进行hash分配,可以解决session问题

Nginx如何解决惊群现象

惊群是指多个子进程在同一时刻监听同一端口引起的

nginx解决方案:同一时刻只能由唯一一个worker 监听web端口,新事件连接只能唤醒唯一监听端口的子进程。

nginx 事件驱动框架

nginx 时间驱动架构:所谓事件驱动架构,由一些事情发生源来产生事件,由一个或多个时间收集器来收集事件(epolld)分发事件,许多时间处理器会主持自己感兴趣的事件,同时会消费这些事件。

传统的web服务器事件局限于TCP连接上,其它时间驱动都不是事件驱动。即前者每一个消费者独占一个进程资源,而后者只是时间分发者短期占用进程。

Nginx的多进程模型

Apache 创建多个进程或线程,每个进程、线程都会分配CPU,容易榨干服务器资源

nginx 单线程来异步非阻塞的处理请求(可以配置工作进程数量)

Nginx处理请求

首先nginx启动时,解析配置文件得到端口与IP地址,然后在master进程中初始化好这个监控的socket并listen

之后master进程fork出多个子进程出来(worker)子进程会竞争accpet连接

此时客户端就可以发起连接,当客户端与nginx三次握手后与nginx建立好连接。某个子进程会accept成功,创建对nginx连接的封装,即ngx_connection_t 结构体。

接着,根据事件调用相应的处理模块,如HTTP进行交换。

最后Nginx或客户端关闭连接。

正向代理

一个位于客户端和原始服务器之间的服务器,为了从原始服务器获得内容,客户端向代理发送一个请求并制定目标(原始服务器),然后代理服务器向原始服务器请求并返回内容给客户端。

正向代理服务器代理的是 客户端

反向代理

代理服务器接受Internet的连接请求,然后将请求分发给内网上的服务器。并将从服务器上得到的结果返回给客户端,这个服务器对外表现为一个反向代理服务器。

反向代理代理的是 服务端

动静分离

软件开发中,有些资源需要后台处理(.jsp do),有些不需要(css、html),不需要经过后台处理的是静态文件,静态、动态资源分离可以进行静态文件的缓存。

Nginx 和 Apache

django

对Django 的认识

Django是走大而全的方向,它是出名的自动化管理后台,只需要使用ORM,简单的对象定义,自动生成数据库结构,全功能的管理后台。

Django内置了一个ORM框架,与其其他模块耦合性较高,理论上必须使用该框架

Django 的最大优势是开发效率高,django项目达到一定规模后需要重构才能满足性能需求

适用于中小型网站、或大型网站的雏形

Django模板设计哲学是彻底将代码、样式分离

MVC 模型和MTV模式

所谓MVC就是把应用分为模型、控制器、视图三层,以插件式、松耦合的方式放在一起,模型负责对象与数据库的映射(ORM)视图负责与用户的交互(页面)控制器接受用户调入模型和视图完成用户请求

1551613825717

而Django的MTV模式本质上和MVC是一样的,知识定义有些序不同

  1. M代表模型;负责业务对象和数据的关系映射(ORM)
  2. T代表模板:负责如何把页面展示给用户
  3. V代表视图:负责业务逻辑,并调用model和Template
  4. 1551613956066

简述Django的请求生命周期

一般用户通过浏览器发送请求,这个请求访问视图函数,视图函数调用模型,模型去数据库查找数据,然后逐级返回,在填充到模板最后返回给用户

  • wsgi 请求封装候交给web框架(flask django)
  • 中间件,对请求进行验校活在请求中添加相关数据,如(csrf, session)
  • 路由匹配,根据浏览器发送的不同URL去匹配不同的函数
  • 视图函数 视图函数中进行业务处理,可能涉及到(ORM 和模板渲染)
  • 中间件,对响应函数进行处理
  • wsgi 将响应内容发给浏览器

什么是FBV和CBV

function based views 视图函数中使用函数处理

class based views 视图函数使用类处理请求

谈谈对ORM的理解

ORM是对象关系映射,是MVC框架的一个重要部分。它实现了数据模型与数据的解耦,即数据模型的设计不依赖特定的数据库,通过简单的配置就可以更换数据库,极大地减轻了开发人员的工作量。

谈谈对restful的理解

restful 其实就是一套编写接口的协议,协议规定如何编写及如何设置返回值,状态信息码等。

其实原理和django的CBV类似,便于前后端分离。

提供了许多方便的组件,

序列化器:用户请求验校+queryset 对象序列化为json

解析器:获取用户请求数据 request.data

分页: 从数据库中获取到的数据在页面进行分页展示

认证、权限、访问频率控制

中间件

中间件是用来处理Django用来处理请求和相应框架级别的钩子,它是一个轻量,低级别的插件系统,用于在全局范围改变Django的输入和输出,每个中间件负责一些特定功能。

1551615657054

中间件会拦截一部分请求,比如验证session,没有登录的进行跳转。

Django中的一些重要语句

1
2
3
4
5
6
7
8
9
10
11
# 原生SQL语句
from django.db import connection

cursor = connection.cursor()
cursor.execute("select * from auth_user")

# Django ORM批量创建数据
querysetlist = []
for i in res:
querysetlist.append(Account(name=i))
Account.objects.bulk_create(querylist)

Django 查询的特性

  • 惰性执行、缓存
  • 创建查询集不会访问数据库,直到使用数据时才会访问数据库,调用数据的情况包括迭代、序列化、与if合用

什么是wsgi、uwsgi、uWSGI

WSGI: web服务器的网关接口,是一套协议,用于接受用户请求并进行初次封装,然后交给web服务器

实现wsgi协议的模块:

  1. wsgiref 本质上是一个socket服务器,用于接受用户请求(django)
  2. werkzeug 本质上是一个socket服务器,用于接受用户请求(flask)

而uwsgi 也是一种通信协议

uWSGI是一个web服务器,实现了上述协议

Django 中的csrf (防止跨域请求伪造)实现机制

第一步: Django 第一次相应客户端请求时,随机产生一个token,把这个token保存在session中,同时把其发送给前端

第二步:下次前端发起请求(比如发帖时)把这个token带入请求头中一起传给后端cokie:(csrftoken:…)

第三步:后端检验前端传入的token与session是否一致

Ajax

Ajax(Asynchronous javascript and xml)能够局部刷新网页数据,而不是重新加载

第一步:创建xmlhettprequest对象,val xmlhttp = new XMLHttpRequest()

第二步:使用xml对象的open和send方法发送资源请求给服务器

第三步:使用xml对象的responstest 和 response XML 获得服务器的响应

第四步:onreadystagechange

Ajax是一种快速创建动态网页的技术

通过在后台与服务器进行少量的服务交换,AJAX可以实现异步更新。这意外着它不需要重新加载整个网页的情况下对网页的某部分进行更新。

传统的网页如果更新内容必须重新加载整个页面

1
2
3
4
5
6
7
8
9
10
11
12
13
function loadXML(){
var xmlhttp;
if(windows.XMLHttpRequset){
xmlhttp = new XMLHttpRequest();
}
xmlhttp.onreadystatechange=function(){
if(xmlhttp.readyState==4 && xmlhttp.states==200){
document.getElementById("MyDiv").innerHTML = xmlhttp.responseText;
}
}
xml.open("GET", "/something", true);
xmlhtthp.send();
}