Django 源码小剖: Django 中的 WSGI
2013-09-06 22:31 by 捣乱小子, 334 阅读, 0 评论, 收藏, 编辑
Django 其内部已经自带了一个方便本地测试的小服务器, 所以在刚开始学习 Django 的时候并不需搭建 apache 或者 nginx 服务器. Django 自带的服务器基于 python wsgiref 模块实现的, 其百分之七八十的代码都是 wsgiref 中的代码, 只重写了一部分, 所以 Django 自带的服务器测试写个 helloworld 就好了.
Django 内置服务器在 django.core.servers 和 django.core.handlers, 这两者共同来实现.先看看 django.core.servers. 下面是目录结构:
E:\DECODE-DJANGO\DJANGO-1.5.1\DJANGO\CORE\SERVERS
basehttp.py 重写 ServerHandler,WSGIServer,WSGIRequestHandler,定义 run() 函数
fastcgi.py
__init__.py
下面的代码足以说明「百分之七八十」:
1
2
3
4
5
|
class ServerHandler(simple_server.ServerHandler, object ):
... class WSGIServer(simple_server.WSGIServer, object ):
... class WSGIRequestHandler(simple_server.WSGIRequestHandler, object ):
|
具体内部做了一些变更:
- 重写了 write 函数, 当传输数据过大的时候分段传输
- 多了一些异常处理
- 错误记录
都是无关痛痒, 不详细展开了.这里定义了一个很有意思的函数 run():
1
2
3
4
5
6
7
8
9
10
11
|
def run(addr, port, wsgi_handler, ipv6 = False , threading = False ):
server_address = (addr, port)
if threading:
httpd_cls = type ( str ( 'WSGIServer' ), (socketserver.ThreadingMixIn, WSGIServer), {})
else :
httpd_cls = WSGIServer
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6 = ipv6)
httpd.set_app(wsgi_handler)
httpd.serve_forever() 永久运行
|
这和上一篇 if __name__ == '__main__': 中的代码效果类似, 实例化服务器类, 让它跑起来. 在 run() 函数中可以根据喜好配置:
add: 地址, 可传入 ip 地址, 一般是 127.0.0.1
port: 端口, 自定义端口
wsgi_handler: 上节提到的 application, 在 django.core.handlers 中定义
ipv6: 如果为 true, 会将协议地址族换成是 AF_INET6
threading: 如果为 true, 服务器会被强制成 type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {})(这个我漏讲了, 但功能是这样), 能处理多线程处理请求.
所以, 调用这个函数可以让一个自定义服务器跑起来.
wsgi_handler 参数定义了 application, 而 application 必须是一个 start_response(status, response_headers, exc_info=None) 形式的函数或者定义了 __call__ 的类. 而 django.core.handlers 就用后一种方式实现了 application.
E:\DECODE-DJANGO\DJANGO-1.5.1\DJANGO\CORE\HANDLERS
base.py application 的基类 BaseHandler
wsgi.py 实现 WSGIHandler 类, 定义了 __call__, 这样就名正言顺的 WSGI 中的 application 了
__init__.py
事实上, 在 WSGI 中除了 application,server 外, 还有一个 middleware, 名曰中间件. 在上一篇中故意漏了, 因为没有涉及到.最后我疏离一下上边提到的类模块等等, 方便大家找源码, 整理如下:
C:\PYTHON27\LIB\WSGIREF
handlers.py 定义了 BaseHandler, SimpleHandler 类
headers.py
simple_server.py 定义了 ServerHandler, WSGIRequestHandler 类, demo_app(), make_server()
util.py
validate.py
__init__.pyE:\DECODE-DJANGO\DJANGO-1.5.1\DJANGO\CORE\SERVERS
basehttp.py 重写 ServerHandler,WSGIServer,WSGIRequestHandler,定义 run() 函数
fastcgi.py
__init__.pyE:\DECODE-DJANGO\DJANGO-1.5.1\DJANGO\CORE\HANDLERS
base.py application 的基类 BaseHandler
wsgi.py 实现 WSGIHandler 类, 定义了 __call__, 这样就名正言顺的 WSGI 中的 application 了
__init__.py
ps: 目录根据实际情况会不同, 看具体情况.我已经在 github 备份了 Django 源码的注释: Decode-Django, 有兴趣的童鞋 fork 吧.
捣乱 2013-9-6
提前预知java8
我们一直在期待着Java 8的新功能,虽然很大一部分是对Java 7的扩展,发布时间也一推再推(原定9月发行,Java 8已经推迟到明年3月,本是要争取时间让安全修复主要针对客户端Java(JavaFX /swing))。但如今甲骨文终于承诺:新版本将是彻底的变革而不仅仅是Java 7的扩展。那Java 8到底是什么样的呢?
java8在试图”创新“,其实就是像腾讯一样,把别人的东西拿过来包装一样,看来腾讯在这方面走在前沿了。正如java社区讨论那样,java8增加了Lambda, streams, functional interfaces、java time 。。还有其他各种各样的东西,但是最主要的还是Lambda, streams, functional interfaces。下面一个一个的挨着介绍吧。
一、Lambda expressions
大家都熟知的Lambda 表达式终于出现在java8的api里面, 我觉得这对程序猿来说是一个巨大的好处,因为lambda表达式的出现让代码越来越简单。当我们JVM在编译lambda表达式的时候会把lambda表达式翻译成为功能接口(functional interface)。下面看看一个实例吧。
①、以前的老方法中没有使用lambda
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
System.out.println(“Action Detected”);
}
});
②、java8中使用lambda表达
button.addActionListener(e -> {
System.out.println(“Action Detected”);
});
通过上面的两个例子是不是感觉使用了lambda表达式比原来简单的多了,如果你还不明白的话,那么我在举一个例子,我相信这个例子大家应该很熟悉了吧 ,建立一个线程。
①、没有使用lambda表达式
//使用以前的方法创建线程
Runnable runnable1 = new Runnable() { @Override
public void run() {
System.out.println("Running without Lambda");
}
};
②、使用了lambda表达式
Runnable runnable2 = () -> {
System.out.println("Running from Lambda");
}
正如您看到的,使用Lambda表达式通常会使代码更容易阅读,需要更少的代码。但是Scala社区是怀疑的(详情请点击scala社区),他们认为java增加的功能有可能取代scala语言(主要是“=》”和“->”操作符),Java 8的语法已经很详细了但是和Scala比起来没有那么的清晰。一方面,如果Java继续发展和实现周围的一切像Scala已经做过的事(这也说明java也有‘抄袭’的可能),那么有可能不需要Scala。另一方面,如果它独自只提供核心功能,如帮助匿名内部类,然后Scala和其他语言将继续繁荣。这是最好的结果,它将允许其他语言继续生长和创造力而不用担心他们是否会变得过时。
二、streams
流,在这里的流不是所谓说的io操作中的InputStream和
OutputStream是在集合包里面增加了一个steams的集合。所以大家不要混淆了。既然它出现在java8中,肯定这个出现是有需求的,而且是必然的。
流并不是为了取代ArrayLists或其他集合。他们只是为了使操纵数据更容易和更快。一个流是一次使用对象。一旦它被遍历那么它就不能在被遍历(是一次使用对象)。streams可以被过滤(filter),也可以被遍历。streams包大约分两种顺序流(sequential stream)和平行流(parallel stream)。两种方式使得streams在多核处理器中扮演了一个重要角色,它使用fork / join并行性来分离工作,加快处理。
①、使用sequential stream:
List <Person> people=list.getStream.collect(Collectors.toList());
②、使用parallel stream
List <Person> people = list.getStream.parallel().collect(Collectors.toList());
说明:当我们遍历sequential stream的时候流中的每一项阅读处理,然后下一项是阅读(也就是读一点输出一点的意思)当遍历parallel stream的是时候,该数组被分成多个部分,每一个都是在一个不同的线程分别处理。结果然后放回一起输出。
遍历Parallel stream的过程
List originalList = someData; split1 = originalList(0, mid); split2 = originalList(mid,end); new Runnable(split1.process()); new Runnable(split2.process()); List revisedList = split1 + split2;
这个非常简单的示例说明了如何处理平行流。这就是它如何利用多核处理器的。
因为一个流只能遍历一次,通常返回另一个流,使用一个终端方法来获得有用的结果,通常使用的终端方法有下面这些 sum()
, collect()
, toArray()。最后只要流终止,但是这个结果是不可行的(至于为什么我相信大家仔细相信就明白了,我在这里就不用说了吧)。请看下面这个例子
Double result = list.getStream().mapToDouble(f -> f.getAmount()).sum(); List<Person> people = list.getStream().filter(f -> f.getAge() > 21).collect(Collectors.toList());
这个功能的最大的好处是能够使用多个处理器核心收集处理。而不是做传统的for循环,使用流在平行模式——理论上,平行模式的速度是每个核心速度之和。
三、功能接口(Functional interfaces)
Java 8将有一个新功能叫做功能接口。基本上,默认的方法都添加了这个接口,但是这个方法不需要写功能接口的实现(这个具体还有待研究,以java8具体出来为准)。这些方法可以直接从接口之中运行。
这样做是为了向后兼容为你的接口。举一个很简单的例子就是让流放入一个接口而无需改变所有的类来实现新方法。基本上,创建一个默认的方法的接口,所有的实现接口的类可以使用流。如果默认的方法是不正确的实现,它可以覆盖在实现它。这本质上是允许的形式做多重继承。这成为实现者的问题,因为实现者需要重写该方法,无论如何。实现者都可以选择使用哪些方法,但这意味着大量的类,实现接口仍然可以被改变。(有点像继承里面的构造方法一样,继承的类可以从写构造方法)
大多数人很熟悉Java。也许不会熟悉java的同胞兄弟Scala。细细比较java8和scala,java8的概念特征基本在Scala中都可以找到,尽管这个概念并不局限于Scala。然而,有一些差异:Java 8功能接口不能得到一个实现类的引用。Scala允许这与self关键字。语言书呆子会说,Java 8的功能性接口允许多重继承的行为,但Scala的特征是对于行为和状态都可以多重继承的。
其实java8还有很多东西比如说Accumulators,TLS SNI,HashMap
fixes就等着下次在介绍了,这次很晚了,程序猿们洗洗睡吧。