完成分布式查询

This commit is contained in:
Jalin
2019-01-09 11:14:49 +08:00
parent 8f681c5e30
commit 7d5b8e2b80
21 changed files with 509 additions and 136 deletions

14
main.py
View File

@@ -1,20 +1,18 @@
# encoding=utf8 # encoding=utf8
import sys import sys
from time import sleep
from py12306.helpers.func import * from py12306.app import *
from py12306.helpers.app import *
from py12306.log.common_log import CommonLog from py12306.log.common_log import CommonLog
from py12306.query.query import Query from py12306.query.query import Query
from py12306.user.user import User
def main(): def main():
if '--test' in sys.argv or '-t' in sys.argv: test() if '--test' in sys.argv or '-t' in sys.argv: test()
App.run()
CommonLog.print_welcome().print_configs() CommonLog.print_welcome().print_configs()
App.did_start()
App.run_check() # App.run_check()
User.run() # User.run()
Query.run() Query.run()
if not Const.IS_TEST: if not Const.IS_TEST:
while True: while True:
@@ -34,7 +32,7 @@ def test():
:return: :return:
""" """
Const.IS_TEST = True Const.IS_TEST = True
config.OUT_PUT_LOG_TO_FILE_ENABLED = False Config.OUT_PUT_LOG_TO_FILE_ENABLED = False
if '--test-notification' in sys.argv or '-n' in sys.argv: if '--test-notification' in sys.argv or '-n' in sys.argv:
Const.IS_TEST_NOTIFICATION = True Const.IS_TEST_NOTIFICATION = True
pass pass

View File

@@ -1,5 +1,8 @@
import signal
import sys
from py12306.helpers.func import * from py12306.helpers.func import *
from py12306.config import * from py12306.config import Config
from py12306.helpers.notification import Notification from py12306.helpers.notification import Notification
from py12306.log.common_log import CommonLog from py12306.log.common_log import CommonLog
from py12306.log.order_log import OrderLog from py12306.log.order_log import OrderLog
@@ -17,31 +20,66 @@ def app_available_check():
return True return True
@singleton
class App: class App:
""" """
程序主类 程序主类
TODO 需要完善 TODO 需要完善
""" """
@classmethod
def run(cls):
self = cls()
self.start()
def start(self):
Config().run()
for sign in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]: signal.signal(sign, self.handler_exit)
self.init_class()
@classmethod
def did_start(cls):
self = cls()
if Config.is_cluster_enabled():
from py12306.cluster.cluster import Distributed
Distributed().join_cluster()
def init_class(self):
from py12306.cluster.cluster import Distributed
if Config.is_cluster_enabled(): Distributed()
def handler_exit(self, *args, **kwargs):
"""
程序退出
:param args:
:param kwargs:
:return:
"""
if Config.is_cluster_enabled():
from py12306.cluster.cluster import Distributed
Distributed().left_cluster()
sys.exit()
@classmethod @classmethod
def check_auto_code(cls): def check_auto_code(cls):
if not config.AUTO_CODE_ACCOUNT.get('user') or not config.AUTO_CODE_ACCOUNT.get('pwd'): if not Config().AUTO_CODE_ACCOUNT.get('user') or not Config().AUTO_CODE_ACCOUNT.get('pwd'):
return False return False
return True return True
@classmethod @classmethod
def check_user_account_is_empty(cls): def check_user_account_is_empty(cls):
if config.USER_ACCOUNTS: if Config().USER_ACCOUNTS:
for account in config.USER_ACCOUNTS: for account in Config().USER_ACCOUNTS:
if account: if account:
return True return True
return False return False
@classmethod @classmethod
def test_send_notifications(cls): def test_send_notifications(cls):
if config.NOTIFICATION_BY_VOICE_CODE: # 语音通知 if Config().NOTIFICATION_BY_VOICE_CODE: # 语音通知
CommonLog.add_quick_log(CommonLog.MESSAGE_TEST_SEND_VOICE_CODE).flush() CommonLog.add_quick_log(CommonLog.MESSAGE_TEST_SEND_VOICE_CODE).flush()
Notification.voice_code(config.NOTIFICATION_VOICE_CODE_PHONE, '张三', Notification.voice_code(Config().NOTIFICATION_VOICE_CODE_PHONE, '张三',
OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT.format('北京', OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT.format('北京',
'深圳')) '深圳'))

