之前安装了redmine,确实功能多、使用简单,但ror架构实在是吃内存,小vps根本hold不住,于是还是选用python写的trac。丰富的插件使trac只要配置得当,功能还是很强大的。

首先安装setuptools。这个可以用apt或yum安装,也是一个类似于apt的包管理器,是针对python的。安装后可使用easy_install命令
然后配置PYTHONPATH,使用easy_install默认是安装到系统路径下的。需要root权限。不推荐使用这种方式,这样会把文件放到用户不可控的位置,为以后的升级备份带来困难。所以就需要--install-dir参数(使用--prefix参数无效,不知为何),但单纯使用该参数会报指定目录不在PYTHONPATH里。这是easy_install会推荐去看一个网页,我看了下,讲的几个方法都很繁琐,也没什么理由。其实只需要export PYTHONPATH=${PYTHONPATH}:your_dir即可。在.bash_profile里设置一下,避免每次都要手动。
这里建议在.bash_profile里设置一下alias easy_install='easy_install --install-dir=your_dir',这样就不用每次安装时都手动输入一大坨地址了。
设置完后source .bash_profile生效一下。
然后开始安装,先执行:easy_install Babel==0.9.5   这个一定要装,否则安装后的trac没有中文。
然后easy_install Trac
ok,trac的安装就完成了。

现在需要建立项目,trac需要为每个项目建立一个实例。这时在your_dir里找到trac-admin,这个是用来管理项目实例的工具。
运行:trac-admin your_proj_dir initenv
会提示项目名和使用的数据源。
在数据源那里我使用官方推荐的:mysql://name:password@localhost:3306/test报错:trac TypeError: unsupported operand type(s) for /: 'int' and 'NoneType'   看了下代码,是数据库没有配成utf8字符集导致的。配了一下,ok了
建立数据库时要使用:CREATE DATABASE IF NOT EXISTS test default charset utf8 COLLATE utf8_general_ci;
建好项目后就可以登陆进行进一步设置了。
首先配置用户具有admin权限:
trac-admin your_proj_dir permission add user TRAC_ADMIN
然后指定使用web auth进行用户验证:
./tracd --port 8000 --auth="*,/your_dir/user.htdigest,trac" /your_dir
user.htdigest文件是用户名密码文件,需要自己生成,比较麻烦,反正也是临时使用,这里贴个成品:

user:trac:fb05f80adf782a74f48a5acdc71dba65
这个的文件名和密码分别是“user”,“password”
启动后进入控制台
进入管理,插件,开启TracAccountManager 0.3.2
修改trac.ini,
[components]下添加trac.web.auth.loginmodule = disabled
然后在account配置里SessionStore选一个1,(为啥不知道),但不开这个就不能注册
然后手动添加管理员账户
然后可以用./tracd --port 8000  /your_dir   启动了。
用permission add给刚才添加的用户加上管理员权限。然后ok了。可以使用web登陆了

然后添加git支持:
easy_install http://github.com/hvr/trac-git-plugin/tarball/master
暂时没找到支持远程git的方法

Tags: ,
    前几天把一个函数的返回值由int改为size_t了。当时心想就是改个类型的问题,逻辑没啥要动的。反正都是算数。
编译器什么也没报。似乎没什么问题。
    后来凑巧又改了一下另外一个程序的相同函数,结果编译的时候报了error,说试图转换-1到unsigned。一检查,果然程序中的异常分支返回了-1.急忙改了过来。
    所以在返回值是size_t类型的函数中,异常处理要注意。(主要是c程序,因为没有异常)
Tags:

为nginx生成自签名ssl证书

[| 不指定 2012/03/03 00:00]
    今天要搭一个ssl加密的站点,由于是自用,所以就准备自签发一张证书。之前搞过几次,比较复杂,早忘光了。找到一篇不错的文章,留下备份。
http://blog.duyao.de/posts/to-generate-an-ssl-certificate-for-nginx-in-linux.html

这里说下Linux 系统怎么通过openssl命令生成 证书。

首先执行如下命令生成一个key

openssl genrsa -des3 -out ssl.key 1024
然后他会要求你输入这个key文件的密码。不推荐输入。因为以后要给nginx使用。每次reload nginx配置时候都要你验证这个PAM密码的。

