|
eurasia_3_1_userguide
EURASIA 用户手册(3.1 版)
Featured 安装需要 unix/linux 系统,在 python2.6、python2.7 下经过测试。
安装过程在此 在此 下载集成安装包: $ tar -xjf eurasia-3.1-snapshot.tar.bz2 $ cd eurasia-3.1-snapshot $ sudo /PATH/TO/DEST/bin/python2.6 setup.py install
也可以安装到指定目录: $ /PATH/TO/PYTHON/bin/python2.6 setup.py install /home/foo/MyProject
快速开始我们将从最简单的 hello world 开始,通过范例快速掌握 eurasia 。 这首先是一个 web 程序。 hello world# 文件名: test.py
from eurasia.web import httpserver, mainloop
def handler(httpfile):
httpfile['Content-Type'] = 'text/html'
httpfile.write('<html>hello world!</html>')
httpfile.close()
httpd = httpserver(('', 8080), handler)
httpd.start()
mainloop()把 handler 绑定到 8080 端口,浏览器访问该端口会得到 hello world 。 执行脚本,启动服务器。 $/usr/bin/python2 test.py
httpfile 对象操作 httpfile 对象,获取请求,并完成响应。 request 操作:
response 操作:
下面将是一个完整的例子(文件服务器): #-*- coding: utf-8 -*-
import os.path
from eurasia.web import httpserver, mainloop
def handler(httpfile):
# 注意,直接组合路径不安全
filename = '/var/www' + httpfile.path_info
if not os.path.exists(filename):
httpfile.status = 404 # 文件未找到
httpfile.write('<h1>Not Found</h1>')
return httpfile.close()
if os.path.isdir(filename):
httpfile.status = 403 # 不支持列出目录
httpfile.write('<h1>Forbidden</h1>')
return httpfile.close()
httpfile['Content-Type'] = 'application/octet-stream'
data = open(filename).read()
httpfile.write(data)
httpfile.close()
httpd = httpserver(('', 8080), handler)
httpd.start()
mainloop()
标准 web 服务器框架的关键应用之一便是 web 。首先通过 httpserver(addr, handler) 创建标准 http 服务器。 地址格式接口 httpserver 允许多种形式的 addr 参数。 httpserver('127.0.0.1:8080', handler) # IPv4 地址,端口 8080
httpserver('[::1]:8080', handler) # IPv6 地址
httpserver(':8080', handler) # 本机 8080 端口
httpserver(('127.0.0.1', 8080), handler) # IPv4
httpserver(('::1', 8080), handler) # IPv6
# 同时指定地址及 family:
from socket import AF_INET6, AF_UNIX
from eurasia.web import httpserver
httpserver((('::1', 8080), AF_INET6), handler)
httpserver(('/var/httpd.sock', AF_UNIX), handler) # unix socket
# 绑定到 fileno 为 0 的资源描述符,默认 family 是 AF_INET:
httpserver(0, handler)
# 绑定到 fileno 为 0 的描述符,指定 family 为 AF_INET6:
from socket import AF_INET6
from eurasia.web import httpserver
httpserver((AF_INET6, 0), handler)启动和暂停服务器通过 httpserver.start() 和 httpserver.stop() 启动和暂停服务器,暂停的服务器可以通过 httpserver.start() 重新启动。 from eurasia.web import httpserver, mianloop
# 工作服务器,绑定到 8080
def handler(httpfile):
httpfile.write('hello world!')
httpfile.close()
httpd = httpserver(':8080', handler)
httpd.start()
# 管理服务器,绑定到 8090
def manager(httpfile):
# 如果请求地址是 /start 则启动工作服务器
if httpfile.path_info == '/start':
httpd.start()
# 如果请求地址是 /pause 则停止工作服务器
elif httpfile.path_info == '/pause':
httpd.stop()
man = httpserver('8090', manager)
man.start()
mainloop() # 开始调度注意区分 httpserver.start() 和 mainloop():
CGI 规范适配httpfile 对象是服务器关键接口,其设计在很大程度上与 CGI/1.1 规范适配,以下是一些对应关系:
可以通过 httpfile.environ[envname] 获取的环境变量:
也可以使用 httpfile.request_uri、httpfile.path_info、httpfile.query_string 操作环境变量: # 操作 httpfile.request_uri 会同时影响到 PATH_INFO 和 QUERY_STRING httpfile.request_uri = '/login?username=tom&passwd=***' print httpfile.path_info, httpfile.query_string # 分别是 "/login" 和 "username=tom&passwd=***" print httpfile.environ['PATH_INFO'], httpfile.environ['QUERY_STRING'] # 同上 # 操作 httpfile.path_info 会影响到 REQUEST_URI httpfile.path_info = '/check' print httpfile.request_uri # uri 变成 "/check?username=tom&passwd=***",query_string 不变 print httpfile.environ['REQUEST_URI'] # 同上 # 操作 httpfile.query_string 会影响到 REQUEST_URI httpfile.query_string = 'username=jerry&passwd=***' print httpfile.request_uri # uri 变成 "/check?username=jerry&passwd=***",path_info 不变
解读请求通过 httpfile.read(size=-1, timeout=-1) 和 httpfile.readline(size=-1, timeout=-1) 读取报文体。 eurasia.cgietc 模块提供了解析 POST 形式表单的工具 form(httpfile, max_size=1048576, timeout=-1):
# -*- conding: utf-8 -*-
from eurasia.cgietc import form
from eurasia.web import httpserver, mainloop
# 返回表单内容
def handler(httpfile):
form1 = form(httpfile)
# 假定提交的表单:a=hello&b=world&c=1&c=2&c=3
# 则输出 {'a': 'hello', 'b': 'world', 'c': ['1', '2', '3']}
httpfile.write(repr(form1))
httpfile.close()
完成响应我们提供了接口 httpfile.start_response(status, response_headers, timeout=-1) 用来简化 httpfile[...]和 httpfile.status 的操作: def handler(httpfile):
response_status = '200 OK'
response_headers = [('Content-Type', 'text/html')]
httpfile.start_response(response_status, response_headers)
httpfile.sendall('hello world!')
httpfile.close()httpfile.start_response() 发送指定的 status 和 headers 报文头,这和 wsgi 规范(pep333)中 start_response() 接口的定义比较接近。 通过 httpfile.start_response() 完成报文头部以后,就可以通过 httpfile.sendall(data, timeout=-1) 发送报文体了。 httpfile.write() 与 httpfile.sendall() 的功能相当接近,都用于发送报文体,他们的区别是:
一般情况下,httpfile.status、httpfile[...] 与 httpfile.write() 配合使用;httpfile.start_response() 与 httpfile.sendall() 是一对
cookie通过 httpfile.cookie 属性配合标准库 Cookie 读取和设置 cookie。 #!/opt/unladen-2.6.4/bin/python2.6
# -*- coding: utf-8 -*-
from uuid import uuid4
from Cookie import SimpleCookie
from time import gmtime, strftime, time
from eurasia.web import httpserver, mainloop
def handler(httpfile):
cookie = httpfile.cookie
if cookie is None: # 客户端没有 cookie 则设置一个
cookie = SimpleCookie()
cookie['uid'] = uuid4().hex
cookie['uid']['path'] = '/'
cookie['uid']['expires'] = strftime( # 设置过期时间是 3600 秒
'%a, %d-%b-%Y %H:%M:%S GMT', gmtime(time() + 3600))
httpfile.cookie = cookie # 设置到 httpfile.cookie
# 通过 start_response(),cookie 随头部一起发送
httpfile.start_response('200 OK', [('Content-Type', 'text/html; charset=utf-8')])
httpfile.sendall('<p>创建 cookie 内容:<br><br>%s</p>'%cookie.output(header=''))
httpfile.sendall('<p><b>该 cookie 已发送至浏览器</b></p>')
httpfile.close()
else: # 客户端已有 cookie,删除该 cookie
cookie_from_request = httpfile.cookie
cookie_to_response = SimpleCookie()
cookie_to_response['uid'] = ''
cookie_to_response['uid']['path'] = '/'
cookie_to_response['uid']['expires'] = 'Mon, 26 Jul 1997 05:00:00 GMT' # 使过期删除
httpfile.cookie = cookie_to_response # 在头部发送前设置好 cookie
httpfile.start_response('200 OK', [('Content-Type', 'text/html')])
httpfile.sendall('<p>收到 cookie 内容:<br><br>%s</p>'%cookie_from_request.output(header=''))
httpfile.sendall('<p><b>该 cookie 已经删除</b></p>')
httpfile.close()
httpserver(':8080', handler).start()
mainloop()
长连接请求将被一直保持,直到你调用 httpfile.close() 在此期间你可以在任何时候向用户发送内容。 # -*- coding: utf-8 -*-
from time import strftime
from eurasia.socket2 import error
from eurasia.web import httpserver, mainloop
httpfiles = set()
def handler(current_httpfile):
response_headers = [('Content-Type', 'text/html; charset=utf-8')]
current_httpfile.start_response('200 OK', response_headers)
current_httpfile.sendall(strftime('[%a, %d-%b-%Y %H:%M:%S GMT] 我加入了<br/>'))
# 通知其他在线用户,有新人加入
disconnected_httpfile = []
for httpfile in httpfiles:
try:
# 告诉其他在线用户
httpfile.sendall(strftime('[%a, %d-%b-%Y %H:%M:%S GMT] 又有新人加入<br/>'))
except error:
# 连接已断开
disconnected_httpfile.append(httpfile)
# 移除断开的 httpfile
for httpfile in disconnected_httpfile:
httpfiles.remove(httpfile)
# 将当前客户端加入全局列表中
httpfiles.add(current_httpfile)
httpserver(':8080', handler).start()
mainloop()
javascript rpc通过 eurasia.cgietc 提供的 browser(httpfile, domain=None, timeout=-1) 接口,服务器端可以在任何时候即时调用客户端的 javascript 函数。 在下面这个例子中,每当有新用户访问站点,所有在线用户都将收到一条 javascript 的提醒。 我们首先定义一个包含有 javascript 的 html 页面, 并与服务器建立一条长连接。 其中的 "my_alert" 函数就是我们即将要用到的: <html>
<head>
<script language="JavaScript">
function my_alert(stuff) { alert(stuff); };
</script>
</head>
<body>
<!-- 与 /remotecall 位置的服务器脚本建立长连接 -->
<iframe src="/remotecall" style="display: none;"></iframe>
</body>
</html>下面是完整的处理脚本: #-*- coding: utf-8 -*-
from eurasia.socket2 import error
from eurasia.cgietc import browser
from eurasia.web import httpserver, mainloop
page = '''\
<html>
<head>
<script language="JavaScript">
function my_alert(stuff) { alert(stuff); };
</script>
</head>
<body>
<!-- 与 /remotecall 位置的服务器脚本建立长连接 -->
<iframe src="/remotecall" style="display: none;"></iframe>
</body>
</html>
'''
# 保存了所有在线用户的全局列表
global_browsers = set()
def handler(httpfile):
if httpfile.path_info != '/remotecall':
httpfile.start_response('200 OK', [('Content-Type', 'text/html')])
httpfile.sendall(page) # 输出前面定义的上传表单
return httpfile.close()
# 创建 Browser 对象
# 调用客户端名为 "my_alert()" 的 js 函数
current_browser = browser(httpfile)
current_browser.my_alert(u'我加入啦!')
# 通知其他在线用户, 有新人加入
disconnected = []
for ibrowser in global_browsers:
try:
# 调用在线用户的 my_alert 函数
ibrowser.my_alert(u'又有新人加入啦!')
except error: # 已经断开连接
disconnected.append(ibrowser)
# 移除已经断开连接的浏览器
for ibrowser in disconnected:
global_browsers.remove(ibrowser)
# 将当前浏览器添加到全局浏览器对象列表中
global_browsers.add(current_browser)
httpd = httpserver(':8080', handler)
httpd.start()
mainloop()
keep-alive如果浏览器支持 keep-alive 特性,服务器将会自动以 keep-alive 方式处理浏览器请求。
超时处理多数 web 接口可以通过 timeout 参数设置超时,timeout 是以秒为单位的浮点数(float),默认 -1 意思是无超时。 # -*- coding: utf-8 -*-
from eurasia.cgietc import form
from eurasia.core import timeout # 超时异常
from eurasia.web import httpserver, mainloop
def handler(httpfile):
try:
form1 = form(httpfile, max_size=10240, timeout=30.) # 30 秒超时
except timeout:
httpfile.shutdown() # 中断连接
try:
httpfile.write(repr(form1), timeout=10.) # 10 秒超时
except timeout:
httpfile.shutdown()
httpfile.close() # 正常结束请求
httpd = httpserver(':8080', handler)
httpd.start()
mainloop()
wsgiserver使用 wsgiserver(addr, app) 创建标准的 wsgi 服务器。 from eurasia.web import wsgiserver, mainloop
def app(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['hello world!']
httpd = wsgiserver(':8080', app)
httpd.start()
mainloop()
或者使用 wsgi(app) 将 app 转换为普通的 http handler。 from eurasia.web import httpserver, wsgi, mainloop
def app(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['hello world!']
def handler(httpfile):
wsgihandler = wsgi(app)
wsgihandler(httpfile)
httpd = httpserver(':8080', handler)
httpd.start()
mainloop()使用标准模板eurasia.template 模块是 Mako 的简化版,带有大部分 Mako 模板的功能,和相同的标签语法。同时也是编译型模板。 template 包涵以下标签语法。
this is x: ${x}进一步,取值表达式可以嵌入 python 代码,并替换为 python 表达式的运算结果: ${int(a) + int(b)}
我们可以在模板中使用条件表达式及叠代循环表达式。 这里是条件表达式: %if x==1:
x is ${x}
%elif x==2:
x is ${x}
%else:
x is ${x}
%endif这里是循环: %for a in ['one', 'two', 'three', 'four', 'five']:
%if a[0] == 't':
its two or three
%elif a[0] == 'f':
four/five
%else:
one
%endif
%endfor
可以在模板中运行 python 代码: <% # 这里是 Python 代码
a = 1
b = 2
%>
测试一下 a + b : ${a + b}第一行(紧跟“<%”之后)的 python 代码的缩进会被忽略,所以请避免使用 def、class、if、for 等依赖缩进的 python 代码。 函数中的 python 代码块在当前函数中可见,函数外的代码块在当前模板中全局可见。
函数是模板中最基本的调用单位,类似于 Python 中的函数: <%def name="myfunc(x)">
this is myfunc, x is ${x}
</%def>
调用: ${myfunc(7)}
call 标签用于调用 <%def%> 标签,可传递额外的内嵌内容。稍后介绍。 使用模板为了演示 eurasia 标准模板的用法,这里将建立一个模板的应用范例。 #!/usr/bin/python2.5
#-*- coding: utf-8 -*-
from eurasia.template import Template
s = '''\ # 字符串模板
<%def name="test1(a)">
test1, a is ${a}
</%def>
<%def name="test2(b)">
test2, b is ${b}
</%def>'''
tmpl = Template(s) # 将字符串模板编译成可执行的 Python 模块
print tmpl.test1('hello') # 调用模板中的 "test1" 函数
print tmpl.test2('world') # 调用模板中的 "test2" 函数在这个例子里,首先是一个字符串模板,带有 test1 和 test2 两个函数。 然后使用 Template() 编译成可执行的 python 模块,并对模板中的 test1、test2 函数进行调用。 结果是: test1, a is hello test2, b is world <%def> 标签定义了模板的调用接口, 在这个例子中, 我们定义了 test1 和 test2 两个调用接口。 我们已经确认 cpython 中存在的一个 bug 将导致 Template(s).test1('hello') 这种写法出错。因此需要分开来写成: tmpl = Template(s)
tmpl.test1('hello')也就是上面这个例子中的写法。非 c 实现的 python 没有这个问题。 一个更为复杂的例子本例中,将用到大量模板常用语法。包括有条件判断,循环等: #!/usr/bin/python2.5
#-*- coding: utf-8 -*-
from eurasia.template import Template
s = '''\
<%def name="main(lst)">
--------------------------------------------
<% # python 代码块示范
class Foo:
def test(self):
return 'from foo.test()'
foo = Foo()
# 在 python 代码块中可以使用 write 函数进行内容输出
write('\\n' + foo.test())
%>
${Foo().test()}
--------------------------------------------
# 循环判断示范
%for i in lst:
%if i == 1:
${1+2}
%elif i == 2:
${'hello' + 'world'}
%else:
${i}
%endif
%endfor
--------------------------------------------
# 嵌入式函数示范, 只在 main 函数中可见
<%def name="bar()">
this is bar
</%def>
${bar()}
--------------------------------------------
</%def>'''
tmpl = Template(s)
print tmpl.main([1, 2, 3, 4, 5])使用 <%call> 标签定义宏<%call> 标签提供了一种较为高级的功能,也就是模板宏: <%def name="macros(a, b)">
${caller.slot1(a)}
${caller.slot2(b)}
</%def>
----------------------------
<%def name="main()">
<%call expr="macros(1, 2)">
<%def name="slot1(a)">
slot1, ${a}
</%def>
<%def name="slot2(b)">
slot1, ${b}
</%def>
</%call>
</%def>这里首先使用 <%def> 定义了一个名为 macros 的模板宏。 然后在 main 函数中使用 <%call> 调用 macros,其中定义了 slot1 和 slot2,在 macros 中可以通过 caller 取出。 该模板的入口函数是 main()。调用结果是: slot1, 1 slot2, 2
缓存编译结果eurasia 标准模板是一种编译型模板,字符串模板经过 Template 函数可以编译成可执行的 python 模块。 我们也可以仅仅把字符串模板转换成 python 源代码,在文件中保存起来,这样可以在下次使用时直接 import 进来,省去了编译过程,尽管这花不了多少时间。 这里我们将用到 template 的 compile 工具。 #!/usr/bin/python2.5
#-*- coding: utf-8 -*-
from eurasia.template import compile
s = '''\
<%def name="main()">
hello world!
</%def>'''
# 得到 python 源码
code = compile(s)
# 保存为 python 模块文件
fd = open('cache.py', w)
fd.write(code)
fd.close()
# 以模块方式导入模板
import cache
print cache.main()<%namespace> 标签eurasia 标准模板中没有 Mako 中 <%namespace> 标签的等价物,这个功能相当于 Python 中的 import。 <%namespace> 需要一系列关于导入路径的配置,这比较复杂,因此 eurasia 换了一种方式。 Template 函数第二个参数可以设定模板中可见的环境。你可以在这里预先导入一些模板中用得到的东西,比如宏。 这个例子在讲解 <%call> 时出现过,这里把宏和调用部分拆离开来。 #!/usr/bin/python2.5
#-*- coding: utf-8 -*-
from eurasia.template import Template
s1 = '''\ # 定义模板宏
<%def name="macros(a, b)">
${caller.slot1(a)}
${caller.slot2(b)}
</%def>'''
s2 = '''\ # 调用模板宏
<%def name="main()">
<%call expr="macros(1, 2)">
<%def name="slot1(a)">
slot1, ${a}
</%def>
<%def name="slot2(b)">
slot1, ${b}
</%def>
</%call>
</%def>'''
tmpl1 = Template(s1)
# 使用 env 参数指定模板环境
tmpl2 = Template(s2, env={'macros': tmpl1.macros})
tmpl2.main()文件及 socket框架提供了专门的文件 IO 接口,用来提高系统性能。 而 socket 则是一种特殊的文件,也在这一节介绍。 文件使用 core.file(fileno) 接口对已打开的文件描述符(fileno)进行高效操作。 #!/usr/bin/python2.6
#-*- coding: utf-8 -*-
# epoll 不支持本地磁盘文件 patch 成 poll
from eurasia.pyev import *
mainloop = default_loop(EVBACKEND_POLL).loop
import os, sys
from eurasia import core
from traceback import print_exc
from eurasia.web import httpserver
# 打开调试输出
core.excepthook = lambda: print_exc(file=sys.stderr)
def handler(httpfile):
httpfile.start_response('200 OK')
# 文件读取
fileno = os.open('test.txt', os.O_RDONLY|os.O_NONBLOCK)
f = core.file(fileno)
s = f.read()
os.close(fileno)
httpfile.sendall(s)
httpfile.close()
httpd = httpserver(':8080', handler)
httpd.start()
mainloop()
在 unix 下,管道、socket、设备等等都是文件,都可以使用 core.file() 接口。 from os import popen
from eurasia.core import file
...
lsdir = popen('ls -alh')
fd = file(lsdir.fileno())
files = fd.readlines()
...
mainloop()
文件对象接口列表
socket使用框架自带的 socket2 以替代 socket 标准库。 # from socket import socket, AF_INET, SOCK_STREAM
from eurasia.socket2 import socket, AF_INET, SOCK_STREAM
...
sock = socket(AF_INET, SOCK_STREAM)
sock.connect(('www.google.com', 80))
sock.sendall('GET / HTTP/1.0\r\n\r\n')
data = sock.read()
...
通过 socket2.install() 替换标准库。 from eurasia import socket2
socket2.install() # 这时标准库 socket 模块已经变成 socket2
import urllib # urllib 将使用 socket2 模块
...
fd = urllib.urlopen('http://www.google.com/')
data = fd.read()
...tcp 服务器使用 server(addr, handler) 创建标准的 tcp 服务器。 这是一个 echo 服务。 # 文件名:test.py
from eurasia.server import server, mainloop
def handler(sock, addr, serv):
data = sock.readline()
while data.strip() != 'quit':
sock.sendall(data)
data = sock.readline()
sock.close()
tcpd = server(':8080', handler)
tcpd.start()
mainloop()
执行脚本,启动服务器。 $/usr/bin/python2 test.py 使用 telnet 连接到服务器,进行测试(输入 quit 退出测试)。 $telnet 127.0.0.1 8080 从超时恢复发生超时,会抛出 eurasia.core.timeout 异常。 当 file.recv()、file.send() 以及 socket.ready() 发生超时,没有产生任何 IO 操作,可以忽略超时。 from eurasia.core import timeout
# recv 超时
try:
data = fd.recv(1024, 1.) # 读取 1024 字节,超时 1 秒
except timeout:
data = ''
# send 超时
try:
fd.send('hello world!', 1.) # 超时 1 秒
sented = 'hello world!'
except timeout:
sented = ''
# ready 超时
try:
sock.ready(1.)
ready = True
except timeout:
ready = False当 file.read()/readline() 发生超时,缓冲区可能已读取部分数据,但不影响下一次 file.read()/readline() 的读取。 try:
data = file.read(1024, 1.)
except timeout:
data = file.read(1024, 1.) # 重试当 file.sendall() 发生超时,可以通过 timeout 对象的 num_sent 属性,得到已经发送的字节数。 data = 'hello world!'
try:
file.sendall(data, 1.)
except timeout, e:
data = data[e.num_sent:] # 扣除已发送,得到剩余的数据
file.sendall(data, 1.)使用 durus 数据库通过 socket2.install() 可以使 eurasia 和 durus 实现兼容。 首先需要一个 durus 数据库服务器。 from durus.file_storage import FileStorage
from durus.storage_server import StorageServer
storage = FileStorage('test.db')
server = StorageServer(storage=storage, address='test.sock')
server.serve()在 eurasia 中通过 ClientStorage 连接数据库服务器。 from eurasia import socket2
socket2.install()
from time import time
from eurasia.web import httpserver, mainloop
form durus.persistent import Persistent
from durus.connection import Connection
from durus.client_storage import ClientStorage
class Guest(Persistent):
def __init__(self, addr, port):
self.addr = addr
self.port = port
def handler(httpfile):
conn = Connection(ClientStorage(address='test.sock'))
root = conn.get_root()
addr = httpfile.environ['REMOTE_ADDR']
port = httpfile.environ['REMOTE_PORT']
guest = Guest(addr, port)
root[int(time())] = guest
httpfile.start_response('200 OK', [('Content-Type', 'text/plain')])
httpfile.sendall('added.')
httpfile.close()
conn.commit()
框架功能介绍这里介绍一些有用的框架工具。 使用 sleep 暂停线索core.sleep(sec) 是标准库 time.sleep() 在框架中的替代实现: from eurasia.core import sleep ... sleep(0.5) # 程序暂停 0.5 秒 ... 创建协程在 eurasia 中可以直接使用 greenlet 库创建协程。 from time import time
from eurasia.greenlet import greenlet
from eurasia.core import sleep, mainloop
def loop():
while 1:
print time()
sleep(1.)
greenlet(loop).switch()
mainloop()调试通过设置 core.excepthook 安装调试钩子。 # -*- coding: utf-8 -*-
import sys, traceback
from eurasia import core
from eurasia.web import httpserver, mainloop
# 安装调试钩子
def excepthook():
traceback.print_exc(file=sys.stderr)
core.excepthook = excepthook
def handler(httpfile):
print not_exists # 这里会报错,变量 not_exists 不存在
httpfile.close()
httpd = httpserver(('', 8080), handler)
httpd.start()
mainloop()发布产品时,通过 core.without_excepthook() 调用忽略调试钩子。 # -*- coding: utf-8 -*-
import sys, traceback
from eurasia import core
from eurasia.web import httpserver, mainloop
# 安装调试钩子
def excepthook():
traceback.print_exc(file=sys.stderr)
core.excepthook = excepthook
...
core.without_excepthook() # 忽略前面设置的调试钩子配置文件eurasia.pyetc 模块为配置文件的读取提供了支持。eurasia 认为使用 python 语法来编写配置文件是个好主意。 pyetc 中的 load(filename, **env) 函数能读取指定的 python 源码文件(文件后缀并不需要一定是“.py”), 并设定配置文件中的可见环境 env。 load 函数能将指定源码文件转换成 python 模块并返回。 我们首先编写一个名为 httpd.conf 的配置文件: Server(controller='Products.default.controller' port=8080) user = 'nobody' 下面是对于这个配置文件的解析: from eurasia import pyetc
config = {}
def Server(**args):
config.update(args)
mod = pyetc.load('httpd.conf', env={Server:Server})
print 'Server:', config
print 'user', mod.user
系统工具eurasia.utility 模块提供了一些常用的系统工具。 这里列出这些接口,以及说明:
一个例子: from eurasia import utility
utility.setprocname('hello')
...
sock.bind(('', 80)) # bind 到 80 需要 root 权限
utility.setuid('nobody') # 以 nobody 身份运行
...
部署eurasia 3.1 不再原生提供对多核处理器和 daemon 的支持,以下是 eurasia 3.1 推荐的部署方案。
最简单的方法是在多个端口上启动多个独立的 eurasia 服务,数量与 cpu 核心相应,这样就可以充分使用 cpu 核心了。 然后通过 iptables(redirect)或者 lvs 做端口负载均衡即可。
eurasia 推荐使用 daemon 命令启动服务器。 在多数 unix/linux 发行版上都可以非常方便地安装 daemon 命令。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
辛苦!工程浩大!
真的感觉很爽,每次学习着都不想干其他事情了。
很棒的哎!致敬。
顶一个.python和服务器联系的就是那么紧密. learn linux
单机和 nginx 这样的web服务器比较 性能有什么优势呢?
真的不错,一直在用tornado,感觉这个不比tornado差