Code war of Angel


  • Home

  • Archives

线程安全

Posted on 2019-04-15 | In 底层

原因一:多核CPU的缓存

当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。
按照数据读取顺序和与CPU结合的紧密程度,CPU缓存可以分为一级缓存(L1),二级缓存(L3),部分高端CPU还具有三级缓存(L3),每一级缓存中所储存的全部数据都是下一级缓存的一部分。
当CPU要读取一个数据时,首先从一级缓存中查找,如果没有找到再从二级缓存中查找,如果还是没有就从三级缓存或内存中查找。
单核CPU只含有一套L1,L2,L3缓存;如果CPU含有多个核心,即多核CPU,则每个核心都含有一套L1(甚至和L2)缓存,而共享L3(或者和L2)缓存。
image.png

  • 单线程。cpu核心的缓存只被一个线程访问。缓存独占,不会出现访问冲突等问题。
  • 单核CPU,多线程。进程中的多个线程会同时访问进程中的共享数据,CPU将某块内存加载到缓存后,不同线程在访问相同的物理地址的时候,都会映射到相同的缓存位置,这样即使发生线程的切换,缓存仍然不会失效。但由于任何时刻只能有一个线程在执行,因此不会出现缓存访问冲突。
  • 多核CPU,多线程。每个核都至少有一个L1 缓存。多个线程访问进程中的某个共享内存,且这多个线程分别在不同的核心上执行,则每个核心都会在各自的caehe中保留一份共享内存的缓冲。由于多核是可以并行的,可能会出现多个线程同时写各自的缓存的情况,而各自的cache之间的数据就有可能不同。这就是缓存一致性问题。

原因二:处理器优化和指令重排

那就是为了使处理器内部的运算单元能够尽量的被充分利用,处理器可能会对输入代码进行乱序执行处理。这就是处理器优化。
除了现在很多流行的处理器会对代码进行优化乱序处理,很多编程语言的编译器也会有类似的优化,比如Java虚拟机的即时编译器(JIT)也会做指令重排。

并发编程

并发编程中的线程安全,必须保证三个条件:

  1. 原子性:是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。
  2. 可见性:是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
  3. 有序性:即程序执行的顺序按照代码的先后顺序执行。

内存模型

为了保证共享内存的正确性(可见性、有序性、原子性),内存模型定义了共享内存系统中多线程程序读写操作行为的规范。通过这些规则来规范对内存的读写操作,从而保证指令执行的正确性。

Python中的线程安全

python中由于GIL(Global Interpreter Lock)的存在,即使开了多线程,同一个时间也只有一个线程在执行,但Python不是线程安全的。
GIL:任何python线程在执行之前都需要先获得GIL锁,然后每执行一部分代码,解释器就会自动释放GIL锁,其他线程就可以竞争这个锁,只有得到才能执行程序。对于CPU密集型程序,GIL锁的影响在于,有了它的存在,开启多线程无法利用多核优势,也就是只能用到一个核CPU来运行代码,要想用到多个核只能开启多进程(或者使用不带有GIL锁的解释器)。

  1. 使用线程锁: threading.Lock() / threading.RLock()
  2. 使用原子操作

参考
再有人问你Java内存模型是什么,就把这篇文章发给他
列表与队列——谈谈线程安全

python -u 命令行参数

Posted on 2019-04-11 | In Python

关闭stdin/stdout/stderr缓冲区.
-u :

  • unbuffered binary stdout and stderr; also PYTHONUNBUFFERED=x.
  • Force stdin, stdout and stderr to be totally unbuffered. On systems where it matters, also put stdin, stdout and stderr in binary mode. Note that there is internal buffering in xreadlines(), readlines() and file-object iterators (“for line in sys.stdin”) which is not influenced by this option. To work around this, you will want to use “sys.stdin.readline()” inside a “while 1:” loop.

参考

python sqlalchemy "缓存"问题

Posted on 2019-04-01 | In Python

描述:
在同一个会话中,使用两次同样的sql查询,即使数据改变了,返回的值相同。

原因一:数据库的事务隔离

在数据库系统中,事务隔离级别(isolation level)决定了数据在系统中的可见性。隔离级别从低到高分为四种:未提交读(Read uncommitted),已提交读(Read committed),可重复读(Repeatable read),可串行化(Serializable)。他们的区别如下表所示。

