金一南评林彪全集视频:惊帆之静默 ? Python

来源:百度文库 编辑:九乡新闻网 时间:2024/07/14 13:09:16

Archive for the ‘Python’ Category

纠结及其他

Monday, January 5th, 2009

07年初进入现在的公司,不久后用twisted写了一个http开发框架,提供内部系统之间的http接口服务。说是框架,其实很底层,基本相当于http服务器+URL分发。所以,开发的目的也很明确,就是性能。

在一年多的应用以来,发现很多问题,依次解决,改进了许多。现在提供一些性能参数吧,给需要对python开发系统,进行性能估算的朋友一点数据。

服务器就是四核志强 E5420,2.50GHz,内存4G。除了日志基本没有硬盘IO。

跑hello world的时候,50并发可以达到1800req/s。关闭accesslog日志以后,可以达到2600req/s。当然,这个服务器只能用一个CPU核心。

在应用中,是4个twistd实例在loadbalance后面一起干活,如上述配置的一台服务器。刚才统计出来的,过去24小时中处理完成约2000万次的http接口调用。调用的后面是有数据库查询的,一般来说都不只一次查询。实际看到的速度接近峰值的一小时内最高可以做约140万次http接口调用,折合389req/s。

总的来说,数据库依旧是几乎所有系统的瓶颈。

再就是高速运行情况下的问题。

默认的twisted使用的是select(),并发性能不要太指望。甚至并发数稍微高一点的时候就会报什么file descriptor out of range of select()的错误。总之默认的twisted能承受的并发数很有限。

twisted是有epoll支持的,你可以在启动twistd时选择epollreactor。不过貌似并不稳定。使用epoll()方式以后,我的印象大约是每几千个请求就会报出一个文件描述符的什么错误。另外,就是在关闭程序时有个语法错误,这个绝对是twisted的bug。从2.5.0升级到8.1.0之后,关闭服务器那个报出的bug依然存在。而twisted-8.1.0在启动时还要很傻的在安装位置写一个什么缓存文件。普通用户根本没有权限,所以每次启动服务器时都会报错,但是服务器可以照常启动。

我对twisted的容忍基本限于如此了。这段时间感觉libevent很优雅,假如我的雅兴还没过去,我会尝试给libevent附带的http服务器加上python binding,也许性能会更高。

Posted in Twisted, Python | 6 Comments »

纪念学习Python三周年

Friday, December 26th, 2008

2005年圣诞节的夜晚,快要考试了,有些压抑,于是翻翻电脑上的书,找到了这个Python,于是一发不可收拾。现在已经做了2年多Python程序员了。世事难料。

Posted in Python | 2 Comments »

greenlet:轻量级并发编程

Wednesday, December 3rd, 2008

greenlet:轻量级并发编程

译者: gashero

  • 1   动机
    • 1.1   例子
  • 2   使用
    • 2.1   简介
    • 2.2   父greenlet
    • 2.3   实例
    • 2.4   切换
    • 2.5   greenlet的方法和属性
    • 2.6   Greenlet与Python线程
    • 2.7   活动greenlet的垃圾收集

1   动机

greenlet 包是 Stackless 的副产品,其将微线程称为 “tasklet” 。tasklet运行在伪并发中,使用channel进行同步数据交换。

一个”greenlet”,是一个更加原始的微线程的概念,但是没有调度,或者叫做协程。这在你需要控制你的代码时很有用。你可以自己构造微线程的调度器;也可以使用”greenlet”实现高级的控制流。例如可以重新创建构造器;不同于Python的构造器,我们的构造器可以嵌套的调用函数,而被嵌套的函数也可以 yield 一个值。(另外,你并不需要一个”yield”关键字,参考例子)。

Greenlet是作为一个C扩展模块给未修改的解释器的。

1.1   例子

假设系统是被控制台程序控制的,由用户输入命令。假设输入是一个个字符的。这样的系统有如如下的样子:

def process_commands(*args):
while True:
line=''
while not line.endswith('n'):
line+=read_next_char()
if line=='quitn':
print "are you sure?"
if read_next_char()!="y":
continue #忽略指令
process_commands(line)

现在假设你要把程序移植到GUI,而大多数GUI是事件驱动的。他们会在每次的用户输入时调用回调函数。这种情况下,就很难实现 read_next_char() 函数。我们有两个不兼容的函数:

def event_keydown(key):


def read_next_char():
需要等待 event_keydown() 的调用

你可能在考虑用线程实现。而 Greenlet 是另一种解决方案,没有锁和关闭问题。你启动 process_commands() 函数,分割成 greenlet ,然后与按键事件交互,有如:

def event_keydown(key):
g_processor.switch(key)

def read_next_char():
g_self=greenlet.getcurrent()
next_char=g_self.parent.switch() #跳到上一层(main)的greenlet,等待下一次按键
return next_char