View File

View File

@@ -0,0 +1,67 @@
import redis
from redis.client import PubSub
from py12306.cluster.redis import Redis
from py12306.config import Config
from py12306.helpers.func import *
from py12306.log.cluster_log import ClusterLog
@singleton
class Distributed():
KEY_QUERY_COUNT = 'query_count'
KEY_QUERY_LAST_TIME = 'query_last_time'
KEY_CONFIGS = 'configs'
KEY_NODES = 'nodes'
KEY_CHANNEL_LOG = 'channel_log'
session: Redis = None
pubsub: PubSub = None
refresh_channel_time = 0.5
retry_time = 2
nodes = {}
def __init__(self, *args):
self.session = Redis()
self.pubsub = self.session.pubsub()
self.pubsub.subscribe(self.KEY_CHANNEL_LOG)
create_thread_and_run(self, 'refresh_data', wait=False)
create_thread_and_run(self, 'subscribe', wait=False)
return self
def join_cluster(self):
node_name = Config().NODE_NAME
if node_name in self.nodes:
node_name = node_name + '_' + str(dict_count_key_num(self.nodes, node_name))
ClusterLog.add_quick_log(ClusterLog.MESSAGE_NODE_ALREADY_IN_CLUSTER.format(node_name)).flush()
self.session.hset(self.KEY_NODES, node_name, Config().NODE_IS_MASTER)
message = ClusterLog.MESSAGE_JOIN_CLUSTER_SUCCESS.format(Config().NODE_NAME, list(self.get_nodes()))
# ClusterLog.add_quick_log(message).flush()
self.session.publish(self.KEY_CHANNEL_LOG, message)
def left_cluster(self):
self.session.hdel(self.KEY_NODES, Config().NODE_NAME)
message = ClusterLog.MESSAGE_LEFT_CLUSTER.format(Config().NODE_NAME, list(self.get_nodes()))
# ClusterLog.add_quick_log(message).flush()
self.session.publish(self.KEY_CHANNEL_LOG, message)
def get_nodes(self):
res = self.session.hgetall(self.KEY_NODES)
res = res if res else {}
self.nodes = res
return res
def refresh_data(self):
while True:
self.get_nodes()
stay_second(self.retry_time)
def subscribe(self):
while True:
message = self.pubsub.get_message()
if message:
if message.get('type') == 'message' and message.get('data'):
ClusterLog.add_quick_log(message.get('data')).flush()
stay_second(self.refresh_channel_time)

59
py12306/cluster/redis.py Normal file
View File

@@ -0,0 +1,59 @@
import json
import pickle
import redis
from py12306.config import Config
from py12306.helpers.func import *
from py12306.log.redis_log import RedisLog
from redis import Redis as PyRedis
@singleton
class Redis(PyRedis):
# session = None
def __init__(self, *args):
if Config.is_cluster_enabled():
args = {
'host': Config().REDIS_HOST,
'port': Config().REDIS_PORT,
'db': 0,
'password': Config().REDIS_PASSWORD,
'decode_responses': True
}
super().__init__(**args)
RedisLog.add_quick_log(RedisLog.MESSAGE_REDIS_INIT_SUCCESS)
else:
super().__init__(**args)
return self
def get(self, name, default=None):
res = super().get(name)
# if decode: res = res.decode()
return res if res else default
def set(self, name, value, ex=None, px=None, nx=False, xx=False):
return super().set(name, available_value(value), ex=ex, px=px, nx=nx, xx=xx)
def set_dict(self, name, value):
return self.set_pickle(name, value)
# return self.set(name, json.dumps(value))
def get_dict(self, name, default={}):
return self.get_pickle(name, default)
# res = self.get(name)
# if res:
# return json.loads(res)
# return default
def set_pickle(self, name, value):
return self.set(name, pickle.dumps(value, 0).decode())
def get_pickle(self, name, default=None):
res = self.get(name).encode()
return pickle.loads(res) if res else default
# def smembers(self, name, default=[]):
# res = super().smembers(name)
# return [val.decode() for val in list(res)] if res else default

View File