隔离级别 脏读 不可重复读 幻读
未提交读(RU) 可能 可能 可能
已提交读(RC) 不可能 可能 可能
可重复读(RR) 不可能 不可能 可能
可串行化 不可能 不可能 不可能

对于MySQL来说,默认的事务隔离级别是RR,RR是可重复读的,因此可以解释这个现象。

原因二:Sqlaalchemy的会话隔离

看作者在stackoverflow的回答:
“
The usual cause for people thinking there’s a “cache” at play, besides the usual SQLAlchemy identity map which is local to a transaction, is that they are observing the effects of transaction isolation. SQLAlchemy’s session works by default in a transactional mode, meaning it waits until session.commit() is called in order to persist data to the database. During this time, other transactions in progress elsewhere will not see this data.

However, due to the isolated nature of transactions, there’s an extra twist. Those other transactions in progress will not only not see your transaction’s data until it is committed, they also can’t see it in some cases until they are committed or rolled back also (which is the same effect your close() is having here). A transaction with an average degree of isolation will hold onto the state that it has loaded thus far, and keep giving you that same state local to the transaction even though the real data has changed - this is called repeatable reads in transaction isolation parlance.
”
SQLAlchemy的会话默认在事务模式下工作,这意味着它会等到调用session.commit()才将数据持久保存到数据库中。
由于事务的隔离性,正在进行的其他事务在提交之前不仅不会看到这个事务的数据,在它们被提交或回滚之前它们也无法在某些情况下看到它,这在事务隔离用语中称为可重复读取。

解决方法

在MySQL的同一个事务中,多次查询同一行的数据得到的结果是相同的,这里既有SQLAlchemy本身“缓存”结果的原因,也受到数据库隔离级别的影响。如果要强制读取最新的结果,最简单的办法就是在查询前手动COMMIT一次。(commit不仅把所有本地修改写入到数据库,同时也提交了该事务)

参考:
https://www.jianshu.com/p/c0a8275cce99
https://stackoverflow.com/questions/10210080/how-to-disable-sqlalchemy-caching
https://stackoverflow.com/questions/12108913/how-to-avoid-caching-in-sqlalchemy

tcp socket 与unix domain socket

Posted on 2019-03-23 | In 底层

什么是Socket

在网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个Socket。Socket可以被定义描述为两个应用通信通道的端点,一个Socket 端点可以用Socket地址(地址IP、端口、协议组成)来描述。Socket作为一种进程通信机制,操作系统会分配唯一一个Socket标识,这个标识与通讯协议有关(不仅限于TCP或UDP)。

Unix Domain Socket

Unix Domain Socket并不是一个实际的协议,它只在同客户机和服务器通信时使用的API,且一台主机与在不同主机间通信时使用相同的API。

Unix Domain Socket有以下特点:

  • Unix Domain Socket使用的地址通常是一个文件 xxx.sock
  • 在同一主机通讯时,传输速率是不同主机间的两倍
  • Unix Domain Socket套接字描述符可以在同一主机不同进程间传递
  • Unix Domain Socket套接字可以向服务器提供用户认证信息

TCP Socket与Unix Domain Socket

无论时TCP Socket套接字还是Unix Domain Socket套接字,每个套接字都是唯一的。TCP Socket通过IP和端口描述,而Unix Domain Socket描述。
TCP属于传输层的协议,使用TCP Socket进行通讯时,需要经过传输层TCP/IP协议的解析。
而Unix Domain Socket可用于不同进程间的通讯和传递,使用Unix Domain Socket进行通讯时不需要经过传输层,也不需要使用TCP/IP协议。所以,理论上讲Unix Domain Socket具有更好的传输效率。

socket缓冲区

TCP套接字的I/O缓冲区示意图

每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。write()/send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。TCP协议独立于 write()/send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,这取决于当时的网络情况、当前线程是否空闲等诸多因素,不由程序员控制。read()/recv() 函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取。
大小:socket默认的是1024×8=8192字节
特性:

  • I/O缓冲区在每个TCP套接字中单独存在;
  • I/O缓冲区在创建套接字时自动生成;
  • 即使关闭套接字也会继续传送输出缓冲区中遗留的数据;
  • 关闭套接字将丢失输入缓冲区中的数据。

