对于一个新闻发布系统,一篇新闻的正文储存有很多种方式,最常见的就是随着标题等信息储存在关系型数据库中,如mysql;另一种方案是使用nosql存储,如mongodb;还有一种简单粗暴的办法就是直接吧文章正文存入一文件的形式存进磁盘。

本文主要测试mongodb和直接存文件的性能。
大约有5万篇文章,平均大小约为50kb。
测试机 Ubuntu 14.04 8核8G内存,5400RPM机械硬盘

测试过程也简单粗暴,随机读取1万个记录,然后输出文章长度,最后计算运行时间。
使用python和php两种语言进行测试。
mongodb的集合带有索引。

python版

import time
import random
import pymongo

print 'filedatabase ...'
start = time.time();
for i in range(10000):
    id = random.randint(0,50000)

    filename = "article/infotext/%d/%d.html" % (id % 100, id)
    try:
        fp = open(filename)
        content = fp.read()
        print len(content),
    except:
        pass
end = time.time();

print "filedatabase\n", end-start


print 'mongo ...'

start = time.time();
for i in range(10000):
    id = random.randint(0,50000)

    client = pymongo.MongoClient("localhost", 27017)
    db = client.files
    doc = db.article.find_one({'_id': id})
    if doc:
        print len(doc['content']),

end = time.time();

print "mongo\n", end-start

php版

<?php 

print "filedatabase ...\n";
$start = microtime(true);
for ($i=0; $i<10000; $i++)
{
    $id = rand(0,50000);

    $filename = sprintf("xjh/infotext/%d/%d.html", $id % 100, $id);
    $content = @file_get_contents($filename);
    print strlen($content).' ';
}

$end = microtime(true);

print "filedatabase\n". ($end-$start) . "\n";

print "mongo ...\n";

$start = microtime(true);
for ($i=0; $i<10000; $i++)
{
    $id = rand(0,50000);

    $mongo = new MongoClient();
    $db = $mongo->files;
    $collection = $db->article;
    $cursor = $collection->findOne(array('_id'=>$id));
    print strlen($cursor['content']).' ';
}
$end = microtime(true);

print "mongo\n". ($end-$start) . "\n";

测试前压缩一下内存

echo 1 > /proc/sys/vm/drop_caches

下面是测试结果:

python

cf@cf-Ubuntu:~/tmp$ python filedatabasevsmongo.py 
filedatabase ...
filedatabase
34.4837019444
mongo ...
mongo
15.6313991547
cf@cf-Ubuntu:~/tmp$ python filedatabasevsmongo.py 
filedatabase ...
filedatabase
27.576843977
mongo ...
mongo
15.4162368774

php

cf@cf-Ubuntu:~/tmp$ php filedatavasevsmongo.php
filedatabase ...
filedatabase
35.346391916275
mongo ...
mongo
0.85509300231934
cf@cf-Ubuntu:~/tmp$ php filedatavasevsmongo.php
filedatabase ...
filedatabase
29.030472993851
mongo ...
mongo
0.83656191825867

对结果进行一定的分析
1运行同一个程序,第二次运行笔第一次运行,在直接读文件上有速度的提升,是因为操作系统会把常用文件缓存在内存中,再次读取时,不必在硬盘里读取。
2php和python在直接读文件方面速度相当python略快
3python的mongo读操作笔php对mongo的读操作要缓慢很多,对于这个现象,只能怀疑是pymongo这个库的效率问题了。
4当反复运行测试程序,基本上会把所有文件缓存进内存,到最后测试结果显示,mongo的性能不如直接读文件。

cf@cf-Ubuntu:~/tmp$ python filedatabasevsmongo.py 
filedatabase ...
filedatabase
0.186345100403
mongo ...
mongo
14.8158619404
cf@cf-Ubuntu:~/tmp$ php filedatavasevsmongo.php
filedatabase ...
filedatabase
0.15131092071533
mongo ...
mongo
0.85830688476562

本文未对并发性能进行测试……
EOF

最近推出了swift语言,使得ios的开发成为热门,于是也想了解一下相关方面的东西。
折腾的第一步就是要整一个Mac OS出来,我的计算机是i7第四代CPU,折腾起来费了不少功夫。

主要参考这个帖子http://wenzhixin.net.cn/2014/02/10/vbox_mac_install