@@ -1,51 +1,31 @@
import json
import re
from os import path from os import path
# 12306 账号 # 12306 账号
USER_ACCOUNTS = [] from py12306.helpers.func import *
@singleton
class Config:
USER_ACCOUNTS = []
# 查询任务 # 查询任务
QUERY_JOBS = [] QUERY_JOBS = []
# 查询间隔 # 查询间隔
QUERY_INTERVAL = 1 QUERY_INTERVAL = 1
# 用户心跳检测间隔 # 用户心跳检测间隔
USER_HEARTBEAT_INTERVAL = 120 USER_HEARTBEAT_INTERVAL = 120
# 多线程查询 # 多线程查询
QUERY_JOB_THREAD_ENABLED = 0 QUERY_JOB_THREAD_ENABLED = 0
# 打码平台账号 # 打码平台账号
AUTO_CODE_ACCOUNT = { AUTO_CODE_ACCOUNT = {'user': '', 'pwd': ''}
'user': '',
'pwd': ''
}
# 输出日志到文件 # 输出日志到文件
OUT_PUT_LOG_TO_FILE_ENABLED = 0 OUT_PUT_LOG_TO_FILE_ENABLED = 0
OUT_PUT_LOG_TO_FILE_PATH = 'runtime/12306.log' OUT_PUT_LOG_TO_FILE_PATH = 'runtime/12306.log'
SEAT_TYPES = {'特等座': 25, '商务座': 32, '一等座': 31, '二等座': 30, '软卧': 23, '硬卧': 28, '硬座': 29, '无座': 26, }
SEAT_TYPES = { ORDER_SEAT_TYPES = {'特等座': 'P', '商务座': 9, '一等座': 'M', '二等座': 'O', '软卧': 4, '硬卧': 3, '硬座': 1, '无座': 1}
'特等座': 25,
'商务座': 32,
'一等座': 31,
'二等座': 30,
'软卧': 23,
'硬卧': 28,
'硬座': 29,
'无座': 26,
}
ORDER_SEAT_TYPES = {
'特等座': 'P',
'商务座': 9,
'一等座': 'M',
'二等座': 'O',
'软卧': 4,
'硬卧': 3,
'硬座': 1,
'无座': 1,
}
PROJECT_DIR = path.dirname(path.dirname(path.abspath(__file__))) + '/' PROJECT_DIR = path.dirname(path.dirname(path.abspath(__file__))) + '/'
@@ -62,19 +42,103 @@ NOTIFICATION_BY_VOICE_CODE = 0
NOTIFICATION_VOICE_CODE_PHONE = '' NOTIFICATION_VOICE_CODE_PHONE = ''
NOTIFICATION_API_APP_CODE = '' NOTIFICATION_API_APP_CODE = ''
if path.exists(CONFIG_FILE): # 集群配置
exec(open(CONFIG_FILE, encoding='utf8').read()) CLUSTER_ENABLED = 1
NODE_IS_MASTER = 1
NODE_NAME = ''
REDIS_HOST = ''
REDIS_PORT = '6379'
REDIS_PASSWORD = ''
envs = []
retry_time = 5
@classmethod
def run(cls):
self = cls()
self.start()
# @classmethod
# def keep_work(cls):
# self = cls()
def start(self):
self.init_envs()
self.save_to_remote()
# self.refresh_configs()
create_thread_and_run(self, 'refresh_configs', wait=False)
def refresh_configs(self):
if not self.is_cluster_enabled(): return
while True:
remote_configs = self.get_remote_config()
self.update_configs_from_remote(remote_configs)
stay_second(self.retry_time)
def get_remote_config(self):
if not self.is_cluster_enabled(): return
from py12306.cluster.cluster import Distributed
return Distributed().session.get_pickle(Distributed().KEY_CONFIGS, {})
def save_to_remote(self):
if not self.is_master(): return
from py12306.cluster.cluster import Distributed
Distributed().session.set_pickle(Distributed().KEY_CONFIGS, self.envs)
def init_envs(self):
self.envs = EnvLoader.load_with_file(self.CONFIG_FILE)
self.update_configs(self.envs)
def update_configs(self, envs):
for key, value in envs:
setattr(self, key, value)
def update_configs_from_remote(self, envs):
if envs == self.envs: return
from py12306.query.query import Query
for key, value in envs:
if key == 'USER_ACCOUNTS' and value != self.USER_ACCOUNTS: # 用户修改
setattr(self, key, value)
print('用户修改了') # TODO
elif key == 'QUERY_JOBS' and value != self.QUERY_JOBS: # 任务修改
setattr(self, key, value) and Query().update_query_jobs(auto=True)
elif key == 'QUERY_INTERVAL' and value != self.QUERY_INTERVAL: # 任务修改
setattr(self, key, value) and Query().update_query_interval(auto=True)
if value != -1:
setattr(self, key, value)
@staticmethod
def is_master(): # 是不是 主
return Config.CLUSTER_ENABLED and Config.NODE_IS_MASTER
@staticmethod
def is_slave(): # 是不是 从
return Config.CLUSTER_ENABLED and not Config.NODE_IS_MASTER
@staticmethod
def is_cluster_enabled():
return Config.CLUSTER_ENABLED
# @staticmethod
# def get_members():
# members = []
# for name, value in vars(Config).items():
# if name.isupper():
# members.append(([name, value]))
# return members
class UserType: class EnvLoader():
ADULT = 1 envs = []
CHILD = 2
STUDENT = 3
SOLDIER = 4
dicts = { @classmethod
'成人': ADULT, def load_with_file(cls, file):
'儿童': CHILD, self = cls()
'学生': STUDENT, if path.exists(file):
'残疾军人、伤残人民警察': SOLDIER, env_content = open(file, encoding='utf8').read()
} content = re.sub(r'^([A-Z]+)_', r'self.\1_', env_content, flags=re.M)
exec(content)
return self.envs
def __setattr__(self, key, value):
self.envs.append(([key, value]))

