HTTP
什么是 HTTP?超文本传输协议(HTTP)的设计目的是保证客户机与服务器之间的通信。举例:客户端(浏览器)向服务器提交 HTTP 请求;服务器向客户端返回响应。响应包含关于请求的状态信息以及可能被请求的内容。
关于http tcp ip请参考这个
关于常用的协议有 grpc http thrift
HTTP的方法GET及POST
- GET 方法
请注意,查询字符串(名称/值对)是在 GET 请求的 URL 中发送的
/test/demo_form.asp?name1=value1&name2=value2
- GET 请求可被缓存
- GET 请求可被收藏为书签
- GET 请求不应在处理敏感数据时使用
- GET 请求有长度限制
-
GET 请求只应当用于取回数据
- POST 方法
请注意,查询字符串(名称/值对)是在 POST 请求的 HTTP 消息主体中发送的
POST /test/demo_form.asp HTTP/1.1
Host: w3schools.com
name1=value1&name2=value2
其他 HTTP 请求方法
- HEAD 与 GET 相同,但只返回 HTTP 报头,不返回文档主体。
- PUT 上传指定的 URI 表示。
- DELETE 删除指定资源。
- OPTIONS 返回服务器支持的 HTTP 方法。
- CONNECT 把请求连接转换到透明的 TCP/IP 通道。
HTTP响应码
1xx: 信息
- 100 Continue 服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求
- 101 Switching Protocols 服务器转换协议:服务器将遵从客户的请求转换到另外一种协议。
2xx: 成功
- 200 OK 请求成功(其后是对GET和POST请求的应答文档。)
- 201 Created 请求被创建完成,同时新的资源被创建。
- 202 Accepted 供处理的请求已被接受,但是处理未完成。
- 203 Non-authoritative Information 文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝。
- 204 No Content 没有新文档。浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的。
- 205 Reset Content 没有新文档。但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容。
- 206 Partial Content 客户发送了一个带有Range头的GET请求,服务器完成了它。
3xx: 重定向
- 300 Multiple Choices 多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址。
- 301 Moved Permanently 所请求的页面已经转移至新的url。
- 302 Found 所请求的页面已经临时转移至新的url。
- 303 See Other 所请求的页面可在别的url下被找到。
- 304 Not Modified 未按预期修改文档。客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。
- 305 Use Proxy 客户请求的文档应该通过Location头所指明的代理服务器提取。
- 306 Unused 此代码被用于前一版本。目前已不再使用,但是代码依然被保留。
- 307 Temporary Redirect 被请求的页面已经临时移至新的url。
4xx: 客户端错误
- 400 Bad Request 服务器未能理解请求。
- 401 Unauthorized 被请求的页面需要用户名和密码。
- 402 Payment Required 此代码尚无法使用。
- 403 Forbidden 对被请求页面的访问被禁止。
- 404 Not Found 服务器无法找到被请求的页面。
- 405 Method Not Allowed 请求中指定的方法不被允许。
- 406 Not Acceptable 服务器生成的响应无法被客户端所接受。
- 407 Proxy Authentication Required 用户必须首先使用代理服务器进行验证,这样请求才会被处理。
- 408 Request Timeout 请求超出了服务器的等待时间。
- 409 Conflict 由于冲突,请求无法被完成。
- 410 Gone 被请求的页面不可用。
- 411 Length Required “Content-Length” 未被定义。如果无此内容,服务器不会接受请求。
- 412 Precondition Failed 请求中的前提条件被服务器评估为失败。
- 413 Request Entity Too Large 由于所请求的实体的太大,服务器不会接受请求。
- 414 Request-url Too Long 由于url太长,服务器不会接受请求。当post请求被转换为带有很长的查询信息的get请求时,就会发生这种情况。
- 415 Unsupported Media Type 由于媒介类型不被支持,服务器不会接受请求。
- 416 服务器不能满足客户在请求中指定的Range头。
- 417 Expectation Failed
5xx: 服务器错误
- 500 Internal Server Error 请求未完成。服务器遇到不可预知的情况。
- 501 Not Implemented 请求未完成。服务器不支持所请求的功能。
- 502 Bad Gateway 请求未完成。服务器从上游服务器收到一个无效的响应。
- 503 Service Unavailable 请求未完成。服务器临时过载或当机。
- 504 Gateway Timeout 网关超时。
- 505 HTTP Version Not Supported 服务器不支持请求中指明的HTTP协议版本。
HTTP缓存
HTTP1.0 HTTP 1.1主要区别
- 长连接
HTTP 1.0需要使用keep-alive参数来告知服务器端要建立一个长连接,而HTTP1.1默认支持长连接。
HTTP是基于TCP/IP协议的,创建一个TCP连接是需要经过三次握手的,有一定的开销,如果每次通讯都要重新建立连接的话,对性能有影响。因此最好能维持一个长连接,可以用个长连接来发多个请求
- 节约带宽
HTTP 1.1支持只发送header信息(不带任何body信息),如果服务器认为客户端有权限请求服务器,则返回100,否则返回401。客户端如果接受到100,才开始把请求body发送到服务器。
这样当服务器返回401的时候,客户端就可以不用发送请求body了,节约了带宽。
另外HTTP还支持传送内容的一部分。这样当客户端已经有一部分的资源后,只需要跟服务器请求另外的部分资源即可。这是支持文件断点续传的基础。
- HOST域 现在可以web server例如tomat,设置虚拟站点是非常常见的,也即是说,web server上的多个虚拟站点可以共享同一个ip和端口。
HTTP1.0是没有host域的,HTTP1.1才支持这个参数
HTTP1.1 HTTP 2.0主要区别
- 多路复用 HTTP2.0使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级。
当然HTTP1.1也可以多建立几个TCP连接,来支持处理更多并发的请求,但是创建TCP连接本身也是有开销的。
TCP连接有一个预热和保护的过程,先检查数据是否传送成功,一旦成功过,则慢慢加大传输速度。因此对应瞬时并发的连接,服务器的响应就会变慢。所以最好能使用一个建立好的连接,并且这个连接可以支持瞬时并发的请求。
关于多路复用,可以参看学习NIO 。
-
数据压缩 HTTP1.1不支持header数据的压缩,HTTP2.0使用HPACK算法对header的数据进行压缩,这样数据体积小了,在网络上传输就会更快。
-
服务器推送 意思是说,当我们对支持HTTP2.0的web server请求数据的时候,服务器会顺便把一些客户端需要的资源一起推送到客户端,免得客户端再次创建连接发送请求到服务器端获取。这种方式非常合适加载静态资源。
服务器端推送的这些资源其实存在客户端的某处地方,客户端直接从本地加载这些资源就可以了,不用走网络,速度自然是快很多的。
缓存
关系型数据库遵循ACID规则(原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)),而Nosql数据库遵循BASE原则(基本可用(Basically Availble)、软/柔性事务(Soft-state )、最终一致性(Eventual Consistency))。由于关系型数据库的数据强一致性,所以对事务的支持很好。关系型数据库支持对事务原子性细粒度控制,并且易于回滚事务。而Nosql数据库是在CAP(一致性、可用性、分区容忍度)中任选两项,因为基于节点的分布式系统中,很难全部满足,所以对事务的支持不是很好,虽然也可以使用事务,但是并不是Nosql的闪光点。
memcache
redis
mongoDB
数据库
mysql
oracle
postgresql
PostgreSQL是一个功能强大的开源对象关系数据库管理系统(ORDBMS)。它是开源的,其源代码是免费提供的。
sqlite
SQLite 是一个软件库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是在世界上最广泛部署的 SQL 数据库引擎。SQLite 源代码不受版权限制。就像其他数据库,SQLite 引擎不是一个独立的进程,可以按应用程序需求进行静态或动态连接。SQLite 直接访问其存储文件。
中间件
MQ
zookeeper/consul
kafka
进程线程协程
线程
多任务可以由多进程完成,也可以由一个进程内的多线程完成。
我们前面提到了进程是由若干线程组成的,一个进程至少有一个线程。
由于线程是操作系统直接支持的执行单元,因此,高级语言通常都内置多线程的支持,Python也不例外,并且,Python的线程是真正的Posix Thread,而不是模拟出来的线程。
Python的标准库提供了两个模块:thread和threading,thread是低级模块,threading是高级模块,对thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。
启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行: 参考
- threading
import time, threading
# 新线程执行的代码:
def loop():
print 'thread %s is running...' % threading.current_thread().name
n = 0
while n < 5:
n = n + 1
print 'thread %s >>> %s' % (threading.current_thread().name, n)
time.sleep(1)
print 'thread %s ended.' % threading.current_thread().name
print 'thread %s is running...' % threading.current_thread().name
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print 'thread %s ended.' % threading.current_thread().name
进程
- multiprocessing
- subprocess
很多时候,子进程并不是自身,而是一个外部进程。我们创建了子进程后,还需要控制子进程的输入和输出。
subprocess模块可以让我们非常方便地启动一个子进程,然后控制其输入和输出。
下面的例子演示了如何在Python代码中运行命令nslookup www.python.org,这和命令行直接运行的效果是一样的:
import subprocess
print('$ nslookup www.python.org')
r = subprocess.call(['nslookup', 'www.python.org'])
print('Exit code:', r)
运行结果:
$ nslookup www.python.org
Server: 192.168.19.4
Address: 192.168.19.4#53
Non-authoritative answer:
www.python.org canonical name = python.map.fastly.net.
Name: python.map.fastly.net
Address: 199.27.79.223
Exit code: 0
如果子进程还需要输入,则可以通过communicate()方法输入:
import subprocess
print('$ nslookup')
p = subprocess.Popen(['nslookup'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate(b'set q=mx\npython.org\nexit\n')
print(output.decode('utf-8'))
print('Exit code:', p.returncode)
上面的代码相当于在命令行执行命令nslookup,然后手动输入:
set q=mx
python.org
exit
运行结果如下:
$ nslookup
Server: 192.168.19.4
Address: 192.168.19.4#53
Non-authoritative answer:
python.org mail exchanger = 50 mail.python.org.
Authoritative answers can be found from:
mail.python.org internet address = 82.94.164.166
mail.python.org has AAAA address 2001:888:2000:d::a6
Exit code: 0
1 p.start():启动进程,并调用该子进程中的p.run()
2 p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
3
4 p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
5 p.is_alive():如果p仍然运行,返回True
6
7 p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
复制代码
进程间通信
Python的multiprocessing提供了Queue、Pipes等多种方式来交换数据,说白了就是调用消息队列
from multiprocessing import Process, Queue
import os, time, random
# 写数据进程执行的代码:
def write(q):
print('Process to write: %s' % os.getpid())
for value in ['A', 'B', 'C']:
print('Put %s to queue...' % value)
q.put(value)
time.sleep(random.random())
# 读数据进程执行的代码:
def read(q):
print('Process to read: %s' % os.getpid())
while True:
value = q.get(True)
print('Get %s from queue.' % value)
if __name__=='__main__':
# 父进程创建Queue,并传给各个子进程:
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
# 启动子进程pw,写入:
pw.start()
# 启动子进程pr,读取:
pr.start()
# 等待pw结束:
pw.join()
# pr进程里是死循环,无法等待其结束,只能强行终止:
pr.terminate()
multiprocessing本身的Queue方法封装了类似于unix的fork()功能