技术收集

国外访问国外视频网站

老婆大人把ipad带去澳洲了,走之前装的一对打发时间的视频app,诸如 优酷,爱奇艺,土豆,迅雷之类,谁想到碰到了一个问题,国外居然禁止访问国内的视频网站。会有下面的提示

国外观看优酷土豆等版权视频

国外禁止访问国内的视频网站

于是到网上一顿搜索,发现碰到同类问题的人还真是不少,伟大的中国人。不过就跟抢火车票的插件一样,任何限制在伟大的程序员面前都是渺小的,这不就是接下来要说的Unlock Youku

设置如下,参考说明在这里

注意,使用或安装本程序代表您同意:本程序仅为交流学习之用,造成的任何后果与程序作者无关。

作为 Chrome 扩展的一个副产品,Unblock-Youku 的源码也可以当作一个代理服务器来运行,用来给别的浏览器和移动设备解除地域限制。

  1. 相对于 Chrome 扩展程序,这种直接设置代理服务器的模式会有安全隐患。如果代理服务器被黑,则可能造成个人隐私泄漏或财产损失。程序作者及服务器提供者对任何可能的后果概不负责。
  2. 因为技术上的原因,这种代理模式功能比较受限,实际不能解封太多的网站。需要更加全面的功能还是建议使用 Chrome 浏览器。
  3. 代理服务器资源有限,欢迎技术达人尝试运行自己的服务器。本文最后给出了简单的介绍。

