php的wsgi框架结构,理解 WSGI 框架

This document specifies a proposed standard interface between web servers and Python web applications or frameworks, to promote web application portability across a variety of web servers.

-—–PEP 0333

An Introduction to WSGI

在 java web 领域,支持 servlet API 的 java application 都可运行在支持 servlet API 的 web server (http server) 上。随着 web 不断发展,python application/framework 也如雨后春笋般涌出,如:zope, webware, skunkweb 等等。但是它们的接口存在差异,导致难以跨 web server 运行,所以 python 社区定义了一种如 servlet API 般标准通用的接口——WSGI。

PEP 0333 – Python Web Server Gateway Interface 是一种 web server or gateway 和 python web application or framework 之间简单通用的接口,符合这种接口的 application 可运行在所有符合该接口的 server 上。通俗的讲,WSGI 规范了一种简单的接口,解耦了 server 和 application,使得双边的开发者更加专注自身特性的开发。

Web server/gateway: 即 HTTP Server,处理 HTTP 协议,接受用户 HTTP 请求和提供并发,调用 web application 处理业务逻辑。通常采用 C/C++ 编写,代表:apache, nginx 和 IIS。

Python Web application/framework: 专注业务逻辑的 python 应用或者框架。

7e405be7e3c06a1c3621b4cf29ca0cd8.png

The Application/Framework Side

Application/framework 端必须定义一个 callable object,callable object 可以是以下三者之一:

function, method

class

instance with a __call__ method

Callable object 必须满足以下两个条件:

接受两个参数:字典(environ),回调函数(start_response,返回 HTTP status,headers 给 web server)

返回一个可迭代的值

基于 callable function 的 application/framework 样例如下:

Python

1

2

3

def

application

(

environ

,

start_response

)

:

start_response

(

'200 OK'

,

[

(

'Content-Type'

,

'text/plain'

)

]

)

return

[

'This is a python application!'

]

基于 callable class 的 application/framework 样例如下:

Python

1

2

3

4

5

6

7

8

class

ApplicationClass

(

object

)

:

def

__init__

(

self

,

environ

,

start_response

)

:

self

.

environ

=

environ

self

.

start_response

=

start_response

def

__iter__

(

self

)

:

self

.

start_response

(

'200 OK'

,

[

(

'Content-type'

,

'text/plain'

)

]

)

yield

"Hello world!n"

The Server/Gateway Side

Server/gateway 端主要专注 HTTP 层面的业务,重点是接收 HTTP 请求和提供并发。每当收到 HTTP 请求,server/gateway 必须调用 callable object:

接收 HTTP 请求,但是不关心 HTTP url, HTTP method 等

为 environ 提供必要的参数,实现一个回调函数 start_response,并传给 callable object

调用 callable object

我们直接使用支持 WSGI 框架的 wsgiref 库,编写一个样例:

1

2

3

4

5

6

7

8

9

10

# application/framework side

def

application

(

environ

,

start_response

)

:

start_response

(

'200 OK'

,

[

(

'Content-Type'

,

'text/plain'

)

]

)

return

[

'This is a python application!'

]

# server/gateway side

if

__name__

==

'__main__'

:

from

wsgiref

.

simple_server

import

make_server

server

=

make_server

(

'0.0.0.0'

,

8080

,

application

)

server

.

serve_forever

(

)

运行后,对于任意的 url 和 method,本例的返回值均为 ‘This is a python application!’:

1

2

3

4

5

$

curl

127.0.0.1

:

8080

This

is

a

python

application

!

$

curl

127.0.0.1

:

8080

/

test

This

is

a

python

application

!

Middleware: Components that Play Both Sides

Unix philosophy: do one thing and do it well.

0c72aa42256304138e534671b2995bee.png

Middleware 处于 server/gateway 和 application/framework 之间,对 server/gateway 来说,它相当于 application/framework;对 application/framework 来说,它相当于 server/gateway。每个 middleware 实现不同的功能,我们通常根据需求选择相应的 middleware 并组合起来,实现所需的功能。比如,可在 middleware 中实现以下功能:

根据 url 把用户请求调度到不同的 application 中。

负载均衡,转发用户请求

预处理 XSL 等相关数据

限制请求速率,设置白名单

e3f3a88a61fe18caf916659909ccb24b.png

WSGI 的 middleware 体现了 unix 的哲学之一:do one thing and do it well。事实上,在定义 WSGI 框架的时候,设计者就要求 server/gateway 和 application/framework 双方尽可能的简单,同时也要求 middleware 设计的简单而专一,PEP 333 提到:

If middleware can be both simple and robust, and WSGI is widely available in servers and frameworks, it allows for the possibility of an entirely new kind of Python web application framework: one consisting of loosely-coupled WSGI middleware components.

本例实现了一个 IPBlacklist 的 middleware:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

class

IPBlacklistMiddleware

(

object

)

:

def

__init__

(

self

,

app

)

:

self

.

app

=

app

def

__call__

(

self

,

environ

,

start_response

)

:

ip_addr

=

environ

.

get

(

'HTTP_HOST'

)

.

split

(

':'

)

[

0

]

if

ip_addr

not

in

(

'127.0.0.1'

)

:

return

forbidden

(

start_response

)

return

self

.

app

(

environ

,

start_response

)

def

forbidden

(

start_response

)

:

start_response

(

'403 Forbidden'

,

[

(

'Content-Type'

,

'text/plain'

)

]

)

return

[

'Forbidden'

]

def

application

(

environ

,

start_response

)

:

start_response

(

'200 OK'

,

[

(

'Content-Type'

,

'text/plain'

)

]

)

return

[

'This is a python application!'

]

if

__name__

==

'__main__'

:

from

wsgiref

.

simple_server

import

make_server

application

=

IPBlacklistMiddleware

(

application

)

server

=

make_server

(

'0.0.0.0'

,

8080

,

application

)

server

.

serve_forever

(

)

测试如下:

1

2

3

4

5

6

7

# 从本机测试

$

curl

127.0.0.1

:

8080

/

test

This

is

a

python

application

!

# 从其它主机测测试

$

curl

10.10.10.2

:

8080

/

test

Forbidden

Path Dispatching

至此样例的一个不足之处是对于任意的 url 和 method,程序的返回值均为 ‘This is a python application!’,所以我们需要增加 path dispatch 功能。由于 WSGI 框架下的 server/gateway 不处理 url 和 method,所以 url mapping 需由 application/framework 端完成。注意到参数 environ,它包含以下变量:

REQUEST_METHOD: 即 HTTP method

PATH_INFO: 即 HTTP url

所以 application/framework 可以根据 environ 的 REQUEST_METHOD 和 PATH_INFO 实现 path dispatch,样例如下:

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

class

IPBlacklistMiddleware

(

object

)

:

def

__init__

(

self

,

app

)

:

self

.

app

=

app

def

__call__

(

self

,

environ

,

start_response

)

:

ip_addr

=

environ

.

get

(

'HTTP_HOST'

)

.

split

(

':'

)

[

0

]

if

ip_addr

not

in

(

'127.0.0.1'

)

:

return

forbidden

(

start_response

)

return

self

.

app

(

environ

,

start_response

)

def

dog

(

start_response

)

:

start_response

(

'200 OK'

,

[

(

'Content-Type'

,

'text/plain'

)

]

)

return

[

'This is dog!'

]

def

cat

(

start_response

)

:

start_response

(

'200 OK'

,

[

(

'Content-Type'

,

'text/plain'

)

]

)

return

[

'This is cat!'

]

def

not_found

(

start_response

)

:

start_response

(

'404 NOT FOUND'

,

[

(

'Content-Type'

,

'text/plain'

)

]

)

return

[

'Not Found'

]

def

forbidden

(

start_response

)

:

start_response

(

'403 Forbidden'

,

[

(

'Content-Type'

,

'text/plain'

)

]

)

return

[

'Forbidden'

]

def

application

(

environ

,

start_response

)

:

path

=

environ

.

get

(

'PATH_INFO'

,

''

)

.

lstrip

(

'/'

)

mapping

=

{

'dog'

:

dog

,

'cat'

:

cat

}

call_back

=

mapping

[

path

]

if

path

in

mapping

else

not_found

return

call_back

(

start_response

)

if

__name__

==

'__main__'

:

from

wsgiref

.

simple_server

import

make_server

application

=

IPBlacklistMiddleware

(

application

)

server

=

make_server

(

'0.0.0.0'

,

8080

,

application

)

server

.

serve_forever

(

)

测试如下:

1

2

3

4

5

6

7

8

$

curl

127.0.0.1

:

8080

/

dog

This

is

dog

!

$

curl

127.0.0.1

:

8080

/

cat

This

is

cat

!

$

curl

127.0.0.1

:

8080

/

monkey

Not

Found

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值