我的机器是Lenovo Y410P 美版 系统是 Ubuntu 14.04LTS

所需要的资料在这个里面http://pan.baidu.com/s/1hqebqYw
在安装过程中遇到了一些问题,如下:

1 我本来是vbox4.3.10,安装过程中貌似出了点小问题
No interval found for Using 8000000
于是降级为4.3.6.
去这里找到需要的安装包,下下来还挺快的。

安装完之后,建立一个虚拟机,按照原帖的步骤来。

2 遇到一个比较常见的问题
hfs:could not initialize summary table for OS X Base System
博主也给了回答,说mac暂时不支持haskwell 架构CPU,要改CPUID。
于是我就查了一下其他CPU的CPUID,但是不知道换哪个型号的,就按楼主说的来吧。

vboxmanage modifyvm Mac --cpuidset 1 000306A9 02100800 7FBAE3FF BFEBFBFF

这里要说一下,原帖的命令提示符是“#”,于是我在运行的时候加了sudo,结果提示我找不到虚拟机,我以为虚拟机名字“Mac”填错了呢,我添了绝对路径,还是找不到。
网上还有说法要注册一下这个虚拟机

vboxmanage registervm /home/user/VirtualBox\ VMs/Mac/Mac.vbox

结果告诉我which has the same UUID as an existing virtual machine
最后前面不加sudo,就修改了成功了。

3遇到一个常见问题
missing Bluetooth controller transport
帖子里也有说法,但是我觉得说的不详细。
关机,选择光盘镜像“Hackboot_Mav.iso”。然后开机(不是重启)。
进入图形界面后,换光盘,选择“OSX Mavericks2.iso”,按下F5,然后按下“下箭头”按键,选择不要缓存的那个选项“Ignore Cache”,按回车。

之后的都比较顺利,但是安装内核扩展的时候,貌似不能粘贴到虚拟机里,所以手敲的比较累。
有位同学说了,安装内核复制之后,要修改属主,我就修改了属主。

chown -R root:wheel /Volumes/mnt/System/Library/Extensions

如果不加上这句话,会在系统里面报错,说XX失败。

其实还有问题,是电源管理什么的,参看了帖子里的回复。

cd /Volumes/mnt/System/Library/Extensions
mkdir intel_back
mv AppleIntelHD* AppleIntelF* intel_back/
touch ../Extensions

然后就可以了的,只不过启动好慢。。。

接下来是安装Xcode。。。。。。
把光驱里的磁盘换成Xcode.dmg 就会自动弹出一个界面,根据提示安装即可。

最近看很多人研究语音接口,于是我也来一发。

很早之前想做一个路由器说话的项目,我的路由器是一台X86架构的小型主机,带有音频输出接口,在装系统的时候驱动都装的很全,插上耳机即可放出音乐。

命令行下播放音乐是用mplayer

mplayer filename.mp3

即可。

接下来要做一个文字转语音的接口,text to speech,简称TTS。网上有很多,python也很容易安装,但是只有英文的。
中文语音处理做的不错的应该是科大讯飞了,科大讯飞也有自己的开放平台,上去注册一个帐号,下载一个开发工具。
工具里有appkey,貌似每个开发者下载的开发包都不一样。

打开Example文件夹下面的ttsdemo,make一下,然后有个可执行文件。执行可执行文件,会生成一个pcm文件,使用mplayer即可播放这个文件,发出声音。

下面把ttsdemo.c这个文件改一下:
改成不生成文件,而是直接从标准输出中输出音频流。注意apppkey要填写自己的。


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include "include/qtts.h"
#include "include/msp_cmn.h"
#include "include/msp_errors.h"


typedef int SR_DWORD;
typedef short int SR_WORD ;

//音频头部格式
struct wave_pcm_hdr
{
        char            riff[4];                        // = "RIFF"
        SR_DWORD        size_8;                         // = FileSize - 8
        char            wave[4];                        // = "WAVE"
        char            fmt[4];                         // = "fmt "
        SR_DWORD        dwFmtSize;                      // = 下一个结构体的大小 : 16

