#!/usr/bin/python
#coding=utf-8
# author Rowland
# edit 2014-03-19 14:16:32
# 虚拟路由总管,拦截请求转发请求到业务线程池
# edit 2014-12-15 18:02:09
# 修改映射表数据结构为单向链表(方便进行优化算法),增加URL冲突检测,优化性能精简代码,高负载下随机拒绝服务
# edit 2015年4月15日14:45:58
# 修改setup规则为装饰器自动注册,方便后续提供热加载策略
import os
import inspect
import sys
import re
import random
import json
from itertools import groupby
import tornado.ioloop
import tornado.web
from concurrent import futures
import path, tools
from configer import conf
MAX_WORKERS = 16
executor = futures.ThreadPoolExecutor(max_workers=MAX_WORKERS)
@conf.register_my_setup(level=2)
def set_up():
''' erase all nodes
this function maybe called for hot deployment
'''
Router.mapper = []
Router.mapper_sentry = {}
Router.last_sentry = {}
global logger
logger = tools.Log().getLog()
#automic scan dirs and initial all node
files_list = os.listdir(path._BIZ_PATH)
files_list = set([x[:x.rfind(".")] for x in files_list if x.endswith(".py")])
map(__import__, files_list)
Router.pre_check()
def _call_wrap(call, params):
handler = params[0]
try:
#logger.info('request: %s %s', handler.request.path, handler.json_args or {})
ret = call(*params)
# stringify result
if isinstance(ret, dict):
ret = json.dumps(ret)
else:
ret = str(ret)
tornado.ioloop.IOLoop.instance().add_callback(lambda: params[0].finish(ret))
except Exception, ex:
logger.exception(ex)
tornado.ioloop.IOLoop.instance().add_callback(lambda: params[0].send_error())
class Router(object):
'''dispather and decortor'''
_GET = 0x001
_POST = 0x002
_PUT = 0x004
_DELETE = 0x008
_OPTIONS = 0x010
mapper = []
mapper_sentry = {}
last_sentry = {}
@classmethod
def check_redefined_node(cls, sentry, url_exp, method):
if not sentry:
return None
if sentry['eUrl'] == url_exp and sentry['method'] & method:
return sentry
else:
return Router.check_redefined_node(sentry['next'], url_exp, method)
@classmethod
def lookup_suitable_node(cls, prev, sentry, url, method, assert_wrong_method=False):
if not sentry:
if assert_wrong_method:
raise tornado.web.HTTPError(405)
raise tornado.web.HTTPError(404)
matcher = sentry['eUrl'].match(url)
m = sentry['method']
wrong = assert_wrong_method
if matcher:
if m & method: #hit!
if prev:
prev['next'] = sentry['next']
sentry['next'] = Router.mapper_sentry
Router.mapper_sentry = sentry
return sentry, matcher
else:
wrong = True
return Router.lookup_suitable_node(sentry, sentry['next'], url, method, wrong)
@classmethod
def pre_check(cls):
check_mapper_list = filter(lambda (x, y): len(y) > 1, [
(key, list(items)) for key, items in
groupby(Router.mapper,
lambda x: x)
]
)
if check_mapper_list:
for check_mapper in check_mapper_list:
logger.fatal('Definition conflict : FUNCTION[%s]', check_mapper[0])
sys.exit(1)
@classmethod
def route(cls, **deco):
def foo(func):
url = deco.get('url') or '/'
eUrl = re.compile('^' + url + '$', re.IGNORECASE)
method = deco.get('method') or Router._GET
if Router.check_redefined_node(Router.mapper_sentry, eUrl, method):
logger.fatal('Definition conflict : URL[%s]', url)
sys.exit(1)
else:
mapper_node = {
'eUrl': eUrl,
'method': method,
'callName': func.__name__,
'className': inspect.stack()[1][3],
'moduleName': func.__module__,
'next': {},
}
Router.mapper.append(
'.'.join([mapper_node['moduleName'], mapper_node['className'], mapper_node['callName']]))
# Yes, I used linked list here
# Any better way to contain urls?
# Disadvantage: have to visit the urls list from head to end to
# determind 404
if Router.mapper_sentry:
Router.last_sentry['next'] = mapper_node
Router.last_sentry = Router.last_sentry['next']
else:
Router.mapper_sentry = mapper_node
Router.last_sentry = Router.mapper_sentry
return func
return foo
@classmethod
def get(cls, path, reqhandler):
Router.emit(path, reqhandler, Router._GET)
@classmethod
def post(cls, path, reqhandler):
Router.emit(path, reqhandler, Router._POST)
@classmethod
def put(cls, path, reqhandler):
Router.emit(path, reqhandler, Router._PUT)
@classmethod
def delete(cls, path, reqhandler):
Router.emit(path, reqhandler, Router._DELETE)
@classmethod
def options(cls, path, reqhandler):
Router.emit(path, reqhandler, Router._OPTIONS)
@classmethod
def verify_passport(cls):
capacity = 0 if len(executor._threads) == 0 else executor._work_queue.qsize() / float(
len(executor._threads))
if 2 > capacity >= 1.0:
#随机拒绝请求
return False if (random.random() + 1) > capacity else True
elif capacity >= 2:
return False
else:
return True
@classmethod
def emit(cls, path, reqhandler, method_flag):
#logger.info('request coming![%s][%s]', path, method_flag)
if not Router.verify_passport():
logger.warn("server is under high pressure ,[free thread:%d] [queue size:%d] [request refused %s]",
len(executor._threads),
executor._work_queue.qsize(),
path)
raise tornado.web.HTTPError(502)
return
mapper_node, m = Router.lookup_suitable_node(None, Router.mapper_sentry, path, method_flag)
if mapper_node and m:
params = (reqhandler,)
for items in m.groups():
params += (items,)
callName = mapper_node['callName']
className = mapper_node['className']
moduleName = mapper_node['moduleName']
module = __import__(moduleName)
clazz = getattr(module, className)
try:
obj = clazz()
except Exception, e:
logger.exception("error occured when creating instance of %s" % className)
raise tornado.web.HTTPError(500)
call = getattr(obj, callName)
executor.submit(_call_wrap, call, params)
没有合适的资源?快使用搜索试试~ 我知道了~
温馨提示
人工智能-项目实践-多线程-tonado的multi-thread 多线程封装 Quick Start 1.在“biz”目录中创建一个py文件,文件名任意但最好不要跟第三方库冲突 2.使用 "Router.route" 装饰器注册函数到路由表中,仿造示例即可 3.到“bin”目录下,使用命令"python serv.py" 启动工程,用浏览器访问步骤二中注册的路径可看到效果 基于tornado改装的多线程业务处理模型框架,自带跨域请求,json/xml参数解析,缓存和路由优化。适合多人合作的service系统后台搭建!
资源推荐
资源详情
资源评论
收起资源包目录
人工智能-项目实践-多线程-tonado的multi-thread 多线程封装.zip (13个子文件)
iceworld-master
multithreadtornado
lib
route.py 7KB
configer.py 2KB
path.py 452B
tools.py 746B
util
__init__.py 0B
cache.py 2KB
etc
includes_dev.json 162B
dev_log.conf 1KB
webapp_dev.json 122B
biz
hello.py 2KB
bin
serv.py 5KB
var
app.log 599B
http.log 368B
共 13 条
- 1
资源评论
博士僧小星
- 粉丝: 1712
- 资源: 5876
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功