View File

@@ -16,10 +16,11 @@ class AuthCode:
验证码类 验证码类
""" """
session = None session = None
data_path = config.RUNTIME_DIR data_path = None
retry_time = 5 retry_time = 5
def __init__(self, session): def __init__(self, session):
self.data_path = config.RUNTIME_DIR
self.session = session self.session = session
@classmethod @classmethod

View File

@@ -6,7 +6,8 @@ import functools
from time import sleep from time import sleep
from types import MethodType from types import MethodType
from py12306 import config
# from py12306 import config
def singleton(cls): def singleton(cls):
@@ -34,13 +35,13 @@ def singleton(cls):
return cls return cls
# 座位 # 座位 # TODO
def get_seat_number_by_name(name): # def get_number_by_name(name):
return config.SEAT_TYPES[name] # return config.SEAT_TYPES[name]
def get_seat_name_by_number(number): # def get_seat_name_by_number(number): # TODO remove config
return [k for k, v in config.SEAT_TYPES.items() if v == number].pop() # return [k for k, v in config.SEAT_TYPES.items() if v == number].pop()
# 初始化间隔 # 初始化间隔
@@ -106,6 +107,16 @@ def dict_find_key_by_value(data, value, default=None):
return result.pop() if len(result) else default return result.pop() if len(result) else default
def dict_count_key_num(data: dict, key, like=False):
count = 0
for k in data.keys():
if like:
if k.find(key) >= 0: count += 1
elif k == key:
count += 1
return count
def array_dict_find_by_key_value(data, key, value, default=None): def array_dict_find_by_key_value(data, key, value, default=None):
result = [v for k, v in enumerate(data) if key in v and v[key] == value] result = [v for k, v in enumerate(data) if key in v and v[key] == value]
return result.pop() if len(result) else default return result.pop() if len(result) else default
@@ -127,6 +138,12 @@ def expand_class(cls, key, value, keep_old=True):
return cls return cls
def available_value(value):
if isinstance(value, str) or isinstance(value, bytes):
return value
return str(value)
@singleton @singleton
class Const: class Const:
IS_TEST = False IS_TEST = False

View File

@@ -1,4 +1,3 @@
from py12306.helpers.app import *
from py12306.helpers.func import * from py12306.helpers.func import *
from requests_html import HTMLSession, HTMLResponse from requests_html import HTMLSession, HTMLResponse
@@ -38,7 +37,7 @@ class Request(HTMLSession):
重写 json 方法,拦截错误 重写 json 方法,拦截错误
:return: :return:
""" """
from py12306.helpers.app import Dict from py12306.app import Dict
try: try:
result = self.old_json() result = self.old_json()
return Dict(result) return Dict(result)