        SR_WORD         format_tag;              // = PCM : 1
        SR_WORD         channels;                       // = 通道数 : 1
        SR_DWORD        samples_per_sec;        // = 采样率 : 8000 | 6000 | 11025 | 16000
        SR_DWORD        avg_bytes_per_sec;      // = 每秒字节数 : dwSamplesPerSec * wBitsPerSample / 8
        SR_WORD         block_align;            // = 每采样点字节数 : wBitsPerSample / 8
        SR_WORD         bits_per_sample;         // = 量化比特数: 8 | 16

        char            data[4];                        // = "data";
        SR_DWORD        data_size;                // = 纯数据长度 : FileSize - 44
} ;

//默认音频头部数据
struct wave_pcm_hdr default_pcmwavhdr =
{
        { 'R', 'I', 'F', 'F' },
        0,
        {'W', 'A', 'V', 'E'},
        {'f', 'm', 't', ' '},
        16,
        1,
        1,
        16000,
        32000,
        2,
        16,
        {'d', 'a', 't', 'a'},
        0
};

int text_to_speech(const char* src_text ,const char* params)
{
        struct wave_pcm_hdr pcmwavhdr = default_pcmwavhdr;
        const char* sess_id = NULL;
        int ret = 0;
        unsigned int text_len = 0;
        char* audio_data;
        unsigned int audio_len = 0;
        int synth_status = 1;
        FILE* fp = NULL;

        printf("begin to synth...\n");
        if (NULL == src_text)
        {
                printf("params is null!\n");
                return -1;
        }
        text_len = (unsigned int)strlen(src_text);
        fp = stdout;
        if (NULL == fp)
        {
                printf("open stdout error\n");
                return -1;
        }
        sess_id = QTTSSessionBegin(params, &ret);
        if ( ret != MSP_SUCCESS )
        {
                printf("QTTSSessionBegin: qtts begin session failed Error code %d.\n",ret);
                return ret;
        }

        ret = QTTSTextPut(sess_id, src_text, text_len, NULL );
        if ( ret != MSP_SUCCESS )
        {
                printf("QTTSTextPut: qtts put text failed Error code %d.\n",ret);
                QTTSSessionEnd(sess_id, "TextPutError");
                return ret;
        }
        fwrite(&pcmwavhdr, sizeof(pcmwavhdr) ,1, fp);
        while (1)
        {
                const void *data = QTTSAudioGet(sess_id, &audio_len, &synth_status, &ret);
                if (NULL != data)
                {
                   fwrite(data, audio_len, 1, fp);
                   pcmwavhdr.data_size += audio_len;//修正pcm数据的大小
                }
                if (synth_status == 2 || ret != 0)
                break;
        }

        //修正pcm文件头数据的大小
        pcmwavhdr.size_8 += pcmwavhdr.data_size + 36;

        //将修正过的数据写回文件头部
        fseek(fp, 4, 0);
        fwrite(&pcmwavhdr.size_8,sizeof(pcmwavhdr.size_8), 1, fp);
        fseek(fp, 40, 0);
        fwrite(&pcmwavhdr.data_size,sizeof(pcmwavhdr.data_size), 1, fp);
        fclose(fp);

        ret = QTTSSessionEnd(sess_id, NULL);
        if ( ret != MSP_SUCCESS )
        {
        printf("QTTSSessionEnd: qtts end failed Error code %d.\n",ret);
        }
        return ret;
}

int main(int argc, char* argv[])
{
        ///APPID请勿随意改动
        const char* login_configs = " appid = xxxxxxxxx, work_dir =   .  ";
        const char* text  = argv[1];
        const char* param = "aue = speex-wb;3, vcn=xiaoyan,  spd = 50, vol = 50, tte = utf8";
        int ret = 0;
        char key = 0;

        //用户登录
        ret = MSPLogin(NULL, NULL, login_configs);
        if ( ret != MSP_SUCCESS )
        {
                printf("MSPLogin failed , Error code %d.\n",ret);
        }
        //音频合成
        ret = text_to_speech(text,param);
        if ( ret != MSP_SUCCESS )
        {
                printf("text_to_speech: failed , Error code %d.\n",ret);
        }
        //退出登录
        MSPLogout();
        return 0;
}

编译一下,改个名叫做say_stdout。say_stdout程序会把命令行参数转换为音频流,从标准输出中输出,接下来是把音频流播放出来。

mplayer具有播放音频流的功能
写一个shell程序,命名为say

#/bin/sh
say_stdout $1 | mplayer -cache 8192 - > /dev/null 2>&1