g_processor=greenlet(process_commands)
g_processor.switch(*args)
gui.mainloop()

这个例子的执行流程是: read_next_char() 被调用,也就是 g_processor的一部分,它就会切换(switch)到他的父greenlet,并假设继续在顶级主循环中执行(GUI主循环)。当GUI调用event_keydown() 时,它切换到 g_processor ,这意味着执行会跳回到原来挂起的地方,也就是read_next_char() 函数中的切换指令那里。然后 event_keydown() 的 key 参数就会被传递到read_next_char() 的切换处,并返回。

注意 read_next_char() 会被挂起并假设其调用栈会在恢复时保护的很好,所以他会在被调用的地方返回。这允许程序逻辑保持优美的顺序流。我们无需重写 process_commands() 来用到一个状态机中。

2   使用

2.1   简介

一个 “greenlet”是一个很小的独立微线程。可以把它想像成一个堆栈帧,栈底是初始调用,而栈顶是当前greenlet的暂停位置。你使用greenlet创建一堆这样的堆栈,然后在他们之间跳转执行。跳转不是绝对的:一个greenlet必须选择跳转到选择好的另一个greenlet,这会让前一个挂起,而后一个恢复。两个greenlet之间的跳转称为 切换(switch)

当你创建一个greenlet,它得到一个初始化过的空堆栈;当你第一次切换到它,他会启动指定的函数,然后切换跳出greenlet。当最终栈底函数结束时,greenlet的堆栈又编程空的了,而greenlet也就死掉了。greenlet也会因为一个未捕捉的异常死掉。

例如:

from py.magic import greenlet

def test1():
print 12
gr2.switch()
print 34

def test2():
print 56
gr1.switch()
print 78

gr1=greenlet(test1)
gr2=greenlet(test2)
gr1.switch()

最后一行跳转到 test1() ,它打印12,然后跳转到 test2() ,打印56,然后跳转回 test1() ,打印34,然后 test1() 就结束,gr1死掉。这时执行会回到原来的 gr1.switch() 调用。注意,78是不会被打印的。

2.2   父greenlet

现在看看一个greenlet死掉时执行点去哪里。每个greenlet拥有一个父greenlet。父greenlet在每个greenlet初始化时被创建(不过可以在任何时候改变)。父greenlet是当greenlet死掉时,继续原来的位置执行。这样,greenlet就被组织成一棵树,顶级的代码并不在用户创建的 greenlet 中运行,而称为主greenlet,也就是树根。

在上面的例子中,gr1和gr2都是把主greenlet作为父greenlet的。任何一个死掉,执行点都会回到主函数。

未捕获的异常会波及到父greenlet。如果上面的 test2() 包含一个打印错误(typo),他会生成一个 NameError而干掉gr2,然后执行点会回到主函数。traceback会显示 test2() 而不是 test1()。记住,切换不是调用,但是执行点可以在并行的栈容器间并行交换,而父greenlet定义了栈最初从哪里来。

2.3   实例

py.magic.greenlet 是一个 greenlet 类型,支持如下操作:

greenlet(run=None,parent=None)

创建一个greenlet对象,而不执行。run是执行回调,而parent是父greenlet,缺省是当前greenlet。

greenlet.getcurrent()

返回当前greenlet,也就是谁在调用这个函数。

greenlet.GreenletExit

这个特定的异常不会波及到父greenlet,它用于干掉一个greenlet。

greenlet 类型可以被继承。一个greenlet通过调用其 run 属性执行,就是创建时指定的那个。对于子类,可以定义一个 run() 方法,而不必严格遵守在构造器中给出 run 参数。

2.4   切换

greenlet之间的切换发生在greenlet的 switch() 方法被调用时,这会让执行点跳转到greenlet的 switch()被调用处。或者在greenlet死掉时,跳转到父greenlet那里去。在切换时,一个对象或异常被发送到目标greenlet。这可以作为两个greenlet之间传递信息的方便方式。例如:

def test1(x,y):
z=gr2.switch(x+y)
print z

def test2(u):
print u
gr1.switch(42)

gr1=greenlet(test1)
gr2=greenlet(test2)
gr1.switch("hello"," world")

这会打印出 “hello world” 和42,跟前面的例子的输出顺序相同。注意 test1() 和 test2() 的参数并不是在 greenlet 创建时指定的,而是在第一次切换到这里时传递的。

这里是精确的调用方式:

g.switch(obj=None or *args)

切换到执行点greenlet g,发送给定的对象obj。在特殊情况下,如果g还没有启动,就会让它启动;这种情况下,会传递参数过去,然后调用g.run(*args)

垂死的greenlet

如果一个greenlet的 run() 结束了,他会返回值到父greenlet。如果 run() 是异常终止的,异常会波及到父greenlet(除非是 greenlet.GreenletExit 异常,这种情况下异常会被捕捉并返回到父greenlet)。

