8月 16

go 操作redis ,对比python 操作redis

  之前做过python入库到redis的测试 http://www.simonzhang.net/?p=430
  今天才发现由于页面生成问题,有部分代码被转译了。借这个机会在同一台机器上再测试一下这两种语言。

  测试服务器为一个Xeon(TM)双核3.20GHz 的cpu,内存4G。操作系统为Centos5.4 64位。go1.0.2,python2.6

  go环境和redis之前已经有记录,go语言的redis开发包安装也比较简单,但是官方文档中目录有写错。
  我的go放在/usr/local/go,我的安装记录如下:
cd /usr/local/go/src/pkg
git clone git://github.com/alphazero/Go-Redis.git redis
cd redis
go install

仍旧用大密码表实验,go语言代码如下:

//www.simonzhang.net
package main 

import (
	"os";
	"log";
	"fmt";
	"redis";
        "encoding/hex"
        "crypto/md5"
)

func main () {
	spec := redis.DefaultSpec().Db(1).Password("");
        //如要操作远程redis服务器连接如下
	//spec := redis.DefaultSpec().Host("192.168.1.200").Db(1).Password("123");
	client, e := redis.NewSynchClientWithSpec (spec);
	if e != nil { log.Println ("failed to create the client", e); return }
        var str string
        userFile := "big_pass.txt"
        fin,err := os.Open(userFile)
        defer fin.Close()
        if err != nil {
                fmt.Println(userFile,err)
                return
        }
        buf := make([]byte, 1)
        for{
                n,_:= fin.Read(buf)
                if 0==n {break}
                if string(buf)=="\n"{
	            h:= md5.New()
   	            h.Write([]byte(str))
                    key:=hex.EncodeToString(h.Sum(nil));
                    value:=[]byte(str)
                    client.Set(key, value);
                    str=""
                 }else{
                    str+=string(buf)
                 }
                  
        }
}

开始测试

go使用build编译运行,cpu使用率在55%左右,load average: 1.98, 1.22, 0.91
real 1m33.310s
user 0m42.586s
sys 0m34.567s

python cpu使用在在80%,load average: 2.19, 1.36, 0.86。
real 1m26.873s
user 0m56.316s
sys 0m19.595s

  总结python写redis的东西比较简单。go对字符串做了MD5,入redis时用MD5值做key。
a)都运行完毕python和go录入redis行数与文本行数相同。
b)从大小来看python不到1K,go编译完成2.3M。如果要一处编译,移到其他服务器运行,go编译完的可以直接运行,python还要再装环境。但是有easy_install,所以装环境对我来说比较简单。
c) cpu使用量来看go要比python好一些,在增加md5计算的情况下所用系统资源也比pyhton少,但是应该快不了一倍。
d) 从代码编写的逻辑和难易程度来看,还是python简单一点。
如果要考虑速度可以试试Cpython,我没有试过。

7月 19

python邮件内容格式问题

发送邮件,内容需要是html格式,content为邮件内容,我使用的都是utf-8编码。实现如下:

引用
from email.mime.text import MIMEText

以网页方式发送
msg = MIMEText(content.encode(‘utf8′), _subtype=’html’, _charset=’utf8′)

以纯文本方式发送
msg = MIMEText(content.encode(‘utf8′), _subtype=’plain’, _charset=’utf8′)

默认以纯文本方式发送

6月 25

如果我做双色球报表系统性能会如何

  双色球有人中了5.7亿,各种怀疑的声音也此起彼伏。有人说停售到开奖直播中间有一段时间是在算出现概率。我对是否在算并不感兴趣,我倒是想知道,如果在算要用什么样子的服务器

,会有多大压力。实际值2012072期销售349310588元,共174655294注。我用一台很老的dell 2850 来试验,双核3.2主频的单cpu,4G内存,ide接口硬盘。 Centos 5.4 64位系统,mysql5。

  写个脚本产生随机数来模拟彩票投注。根据投注号产生MD5码,取MD5的前两位作为分表依据。
  需要速度,所以开始用C来作,因学艺不精,C写的东西速度很慢,并且也没有找到原因,我将c的代码放在最后,如有朋友能帮忙解决烦请告知。最后还是用python来做。通过初步试验,

产生100万数据并入库需要3分多钟,如果产生1.8亿需要12个多小时。最后决定只产生一张表的数据,一张表数据100万。取MD5前两位,如果每张表都是100万,总共能产生2.56亿注,这个值

可以满足测试要求。

  产生表的SQL如下:

CREATE TABLE `dt_00` ( \
             `Id` int(11) NOT NULL AUTO_INCREMENT, \
             `CreateUnixTime` varchar(13) NOT NULL, \
             `RedBlue` varchar(21) DEFAULT NULL, \
             `HashValues` varchar(32) DEFAULT NULL, \
             `SaleId` int(11) DEFAULT NULL, \
             PRIMARY KEY (`Id`), \
             KEY `i_RedBlue` (`RedBlue`), \
             KEY `i_HashValues` (`HashValues`) \
             ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  产生投注的脚本如下,因为开始考虑到要放到256张表里,所以就没有做成批量入库,而是做成生成SQL语句,5000条提交一次。:

#!/bin/env python
# ----------------------------------------
# Filename:    rand.py
# Revision:    1.0
# Date:        2012-06-24
# Web:        http://www.simonzhang.net
# Author:      simonzhang
# Email:       simon-zzm@163.com
# ----------------------------------------
import random
import time
import MySQLdb
import hashlib


def mysql_run(sql):
    try:
        db = MySQLdb.connect(host = '192.168.1.41', user = 'double', passwd = 'ball', db = 'doubleball')
        cursor = db.cursor()
    except MySQLdb.Error, e:
        print "Error %d:%s" % (e.args[0], e.args[1])
        exit(1)
    try:
        result_set = ''
        cursor.execute('%s' % sql)
        result_set = cursor.fetchall()
        cursor.close()
        db.close()
        return  result_set
    except MySQLdb.Error, e:
        print "Error %d:%s" % (e.args[0], e.args[1])
        cursor.close()
        db.close()


def create_sql():
    z = 0
    _tmp_di = []
    while (z < 6):
        red = random.randint(1, 33)
        count_rand_di = 0
        if len(_tmp_di) > 0:
            for i in _tmp_di:
                if red == i:
                    count_rand_di += 1
        if count_rand_di == 0:
            _tmp_di.append(red)
            z += 1
    _tmp_di.sort()
    blue = random.randint(1, 16)
    _result_di = ''
    for j in xrange(6):
        if j == 1:
            _result_di = "%s" % _tmp_di[j]
        else:
            _result_di += " %s" % _tmp_di[j]
    _get_md5 = hashlib.md5("%s+%s" % (_result_di, blue)).hexdigest()
    if _get_md5[0:2] == "00":
       _create_sql = "INSERT INTO  dt_00(CreateUnixTime,RedBlue,HashValues) VALUES (\'%s\',\'%s+%s\',\'%s\');" % (int(time.time()), _result_di, blue, _get_md5)
    else:
       _create_sql = ""
    return _create_sql


def write_time():
    _open_file = open("history.txt", "ab")
    get_time = time.localtime()
    _open_file.write("%s-%s-%s %s:%s:%s\n" % (get_time[0], get_time[1], get_time[2], get_time[3], get_time[4], get_time[5]))
    _open_file.close()


def main():
    _get_sql = ''
    for i in xrange(1000000):
        _loop_status = 0
        while _loop_status == 0:
            _tmp_sql = create_sql()
            if len(_tmp_sql) >2:
                _loop_status = 1
        _get_sql += _tmp_sql
        if i % 5000  == 0:
            print i
            mysql_run("%scommit;" % _get_sql)
            _get_sql = ''


if __name__ == "__main__":
    write_time()
    main()
    write_time()

  运行完毕使用sql分组统计,查看分组和排序结果SQL如下,运行一次用时6.693秒。

SELECT
	count(*) AS c,
	RedBlue,
	HashValues
FROM
	dt_00 d
GROUP BY
	d.HashValues
ORDER BY
	c DESC

  结论:因为要保证速度和稳定,所以购买系统应该是各省有各省的一套系统,然后准实时向中心汇数据,所以汇总需要的时间基本可以忽略。统计是按照分表的方式进行计算,然后在合

并结果,从计算效率来看,如果使用Dell R420高配的服务器,即使只有一台,系统也可以在3-5分钟产生出所有的分析报表(这个是估算,因为不知道需要多少统计表,也不知道需要统计的

逻辑)。如果是用oracle数据库应该会更安全快速。

  我写失败的C代码:

#include 
#include 
#include 

/*快速排序*/
void quickSort(int a[],int left,int right)
{
  int i,j,temp;
  i=left;
  j=right;
  temp=a[left];
  if (left>right)
    return;
  while(i!=j)
  {
    while(a[j]>=temp && j>i)
      j--;
    if(j>i)
      a[i++]=a[j];
    while (a[i]<=temp && j>i)
      i++;
    if(j>i)
      a[j--]=a[i];
  }
  a[i]=temp;
  quickSort(a,left,i-1);
  quickSort(a,i+1,right);
}

void main()
{
   int i,z;
   int red,blue;
   int count_rand_di;
   int di[6];
   char result_di[30]="";
   z=0;
   while(z<6)
   {
      srand(time(0)); 
      red=1+(int)(33.0*rand()/(RAND_MAX+1.0));
      count_rand_di=0;
      for(i=0;i

  编译命令:
gcc rand.c -o rand -Wall

参考资料:

http://baike.baidu.com/view/19016.htm

6月 20

nginx 自动封 ip 过高连接

  用命令查看web连接过高的IP地址,但是需要人工智能去封,太麻烦了,直接写个脚本自动解决。web服务器是用nginx,python为2.6
  首先在nignx的config中建立空文件deny.ip, 然后在nginx.conf 的http标签中添加“include deny.ip;”。在nginx下sbin的目录中放入自动脚本。脚本可以查到连接最大的IP,并插入屏蔽列表中,验证正确性后导入配置。全部完成或者出错后发送邮件。被封ip再次访问会报403错误,如果不希望报错可以跳转到其它页面。源码如下:

check_deny_up.py

#!/bin/python
#-*- coding:utf-8 -*-
# Filename:    main.py
# Revision:    1.0
# Date:        2012-06-20
# Author:      simonzhang
# web:         www.simonzhang.net
# Email:       simon-zzm@163.com
### END INIT INFO
import os
from string import strip
from email.mime.text import MIMEText
import smtplib

####
check_comm = "/bin/netstat -antp|grep :80|awk ' ''{print $5}'|awk -F: '{print $1}'|sort -r|uniq -c|sort -n -k1 -r"
max_ip = 100
mail_host = ‘’;
mail_user = ‘’;
mail_pwd = ‘’;
mail_to = ‘’;
mail_cc = ‘’;


def reboot_nginx_sendmail(ip_list):
    #### reboot nginx
    _get_check_confile = os.popen('./nginx -t').readlines()
    if str(_get_check_confile.find('ok')) != '-1':
        os.system('./nginx -s reload')
        _mail_content = ip_list
    else:
        _mail_content = 'Error'
    #### send mail
    msg = MIMEText(_mail_content)
    msg['From'] = mail_user
    msg['Subject'] = ' force ip.'
    msg['To'] = mail_to
    try:
        s = smtplib.SMTP()
        s.connect(mail_host)
        s.login(mail_user, mail_pwd)
        s.sendmail(mail_user, [mail_to, mail_cc], msg.as_string())
        s.close()
    except Exception, e:
        print e


#### force out IP
def force_out(_deny_ip):
    _write_status = 0
    _read_force_file = open('../conf/deny.ip', 'rb').read()
    if  str(_read_force_file.find(_deny_ip)) == '-1':
        try:
            _get_force_file = open('../conf/deny.ip', 'ab')
            _get_force_file.write('deny %s ;\n' % _deny_ip)
            _get_force_file.close()
            _write_status = 1
            return _write_status
        except:
            return _write_status
            reboot_nginx_sendmail("Error !")
    return _write_status


def main():
    get_high_ip = os.popen('%s' % check_comm).readlines()
    _count_force_ip = 0
    _force_ip_list = ''
    for i in xrange(3):
        try:
            _get_count = strip(get_high_ip[i]).split(' ')[0]
            _get_ip = strip(strip(get_high_ip[i]).split(' ')[1])
        except:
            _get_count = 0
            _get_ip = ''
        # Maximum connection IP is Beyond the limit value
        if (int(_get_count) > max_ip) and (len(_get_ip) > 0):
            force_ip = _get_ip
            _get_status = force_out(force_ip)
            # check maximum is added in the deny.ip file
            if str(_get_status) == '1':
                _count_force_ip += 1
                _force_ip_list += ' %s ' % force_ip
#    if _count_force_ip > 0:
#        reboot_nginx_sendmail(_force_ip_list)


if __name__ == '__main__':
    main()

启动i脚本
check_deny_up.sh

#! /bin/bash
#
# make simon-zzm@163.com
#
#
### END INIT INFO

# Source function library.
. /etc/profile
cd /Data/apps/nginx/sbin/

# See how we were called.
case "$1" in
  start)
      /usr/local/bin/python check_ip_deny.py
      ;;
  *)
        echo $"Usage: $0 {start}"
        exit 1
esac

exit

将启动脚本放在crontab中运行。

6月 09

python 统计一段字符串中某字符串出现次数

统计一段字符串中某字符串出现次数。如查询“It takes only a minute to get a crush on someone,an hour to like someone,and a day to love someone- but it takes a lifetime to forget someone”中出现“takes”出现的次数。

str = ‘It takes only a minute to get a crush on someone,an hour to like someone,and a day to love someone- but it takes a lifetime to forget someone’
get_count = count_find_str(str, 'takes')
print get_count

函数如下

def count_find_str(str,find_str):
    _str = str
    _find_str = find_str
    _pos = _str.find(_find_str)
    _find_str_count = 0
    while _pos != -1:
        _find_str_count = _find_str_count + 1
        _pos = _pos + len(_find_str)
        _pos = _str.find(_find_str, _pos)
    return _find_str_count

注意:在单词过短的情况下会出现错误,如统计“a”,除了“a”本身作为单词出现,在单词中的“a”字母也会被统计。所以,建议统计字符串短写的长单词。我是为了统计数据库连接数所写的这个,实际测试还是很好用。