实时性:

  • TCP socket的send缓冲区有自己的timeout,因为默认开启Nagle算法,所以缓冲区没满的话要等到超时再发送。对实时性有要求可以用setsockopt关闭Nagle算法。
  • 设置合适的缓存区的大小。

Nagle算法

Nagle算法由John Nagle在1984年提出,这个算法可以减少网络中小的packet的数量,从而降低网络的拥塞程度。
为了减小网络开销,Nagle算法指出,当TCP发送了一个小的segment(小于MSS),它必须等到接收了对方的ACK之后,才能继续发送另一个小的segment。那么在等待的过程中(一个RTT时间),TCP就能尽量多地将要发送的数据收集在一起,从而减少要发送的segment的数量。
默认情况下,TCP开启了Nagle算法,然而Nagle算法并不是灵丹妙药,它会增加TCP发送数据的延迟。在一些要求低延迟的应用程序中(例如即时通讯应用),则需要禁用Nagle算法。

Nagle算法的规则:

  • 如果包长度达到MSS,则允许发送;
  • 如果该包含有FIN,则允许发送;
  • 设置了TCP_NODELAY选项,则允许发送;
  • 未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;
  • 上述条件都未满足,但发生了超时(一般为200ms),则立即发送。

禁用:

1
2
int optval = 1;
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));

Delayed ACK

TCP的 Delayed ACK 与Nagle算法有异曲同工之妙,Delayed ACK很好理解,当TCP接收到数据时,并不会立即发送ACK给对方,相反,它会等待应用层产生数据,以便将ACK和数据一起发送(在Linux最多等待40ms)。
为避免这种延迟的出现,需要做两件事:

  • 设置TCP_NODELAY选项。
  • 将客户端的两次write()合并成一个,避免服务端的Delayed ACK。

TCP_CORK

Linux提供了TCP_CORK选项,如果在某个TCP socket上开启了这个选项,那就相当于在这个socket的出口堵上了塞子,往这个socket写入的数据都会聚集起来。下面几种情况都会导致这个塞子打开,这样TCP就能继续发送segment出来了。

  • 程序取消设置TCP_CORK这个选项。
  • socket聚集的数据大于一个MSS的大小。
  • 自从堵上塞子写入第一个字节开始,已经经过200ms。
  • socket被关闭了。

提高socket性能

  • 禁用 Nagle 算法来减少传输延时
  • 通过设置缓冲区的大小来提高 socket 带宽的利用
  • 通过最小化系统调用的个数来降低系统调用的负载
  • 以及使用可调节的内核参数来优化 Linux 的 TCP/IP 栈。

参考:

  1. 参考
  2. Unix domain socket 的一些小结
  3. Nagle 算法与 TCP socket 选项 TCP_CORK
  4. *提高 Linux 上 socket 性能-IBM
  5. socket.recv 完整接收数据
  6. *Python 中的 Socket 编程(指南)

flask blurprint 增加路由自动发现

Posted on 2019-03-05 | In Python

路由自动发现基于约定代码结构及命名规范实现的。
例子:
约定 [name]_api.py 输出对象名:[name]_bp
目录结构:
—– view

- __init__.py
- user_api.py
- history_api.py
- type_api.py

在文件__init__.py中,原本写法

1
2
3
4
5
6
7
8
9
from .user_api import user_bp
from .history_api import history_bp
from .type_api import type_api

blue_print = [user_bp, history_bp, type_api]
......
......
for bp in blue_prints:
app.register_blueprint(bp)

现在改成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import os
import re
path = os.getcwd() + '/app/views'

# 路由自动查找
blue_prints = []
for root, dirs, files in os.walk(path):
for f in files:
if re.match('(.*?)_api.py', f):
name = f.replace('_api.py', '')
_temp = __import__('app.views.'+name+'_api', fromlist=[name+'_bp'])
if(hasattr(_temp,name+'_bp')):
blue_prints.append(getattr(_temp,name+'_bp'))
......
......
for bp in blue_prints:
app.register_blueprint(bp)

nginx+gunicorn+flask-sockerio docker部署实战

Posted on 2019-03-01 | In Python

部署方案:flask+uwsgi服务部署一个容器(服务使用flask-socketio实现websocket)、nginx部署一个容器

如何配置:

nginx

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
server {
listen 80;
server_name test.local.com;
error_log /apps/logs/error_nginx.log ;
access_log /apps/logs/access_log.log ;

location / {
include proxy_params;
;;; 如果include proxy_params 没找到 替换成
; proxy_set_header Host $http_host;
; proxy_set_header X-Real-IP $remote_addr;
; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
; proxy_set_header X-Forwarded-Proto $scheme;

proxy_pass http://host_ip:8002;
}

location /socket.io {
include proxy_params;
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass http://host_ip:8002/socket.io;
}
}

注意:host_ip为宿主机ip

uwsgi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[uwsgi]
# uwsgi 启动时所使用的地址与端口
http-socket = :8001

master = true
;lazy-apps = true
# 指向网站目录
chdir = /app

# python 启动程序文件
wsgi-file = uwsgi_entry.py
# python 程序内用以启动的 application 变量名
callable = app

# 处理器数
processes = 8
;listen = 1024
gevent = 100
http-websockets = true

uwsgi_entry.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from config import app_config
from app import create_app
from flask_socketio import SocketIO
from app.services.message_push import MessagePush

app = create_app(app_config)
# 初始化socket 注意async_mode
socketio = SocketIO(app, async_mode="threading")
# nameSpace以类形式初始化
MessagePushHelper = MessagePush('/test')
socketio.on_namespace(MessagePushHelper)

# 实际nginx转发的端口是socket运行的端口 注意host配置0.0.0.0
socketio.run(app, host="0.0.0.0", port=8002, use_reloader=False)

后续改用gunicorn

后面发现这个方案其实只是利用uwsgi起了个嵌入式服务器,所有请求根本没有经过uwsgi服务器。
后面看官方文档,改成

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
30
31
32
33
34
35
36
37
38
39
40
[uwsgi]
# uwsgi 启动时所使用的地址与端口
http-socket = :8001

master = true
;lazy-apps = true
# 指向网站目录
chdir = /app

# python 启动程序文件
wsgi-file = uwsgi_entry.py
# python 程序内用以启动的 application 变量名
callable = app

# 处理器数
processes = 1
;listen = 1024
gevent = 100
http-websockets = true

........
uwsgi_entry.py

from config import app_config
from app import create_app
from flask_socketio import SocketIO
from app.services.message_push import MessagePush

# 初始化socket 注意async_mode
socketio = SocketIO()
app = create_app(app_config,socketio)
# nameSpace以类形式初始化
MessagePushHelper = MessagePush('/test')
socketio.on_namespace(MessagePushHelper)

.....
def create_app(app,socketio):
....
socketio.init_app(app, message_queue='redis://'+ config.HOST_IP) #mutil thread
return app

测试发现,还是有间隔400出现,前端不断的connect,disconnect
尝试n个方法无果,改用gunicorn服务器
gunicorn配置

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
    #  -*- coding: utf-8 -*-
# 预加载资源
preload_app = True
# 绑定
bind = "0.0.0.0:8002"
# 进程数
workers = 1
# 线程数
threads = 10
# 等待队列最大长度,超过这个长度的链接将被拒绝连接
backlog = 100
# 工作模式
# worker_class = "egg:meinheld#gunicorn_worker"
worker_class = "geventwebsocket.gunicorn.workers.GeventWebSocketWorker"
# 最大客户客户端并发数量,对使用线程和协程的worker的工作有影响
worker_connections = 100
```
因为websocket需要粘性会话,所以worker只能设为1
[gunicorn不同woker type](https://medium.com/@genchilu/淺談-gunicorn-各個-worker-type-適合的情境-490b20707f28)
其余代码跟上面一样,目前测试稳定。

## 如何访问
1. 容器间通讯,目前通过docker run -p 3306:3306 暴露端口给宿主机,容器内部服务访问通过 宿主机ip+暴露的端口进行访问,[其他方法](https://birdben.github.io/2017/05/02/Docker/Docker实战(二十七)Docker容器之间的通信/)
2. mysql 持久化:通过 -v /home/data/mysql_data/:/var/lib/mysql/ 保存数据文件 默认路径可以查看 /etc/my.cnf里的datadir配置
3. 由于uwsgi_entry.py 直接运行了 socketio.run命令,实际上所有的服务是通过这个命令的端口访问的,-p暴露的也是这里的端口
4. uwsgi_docker.ini 要配置http-socket模式

## 问题与方案
1. 逻辑在子线程进行websocket的emit时,参考flask-socketio的文档:

In all the examples shown until this point the server responds to an event sent by the client. But for some applications, the server needs to be the originator of a message. This can be useful to send notifications to clients of events that originated in the server, for example in a background thread. The socketio.send() and socketio.emit() methods can be used to broadcast to all connected clients:

def some_function():
    socketio.emit('some event', {'data': 42})

Note that socketio.send() and socketio.emit() are not the same functions as the context-aware send() and emit(). Also note that in the above usage there is no client context, so broadcast=True is assumed and does not need to be specified.
1
2
注意需要使用 socketio.emit方法,默认是广播的。
socketio 是flask-socketio初始化的实际,注意初始化时配置 async_mode="threading" 参数,否则emit不成功
async_mode – The asynchronous model to use. See the Deployment section in the documentation for a description of the available options. Valid async modes are threading, eventlet, gevent and gevent_uwsgi. If this argument is not given, eventlet is tried first, then gevent_uwsgi, then gevent, and finally threading. The first async mode that has all its dependencies installed is then one that is chosen

有效的异步模式参数是 threading, eventlet, gevent, gevent_uwsgi。

`

  1. 时区: 在docker run 时增加参数 -v /etc/localtime:/etc/localtime
    其他方法

  2. async_mode=eventlet 有问题,see this

