网络编程篇
1. 简述 OSI 七层协议
是网络传输协议,人为的把网络传输的不同阶段划分成不同的层次。
七层划分为:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。
五层划分为:应用层、传输层、网络层、数据链路层、物理层。
物理层:网线,电缆等物理设备
数据链路层:Mac 地址
网络层:IP 地址
传输层:TCP,UDP 协议
应用层:FTP 协议,Email,WWW 等
2. 三次握手、四次挥手的流程
都发生在传输层
三次握手:
TCP 协议是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接。
TCP 标志位(位码),有6种标示:SYN(synchronous建立联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急)
Sequence number(顺序号码) Acknowledge number(确认号码)
第一次握手:主机 A 发送位码为 syn=1,随机产生 seq number=1234567 的数据包到服务器,并进入 SYN_SEND 状态,主机 B 由 SYN=1 知道,A 要求建立联机;
第二次握手:主机 B 收到请求后要确认联机信息,向 A 发送 ack number=(主机 A 的 seq+1),syn=1,ack=1,随机产生 seq=7654321 的包,并进入 SYN_RECV 状态;
第三次握手:主机 A 收到后检查 ack number 是否正确,即第一次发送的 seq number+1,以及位码 ack 是否为 1,若正确,主机 A 会再发送 ack number=(主机 B 的 seq+1),ack=1,主机 B 收到后确认 seq 值与 ack=1 则连接建立成功,两个主机均进入 ESTABLISHED 状态。
以上完成三次握手,主机 A 与主机 B 开始传送数据。
四次挥手:
因为 TCP 连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个 FIN 来终止这个方向的连接。收到一个 FIN 只意味着这一方向上没有数据流动,一个 TCP 连接在收到一个 FIN 后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
- 服务器 A 发送一个 FIN,用来关闭 A 到服务器 B 的数据传送。
- 服务器 B 收到这个 FIN,它发回一个 ACK,确认序号为收到的序号加1。和 SYN 一样,一个 FIN 将占用一个序号。
- 服务器 B 关闭与服务器 A 的连接,发送一个 FIN 给服务器 A。
- 服务器 A 发回 ACK 报文确认,并将确认序号设置为收到序号加1。
3. 什么是C/S和B/S架构
B/S 又称为浏览器/服务器模式。比如各种网站,jupyter notebook 等。
优点:零安装,维护简单,共享性好。
缺点:安全性较差,个性化不足。
C/S 又称为客户端/服务器模式。比如微信客户端,Oracle 客户端等。
优点:安全性好,数据传输较快,稳定。
缺点:对 PC 机操作系统等有要求,当客户端较多时,服务器端负载较大。
4. TCP和UDP的区别
TCP 和 UDP 都是 OSI 模型中运输层的协议。TCP 提供可靠的通信传输,而 UDP 则常被用于广播和细节控制交给应用的通信传输。
UDP 不提供复杂的控制机制,利用 IP 提供面向无连接的通信服务。
TCP 充分实现了数据传输时各种控制功能,可以进行丢包的重发控制,还可以对次序乱掉的分包进行顺序控制。
TCP 应用:FTP 传输,点对点短信等。
UDP 应用:媒体流等。
5. 局域网和广域网
广域网(WAN,Wide Area Network)也称远程网(long haul network )。通常跨接很大的物理范围,所覆盖的范围从几十公里到几千公里,它能连接多个城市或国家,或横跨几个洲并能提供远距离通信,形成国际性的远程网络。
局域网(Local Area Network,LAN)是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网是封闭型的,可以由办公室内的两台计算机组成,也可以由一个公司内的上千台计算机组成。
6. arp 协议
ARP(Address Resolution Protocol)即地址解析协议, 用于实现从 IP 地址到 MAC 地址的映射,即询问目标 IP 对应的 MAC 地址。
7. 什么是 socket?简述基于 TCP 协议的套接字通信流程。
socket 是对 TCP/IP 协议的封装,它的出现只是使得程序员更方便地使用 TCP/IP 协议栈而已。socket 本身并不是协议,它是应用层与 TCP/IP 协议族通信的中间软件抽象层,是一组调用接口(TCP/IP网络的API函数)。
“TCP/IP 只是一个协议栈,就像操作系统的运行机制一样,必须要具体实现,同时还要提供对外的操作接口。
这个就像操作系统会提供标准的编程接口,比如win32编程接口一样。TCP/IP 也要提供可供程序员做网络开发所用的接口,这就是 Socket 编程接口。”
Server:
import socket import threading def tcplink(sock, addr): print('Accept new connection from %s:%s...' % addr) sock.send(b'Welcome!') while True: data = sock.recv(1024) time.sleep(1) if not data or data.decode('utf-8') == 'exit': break sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8')) sock.close() print('Connection from %s:%s closed.' % addr) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 监听端口: s.bind(('127.0.0.1', 9999)) s.listen(5) print('Waiting for connection...') while True: # 接受一个新连接: sock, addr = s.accept() # 创建新线程来处理TCP连接: t = threading.Thread(target=tcplink, args=(sock, addr)) t.start()
Client:
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 建立连接: s.connect(('127.0.0.1', 9999)) # 接收欢迎消息: print(s.recv(1024).decode('utf-8')) for data in [b'Michael', b'Tracy', b'Sarah']: # 发送数据: s.send(data) print(s.recv(1024).decode('utf-8')) s.send(b'exit') s.close()
例子来源于廖雪峰的官网
8. 简述 进程、线程、协程的区别以及应用场景
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。
多进程:密集 CPU 任务,需要充分使用多核 CPU 资源(服务器,大量的并行计算)的时候,用多进程。
缺陷:多个进程之间通信成本高,切换开销大。
多线程:密集 I/O 任务(网络 I/O,磁盘 I/O,数据库 I/O)使用多线程合适。
缺陷:同一个时间切片只能运行一个线程,不能做到高并行,但是可以做到高并发。
协程:又称微线程,在单线程上执行多个任务,用函数切换,开销极小。不通过操作系统调度,没有进程、线程的切换开销。
缺陷:单线程执行,处理密集 CPU 和本地磁盘 IO 的时候,性能较低。处理网络 I/O 性能还是比较高。
多线程请求返回是无序的,哪个线程有数据返回就处理哪个线程,而协程返回的数据是有序的。
9. 如何使用线程池和进程池
池的功能是限制启动的进程数或线程数。当并发的任务数远远超过了计算机的承受能力时,即无法一次性开启过多的进程数或线程数时,就应该用池的概念将开启的进程数或线程数限制在计算机可承受的范围内。
多进程
from multiprocessing import Pool import os import time import random def long_time_task(name): print('Run task %s (%s)...' % (name, os.getpid())) start = time.time() time.sleep(random.random() * 3) end = time.time() print('Task %s runs %0.2f seconds.' % (name, (end - start))) def test_pool(): print('Parent process %s.' % os.getpid()) p = Pool(4) for i in range(5): p.apply_async(long_time_task, args=(i,)) print('Waiting for all subprocesses done...') p.close() p.join() print('All subprocesses done.') if __name__ == '__main__': test_pool()
output
Parent process 32432. Waiting for all subprocesses done... Run task 0 (15588)... Run task 1 (32372)... Run task 2 (12440)... Run task 3 (18956)... Task 2 runs 0.72 seconds. Run task 4 (12440)... Task 3 runs 0.82 seconds. Task 1 runs 1.21 seconds. Task 0 runs 3.00 seconds. Task 4 runs 2.95 seconds. All subprocesses done.
apply_async(func[, args[, kwds]]) :使用非阻塞方式调用 func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args 为传递给 func 的参数列表,kwds 为传递给 func 的关键字参数列表;
close():关闭 Pool,使其不再接受新的任务;
terminate():不管任务是否完成,立即终止;
join():主进程阻塞,等待子进程的退出, 必须在 close 或 terminate 之后使用;
也可以使用 concurrent.futures 模块提供的功能来实现
def test_future_process(): print('Parent process %s.' % os.getpid()) p = ProcessPoolExecutor(4) for i in range(5): p.submit(long_time_task, i) p.shutdown(wait=True) print('Finish') if __name__ == '__main__': # test_pool() test_future_process()
output
Parent process 29368. Run task 0 (32148)... Run task 1 (31552)... Run task 2 (24012)... Run task 3 (29408)... Task 2 runs 0.52 seconds. Run task 4 (24012)... Task 3 runs 0.86 seconds. Task 1 runs 1.81 seconds. Task 0 runs 1.83 seconds. Task 4 runs 1.69 seconds. Finish
多线程
def sayhello(a): print("hello: " + a) start = time.time() time.sleep(random.random() * 3) end = time.time() print('Task %s runs %0.2f seconds.' % (a, (end - start))) def test_future_thread(): seed = ["a", "b", "c", "d"] start = time.time() with ThreadPoolExecutor(3) as executor: for i in seed: executor.submit(sayhello, i) end = time.time() print("Thread Run Time: " + str(end - start))
output
hello: a hello: b hello: c Task a runs 0.40 seconds. hello: d Task b runs 0.56 seconds. Task d runs 1.70 seconds. Task c runs 2.92 seconds. Thread Run Time: 2.9195945262908936
可以看出,由于是创建了限制为3的线程池,所以只有三个任务在同时执行。
10. 进程之间如何进行通信
def write(q): print("write(%s), 父进程为(%s)" % (os.getpid(), os.getppid())) for i in "Python": print("Put %s to Queue" % i) q.put(i) def read(q): print("read(%s), 父进程为(%s)" % (os.getpid(), os.getppid())) for i in range(q.qsize()): print("read 从 Queue 获取到消息: %s" % q.get(True)) def test_commun(): print("(%s) start" % os.getpid()) q = Manager().Queue() pw = Process(target=write, args=(q, )) pr = Process(target=read, args=(q, )) pw.start() pr.start() pw.join() pr.terminate()
output
(23544) start write(29856), 父进程为(23544) Put P to Queue Put y to Queue Put t to Queue Put h to Queue Put o to Queue Put n to Queue read(25016), 父进程为(23544) read 从 Queue 获取到消息:P read 从 Queue 获取到消息:y read 从 Queue 获取到消息:t read 从 Queue 获取到消息:h read 从 Queue 获取到消息:o read 从 Queue 获取到消息:n
Python 的 multiprocessing 模块包装了底层的机制,提供了 Queue、Pipes 等多种方式来交换数据。
11. 进程锁和线程锁
进程锁:是为了控制同一操作系统中多个进程访问一个共享资源,只是因为程序的独立性,各个进程是无法控制其他进程对资源的访问的,但是可以使用本地系统的信号量控制。
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。
线程锁:当多个线程几乎同时修改一个共享数据的时候,需要进行同步控制,线程同步能够保证多个线程安全的访问竞争资源(全局内容),最简单的同步机制就是使用互斥锁。
某个线程要更改共享数据时,先将其锁定,此时资源的状态为锁定状态,其他线程就能更改,直到该线程将资源状态改为非锁定状态,也就是释放资源,其他的线程才能再次锁定资源。互斥锁保证了每一次只有一个线程进入写入操作。从而保证了多线程下数据的安全性。
12. 什么是并发和并行
并行:多个 CPU 核心,不同的程序就分配给不同的 CPU 来运行。可以让多个程序同时执行。
并发:单个 CPU 核心,在一个时间切片里一次只能运行一个程序,如果需要运行多个程序,则串行执行。
13. threading.local 的作用
ThreadLocal 叫做线程本地变量,ThreadLocal 在每一个变量中都会创建一个副本,每个线程都可以访问自己内部的副本变量,对其他线程时不可见的,修改之后也不会影响到其他线程。
14. 什么是域名解析
域名解析是指将域名解析为 IP 地址。也有反向的“逆解析”,将 IP 通过 DNS 服务器查找到对应的域名地址。
DNS 是域名系统 (Domain Name System),域名系统为因特网上的主机分配域名地址和 IP 地址。用户使用域名地址,该系统就会自动把域名地址转为 IP 地址。
15. LVS 是什么及作用
LVS 是 Linux Virtual Server 的简写,意即 Linux 虚拟服务器,是一个虚拟的服务器集群系统,即负载均衡服务器。
LVS 工作模式分为 NAT 模式、TUN 模式、以及 DR 模式。
16. Nginx 的作用
Nginx 主要功能:1、反向代理 2、负载均衡 3、HTTP 服务器(包含动静分离) 4、正向代理
正向代理:某些情况下,代理用户去访问服务器,需要手动设置代理服务器的 IP 和端口号。
反向代理:是用来代理服务器的,代理要访问的目标服务器。代理服务器接受请求,然后将请求转发给内部网络的服务器(集群化),并将从服务器上得到的结果返回给客户端,此时代理服务器对外就表现为一个服务器。
负载均衡服务器类似于 LVS
HTTP 服务器类似于 Tomcat 等。
17. keepalived 及 HAProxy
HAProxy 提供高可用性、负载均衡,以及基于 TCP 和 HTTP 的应用程序代理。
keepalived 是集群管理中保证集群高可用的一个服务软件,其功能类似于 heartbeat,用来防止单点故障。
18. 什么是 rpc
RPC 是指远程过程调用,也就是说两台服务器 A,B,一个应用部署在 A 服务器上,想要调用 B 服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
19. 从浏览器输入一个网址到展示网址页面的过程
- 浏览器通过 DNS 服务器查找到域名对应的 IP 地址
- 浏览器给 IP 对应的 web 服务器发送 HTTP 请求
- web 服务器接收到 HTTP 请求后,返回响应给浏览器
- 浏览器接收到响应后渲染页面
20. 什么是cdn
CDN 的全称是 Content Delivery Network,即内容分发网络。CDN 是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN 的关键技术主要有内容存储和分发技术。
数据库和框架篇
21. 列举常见的数据库
关系型数据库:MySQL,Oracle,SQLServer,SQLite,DB2
非关系型数据库:MongoDB,Redis,HBase,Neo4j
22. 数据库设计三大范式
建立科学的,规范的的数据库是需要满足一些规范的,以此来优化数据数据存储方式,在关系型数据库中这些规范就可以称为范式。
第一范式:当关系模式 R 的所有属性都不能在分解为更基本的数据单位时,称 R 是满足第一范式的,简记为 1NF。
关系模式R的所有属性不能再分解
第二范式:如果关系模式 R 满足第一范式,并且 R 的所有非主属性都完全依赖于 R 的每一个候选关键属性,称 R 满足第二范式,简记为 2NF。
非主属性都要依赖于每一个关键属性
三范式:设 R 是一个满足第一范式条件的关系模式,X 是 R 的任意属性集,如果 X 非传递依赖于 R 的任意一个候选关键字,称 R 满足第三范式,简记为 3NF。
数据不能存在传递关系,即每个属性都跟主键有直接关系而不是间接关系
23. 什么是数据库事务
事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
在关系数据库中,一个事务可以是一条 SQL 语句、一组 SQL 语句或整个程序。
四个属性:原子性,一致性,隔离性和持久性。
24. MySQL 索引种类
MySQL 目前主要有以下几种索引类型:
- 普通索引
- 唯一索引
- 主键索引
- 组合索引
- 全文索引
25. 数据库设计中一对多和多对多的应用场景
一对一关系示例:
一个学生对应一个学生档案材料,或者每个人都有唯一的身份证编号。
一对多关系示例:
一个学生只属于一个班,但是一个班级有多名学生。
多对多关系示例:
一个学生可以选择多门课,一门课也有多名学生。
26. 简述触发器、函数、视图、存储过程
触发器:触发器是一个特殊的存储过程,它是数据库在 insert、update、delete 的时候自动执行的代码块。
函数:数据库中提供了许多内置函数,还可以自定义函数,实现 sql 逻辑。
视图:视图是由查询结果形成的一张虚拟表,是表通过某种运算得到的一个投影。
存储过程:把一段代码封装起来,当要执行这一段代码的时候,可以通过调用该存储过程来实现(经过第一次编译后再次调用不需要再次编译,比一个个执行 sql 语句效率高)
27. 常用 SQL 语句
DML(数据操作语言)
- SELECT - 从数据库表中获取数据
- UPDATE - 更新数据库表中的数据
- DELETE - 从数据库表中删除数据
- INSERT INTO - 向数据库表中插入数据
DDL(数据定义语言)
- CREATE DATABASE - 创建新数据库
- ALTER DATABASE - 修改数据库
- CREATE TABLE - 创建新表
- ALTER TABLE - 变更(改变)数据库表
- DROP TABLE - 删除表
- CREATE INDEX - 创建索引(搜索键)
- DROP INDEX - 删除索引
28. 主键和外键的区别
定义主键和外键主要是为了维护关系数据库的完整性
主键是能确定一条记录的唯一标识。不能重复,不允许为空。
外键用于与另一张表关联。是能确定另一张表记录的字段,用于保持数据的一致性。
主键 | 外键 | 索引 | |
定义 | 唯一标识一条记录,不能重复,不允许为空 | 表的外键是另一表的主键,外键可以重复,可以是空值 | 该字段没有重复值,但可以有空值 |
作用 | 用来保证数据完整性 | 用来和其他表建立联系 | 提高查询排序的速度 |
个数 | 只能有一个 | 可有多个 | 可有多个 |
29. 如何开启 MySQL 慢日志查询
- 修改配置文件,然后重启服务生效
在linux下,vim /etc/my.cnf,在[mysqld]内容项下增加:
slow_query_log = ON
long_query_time = 2 # 查询超过2秒的就会记录 - 命令行,但是重启服务后会失效
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2;
30. MySQL 数据库备份命令
mysqldump -u 用户名 -p 数据库名 > 导出的文件名
31. char 和 varchar 的区别
char:存储定长数据很方便,CHAR 字段上的索引效率级高,必须在括号里定义长度,可以有默认值,比如定义 char(10)。
varchar:存储变长数据,但存储效率没有 CHAR 高,必须在括号里定义长度,可以有默认值。
32. 最左前缀原则
mysql 建立多列索引(联合索引)有最左前缀的原则,即最左优先,如:
如果有一个2列的索引(col1,col2),则已经对(col1)、(col1,col2)上建立了索引;
如果有一个3列索引(col1,col2,col3),则已经对(col1)、(col1,col2)、(col1,col2,col3)上建立了索引;
33. 无法命中索引的情况
- 使用or关键字会导致无法命中索引
- 左前导查询会导致无法命中索引,如 like '%a' 或者 like '%a%'
- 单列索引的索引列为 null 时全值匹配会使索引失效,组合索引全为 null 时索引失效
- 组合索引不符合左前缀原则的列无法命中索引,如我们有4个列 a、b、c、d,我们创建一个组合索引 INDEX(
a
,b
,c
,d
),那么能命中索引的查询为 a,ab,abc,abcd,除此之外都无法命中索引 - 强制类型转换会导致索引失效
- 负向查询条件会导致无法使用索引,比如 NOT IN,NOT LIKE,!= 等
- 如果 mysql 估计使用全表扫描要比使用索引快,则不使用索引
34. 数据库读写分离
读写分离,就是将数据库分为了主从库,一个主库用于写数据,多个从库完成读数据的操作,主从库之间通过某种机制进行数据的同步,是一种常见的数据库架构。
35. 数据库分库分表
数据库水平切分,是一种常见的数据库架构,是一种通过算法,将数据库进行分割的架构。一个水平切分集群中的每个数据库,通常称为一个“分片”。每一个分片中的数据没有重合,所有分片中的数据并集组成全部数据。
水平切分分为库内分表和分库分表,是根据表内数据内在的逻辑关系,将同一个表按不同的条件分散到多个数据库或多个表中,每个表中只包含一部分数据,从而使得单个表的数据量变小,达到分布式的效果。
36. redis 和 memcached 比较
- redis 和 memcached 都是将数据存放在内存中,都是内存数据库。不过 memcached 还可用于缓存其他东西,例如图片、视频等等。
- redis 不仅仅支持简单的 k/v 类型的数据,同时还提供 list,set,hash 等数据结构的存储。
- 分布式设定, 都可以做一主多从或一主一从 。
- 存储数据安全,memcached 挂掉后,数据完全丢失;redis 可以定期保存到磁盘(持久化)。
- 灾难恢复,memcached 挂掉后,数据不可恢复; redis 数据丢失后可以通过 aof 恢复。
37. redis中数据库默认是多少个 db 及作用
redis 默认有16个数据库,每个数据库中的数据都是隔离的,这样,在存储数据的时候,就可以指定把不同的数据存储到不同的数据库中。
且只有单机才有,如果是集群就没有数据库的概念。
38. redis 有哪几种持久化策略
RDB 持久化:是将 Reids 在内存中的数据库记录定时 dump 到磁盘上的持久化
AOF(append only file)持久化:将 Reids 的操作日志以追加的方式写入文件
39. redis 支持的过期策略
通用的三种过期策略
- 定时删除
在设置 key 的过期时间的同时,为该 key 创建一个定时器,让定时器在 key 的过期时间来临时,对 key 进行删除 - 惰性删除
key 过期的时候不删除,每次从数据库获取 key 的时候去检查是否过期,若过期,则删除,返回 null。 - 定期删除
每隔一段时间执行一次删除过期 key 操作
redis 采用惰性删除+定期删除策略
40. 如何保证 redis 中的数据都是热点数据
限定 Redis 占用的内存,Redis 会根据自身数据淘汰策略,加载热数据到内存。
所以,计算一下所有热点数据大约占用的内存,然后设置一下 Redis 内存限制即可。
41. Python 操作 redis
使用 redis 第三方库来操作
import redis # 创建一个 redis 连接池 def redis_conn_pool(): pool = redis.ConnectionPool(host='redis-host', port=redis-port, decode_responses=True, password='redis-pwd') r = redis.Redis(connection_pool=pool) return r
42. 基于 redis 实现发布和订阅
订阅者
if __name__ == "__main__": conn = redis.Redis(host='', port=12143, password='') ps = conn.pubsub() ps.subscribe('chat') # 从 chat 订阅消息 for item in ps.listen(): # 监听状态:有消息发布了就拿过来 if item['type'] == 'message': print(item) print(item['channel']) print(item['data'])
发布者
if __name__ == "__main__": number_list = ['300033', '300032', '300031', '300030'] signal = ['1', '-1', '1', '-1'] pool = redis.ConnectionPool(host='redis-12143.c8.us-east-1-3.ec2.cloud.redislabs.com', port=12143, decode_responses=True, password='pkAWNdYWfbLLfNOfxTJinm9SO16eSJFx') r = redis.Redis(connection_pool=pool) for i in range(len(number_list)): value_new = str(number_list[i]) + ' ' + str(signal[i]) print(value_new) r.publish("chat", value_new)
43. 如何高效的找到 redis 中的某个 KEY
import redis con = redis.Redis() con.keys(pattern='key*') # *代表通配符
44. 基于 redis 实现先进先出、后进先出及优先级队列
class Zhan: def __init__(self,conn): self.conn = conn def push(self,val): self.conn.rpush('aaa',val) def pop(self): return self.conn.rpop('aaa') class Dui: def __init__(self,conn): self.conn = conn def push(self,val): self.conn.rpush('bbb',val) def get(self): return self.conn.lpop('bbb') class Xu: def __init__(self,conn): self.conn = conn def push(self,val,count): self.conn.zadd('ccc',val,count) def get(self): a = self.conn.zrange('ccc', 0, 0)[0] self.conn.zrem('ccc', a) return a
45. redis 如何实现主从复制
在从服务器中配置 SLAVEOF 127.0.0.1 6380 # 主服务器 IP,端口
46. 循环获取 redis 中某个非常大的列表数据
def list_iter(name): """ 自定义redis列表增量迭代 :param name: redis中的name,即:迭代name对应的列表 :return: yield 返回 列表元素 """ list_count = r.llen(name) for index in xrange(list_count): yield r.lindex(name, index)
47. redis 中的 watch 的命令的作用
watch 用于在进行事务操作的最后一步也就是在执行 exec 之前对某个 key 进行监视,如果这个被监视的 key 被改动,那么事务就被取消,否则事务正常执行。
48. redis 分布式锁
为 redis 集群设计的锁,防止多个任务同时修改数据库,其本质就是为集群中的每个主机设置一个会超时的字符串,当集群中有一半多的机器设置成功后就认为加锁成功,直至锁过期或解锁不会有第二个任务加锁成功。
49. http 协议
超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。HTTP 是一个客户端和服务器端请求和应答的标准。客户端是终端用户,服务器端是网站。一般由 HTTP 客户端发起一个请求,建立一个到服务器指定端口(默认是80端口)的 TCP 连接,HTTP 服务器则在那个端口监听客户端发送过来的请求,并给与响应。
50. uwsgi,uWSGI 和 WSGI 的区别
WSGI:全称是 Web Server Gateway Interface,是一种描述 web server 如何与 web application 通信的规范。django,flask 等都遵循该协议。
uwsgi:是服务器和服务端应用程序的一种协议,规定了怎么把请求转发给应用程序和返回; uwsgi 是一种线路协议而不是通信协议,在此常用于在 uWSGI 服务器与其他网络服务器的数据通信。
uWSGI:是一个 Web 服务器,它实现了 WSGI 协议、uwsgi、http 等协议。Nginx 中 HttpUwsgiModule 的作用是与 uWSGI 服务器进行交换。
51. HTTP 状态码
1xx: 信息
2xx:成功
3xx:重定向
4xx:客户端错误
5xx:服务器错误
52. HTTP常见请求方式
GET,POST,PUT,DELETE,PATCH 等
53. 响应式布局
响应式布局是 Ethan Marcotte 在2010年5月份提出的一个概念,简而言之,就是一个网站能够兼容多个终端——而不是为每个终端做一个特定的版本。
54. 实现一个简单的 AJAX 请求
AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
AJAX = 异步 JavaScript 和 XML
$(function(){ $('#send').click(function(){ $.ajax({ type: "GET", url: "test.json", data: {username:$("#username").val(), content:$("#content").val()}, dataType: "json", success: function(data){ $('#resText').empty(); //清空resText里面的所有内容 var html = ''; $.each(data, function(commentIndex, comment){ html += '<div class="comment"><h6>' + comment['username'] + ':</h6><p class="para"' + comment['content'] + '</p></div>'; }); $('#resText').html(html); } }); }); });
55. 同源策略
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
如果两个页面的协议,端口(如果有指定)和主机都相同,则两个页面具有相同的源。我们也可以把它称为“协议/主机/端口 tuple”,或简单地叫做“tuple". ("tuple" ,“元”,是指一些事物组合在一起形成一个整体,比如(1,2)叫二元,(1,2,3)叫三元)
56. 什么是 CORS
CORS 全称是跨域资源共享(Cross-Origin Resource Sharing),是一种 AJAX 跨域请求资源的方式,支持现代浏览器。
57. 什么是 CSRF
CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。
58. 前端实现轮询、长轮询
轮询
var xhr = new XMLHttpRequest(); setInterval(function(){ xhr.open('GET','/user'); xhr.onreadystatechange = function(){ }; xhr.send(); },1000)
长轮询
function ajax(){ var xhr = new XMLHttpRequest(); xhr.open('GET','/user'); xhr.onreadystatechange = function(){ ajax(); }; xhr.send(); }
59. 简述 MVC 和 MTV
所谓 MVC 就是把 web 应用分为模型(M),控制器(C),视图(V)三层,他们之间以一种插件似的,松耦合的方式连接在一起。
模型负责业务对象与数据库的对象(ORM),视图负责与用户的交互(页面),控制器(C)接受用户的输入调用模型和视图完成用户的请求。
Django 中的 MTV 模式:Model(模型):负责业务对象与数据库的对象(ORM),Template(模版):负责如何把页面展示给用户,View(视图):负责业务逻辑,并在适当的时候调用 Model 和 Template,本质上与 MVC 相同。
60. 接口的幂等性
接口幂等性就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。
61. Flask 框架的优势
简洁,轻巧,扩展性强,自由度高。
62. 什么是 ORM
ORM 的全称是 Object Relational Mapping,即对象关系映射。它的实现思想就是将关系数据库中表的数据映射成为对象,以对象的形式展现,这样开发人员就可以把对数据库的操作转化为对这些对象的操作。
63. PV、UV 的含义
PV:是(page view)访问量,页面浏览量或点击量,衡量网站用户访问的网页数量。在一定统计周期内用户每打开或刷新一个页面就记录1次,多次打开或刷新同一页面则浏览量累计。
UV:是(Unique Visitor)独立访客,统计一段时间内访问某站点的用户数(以cookie为依据)。
64. supervisor 的作用
supervisor 管理进程,是通过 fork/exec 的方式将这些被管理的进程当作 supervisor 的子进程来启动,所以我们只需要将要管理进程的可执行文件的路径添加到 supervisor 的配置文件中即可。
65. 使用 ORM 和原生 SQL 的优缺点
优点:
- 方便的使用面向对象,语句清晰;
- 有效的防止 SQL 注入;
- 方便动态构造语句,对于不同的表的相同操作采用多态实现更优雅;
- 一定程度上方便重构数据层
- 方便设置设置钩子函数
缺点:
- 不太容易处理复杂查询语句
- 性能较直接用 SQL 差
66. 列举一些 django 的内置组件
- Admin 组件:是对 model 中对应的数据表进行增删改查提供的组件
- model 组件:负责操作数据库
- form 组件:生成 HTML 代码;数据有效性校验;校验信息返回并展示
- ModelForm 组件:用于数据库操作,也可用于用户请求的验证
67. 列举 Django 中执行原生 sql 的方法
- 使用 execute 执行自定义的 SQL
直接执行 SQL 语句(类似于 pymysql 的用法)
from django.db import connection cursor = connection.cursor() cursor.execute("SELECT DATE_FORMAT(create_time, '%Y-%m') FROM blog_article;") ret = cursor.fetchall() print(ret)
- 使用 extra 方法:queryset.extra(select={"key": "原生的SQL语句"})
- 使用 raw 方法
- 执行原始 sql 并返回模型
- 依赖于 model 模型,多用于查询操作
68. cookie 和 session 的区别
cookie 是保存在浏览器端的键值对,可以用来做用户认证。
sesseion 是将用户的会话信息保存在服务端,key 值是随机产生的字符串,value 值是 session 的内容,依赖于 cookie 将每个用户的随机字符串保存到用户浏览器中。
69. beautifulsoup 模块的作用
BeautifulSoup 库是解析、遍历、维护“标签树”的功能库。
url = "http://www.baidu.com/" request = requests.get(url) html = request.content soup = BeautifulSoup(html, "html.parser", from_encoding="utf-8")
70. Selenium 模块简述
Selenium 是模拟操作浏览器的库,可以根据我们的指令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏,或者判断网站上某些动作是否发生等。
from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.taobao.com') print(browser.page_source) # browser.page_source 是获取网页的全部 html browser.close()
The End!