# -*- coding: utf-8 -*- #!/usr/bin/python import pymysql import hashlib import json import urllib.request import base64 import tornado.ioloop import tornado.web import time import datetime import redis import os import functools CONFIG_DIR = '' def IsOnlineEnv(): return os.getenv("SERVER_ENV"); if (IsOnlineEnv()): CONFIG_DIR = '/var/data/conf_test/game2003api_rankserver/config' else: CONFIG_DIR = '../config' def info(msg): print(str(datetime.datetime.now()) + '[INFO] ' + msg) def take_pass(elem): return elem[3] def take_coin_num(elem): return elem[4] def safeDiv(a, b): if b == 0: return 0 else: return a / b def getRedis(): redis_conf = json.loadsmysql_conf = json.loads(open(CONFIG_DIR + '/rankserver.redis.cluster.json', 'r').read()) for conf in redis_conf: r = redis.Redis(host = conf['host'], port = conf['port'], password = conf['passwd'], charset = 'utf8' ) return r; def getDaySeconds(time_val, incdays): time_zone = 8 dayseconds = int((time_val + time_zone * 3600)/3600/24 + incdays) * 3600 * 24 - 3600 * time_zone; return dayseconds #数据去重 def delRepeatData(row, data_list): temp_list = [] for data in data_list: if data[0] == row[0]: temp_list.append(data) for temp_data in temp_list: data_list.remove(temp_data) #刷新通关数据 def refreshData(row, pass_list): pass_list.append((row[0], row[1].decode('utf-8'), row[2], row[3], row[4])) pass_list.sort(key=take_pass, reverse=True) if (len(pass_list) > 50): del pass_list[50:] #刷新金币数据 def refreshCoinData(row, coin_list): coin_list.append((row[0], row[1].decode('utf-8'), row[2], row[3], row[4])) coin = sorted(coin_list, key=functools.cmp_to_key(customCmp), reverse = True) if (len(coin) > 50): del coin[50:] #字符串排序 def customCmp(a, b): if len(a[4]) < len(b[4]): return -1 elif len(a[4]) > len(b[4]): return 1 #endif if a[4] < b[4]: return -1 elif a[4] > b[4]: return 1 else: return 0 #更新排行榜 def updateRank(r, pass_list, coin_list): pass_list.sort(key=take_pass, reverse=True) pass_rank = [] for pass_index in range(min(50, len(pass_list))): pass_rank.append(pass_list[pass_index]) r.set("game2003api:pass_rank", json.dumps(pass_rank)) coin = sorted(coin_list, key=functools.cmp_to_key(customCmp), reverse = True) coin_rank = [] for coin_index in range(min(50, len(coin))): coin_rank.append(coin[coin_index]) r.set("game2003api:coin_rank", json.dumps(coin_rank)) def internalDayReadMysqlData(): mysql_conf = json.loads(open(CONFIG_DIR + '/rankserver.mysql.cluster.json', 'r').read()) pass_list = [] coin_list = [] for conf in mysql_conf: conn = pymysql.connect(host = conf['host'], port = conf['port'], user = conf['user'], passwd = conf['passwd'], db = 'gamedb2003_' + str(conf['instance_id']), charset = 'utf8' ) cursor = conn.cursor() last_idx = 0 temp_idx = 0 while 1: cursor.execute('SELECT accountid, user_name, avatar_url, pass, cumul_coin, idx' ' FROM user WHERE idx > %s LIMIT 0, 1000' % (last_idx)) has_data = False for row in cursor: has_data = True #更新通关榜 refreshData(row, pass_list) #更新金钱榜 refreshCoinData(row, coin_list) temp_idx = int(row[5]) if (temp_idx > last_idx) : last_idx = int(row[5]) if not has_data: break r = getRedis() updateRank(r, pass_list, coin_list) #每日定时读取mysql里的数据生成排行榜写入redis后php读取redis返回客户端显示 def dayReadMysqlData(rushtime): internalDayReadMysqlData() tornado.ioloop.IOLoop.current().call_at(getDaySeconds(time.time(), 1) + rushtime, lambda : dayReadMysqlData(rushtime) ) #每5分钟读取mysql里发生改变过的数据更新排行榜 def readMysqlData(rushtime): mysql_conf = json.loads(open(CONFIG_DIR + '/rankserver.mysql.cluster.json', 'r').read()) r = getRedis() pass_list_str = r.get("game2003api:pass_rank") if (not pass_list_str): pass_list = [] else: pass_list = json.loads(pass_list_str) coin_list_str = r.get("game2003api:coin_rank") if (not coin_list_str): coin_list = [] else: coin_list = json.loads(coin_list_str) for conf in mysql_conf: conn = pymysql.connect(host = conf['host'], port = conf['port'], user = conf['user'], passwd = conf['passwd'], db = 'gamedb2003_' + str(conf['instance_id']), charset = 'utf8' ) cursor = conn.cursor() last_idx = 0 temp_idx = 0 while 1: cursor.execute('SELECT accountid, user_name, avatar_url, pass, cumul_coin, idx FROM user ' ' WHERE idx > %s LIMIT 0, 1000' % (last_idx)) has_data = False for row in cursor: has_data = True #更新击杀榜 delRepeatData(row, pass_list) refreshData(row, pass_list) temp_idx = int(row[5]) if (temp_idx > last_idx) : last_idx = int(row[5]) if not has_data: break last_idx = 0 temp_idx = 0 while 1: cursor.execute('SELECT accountid, user_name, avatar_url, pass, cumul_coin, idx FROM user ' ' WHERE idx > %s LIMIT 0, 1000' % (last_idx)) has_data = False for row in cursor: has_data = True #更新胜场榜 delRepeatData(row, coin_list) refreshCoinData(row, coin_list) temp_idx = int(row[5]) if (temp_idx > last_idx) : last_idx = int(row[5]) if not has_data: break updateRank(r, pass_list, coin_list) tornado.ioloop.IOLoop.current().call_later(rushtime, lambda : readMysqlData(rushtime) ) class SelfCheckingHandler(tornado.web.RequestHandler): def get(self): self.write(json.dumps({ 'errcode': 0, 'errmsg': '', 'healthy': 1, 'max_rundelay': 10 })) def make_app(): return tornado.web.Application([ (r"/webapp/index[\.]php", SelfCheckingHandler), ]) if __name__ == "__main__": conf = json.loads(open(CONFIG_DIR + '/rankserver.json', 'r').read()) app = make_app() app.listen(conf['listen_port']) conf['rushtime'] = 300 tornado.ioloop.IOLoop.current().call_later(conf['rushtime'], lambda : readMysqlData(conf['rushtime']) ) conf['day_rushtime'] = 5 * 3600 tornado.ioloop.IOLoop.current().call_at(getDaySeconds(time.time(), 1) + conf['day_rushtime'], lambda : dayReadMysqlData(conf['day_rushtime']) ) tornado.ioloop.IOLoop.current().start()