docker部署uwsgi+flask问题

Posted on 2019-01-30 | In docker

问题:
docker中pip install opencv-python失败.

dockerFile中

1
2
3
4
From Alpine
....

RUN pip3 install opencv-python

docker build 时出现报错,即使升级了pip之后:
Could not find a version that satisfies the requirement opencv-python (from version: )
No matching distribution found for opencv-python

原因:
from stackoverflow
I’ve just run into this issue as well. It turns out that this is not working because opencv-python does not have any prebuilt wheels for Alpine (the distribution you’re using as your base docker image).

The conversation in this issue on the opencv-python package explains why this happens in greater detail. The TL;DR is: if you really need to use Alpine, you can try forcing the installation of the manylinux wheel for opencv-python, but this can break. Your best option if you need to keep Alpine is to build the module from source. Since you are running this on OpenFAAS, I suspect you will want to keep your size low, so building from source may be a good option for you.

If you’re not attached to Alpine, I would suggest moving to a different base docker image. If you’re not sure which image to use as your base, I would recommend python:3.7-slim, since it will come with Python already installed (substitute 3.7 for whichever version you are using, but really. . . 3.7 is nice). With this container, you can simply run pip install opencv-python numpy scipy to have all three of your desired packages installed. The rest of your Dockerfile should work mostly unmodified; you will just need to install/uninstall curl using apt instead of apk.

简单来说就是,Alpine中没有prebuild opencv-python,如果一定要用Alpine,可以强制下载编译,但是有可能出错。也可以更换使用 python:3.x-slim镜像。
最后Dockerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FROM python:3.5.6-slim-stretch
WORKDIR /app
ADD ....