未能做详细的测试,有问题可以在这里新开一个 Issue,或者查找之前的 Issue 中的解决办法(例如 Issue #55 和 Issue #65)。

不过因为个人精力有限,只对这个代理服务器做适当的技术支持。

代理设置示例

简单的来说,只用给浏览器或者 iOS 设备设定一个代理自动配置 PAC (proxy auto-config) 文件就可以了。PAC 文件的地址为

http://yo.uku.im/proxy.pac

下面是 iPad, Firefox 和 IE 的设置截图。

iOS 的设备可以在当前使用的WiFi的高级选项里设置 HTTP 代理。

Firefox

IE


检查代理设置是否成功

按照上面的步骤设置之后,可以在浏览器中打开页面 http://uku.im/check

如果页面显示的是 true,便说明代理设置已经生效,例如下图中的结果


代理服务器状态监控

提供下面的状态图便于查看代理服务器最近的运行状态,点击图片可以得到更详细的信息。


运行自己的服务器

服务器端程序是基于 Node.js 写成,并且代码已经放在了 npm 仓库里,下载和运行均很方便。

注意:这个代理服务器暂时只能运行在本地电脑,或者 Linode、AWS 等 VPS 平台上。

1. 下载安装 Node.js

Windows 和 Mac 的安装文件可以从这里下载 http://nodejs.org/download/

Linux 可以直接通过包管理系统安装,具体参考 https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager

2. 命令行下载安装 ub.uku.js

Unblock-Youku 在 npm 仓库中的名字为 ub.uku.js,可以很方便地在 Mac/Linux 的 Terminal 或 Windows 的 Node.js Command Prompt 中用一条命令安装。

Linux / Mac

sudo npm install -g ub.uku.js

Windows

npm install -g ub.uku.js

3. 运行 ub.uku.js

Linux / Mac 下直接运行

ub.uku.js

Windows 下运行

ub.uku.js.cmd

另外还可以使用命令行参数

  • --ip=10.1.2.3:指定监听的本机 IP 地址,默认为 0.0.0.0,即监听本机所有的 IP 地址。
  • --port=8080: 指定监听端口,默认为 8888。
  • --local_only: 仅允许同一台电脑上的别的软件连接代理服务器,不允许网络上的别的电脑连接。
  • --ext_ip=1.2.3.4:指定外网访问用的 IP 地址,即 PAC 文件中的代理服务器的 IP 地址。方便在内网架设服务器时使用。默认会使用程序搜索到的本机的第一个 IP 地址。
  • --ext_port=1234: 指定外网访问用的端口号,即 PAC 文件中的代理服务器的端口号码。默认直接使用内网的端口号。
  • --mitm_proxy: Debug 用代理模式,可以当作普通的 HTTP 代理服务器直接使用。特定的 URL 还是会经过国内的代理服务器。
  • --nolog: 不再显示网络访问日志,只提示错误信息。

下面是实际安装和运行的截图:

Linux / Mac

Windows

换笔记本

工作了5年的T61最终还是倒下去了,传说中的“显卡门”,也该退出历史舞台了,哈哈。
正好借此机会更新笔记本,不过换了一个二手的T410,速度内存都上来了,加上ssd当系统盘,还是很不错的,总共才2500块。

更新T410

换本本了

老本子坏掉了,换了新的

org.springframework.dao.IncorrectResultSizeDataAccessException

今天在写一个游戏接口的时候,以为数据库中就一条记录,想当然的写下了

select appid, code, name, url from hall_games where code=:code

测试机上一运行就出现这个异常。
org.springframework.dao.IncorrectResultSizeDataAccessException: Incorrect result size: expected 0 or 1, actual 8: com.xiaonei.in.dao.LoginUserDAO#getHallGameInfo

于是查看文档。
Data access exception thrown when a result was not of the expected size, for example when expecting a single row but getting 0 or more than 1 rows。
当期望返回的结果记录是1时,如果返回值为0或者>1会抛此异常。于是修改sql,当需要一条数据的时候,还是老实加上limit 1比较好。

select appid, code, name, url from hall_games where code=:code limit 1

python 2.x和3.x中maketrans和translate函数的使用

本文的缘由在于今天在网上逛来逛去的时候,发现了一个Python解谜的网站http://projecteuler.net/www.pythonchallenge.com,鉴于自己Python一瓶不满的水平,直接草草做了一下,发现水的很,搞不出来就直接看答案了,主要题目是这个http://www.pythonchallenge.com/pc/def/map.html,具体的解答可以参考这篇文章http://garethrees.org/2007/05/07/python-challenge/
maketrans和translate函数是进行字符串字符编码的常用方法。本文着重点在于演示其基本用法和在不同版本下操作的差异。本文提到的2.X版本指2.6以上的版本,3.X版本指3.1以上的版本。
2.X版本把字符串基本分为两种:unicode字符串和8位字符串str,后者包含字节数据和我们常见的ASCII码数据;而3.X版本则重新对字符串进行了划分,分为了字节字符串bytes和文本字符串str,两者都是不可变的,所以添加了一个可变的字节字符串类型bytearray。
2.X版本中string类型和str、unicode类型大量方法是重复的,所以3.X版本不提倡使用string模块中与str重复的方法。string模块中还有很多有用的常量和方法,比如string.digits,可以在字符串编码中方便地使用。
2.X中maketrans和translate函数的签名:

    string.maketrans(from, to)  #Return a translation tablesuitable for use in string.translate.  The strings frm and to must be of the same length.
    string.translate(s, table[, deletechars])  #Return a copy of the string s, where all characters occurring in the optional argument deletions are removed, and the remaining characters have been mapped through the given translation table, which must be a string of length 256.  The deletions argument is not allowed for Unicode strings.
    str.translate(table[, deletechars])  
    unicode.translate(table)

2.X中maketrans和translate函数的签名:

    static str.maketrans(x[, y[, z]])  
    static bytes.maketrans(from, to)  
    static bytearray.maketrans(from, to)  
    str.translate(map)  
    bytes.translate(table[, delete])  
    bytearray.translate(table[, delete])  

从中可以看出,相对于2.X的string模块的maketrans方法,3.X中分别提供了三个静态方法用于创建映射表。下面让我们看一个简单的例子来说明字符串转换的过程:
2.X下的演示过程:

    >>> import string                       #导入string模块  
    >>> map = string.maketrans('123', 'abc')#建立映射表,将字符串中含有的'1','2','3'替换为'a','b','c'  
    >>> s = '54321123789'                   #转换前的字符串  
    >>> s.translate(map)                    #用创建的映射表map转换字符串  
    '54cbaabc789'                           #转换后的字符串

3.X下的演示过程:

    >>> map = str.maketrans('123','abc')  
    >>> s = '54321123789'  
    >>> s.translate(map)  
    '54cbaabc789'

2.X使用了string的maketrans函数,而3.X使用了str的maketrans函数,除了这一点,使用方法是基本相同的。若指定字符串中要删除的字符时,使用就会略有不同,如下:
2.X下的演示过程:

    >>> import string  
    >>> map = string.maketrans('123', 'abc')  
    >>> s = '54321123789'  
    >>> s.translate(map, '78')        #除了转换,还要删除字符串中的字符'7','8'  
    '54cbaabc9'                       #转换后的字符串没有字符'7','8'  

3.X下的演示过程:

    >>> map = str.maketrans('123','abc', '78')#要删除的字符需要在这指定  
    >>> s = '54321123789'  
    >>> s.translate(map)  
    '54cbaabc9'

在读《Python Cookbook》遇到了一个基于2.X版本的例子,如下

    import string   
    def translator(frm='', to='', delete='', keep=None):   
        if len(to) == 1:   
            to = to * len(frm)   
        trans = string.maketrans(frm, to)   
        if keep is not None:   
            allchars = string.maketrans('', '')   
            delete = allchars.translate(allchars, keep.translate(allchars,delete))  
        def translate(s):   
            return s.translate(trans, delete)   
        return translate  

见第8行代码,allchars应该是一个返回的映射表,为什么还可以调用translate方法,所以它应该是一个str类型,测试如下:

    >>> import string  
    >>> map = string.maketrans('123', 'abc')  
    >>> type(map)  
      

在3.X版本中这个方法不能正常通过运行,那么错在什么地方呢,我们看看映射表是什么类型:

    >>> map = str.maketrans('123','abc')  
    >>> type(map)  
      

知道了映射表的类型了,我们就可以对其进行“后期加工”,像上面《Python Cookbook》中的例子一样,来满足我们的编码要求。

上面讨论的例子用的字符串是ASCII字符组成的,如果是字节类型,2.X版本中操作是一样的,3.X中调用bytes或bytearray的函数;若是unicode类型的,2.X需要用unicode的translate方法,注意下面的代码


    >>> print u"hallo".translate({97:u'e'})  
    hello  
    >>> print u"hallo".translate({'a':u'e'})  
    hallo  
    >>> print u"hallo".translate({u'a':u'e'})  
    hallo  

结果之所以不一样,查阅手册可知unicode的translate方法的映射表也就是字典的键必须是unicode的位序数,值可以是unicode的位序数、unicode字符串或这None。
具体的相关信息,可以查阅Python的手册或者help(string)。

discuz的那点事儿

背景介绍:目前的论坛是直接使用Discuz!搭建的,版本比较古老,一直没人动过,从我接收就一直感觉是个烫手的山药,不是说处理不了(凭我浸淫php多年的经验,想来这点毛毛论坛还是可以拿下的,只是前人改过不少代码,注释也没有,于是一直懒得动),但是最近产品部说要整改论坛了,只好捡起来从头开始研究,记录下点滴。
本文先说下跟人人网连接,直接使用connect那套系统就行了,简单的很。
1,引入一个JS,比如命名为connect.js

function addScript(src) {
   var scriptElement = document.createElement("script");
   scriptElement.type = "text/javascript";
   scriptElement.src = src;
   document.getElementsByTagName("HEAD")[0].appendChild(scriptElement);
}
function connectDyload() {
  addScript("http://static.connect.renren.com/js/v2.0/FeatureLoader.jsp?loaded=connectInit");
}
function connectInit() {
  XN_RequireFeatures(["Base"], function(){
    XN.Main.init("efc590d209f847c1ae0d830dcb99c972", "/xd_receiver.html", {"doNotUseCachedConnectState":true});
    XN.Connect.c(function() {
      document.location = "http://www.rrgbbs.com/toGamebbs.do?tid=&redirectUrl="+window.location.href;
    });
  });
}

其中,connect成功后的跳转地址,自己写了个java类,toGamebbs,这个类用来调用Discuz!的一系列验证方式,比如判断用户是不是登录过了cdb_session表,然后新用户的话,自动在后台帮助用户完成注册功能。
2.把模版中的header.html的登录块注释掉,换成connect的代码


登录
{lang logout}
3.修改全局变量template/default/templates.lang.php,把home_welcome_guest修改为上面类似的登录模块

人人网用户请直接点击登录,以浏览更多精彩内容,并随时发布观点,与大家交流。

4.需要在本地放一个跨域文件xd_receiver.html




    





更多renren connect的原理,可以参考官方网站 http://wiki.dev.renren.com/wiki/%E4%BA%BA%E4%BA%BAConnect

HttpClient模拟操作https网站

在日常工作中,我们常常需要抓取一些网站的信息为自己所用,比如自动抓取天气预报啊,抓取sina的股票数据,某些图片网站的美图等等,这就需要spider出场了,其实每种语言都有自己的写法,比如在Linux下,第一眼想到的就是wget,这是个强大的工具,可以递归地抓取网站,有windows下的版本,在本文章中还是说下Java的方式,那就是著名的开源软件HttpClient,在使用HttpClient的时候,我们需要知道网站的运行方式,可能在中间跳转了好几次,为了看到这些信息,我们还需要一点工具,Firefox的话直接中Httpfox就可以了,在IE下可以使用Httpwatch,官方下载地址是这个http://www.httpwatch.com,具体的使用很简单,不会的可以参考这个:http://www.cnblogs.com/mayingbao/archive/2007/11/30/978530.html
完事具备了,我们来看看怎么使用Httpclient

public static HttpMethod loginXN() throws IOException {
                PostMethod post = new PostMethod("http://passport.renren.com/PLogin.do");
		NameValuePair user = new NameValuePair("email",
				"zhangsan@hotmail.com");
		NameValuePair pwd = new NameValuePair("password", "12345");
		NameValuePair domain = new NameValuePair("domain", "renren.com");
		post.setRequestBody(new NameValuePair[] { user, pwd, domain});
		return post;
}

如果是https的请求,略微麻烦一点,需要使用SSLProtocolSocketFactory,在new HttpClient实例的时候,需要指定一下。

public static String httpGet(String url,Header header) {
        Protocol myhttps = new Protocol("https", new SSLProtocolSocketFactory(), 443);
        String result = "";
        HttpClient httpClient = new HttpClient();
        GetMethod getMethod = new GetMethod(url);
        getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
                new DefaultHttpMethodRetryHandler());
        int statusCode = 1000;//默认代码Connection refused
        try {
            httpClient.getHostConfiguration().setHost("passport.dudusp.com",443,myhttps);
            getMethod.addRequestHeader(header);            
            statusCode = httpClient.executeMethod(getMethod);//执行getMethod
            if (statusCode == HttpStatus.SC_OK) {
                result = getMethod.getResponseBodyAsString();
            }
        } catch (Exception e) {
            log.info(e.getMessage(), e);
        } finally {
            getMethod.releaseConnection(); //释放连接
        }
        return result;
    }