由于生成时候必须输入密码。你可以输入后 再删掉。

mv ssl.key xxx.key
openssl rsa -in xxx.key -out ssl.key
rm xxx.key
然后根据这个key文件生成证书请求文件

openssl req -new -key ssl.key -out ssl.csr
以上命令生成时候要填很多东西 一个个看着写吧(可以随便,毕竟这是自己生成的证书)

最后根据这2个文件生成crt证书文件

openssl x509 -req -days 365 -in ssl.csr -signkey ssl.key -out ssl.crt
这里365是证书有效期 推荐3650哈哈。这个大家随意。最后使用到的文件是key和crt文件。

如果需要用pfx 可以用以下命令生成

openssl pkcs12 -export -inkey ssl.key -in ssl.crt -out ssl.pfx
在需要使用证书的nginx配置文件的server节点里加入以下配置就可以了。

ssl on;
ssl_certificate /home/ssl.crt;
ssl_certificate_key /home/ssl.key;
ssl_session_timeout 5m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;
然后重启nginx就大功告成了
Tags: ,

strncpy和snprintf

[| 不指定 2012/02/29 19:06]
    之前用strncpy总是感觉比较恶,老是要考虑最后\0的问题,今天仔细看了下,发现如果源串长度大于等于最大长度的话,strncpy会直接拷贝最大长度,不在后面加\0,也就是说在用一个字符串覆盖另一个字符串一部分的时候用strncpy是很不错的,但全覆盖的话比较麻烦,很容易出bug。
    而snprintf会拷贝最大长度-1的字符数,并在后面加\0,使用一个字符串覆盖另一个时很不错。
    看了一下资料,发现snprintf的效率也要高于strncpy。
    日常字符串拷贝还是推荐snprintf。
Tags: ,
    最近有一个困扰近两个月的bug终于解决了,心情愉快。503问题之前一直没找到头绪,压力一大就开始有,越大就越多。刚开始一直认为503是正常现象,后端负载能力不足的必然结果。加之之前后端用的自己写的模拟server,性能什么的没什么保证。所以一直在看是不是程序逻辑上有什么漏洞会导致封禁所有后端,一直找不到头绪。

    周五测试同学突然说:现在用的后端server肯定能力要强于前边这个啊,怎么可能会因为负载力不足导致503呢?一想确实啊,肯定是流量调度部分的问题。开gdb仔细一找,结果大跌眼镜,原来是跟后端的连接池的最大允许并发连接数开的太小了。。

    之前那个值设置的比较小是有道理的,因为php是每个进程处理一个请求的,所以并发数肯定不会超过启动的php进程数。但现在后端改用了lighttpd,lighttpd可以承载的并发数是很多的,这样的话在高并发请求的情况下后端连接池很容易就用完了。

    由此有两个感触,一是不同场景下配置项一定要仔细想想如何调优。二是bug并不都是逻辑错误导致的,还有可能是配置错了。。。。
Tags:
    今天qa说http请求host字段最后以"."结尾时会有问题,看了下发现lighttpd自动把点号去掉了,试了试nginx,也是这样。查了很多rfc,没找到为什么,跑到群里问,有人说是根域的问题,回想起来配dns的cname记录时,最后必须是有“点”结尾的,于是搜索了下。原来真正完整的域名最后是带点的,com、net、cn这都是顶级域名,点后面的“”是根域名。dns解析最顶部是找到根域,然后再找顶级域。之前以为com就是根域,由跟域名服务器解析。现在看是错的
Tags: , ,

lighttpd的超时参数详解

[| 不指定 2012/02/14 21:00]
Lighttpd配置中,关于超时的参数有如下几个(篇幅考虑,只写读超时,写超时参数同理):


server.max-keep-alive-idle = 5
server.max-read-idle = 60
server.read-timeout = 0
server.max-connection-idle = 360


这几个参数意思相近,配置的时候很容易搞混。