View File

@@ -1,4 +1,6 @@
from os import path from os import path
from py12306.config import Config
from py12306.helpers.func import * from py12306.helpers.func import *
@@ -7,8 +9,8 @@ class Station:
stations = [] stations = []
def __init__(self): def __init__(self):
if path.exists(config.STATION_FILE): if path.exists(Config().STATION_FILE):
result = open(config.STATION_FILE, encoding='utf-8').read() result = open(Config().STATION_FILE, encoding='utf-8').read()
result = result.lstrip('@').split('@') result = result.lstrip('@').split('@')
for i in result: for i in result:
tmp_info = i.split('|') tmp_info = i.split('|')

46
py12306/helpers/type.py Normal file
View File

@@ -0,0 +1,46 @@
from py12306.helpers.func import *
@singleton
class UserType:
ADULT = 1
CHILD = 2
STUDENT = 3
SOLDIER = 4
dicts = {
'成人': ADULT,
'儿童': CHILD,
'学生': STUDENT,
'残疾军人、伤残人民警察': SOLDIER,
}
@singleton
class OrderSeatType:
dicts = {
'特等座': 'P',
'商务座': 9,
'一等座': 'M',
'二等座': 'O',
'软卧': 4,
'硬卧': 3,
'硬座': 1,
'无座': 1,
}
@singleton
class SeatType:
dicts = {
'特等座': 25,
'商务座': 32,
'一等座': 31,
'二等座': 30,
'软卧': 23,
'硬卧': 28,
'硬座': 29,
'无座': 26,
}

View File

@@ -1,6 +1,7 @@
import os import os
import sys import sys
from py12306.config import Config
from py12306.helpers.func import * from py12306.helpers.func import *
@@ -26,8 +27,8 @@ class BaseLog:
self = cls() self = cls()
logs = self.get_logs() logs = self.get_logs()
# 输出到文件 # 输出到文件
if file == None and config.OUT_PUT_LOG_TO_FILE_ENABLED: # TODO 文件无法写入友好提示 if file == None and Config().OUT_PUT_LOG_TO_FILE_ENABLED: # TODO 文件无法写入友好提示
file = open(config.OUT_PUT_LOG_TO_FILE_PATH, 'a') file = open(Config().OUT_PUT_LOG_TO_FILE_PATH, 'a')
if not file: file = None if not file: file = None
print(*logs, sep=sep, end=end, file=file) print(*logs, sep=sep, end=end, file=file)
self.empty_logs(logs) self.empty_logs(logs)

View File

@@ -0,0 +1,15 @@
from py12306.log.base import BaseLog
from py12306.helpers.func import *
@singleton
class ClusterLog(BaseLog):
# 这里如果不声明,会出现重复打印,目前不知道什么原因
logs = []
thread_logs = {}
quick_log = []
MESSAGE_JOIN_CLUSTER_SUCCESS = '# 节点 {} 成功加入到集群,当前节点列表 {} #'
MESSAGE_LEFT_CLUSTER = '# 节点 {} 已离开集群,当前节点列表 {} #'
MESSAGE_NODE_ALREADY_IN_CLUSTER = '# 当前节点已存在于集群中,自动分配新的节点名称 {} #'

View File