除了上面的情况外,目标greenlet会接收到发送来的对象作为 switch() 的返回值。虽然 switch()并不会立即返回,但是它仍然会在未来某一点上返回,当其他greenlet切换回来时。当这发生时,执行点恢复到 switch() 之后,而switch() 返回刚才调用者发送来的对象。这意味着x=g.switch(y) 会发送对象y到g,然后等着一个不知道是谁发来的对象,并在这里返回给x。

注意,任何尝试切换到死掉的greenlet的行为都会切换到死掉greenlet的父greenlet,或者父的父,等等。最终的父就是 main greenlet,永远不会死掉的。

2.5   greenlet的方法和属性

g.switch(obj=None or *args)

切换执行点到greenlet g,同上。

g.run

调用可执行的g,并启动。在g启动后,这个属性就不再存在了。

g.parent

greenlet的父。这是可写的,但是不允许创建循环的父关系。

g.gr_frame

当前顶级帧,或者None。

g.dead

判断是否已经死掉了

bool(g)

如果g是活跃的则返回True,在尚未启动或者结束后返回False。

g.throw([typ,[val,[tb]]])

切换执行点到greenlet g,但是立即抛出指定的异常到g。如果没有提供参数,异常缺省就是 greenlet.GreenletExit 。根据异常波及规则,有如上面描述的。注意调用这个方法等同于如下:

def raiser():
raise typ,val,tb

g_raiser=greenlet(raiser,parent=g)
g_raiser.switch()

2.6   Greenlet与Python线程

greenlet可以与Python线程一起使用;在这种情况下,每个线程包含一个独立的 main greenlet,并拥有自己的greenlet树。不同线程之间不可以互相切换greenlet。

2.7   活动greenlet的垃圾收集

如果不再有对greenlet对象的引用时(包括其他greenlet的parent),还是没有办法切换回greenlet。这种情况下会生成一个 GreenletExit 异常到greenlet。这是greenlet收到异步异常的唯一情况。应该给出一个 try .. finally用于清理greenlet内的资源。这个功能同时允许greenlet中无限循环的编程风格。这样循环可以在最后一个引用消失时自动中断。

如果不希望greenlet死掉或者把引用放到别处,只需要捕捉和忽略 GreenletExit 异常即可。

greenlet不参与垃圾收集;greenlet帧的循环引用数据会被检测到。将引用传递到其他的循环greenlet会引起内存泄露。

Posted in Python | 1 Comment »

ProtoBuf开发者指南 - 非官方不完整版

Monday, September 22nd, 2008

ProtoBuf开发者指南

译者: gashero

目录

  • 1   概览
    • 1.1   什么是protocol buffer
    • 1.2   他们如何工作
    • 1.3   为什么不用XML?
    • 1.4   听起来像是为我的解决方案,如何开始?
    • 1.5   一点历史
  • 2   语言指导
    • 2.1   定义一个消息类型
    • 2.2   值类型
    • 2.3   可选字段与缺省值
    • 2.4   枚举
    • 2.5   使用其他消息类型
    • 2.6   嵌套类型
    • 2.7   更新一个数据类型
    • 2.8   扩展
    • 2.9   包
    • 2.10   定义服务
    • 2.11   选项
    • 2.12   生成你的类
  • 3   代码风格指导
    • 3.1   消息与字段名
    • 3.2   枚举
    • 3.3   服务
  • 4   编码
    • 4.1   一个简单的消息
    • 4.2   基于128的Varints
    • 4.3   消息结构
    • 4.4   更多的值类型
    • 4.5   内嵌消息
    • 4.6   可选的和重复的元素
    • 4.7   字段顺序
  • 5   ProtocolBuffer基础:C++
  • 6   ProtocolBuffer基础:Java
  • 7   ProtocolBuffer基础:Python
    • 7.1   为什么使用ProtocolBuffer?
    • 7.2   哪里可以找到例子代码
    • 7.3   定义你的协议格式
    • 7.4   编译你的ProtocolBuffer
    • 7.5   ProtocolBuffer API
      • 7.5.1   枚举
      • 7.5.2   标准消息方法
      • 7.5.3   解析与串行化
    • 7.6   写消息
    • 7.7   读消息
    • 7.8   扩展ProtocolBuffer
    • 7.9   高级使用
  • 8   参考概览
  • 9   C++代码生成
  • 10   C++ API
  • 11   Java代码生成
  • 12   Java API
  • 13   Python代码生成
    • 13.1   编译器的使用
    • 13.2   包
    • 13.3   消息
    • 13.4   字段
      • 13.4.1   简单字段
      • 13.4.2   简单消息字段
      • 13.4.3   重复字段
      • 13.4.4   重复消息字段
      • 13.4.5   枚举类型
      • 13.4.6   扩展
    • 13.5   服务
      • 13.5.1   接口
      • 13.5.2   存根(Stub)
  • 14   Python API
  • 15   其他语言