对于一个keep-alive连接上的连续请求,发送第一个请求内容的最大间隔由参数max-read-idle决定,从第二个请求起,发送请求内容的最大间隔由参数max-keep-alive-idle决定。请求间的间隔超时也由max-keep-alive-idle决定。发送请求内容的总时间超时由参数read-timeout决定。Lighttpd与后端交互数据的超时由max-connection-idle决定。



例子:


下面是模拟客户端代码:


$fp = fsockopen("127.0.0.1", 8902, $errno, $errstr, 30);

fwrite($fp, "GET / HTTP/1.1\r\n");
sleep(3);                                             //$1这个时间必须小于max-read-idle,否则会超时
fwrite($fp, "Host: a.com\r\n");
sleep(3);                                             //$2这个时间必须小于max-read-idle,否则会超时。且$1+$2时间之和必须小于read-timeout,否则超时
fwrite($fp, "Connection: Keep-Alive\r\n\r\n");
echo fread($fp, 1024);



sleep(7);                                             //$3 这个时间必须小于max-keep-alive-idle,否则超时



fwrite($fp, "GET / HTTP/1.1\r\n");
fwrite($fp, "Host: a.com\r\n");
sleep(15);                                           //$4  这个时间必须小于max-keep-alive-idle,否则超时,可以大于max-read-idle,但仍然不能超过read-timeout
fwrite($fp, "Connection: Keep-Alive\r\n\r\n");
echo fread($fp, 1024);
fclose($fp);

                                                  //以上时间均不受max-connection-idle限制


下面是模拟后端server代码:



$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if($sock == NULL)
{
    echo "can't create socket";
    exit;
}
if(!socket_bind($sock, "0.0.0.0", 8904))
{
    echo "can't bind socket";
    exit;
}
socket_listen($sock, 100);

while(1)
{
    if($new_conn = socket_accept($sock))
    {  
        $recv = socket_read($new_conn, 100000);
        //echo $recv;
        echo "begin sleep\n";
        sleep(10);                                                                                  //这个时间必须小于max-connection-idle,否则会超时
        echo "end sleep\n";
        socket_write($new_conn, "HTTP/1.1 200 OK\r\nDate: Tue, 01 Nov 2011 05:58:25 GMT\r\nServer: TestServer/1.0\r\nContent-Length: 1\r\nContent-Type: text/html;charset=gb2312\r\nConnection: Keep-Alive\r\n\r\na");
    }
    else
    {
        echo "accept failed!";
    }
}




下面是lighttpd中关于这几个参数实现的代码:

if (con->recv->is_closed) {                                                                        
                                                                 if (srv->cur_ts - con->read_idle_ts > con->conf.max_connection_idle) {                                              //对于客户端已经发送完请求数据的情况下,超时时间max-connection-idle
                                                                           /* time - out */
#if 1
                                                                           WARNING("(connection process timeout) [%s]", SAFE_BUF_STR(con->dst_addr_buf));
#endif
                                                                           connection_set_state(srv, con, CON_STATE_ERROR);
                                                                           changed = 1;
                                                                 }
                                                        }
                                                        else {

                                                                 if (con->request_count == 1) {                  
                                                                           if (srv->cur_ts - con->read_idle_ts > con->conf.max_read_idle) {                                              //对于第一个请求,发送的数据最大时间间隔:max_read_idle
                                                                                    /* time - out */
#if 1
                                                                                    if (con->conf.log_timeouts) {
                                                                                             WARNING("(initial read timeout) [%s]", SAFE_BUF_STR(con->dst_addr_buf));
                                                                                    }
#endif
                                                                                    connection_set_state(srv, con, CON_STATE_ERROR);
                                                                                    changed = 1;
                                                                           }
                                                                 } else {                                                                                               //从第二个请求开始,发送的数据最大时间间隔:keep_alive_idle
                                                                           if (srv->cur_ts - con->read_idle_ts > con->keep_alive_idle) {
                                                                                    /* time - out */
#if 1
                                                                                    if (con->conf.log_timeouts) {
                                                                                             DEBUG("(keep-alive read timeout) [%s]", SAFE_BUF_STR(con->dst_addr_buf));
                                                                                    }
#endif
                                                                                    connection_set_state(srv, con, CON_STATE_ERROR);
                                                                                    changed = 1;
                                                                           }
                                                                 }

                                                                 if (con->conf.read_timeout > 0 && con->read_start_ts > 0)                                                    //在read_timeout设置不为0的情况下,发送数据的最大总时间:read_timeout
                                                                 {
                                                                           used_time = srv->cur_ts - con->read_start_ts;
                                                                           if (used_time > con->conf.read_timeout)
                                                                           {
                                                                                    WARNING ("read timeout, client[%s], time=%lu",
                                                                                             SAFE_BUF_STR(con->dst_addr_buf), used_time);
                                                                                    connection_set_state(srv, con, CON_STATE_ERROR);
                                                                                    changed = 1;
                                                                           }
                                                                 }
                                                        }
    今天程序出了一个core,是strcmp的时候有一个参数没有判断为NULL导致的,当我编写了一个小程序:

引用
#include
int main()
{
strcmp(NULL, "“);
return 1;
}

测试的时候发现程序跑的毫无问题。
编译参数是gcc -g -O0,没有开任何优化。

gdb进去后发现strcmp根本没有被执行,改成int a = strcmp(NULL, "");后,出core了。

看来编译器默认还是提供一定优化的。

另外需要注意strcmp不会检查参数(效率考虑),所以需要自己检查。

run-parts命令的用法及原理

[| 不指定 2012/02/01 22:10]
    在很多系统中,用户目录下都有cron.daily之类的文件夹,里面的可执行文件每天都会被执行一次。也就是说如果想添加一个每天都被执行的任务的话,在目录下放置该任务的脚本即可。使用很方便,原理是什么呢,就是run-parts命令。

    在centos5下,run-parts命令位于/usr/bin/run-parts,内容是很简单的一个shell脚本,就是遍历目标文件夹,执行第一层目录下的可执行权限的文件。
    

#!/bin/bash

# run-parts - concept taken from Debian

# keep going when something fails
set +e

if [ $# -lt 1 ]; then
    echo "Usage: run-parts <dir>"
    exit 1
fi

if [ ! -d $1 ]; then
    echo "Not a directory: $1"
    exit 1
fi

# Ignore *~ and *, scripts
for i in $1/*[^~,] ; do
    [ -d $i ] && continue
    # Don't run *.{rpmsave,rpmorig,rpmnew,swp} scripts
    [ "${i%.rpmsave}" != "${i}" ] && continue
        [ "${i%.rpmorig}" != "${i}" ] && continue
        [ "${i%.rpmnew}" != "${i}" ] && continue
        [ "${i%.swp}" != "${i}" ] && continue
    [ "${i%,v}" != "${i}" ] && continue

    if [ -x $i ]; then
        $i 2>&1 | awk -v "progname=$i" \
                  'progname {
                   print progname ":\n"
                   progname="";
                   }
                   { print; }'
    fi
done

exit 0

     在ubuntu下,该文件位于/bin/run-parts,是个二进制文件,功能更为强大,支持--test等参数。
Tags:
    最近程序遇到一个小概率出core的bug,高压力下大概10分钟左右就会出core,gdb查看发现一个指针高四字节被置0xffffffff了,低四字节正常。
    该指针是局部变量,存放在栈上,排除了线程间同步互斥写坏数据的可能。
    该指针前后变量均正常,都是指针,排除了写越界的可能。

    通过日志查看,在返回该指针的函数返回前,指针正常,返回后高四字节即被置0xffffffff了。推断应该是函数返回的过程中遭到了破坏。

    但不知道为什么返回的过程中会出错,请教了下组内高工,高人就是高人,一听问题描述就表示应该是函数声明的问题。

    原来,在调用另一个so文件中的函数时,如果没有该函数的声明,由于从该so的符号表里可以找到函数,所以编译可以通过,但gcc会把这个函数返回值按默认的int处理,这种情况下,32位机编译的程序是没问题的,但64位机上指针是8字节,导致高四字节数据丢失。但返回的指针超过int值域时,高四字节数据丢失,导致指针被破坏。

   所以函数声明还是不可或缺的。
Tags: ,
分页: 10/33 第一页 上页 5 6 7 8 9 10 11 12 13 14 下页 最后页 [ 显示模式: 摘要 | 列表 ]