接下来使用say命令就能让路由器说话了。

say "我会讲话了!"

嘿嘿,还可以调节语速和音量参数。

路由器能说话之后,我让他来个正点报时的功能。
在crontab中增加一行

0 * * * * say "现在时刻`date +%I`点整" | mplayer -cache 8192 - > /dev/null 2>&1

完工!
参看http://open.voicecloud.cn/

======================================

忘了说了,会说话只让他正点报时肯定不爽,于是我又开发了一个自定义内容的说话接口,是一个python 写的 cgi程序。
因为cgi程序运行时不是root用户所以不能发出声音,具体原因我也不清楚,至少是不能通过cgi直接来发音的。
于是我做了一个客户端和一个服务器端,客户端是cgi程序,以www用户身份执行,服务器端是开机启动,并且一直在后台运行,两者之间通过unix的套接字通信。
cgi端(客户端)

#!/usr/bin/python
# coding:utf8
print "Content-type: text/html"
print
import cgi,socket,sys
try:
    form = cgi.FieldStorage()
    content = form.getvalue('saywhat')
except:
    content = ''

if content:
    content = content.replace(' ','_')
    content = content.replace('\t','_')
    content = content.replace('\r','_')
    content = content.replace('\n','_')
    try:
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        sock.connect("/tmp/say.sock")
        sock.send(content)
        print sock.recv(1024)
        sock.close()
    except:
        print "Error",sys.exc_type

print "<html>"
print "<head><title>路由器说话</title><meta charset='UTF-8'></head>"
print "<body>"
print "said:", content
print "<form method='post' action=''>"
print "    <input type='text' name='saywhat' />"
print "    <input type='submit' value='SAY' />"
print "</form>"
print "</body>"
print "</html>"

服务器端

#!/usr/bin/python
import socket,os,commands,stat

def main():
    sockfile="/tmp/say.sock"
    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    if os.path.exists(sockfile):
        os.unlink(sockfile)
    sock.bind(sockfile)
    sock.listen(5)
    os.chmod(sockfile,stat.S_IRWXU|stat.S_IRWXG|stat.S_IRWXO)
    while True:
        connection,address = sock.accept()
        content = connection.recv(1024)
        if content:
            print 'say "%s"' % content
            (rc,rs) = commands.getstatusoutput('say ' + content)
            connection.send(rs)
        connection.close()

if __name__ == "__main__":
    main()

目前即可通过网页来命令路由器讲话了。

今天是周一,即将面临新一轮挑战。

上午看一会书,过会听说今天的502已经9%了,我就调了若干参数,把nginx 的worker prosess 调成8 了,php-fpm 调到250,接着502飙到了20%,资源监视窗更是变成了条形码。

据一个大牛说,要开启连接池,就是mysql的优化。
一个叫sqlrelay的东西,百度了一下。

参看了http://hi.baidu.com/mrvsumbeujeinxe/item/8ba0c60888cf47c490571896

wget在sourseforge下载了rudiments和sqlrelay。
解压了,./configure –prefix=XXX,按照帖子上面说的做了,结果运行
sqlr-start -id mysqltest
被提示Unable to load dynamic library ‘/usr/local/php/lib/php/extensions/no-debug-non-zts-20060613/sql_relay.so’
仔细检查,发现拼错了–with-php-prefix=/usq/local/php,囧
重新make make install,结果这个错误解决了。

再次运行sqlr-start -id mysqltest被提示
sqlr-listener error:Unable to create a shared memory segment.
搜索找不到
重启php-fpm,报错shmget() failed:  Invalid argument
觉得应该是shm共享内存的错误。
这下可以搜索到,http://blog.sina.com.cn/s/blog_9a4d43d801014avs.html
sysctl -a| grep shmm 居然发现 shmmax是0,什么情况!!
vi /etc/sysctl.conf 了,发现kernel.shmmax=4294967296,觉得是有些大了,就为0 了,
尝试设置为32M,重启reboot,经过漫长等待…………………………

擦,还得不行,只不过报的错不同了:shmget() failed:  No space left on device
搜索之,发现还有一个shmall的东西,应该和shmmax设置的一样。
再次重启,ok。。。

这是原来的配置文件
# Controls the maximum shared segment size, in bytes
kernel.shmmax = 4294967295
# Controls the maximum number of shared memory segments, in pages
kernel.shmall = 268435456