RUN apt-get update && apt-get install -y --no-install-recommends apt-utils && \
apt-get install -y \
libc6-dev \
gcc \
mime-support vim libtiff5 libglib2.0-0 libsm6 libxext6 libxrender1 libxext-dev libpcre3 libpcre3-dev && \
pip3 install --no-cache-dir uWSGI \
&& apt-get remove -y \
gcc \
libc6-dev \
&& rm -rf /var/lib/apt/lists/* && \
pip3 install setuptools==39.1.0 && \
pip3 install -r requirements.txt

ENV LD_LIBRARY_PATH=/app/project/services/libs
ENV PYTHONPATH=/app/project/services/libs

CMD ["uwsgi", "--ini", "uwsgi_docker.ini"]

在部署docker过程中会遇到:
error
此时需要删掉uwsgi的配置 plugin=python
最后uwsgi.ini配置:

1
2
3
4
5
6
7
[uwsgi]
socket = 0.0.0.0:5051
chdir = /app
wsgi-file = /app/app.py
callable = app
processes = 4
threads = 2

nginx配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {
charset utf-8;
client_max_body_size 128M;

listen 80;

server_name xxx.local.com;
root /vagrant/xxx;
index index.py;

location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:5051;
}
}

Nginx通讯机制

Posted on 2019-01-29 | In 服务器

PHP-FPM与Nginx通讯机制

CGI协议

  1. 通用网关接口(Common Gateway Interface/CGI),CGI描述了服务器和请求处理程序之间传输数据的一种标准。
  2. Common:比如Java的Servlet,Python的WSGI,理论上来说,所有支持标准输出,支持获取环境变量的编程语言都能用来编写CGI程序。
  3. Gateway:“协议翻译机”,通常与网关输入输出两端通信使用的是不同的协议。即一方是HTTP协议,另一方可能是其他协议,比如企业内部的自定义协议。CGI程序既是如此。
  4. Interface:CGI其实是构架在HTTP协议之上的。它描述的是另一个维度的共识标准。
    CGI协议

FastCGI

由于 CGI 的机制是每处理一个请求需要 fork 一个 CGI 进程,请求结束再kill掉这个进程,在实际应用上比较浪费资源,于是就出现了CGI 的改良版本 FastCGI,FastCGI 在请求处理完后,不会 kill 掉进程,而是继续处理多个请求,这样就大大提高了效率。

PHP-FPM

PHP-FPM 即 PHP-FastCGI Process Manager, 它是 FastCGI 的实现,并提供了进程管理的功能。进程包含 master 进程和 worker 进程两种;master 进程只有一个,负责监听端口,接收来自服务器的请求,而 worker 进程则一般有多个(具体数量根据实际需要进行配置),每个进程内部都会嵌入一个 PHP 解释器,是代码真正执行的地方。
PHP-FPM

参考

Python

uwsgi

  1. WSGI: 它是用在 python web 框架编写的应用程序与web服务器之间的规范。它使得Web App可以与Web Server顺利通信。所有使用 WSGI 的服务器都可以运行使用 WSGI 规范的web 框架。它规定WSGI application应该实现为一个可调用对象。
  2. uWSGI: uWSGI: 是一个Web服务器(WSGI容器),它实现了WSGI协议、uwsgi、http等协议。用于接收前端服务器转发的动态请求并处理后发给 web 应用程序。
  3. uwsgi: uwsgi是uWSGI服务器实现的独有的协议。

FLASK

Flask是一个web框架,而且Flask是基于werkzeug开发的,那werkzeug是什么呢?

Werkzeug是一个WSGI工具包,他可以作为一个Web框架的底层库。werkzeug 不是一个web服务器,也不是一个web框架,而是一个工具包,官方的介绍说是一个 WSGI 工具包,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等。使用它可以减轻web框架开发工作量。我看过werkzeug的源码后发现,werkzeug也实现了WSGI容器的功能,而且利用python/http/server.py库实现了一个简易的http服务器。因此在调试的时候可以直接使用app.run()把服务器给运行起来。

参考

HTTP中的"链接"

Posted on 2019-01-22 | In 浏览器

短链接

在HTTP/1.0中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。如果客户端浏览器访问的某个HTML或其他类型的 Web页中包含有其他的Web资源,如JavaScript文件、图像文件、CSS文件等;当浏览器每遇到这样一个Web资源,就会建立一个HTTP会话。

长链接

HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头有加入这行代码:Connection:keep-alive
在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接要客户端和服务端都支持长连接。
HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。

Websocket

WebSocket是一种在单个TCP连接上进行全双工通信的协议。
网站为了实现推送技术,所用的技术都是轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
而比较新的技术去做轮询的效果是Comet。这种技术虽然可以双向通信,但依然需要反复发出请求。而且在Comet中,普遍采用的长链接,也会消耗服务器资源。
在这种情况下,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
Websocket使用ws或wss的统一资源标志符,类似于HTTPS,其中wss表示在TLS之上的Websocket。

HTTP2

wiki

  1. 新的二进制格式:HTTP1.x的解析是基于文本,HTTP2.0的协议解析决定采用二进制格式
  2. 多路复用
    多路复用
  3. header压缩
  4. 服务端推送
  5. HTTP2.0其实可以支持非HTTPS的,但是现在主流的浏览器像chrome,firefox表示还是只支持基于 TLS 部署的HTTP2.0协议,所以要想升级成HTTP2.0还是先升级HTTPS为好

机器学习基石学习笔记(三)

Posted on 2019-01-18 | In 机器学习
1…567…10
Angel Teng

Angel Teng

97 posts
14 categories
37 tags
© 2021 Angel Teng
Powered by Hexo
|
Theme — NexT.Muse v5.1.4