[增强] 增加分布式任务。 celery_app,可用于分布式回测,或计划任务执行

This commit is contained in:
msincenselee 2020-01-20 16:40:43 +08:00
parent 5208c85d77
commit 8bc0ed5bf5
3 changed files with 165 additions and 0 deletions

62
vnpy/task/README.md Normal file
View File

@ -0,0 +1,62 @@
### 基于Celery的分布式任务
# 安装
Windows 10:
1) conda activate py37
2) pip install celery
3) pip install eventlet
Centos 7:
trade用户
1) conda activate py37
2) pip install celery
root 用户 如果使用redis作为中介
1) 安装redis服务器 yum install redis
2) 修改/etc/redis.conf 在127.0.0.1后增加对外的IP地址
3) 启动redis服务器 systemctl start redis
4) 允许外部访问简单粗暴的方式iptables -F
root 用户如果使用rabbitMQ作为中介
1) 自行百度下如何安装rabbitMQ 服务程序
2) 缺省账号/密码: admin/admin
3) web访问界面 http://[ip]:15672/
Celery flower 监控
1) pip install flower
2) celery -A vnpy.task.celery_app --port=5555 --broker
# 启动
windows 10
1) conda activate py37
2) 进入项目目录后,例如c:\vnpy2, 运行命令:
celery -A vnpy.task.celery_app worker -P eventlet -l debug -f celery.log
其中:
-A 代表运行的模块, worker表明这是celery的worker
-P windows10下使用的协议
-l (小写L表示logging的级别 debug, info 等。
-f 代表输出日志文件名
3) 停止 Ctrl + C
# 添加任务的例子
可在jupyter_notebook 中或普通py程序中往celery_app添加任务
其中:
1) excutue, 是celery_app中定义的Task类型方法。
2) 它通过参数进行传递:
- func: 你要运行的某个命名空间函数例如vnpy.app.cta_strategy_pro.portfolio_testing.single_test
- 后续其他是这个函数所需要的若干参数了用dict字典即可。
3) task_id提前定义可把它作为任务的唯一标识。
调用前可存储在mongo数据库中调用完成后可更新mongo数据库的结果。
from vnpy.task.celery_app import execute
task_id = str(uuid1())
print(f'添加celery 任务:{task_id}')
execute.apply_async(kwargs={
'func': 'vnpy.app.cta_strategy_pro.portfolio_testing.single_test',
'test_setting': test_setting,
'strategy_setting': strategy_setting},
task_id=task_id)

99
vnpy/task/celery_app.py Normal file
View File

@ -0,0 +1,99 @@
# encoding: UTF-8
# Celery app
# 该py脚本为启动celery worker app
# 在项目根目录下,运行 celery -A vnpy.task.celery worker
import time
from celery import Celery
import sys
import os
import traceback
from vnpy.trader.utility import import_module_by_str
from celery.utils.log import get_task_logger
logger = get_task_logger(__name__)
# 添加项目目录
vnpy_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
if vnpy_root not in sys.path:
print('append {} into sys.path'.format(vnpy_root))
sys.path.append(vnpy_root)
# 使用本地配置的
from vnpy.trader.utility import load_json
file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'celery_config.json'))
celery_config = load_json(file_path)
# 使用 redis
# broker = celery_config.get('celery_broker','redis://192.168.0.202:6379')
#backend = celery_config.get('celery_backend','redis://192.168.0.202:6379/0')
# 使用rabbitMQ
broker = celery_config.get('celery_broker', 'amqp://admin:admin@192.168.0.202:5672//')
backend = celery_config.get('celery_backend', 'amqp://admin:admin@192.168.0.202:5672//')
print(u'Celery 使用redis配置:\nbroker:{}\nbackend:{}'.format(broker, backend))
app = Celery('vnpy_task', broker=broker)
# 动态导入task目录下子任务
#app.conf.CELERY_IMPORTS = ['vnpy.task.celery_app.worker_started']
#app.conf.update(
# CELERY_TASK_SERIALIZER='json',
# CELERY_RESULT_SERIALIZER='json',
# CELERY_ACCEPT_CONTENT=['json'],
# CELERY_TIMEZONE='Asia/Shanghai',
# CELERY_ENABLE_UTC=True
# )
def worker_started():
"""发送worker启动的通知"""
try:
import socket
from vnpy.trader.util_logger import setup_logger
logger = setup_logger(file_name='celery_worker')
logger.inf('celery worker started')
except:
pass
@app.task(bind=True)
def execute(self, func, *args, **kwargs):
"""
通用的执行任务方法
:param func: 函数方法在绝对路径字符串mod.sub_mod.func
:param args: 参数
:param kwargs: 扩展参数
:return:
"""
try:
logger.info('working folder:{}'.format(os.getcwd()))
logger.info('task id:{}'.format(self.request.id))
logger.info('celery will execute:{}'.format(func))
logger.info('args:{}'.format(args))
logger.info('kwargs:{}'.format(kwargs))
# Str =》 加载 模块( 模块如果修改代码,会加载新得代码)
f = import_module_by_str(func)
# 加载不成功得话,告警
if f is None:
msg = 'celery can not load function :{}'.format(func)
print(msg, file=sys.stderr)
return False
# 真正执行任务
ret = f(*args, **kwargs)
# 返回执行结果给celery
return ret
except Exception as ex:
msg = 'celery execute func:{} exception:{},{}'.format(func, str(ex), traceback.format_exc())
logger.error(msg)
return False

View File

@ -0,0 +1,4 @@
{
"celery_broker": "amqp://admin:admin@192.168.0.202:5672//",
"celery_backend": "amqp://admin:admin@192.168.0.202:5672//"
}