kernel.shmmax 数值说明:单位:字节。一般建议使用物理内存的一半
kernel.shmall 数值说明:单位:页。1页=4k,设置数值则为物理内存大小
以4G内存为例:4096*1024*1024/4000=1073742
注:以上两项数值如果填写大于本身物理内存则会不生效

看了这个文章http://www.xmydlinux.org/?p=366
觉得我们的kernel.shmmax=1073741824;kernel.shmall=262144

大功告成,502很少了!!共享内存也解决了。

主要是kernel.shmmax=1073741824;kernel.shmall=262144 这两个参数的设置问题。

终于找了个有兴致的时间折腾一下路由器了。。。
话说,路由器已经到手快三个月了,一直没派上用场,之前是因为6月份考试多,没闲心搞这些东西,9月份应该有闲心吧。

三个月过去了,ROM应该也有新的了吧,上openwrt官网,找了个最新了rom,随便在网上找了一个刷机教程:
按住reset开机,灯变红,改ip,连路由器,传rom,等待……红灯熄灭。
大功告成。

新刷的系统没有开启无线,开启即可。
然后把lan4(wan)接上电信网,快速配置,DB120,DHCP,OK!
无线连接上路由器,可以上网了!

路由器一大把功能:
开通了Transmission,脱机下载……
还有QoS,方便给蹭网的童鞋限一下速……
pppoe server 自己做ISP……
ftp……
很多我也不知道干嘛的。

看了教程,装了lighttpd+php5+sqlite,从此,路由器也可以跑php了。
找教程的时候,不小心攻入了站长大人的路由器中,不干坏事……嘿嘿

搞了大半天,不过还是不能用mentohust连上宿舍这边的教育网,以后有兴致了再研究……

马老板要毕业了,留下来一个项目没有完成。我接了他的活,是一个thinkPHP框架下的内容管理系统。
我在59上用webmin的“文件管理”功能压包下载了下来,却在windows下不能用,难道要修改一个文件,传一个文件那样调试?
——no。

之前cyy搞了个pjchat,用的sae,代码管理里面有个程序不错,可以再浏览器里修改代码,还是高亮的。
果断进去看看,叫ecoder。下载之……

下载、安装完毕,打开配置文件,配置文件是个最大的学问。不是简单的localhost;root;XXX就行的。非安全模式还好配置,安全模式至今没搞明白。
想想觉得前辈们挺伟大的,神马firebug,phpmyadmin,ecoder,都给开发者带来很大的便利。

wordpress真的很强大。很多内置的函数,
is_home() : 判断是否为主页
is_single() : 判断是否为日志页
is_page() : 判断是否为页面类型(page)
is_category() : 是否为Category/Archive页
is_tag() : 是否为Tag存档页
is_date() : 是否为指定日期存档页
is_year() : 是否为指定年份存档页
is_month() : 是否为指定月份存档页
is_day() : 是否为指定日存档页
is_time() : 是否为指定时间存档页
is_archive() : 是否为存档页
is_search() : 是否为搜索结果页
is_404() : 是否为 “HTTP 404: Not Found” 错误页
is_paged() : 主页/Category/Archive页是否以多页显示

: 博客名称(Title)
: CSS文件路径
: PingBack Url
: 模板文件路径
: WordPress版本
: Atom Url
: RSS 2.o Url
: 博客 Url
: 博客网页Html类型
: 博客网页编码
: 博客描述
: 特定内容页(Post/Page)的标题
: 调用Header模板,加载header.php文件。
: 调用Sidebar模板,加载sidebar.php文件。
: 调用Footer模板,加载footer.php。

有空继续研究!

搞了一周的ecshop模版,最终因为任务量太大而选择另外一个方法来实现。

这一周主要是改html,添加少部分css。

另外一种方法应该是最大程度上和原来的html保持一致,css多改点没问题。

escshop模版比较复杂,最底层是缓存文件,接着是编译后的文件,如果两者都不存在,系统产生新的页面,在theme里有很多dwt,库文件夹里还有很多lbi,不过,还有在includes/lib_insert.php里面的。

经过了一上午的更改,解决了很多问题,发现,一上午的工作比过去一周的工作量更高一些。

最后希望商城可以快点上线。