@@ -37,9 +37,9 @@ class CommonLog(BaseLog):
if Const.IS_TEST: if Const.IS_TEST:
self.add_quick_log() self.add_quick_log()
self.add_quick_log('当前为测试模式,程序运行完成后自动结束') self.add_quick_log('当前为测试模式,程序运行完成后自动结束')
if not Const.IS_TEST and config.OUT_PUT_LOG_TO_FILE_ENABLED: if not Const.IS_TEST and Config().OUT_PUT_LOG_TO_FILE_ENABLED:
self.add_quick_log() self.add_quick_log()
self.add_quick_log('日志已输出到文件中: {}'.format(config.OUT_PUT_LOG_TO_FILE_PATH)) self.add_quick_log('日志已输出到文件中: {}'.format(Config().OUT_PUT_LOG_TO_FILE_PATH))
self.add_quick_log() self.add_quick_log()
self.flush(file=False) self.flush(file=False)
@@ -52,10 +52,10 @@ class CommonLog(BaseLog):
enable = '已开启' enable = '已开启'
disable = '未开启' disable = '未开启'
self.add_quick_log('**** 当前配置 ****') self.add_quick_log('**** 当前配置 ****')
self.add_quick_log('多线程查询: {}'.format(get_true_false_text(config.QUERY_JOB_THREAD_ENABLED, enable, disable))) self.add_quick_log('多线程查询: {}'.format(get_true_false_text(Config().QUERY_JOB_THREAD_ENABLED, enable, disable)))
self.add_quick_log('语音验证码: {}'.format(get_true_false_text(config.NOTIFICATION_BY_VOICE_CODE, enable, disable))) self.add_quick_log('语音验证码: {}'.format(get_true_false_text(Config().NOTIFICATION_BY_VOICE_CODE, enable, disable)))
self.add_quick_log('查询间隔: {}'.format(config.QUERY_INTERVAL)) self.add_quick_log('查询间隔: {}'.format(Config().QUERY_INTERVAL))
self.add_quick_log('用户心跳检测间隔: {}'.format(config.USER_HEARTBEAT_INTERVAL)) self.add_quick_log('用户心跳检测间隔: {}'.format(Config().USER_HEARTBEAT_INTERVAL))
self.add_quick_log() self.add_quick_log()
self.flush() self.flush()
return self return self

View File

@@ -2,6 +2,9 @@ import datetime
import json import json
import sys import sys
from os import path from os import path
from py12306.config import Config
from py12306.cluster.cluster import Distributed
from py12306.log.base import BaseLog from py12306.log.base import BaseLog
from py12306.helpers.func import * from py12306.helpers.func import *
@@ -17,7 +20,7 @@ class QueryLog(BaseLog):
'query_count': 1, 'query_count': 1,
'last_time': '', 'last_time': '',
} }
data_path = config.QUERY_DATA_DIR + 'status.json' data_path = None
LOG_INIT_JOBS = '' LOG_INIT_JOBS = ''
@@ -26,21 +29,46 @@ class QueryLog(BaseLog):
MESSAGE_QUERY_LOG_OF_TRAIN_INFO = '{} {}' MESSAGE_QUERY_LOG_OF_TRAIN_INFO = '{} {}'
MESSAGE_QUERY_START_BY_DATE = '出发日期 {}: {} - {}' MESSAGE_QUERY_START_BY_DATE = '出发日期 {}: {} - {}'
MESSAGE_JOBS_DID_CHANGED = '\n任务已更新,正在重新加载...'
cluster = None
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.data_path = Config().QUERY_DATA_DIR + 'status.json'
self.cluster = Distributed()
self.init_data() self.init_data()
def init_data(self): def init_data(self):
# 获取上次记录 # 获取上次记录
if Const.IS_TEST: return if Const.IS_TEST: return
if path.exists(self.data_path): result = False
if not Config.is_cluster_enabled() and path.exists(self.data_path):
with open(self.data_path, encoding='utf-8') as f: with open(self.data_path, encoding='utf-8') as f:
result = f.read() result = f.read()
if result: if result:
result = json.loads(result) result = json.loads(result)
if Config.is_cluster_enabled():
result = self.get_data_from_cluster()
if result:
self.data = {**self.data, **result} self.data = {**self.data, **result}
self.print_data_restored() self.print_data_restored()
def get_data_from_cluster(self):
query_count = self.cluster.session.get(Distributed.KEY_QUERY_COUNT, 0)
last_time = self.cluster.session.get(Distributed.KEY_QUERY_LAST_TIME, '')
if query_count and last_time:
return {'query_count': query_count, 'last_time': last_time}
return False
def refresh_data_of_cluster(self):
return {
'query_count': self.cluster.session.incr(Distributed.KEY_QUERY_COUNT),
'last_time': self.cluster.session.set(Distributed.KEY_QUERY_LAST_TIME, time_now()),
}
@classmethod @classmethod
def print_init_jobs(cls, jobs): def print_init_jobs(cls, jobs):
""" """
@@ -112,9 +140,9 @@ class QueryLog(BaseLog):
@classmethod @classmethod
def print_job_start(cls): def print_job_start(cls):
self = cls() self = cls()
self.refresh_data()
self.add_log('=== 正在进行第 {query_count} 次查询 === {time}'.format(query_count=self.data.get('query_count'), self.add_log('=== 正在进行第 {query_count} 次查询 === {time}'.format(query_count=self.data.get('query_count'),
time=datetime.datetime.now())) time=datetime.datetime.now()))
self.refresh_data()
if is_main_thread(): if is_main_thread():
self.flush() self.flush()
return self return self
@@ -134,6 +162,9 @@ class QueryLog(BaseLog):
return self return self
def refresh_data(self): def refresh_data(self):
if Config.is_cluster_enabled():
self.data = {**self.data, **self.refresh_data_of_cluster()}
else:
self.data['query_count'] += 1 self.data['query_count'] += 1
self.data['last_time'] = str(datetime.datetime.now()) self.data['last_time'] = str(datetime.datetime.now())
self.save_data() self.save_data()