公司的passport验证原理

最近为了安全考虑,出了套验证机制。passport处理未登录用户的过程大致为:
1.用户访问应用服务器A
2.如果用户未登录A,自动重定向到passport服务器
3.passport验证登录成功后,返回验证票
4.应用服务器A使用验证票到passport服务器进行二次验证
5.passport二次验证成功后,返回用户名

根据上述处理过程,passport的使用方法为:
1.passport的请求地址为:passport.no.xxxx.com;
2.应用服务器请求passport页面时,需提供forward跳转参数;
3.passport接收请求,验证正确的情况下,返回验证票(ticket参数);
4.应用服务器二次验证的过程为:
1)构造HTTP REFERER请求头;
2)携带HTTP REFERER请求头,远程读取passport.no.xxxx.com/verify.php?t=$ticket(该$ticket为 passport返回的ticket)地址的内容;
3)如果passport验证成功,返回用户名,否则返回空串。

passport的使用示例如下(以MM系统为例http://mm.renren.com/index.php):
1.MM请求访问passport的地址:https://passport.no.xxxx.com/login.php?forward=http://mm.renren.com/index.php
2.passport验证成功后,将地址重定向到http://mm.renren.com/index.php/index.php?ticket=nscr3omh80rn367h5pu0dq76u0
3.MM系统处理passport验证票($ticket参数)的过程为:

   function auth_check_valid($ticket){
       if($ticket == "")
           return false;

       $opts=array(
           'http'=>array(
               'header'=>"Referer :".$_SERVER['REQUEST_URI']
           )
       );

       $context = stream_context_create($opts);//构造HTTP REFERER头   
       $url = "https://passport.no.xxxx.com/verify.php?t=".$ticket;
       $user_id = file_get_contents($url,false,$context);//二次验证,远程请求用户名       
       return $user_id;//返回登录用户名,需进行后续判断是否为空串
   }

在接下来的文章中,我们会进一步讲解如果使用程序模拟登录验证系统,该怎么处理。

linux shell program – for in 循环语句的用法

在写一个小脚本时,需要循环显示每月日期当参数,传递给请求的地址。大脑一拍,直接用for循环做了,如果python可以直接使用for xxx in range(0,32),那么Linux是不是也有类似的写法呢,答案是正确的,不过略微有点小改动。语法如下:
for 无$变量 in 字符串
do
$变量
done
利用for in格式对字符串按空格切份的功能,直接遍历,于是上面的字符串我们可以随便定义,只需空格隔开即可。

SERVICES="22 80 25 110 8000 23 20 21 3306 "
for x in $SERVICES
do
    iptables -A INPUT -p tcp --dport $x -m state --state NEW -j ACCEPT
done

for i in a b c 字符串列表A B C
字符串用空格分隔,没有括号,没有逗号, 然后循环将其依次赋给变量i.变量没有$
于是我们的日志处理脚本,可以这么写了

#!/bin/sh
DAYS="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31"
for dst in $DAYS
do
/usr/bin/wget "http://gamecenter.renren.com/weige.jsp?y=$1&m=$2&d=$dst" -O a.txt
done

Nginx出现502和504错误提示的解决方案

今天在测试机器上搞了个web论坛,discuz的,还算轻车熟路,直接用nginx+fast-cgi,搞了几下就运行一起来了,可是没多久就出现问题了,访问首页的时候,总是提示502或者504的,很是郁闷,潜意识里觉得是fast-cgi没搞正确,在度娘上搜了下果然如此,简单记录下吧。
Nginx 502 Bad Gateway的含义是请求的PHP-CGI已经执行,但是由于某种原因(一般是读取资源的问题)没有执行完毕而导致PHP-CGI进程终止。
Nginx 504 Gateway Time-out的含义是所请求的网关没有请求到,简单来说就是没有请求到可以执行的PHP-CGI。
一般来说Nginx 502 Bad Gateway和php-fpm.conf的设置有关,而Nginx 504 Gateway Time-out则是与nginx.conf的设置有关。
php-fpm.conf有两个至关重要的参数,一个是“max_children”,另一个是”request_terminate_timeout” ,但是这个值不是通用的,而是需要自己计算的。
解决办法如下:
1、调整php-fpm.conf的相关设置:

32
60s

2、调整nginx.conf的相关设置:

fastcgi_connect_timeout 600; #set timeout
fastcgi_send_timeout 600;
fastcgi_read_timeout 600;
fastcgi_buffer_size 256k;
fastcgi_buffers 16 256k;   # 16*256,可以继续调大
fastcgi_busy_buffers_size 512k;
fastcgi_temp_file_write_size 512k;

关于fastcgi buffer相关的日志,可以参考这篇
3、终级解决方案: More

work tools – ip log analysis and record

收录一个工作中使用的日志分析脚本,使用python写成,简单明了,直接上代码:

#! /usr/bin/env python
# -*- coding: utf8

import MySQLdb
import datetime
import sys, time
import traceback
import devintool as t
from threading import Thread
import threading
import Queue as q
THREAD_NUM = 100
BATCH_SIZE = 1000

clients=[]

class Conn():
    def __init__(self, host, port, db="fb", user="", passwd="", charset="utf8"):
        self.host=host
        self.port=port
        self.db=db
        self.user=user
        self.passwd=passwd
        self.charset=charset
        self.is_open=False
        self.__conn = None

    def open(self):
        self.__conn = MySQLdb.connect(host=self.host, port=self.port, user=self.user,
            passwd=self.passwd, db=self.db,  charset=self.charset)
        self.is_open=True
        return self.__conn

    def close(self):
        self.__conn.close()
        self.is_open=False

    def cursor(self):
        return self.__conn.cursor()

stdoutMutex = threading.Lock()
class mysqlThread(Thread):
    #def __init__(self, host1, port1, db1, host2, port2, db2, queue):
    def __init__(self, host1, port1, db1, queue):
        Thread.__init__(self)
        self.queue=queue
        self.conn1=Conn(host=host1,port=port1,db=db1)
        self.cursor1=self.conn1.open().cursor()
        self.cursor1.execute('set autocommit=1')
        '''
	self.cursor2=self.conn2.open().cursor()
	self.conn2=Conn(host=host2,port=port2,db=db2)
        self.cursor2.execute('set autocommit=1')
	'''
        self._stop = threading.Event()


    def run(self):
        print "run"
        while True:
	    #try:
                sql = self.queue.get()
                if not self.cursor1:
                    self.cursor1 = self.conn1.open().cursor()
                    self.cursor1.execute('set autocommit=1')
                #if not self.cursor2:
                #    self.cursor2 = self.conn2.open().cursor()
                #    self.cursor2.execute('set autocommit=1')
                self.cursor1.execute(sql)
                #self.cursor2.execute(sql)
                self.queue.task_done()
                '''
          commit_cnt+=1
          print commit_cnt
          if commit_cnt%10000==0:
                stdoutMutex.acquire()
                print datetime.datetime.now()
                print commit_cnt 
                stdoutMutex.release()
                '''
            #except Exception,e:
	    #	print e
class iplogThread(Thread):
    def __init__(self, queue, date):
         Thread.__init__(self)
         self.queue = queue
         self.date = date
	 self.conn = Conn(host="log1.kdb.d.kaixin.com",port=3306,db="fb")
         self.cursor = self.conn.open().cursor()

    def run(self):
        user_cnt=0        
        id_begin=0
        while(1):
            print datetime.datetime.now()
            user_set = self.get_user_ids(self.cursor, id_begin, BATCH_SIZE) 
            if len(user_set)==0:
               PUT_DONE = 1
               break
               
            user_cnt+=len(user_set)
            print id_begin
            id_begin=int(user_set[-1][0])
            self.__handle(user_set) 
            print "%d, %d"%(len(user_set), user_cnt)
            print datetime.datetime.now()
            if len(user_set)=0 and user[1]<=3) and [str(user[1])] or ["_"])[0]
           locals()["user_set_type%s" % (type,)].append(user[0])

       for type in ("0", "1", "3", "_", "4", "__"):
           listName = "user_set_type%s" % (type,)
	   if len(locals()[listName])>0:
               str1 = ",".join("(%d, 1<<%d, 1<<%d)"%(id, self.date.day-1, self.date.day-1) for id in locals()[listName])
               str2 = ",".join("%d"%id for id in locals()[listName])
               str1 = str1.rstrip(',')
               str2 = str2.rstrip(',')
               sql = "insert ignore into user_login_%s (id, login_days, %s) values %s" %(self.date.strftime('%Y%m'),keyMap[listName],str1)
               #print sql
               self.queue.put(sql)
               #bit record log days
               sql = "update user_login_%s set login_days=login_days|1<<%d, %s=%s|1<<%d where id in (%s)"%(self.date.strftime('%Y%m'), (self.date.day-1), keyMap[listName], keyMap[listName], (self.date.day-1), str2)
               #print sql
               self.queue.put(sql)
	   else:
	       print "len=0:", keyMap[listName]
       
       '''
       #deal with Other
       str1 = ",".join("(%d, 1<<%d)"%(id, self.date.day-1) for id in user_set_typeOther)
       str2 = ",".join("%d"%id for id in user_set_typeOther)
       str1 = str1.rstrip(',')
       str2 = str2.rstrip(',')
       sql = "insert ignore into user_login_%s (id, login_days) values %s" %(self.date.strftime('%Y%m'), str1)
       self.queue.put(sql)
       sql = "update user_login_%s set login_days=login_days|1<<%d where id in (%s)"%(self.date.strftime('%Y%m'), (self.date.day-1), str2)
       self.queue.put(sql)
       '''
           
    def get_user_ids(self, cursor, id_begin=0, limit=10000):
        sql="select id, type from iplog_typeid_%s where id>%d order by id limit %d" % (self.date.strftime("%Y%m%d"), id_begin, limit)
        return t.get_set_cur(sql, cursor)