12
py12306/log/redis_log.py Normal file
View File

@@ -0,0 +1,12 @@
from py12306.log.base import BaseLog
from py12306.helpers.func import *
@singleton
class RedisLog(BaseLog):
# 这里如果不声明,会出现重复打印,目前不知道什么原因
logs = []
thread_logs = {}
quick_log = []
MESSAGE_REDIS_INIT_SUCCESS = 'Redis 初始化成功'

View File

@@ -1,13 +1,10 @@
import urllib import urllib
import random
from py12306.config import UserType # from py12306.config import UserType
from py12306.helpers.api import * from py12306.helpers.api import *
from py12306.helpers.app import *
from py12306.helpers.func import * from py12306.helpers.func import *
from py12306.helpers.notification import Notification from py12306.helpers.notification import Notification
from py12306.log.order_log import OrderLog from py12306.log.order_log import OrderLog
from py12306.log.user_log import UserLog
# from py12306.query.job import Job # from py12306.query.job import Job

View File

@@ -1,5 +1,7 @@
from py12306.config import Config
from py12306.helpers.api import LEFT_TICKETS from py12306.helpers.api import LEFT_TICKETS
from py12306.helpers.station import Station from py12306.helpers.station import Station
from py12306.helpers.type import OrderSeatType, SeatType
from py12306.log.query_log import QueryLog from py12306.log.query_log import QueryLog
from py12306.helpers.func import * from py12306.helpers.func import *
from py12306.log.user_log import UserLog from py12306.log.user_log import UserLog
@@ -97,8 +99,7 @@ class Job:
通过日期进行查询 通过日期进行查询
:return: :return:
""" """
QueryLog.add_log( QueryLog.add_log(('\n' if not is_main_thread() else '') + QueryLog.MESSAGE_QUERY_START_BY_DATE.format(date,
('\n' if not is_main_thread() else '') + QueryLog.MESSAGE_QUERY_START_BY_DATE.format(date,
self.left_station, self.left_station,
self.arrive_station)) self.arrive_station))
url = LEFT_TICKETS.get('url').format(left_date=date, left_station=self.left_station_code, url = LEFT_TICKETS.get('url').format(left_date=date, left_station=self.left_station_code,
@@ -128,7 +129,7 @@ class Job:
if not self.is_has_ticket(ticket_info): if not self.is_has_ticket(ticket_info):
continue continue
allow_seats = self.allow_seats if self.allow_seats else list( allow_seats = self.allow_seats if self.allow_seats else list(
config.SEAT_TYPES.values()) # 未设置 则所有可用 TODO 合法检测 Config.SEAT_TYPES.values()) # 未设置 则所有可用 TODO 合法检测
self.handle_seats(allow_seats, ticket_info) self.handle_seats(allow_seats, ticket_info)
def handle_seats(self, allow_seats, ticket_info): def handle_seats(self, allow_seats, ticket_info):
@@ -192,8 +193,8 @@ class Job:
self.passengers = passengers self.passengers = passengers
def set_seat(self, seat): def set_seat(self, seat):
self.current_seat = get_seat_number_by_name(seat) self.current_seat = SeatType.dicts.get(seat)
self.current_order_seat = config.ORDER_SEAT_TYPES[seat] self.current_order_seat = OrderSeatType.dicts.get(seat)
def get_user(self): def get_user(self):
user = User.get_user(self.account_key) user = User.get_user(self.account_key)

View File

@@ -1,27 +1,41 @@
import threading from py12306.config import Config
from py12306.cluster.cluster import Distributed
from py12306.app import app_available_check
from py12306.helpers.app import app_available_check
from py12306.helpers.func import * from py12306.helpers.func import *
from py12306.helpers.request import Request from py12306.helpers.request import Request
from py12306.log.query_log import QueryLog from py12306.log.query_log import QueryLog
from py12306.query.job import Job from py12306.query.job import Job
@singleton
class Query: class Query:
""" """
余票查询 余票查询
""" """
jobs = [] jobs = []
query_jobs = []
session = {} session = {}
# 查询间隔 # 查询间隔
interval = {} interval = {}
cluster = None
def __init__(self): def __init__(self):
self.interval = init_interval_by_number(config.QUERY_INTERVAL)
self.session = Request() self.session = Request()
self.cluster = Distributed()
self.update_query_interval()
self.update_query_jobs()
def update_query_interval(self, auto=False):
self.interval = init_interval_by_number(Config().QUERY_INTERVAL)
def update_query_jobs(self, auto=False):
self.query_jobs = Config().QUERY_JOBS
if auto:
self.jobs = []
QueryLog.add_quick_log(QueryLog.MESSAGE_JOBS_DID_CHANGED).flush()
self.init_jobs()
@classmethod @classmethod
def run(cls): def run(cls):
@@ -33,20 +47,35 @@ class Query:
def start(self): def start(self):
# return # DEBUG # return # DEBUG
self.init_jobs() self.init_jobs()
QueryLog.print_init_jobs(jobs=self.jobs)
stay_second(1) stay_second(1)
while True: while True:
app_available_check() app_available_check()
if config.QUERY_JOB_THREAD_ENABLED: # 多线程 if Config().QUERY_JOB_THREAD_ENABLED: # 多线程
create_thread_and_run(jobs=self.jobs, callback_name='run') create_thread_and_run(jobs=self.jobs, callback_name='run')
else: else:
for job in self.jobs: for job in self.jobs: job.run()
job.run()
if Const.IS_TEST: return if Const.IS_TEST: return
# self.refresh_jobs() # 刷新任务
def init_jobs(self): def init_jobs(self):
jobs = config.QUERY_JOBS for job in self.query_jobs:
for job in jobs:
job = Job(info=job, query=self) job = Job(info=job, query=self)
self.jobs.append(job) self.jobs.append(job)
QueryLog.print_init_jobs(jobs=self.jobs)
# def get_jobs_from_cluster(self):
# jobs = self.cluster.session.get_dict(Distributed.KEY_JOBS)
# return jobs
#
# def update_jobs_of_cluster(self):
# if config.CLUSTER_ENABLED and config.NODE_IS_MASTER:
# return self.cluster.session.set_dict(Distributed.KEY_JOBS, self.query_jobs)
#
# def refresh_jobs(self):
# if not config.CLUSTER_ENABLED: return
# jobs = self.get_jobs_from_cluster()
# if jobs != self.query_jobs:
# self.jobs = []
# self.query_jobs = jobs
# QueryLog.add_quick_log(QueryLog.MESSAGE_JOBS_DID_CHANGED).flush()
# self.init_jobs()

View File

@@ -1,11 +1,7 @@
import json
import pickle import pickle
import re
from os import path
from py12306.config import *
from py12306.helpers.api import * from py12306.helpers.api import *
from py12306.helpers.app import * from py12306.app import *
from py12306.helpers.auth_code import AuthCode from py12306.helpers.auth_code import AuthCode
from py12306.helpers.func import * from py12306.helpers.func import *
from py12306.helpers.request import Request from py12306.helpers.request import Request

View File

@@ -1,4 +1,4 @@
from py12306.helpers.app import * from py12306.app import *
from py12306.helpers.func import * from py12306.helpers.func import *
from py12306.log.user_log import UserLog from py12306.log.user_log import UserLog
from py12306.user.job import UserJob from py12306.user.job import UserJob