def getClients():
    sql = "select app_id from ClientDefine;"
    cursor = MySQLdb.connect(host="10.22.241.148", user="xiaoneilogs", passwd="Pycd8452", port=3306, db="xnstat", charset="utf8").cursor()
    rs = t.get_set_cur(sql, cursor)
    return [l[0] for l in rs]
         
if __name__ == "__main__":
    #try:
        if len(sys.argv)>1:
            date_str = sys.argv[1]
            print date_str
            date = datetime.datetime(int(date_str.split('-')[0]),int(date_str.split('-')[1]),int(date_str.split('-')[2]) )
            print date
        #x = raw_input('ok')
        else:
  	    date = (datetime.datetime.today() - datetime.timedelta(days=1)).date()
	print date

	clients=getClients()
	print "clients:n", clients

	conn1 = Conn(host="10.22.200.224", db="ugc_xn", port=3306)
	conn2 = Conn(host="123.125.45.215", db="user_login", port=3306)
	#for conn in (conn1,conn2):
	for conn in (conn1, ):
            sql = "create table if not exists user_login_%s like user_login; "%date.strftime('%Y%m') 
            print sql
            conn.open()
            conn.cursor().execute(sql)
            conn.close()
        queue =  q.Queue(THREAD_NUM)
        for i in range(THREAD_NUM):
            #th = mysqlThread(host1='10.22.200.224', port1=3306, db1='ugc_xn', host2='123.125.45.215',port2=3306, db2='user_login', queue=queue)
	    
    	    th = mysqlThread(host1='10.22.200.224', port1=3306, db1='ugc_xn', queue=queue)
            th.setDaemon(True)
            th.start()
        iplog = iplogThread(queue, date)
        iplog.start()    
        queue.join()
    #except Exception,e:
    #	print "Exception happened : ",e
    #	t.putout_except(e,os.path.dirname(os.path.realpath(__file__)))