CentOS 下配置安装 Fossil 公共库

June 5th, 2013

fossil 是一个非常简洁的 DVCS,公共仓库的配置也很简单,非常适合个人和小型项目使用。不过我是一个念旧的人,所以只是试用一下,还不打算将我的程序从 git 迁移过来。

编译安装 fossil
~~~~~~~~~~~~~~
官网下载最新的源码,编译后会生成一个名为 fossil 的文件

$ ./configure --with-openssl=none

如果要在 chroot jail 环境下使用 (如 sdf.org),需要加 –static 选项。执行 make 生成 fossil 文件。

$ make
# 复制到 /usr/local/bin 目录下
$ sudo cp fossil /usr/local/bin

配置 apache 虚拟主机
~~~~~~~~~~~~~~~~~~
为了 url 看起来比较简洁,可以通过apache 的 RewriteRule 功能来去掉 url 中的 fossil.cgi 字符串。

$ sudo vi /etc/httpd/conf.d/vhost.conf
 
<VirtualHost *:80>
     ServerAdmin webmaster@yourdomain.com
     ServerName yourdomain.com
     ServerAlias www.yourdomain.com
     DocumentRoot /var/www/yourdomain.com/public_html/
     <Directory /var/www/yourdomain.com/public_html>
         Options Indexes FollowSymLinks Includes ExecCGI
         AddHandler cgi-script .cgi
 
         AllowOverride All
         Order deny,allow
         Allow from all
     </Directory>     
 
     RewriteEngine On
     #RewriteCond %{REQUEST_URI} !^/fossil.cgi?/
     RewriteCond %{REQUEST_URI} ^/tommy? [OR]
     RewriteCond %{REQUEST_URI} ^/test?
     RewriteRule ^(.*)$ /var/www/yourdomain.com/public_html/fossil.cgi/$1 [T=application/x-httpd-cgi]
 
     ErrorLog /var/www/yourdomain.com/logs/error.log
     CustomLog /var/www/yourdomain.com/logs/access.log combined
</VirtualHost>

创建配置 fossil 仓库
~~~~~~~~~~~~~~~~~~
创建一个 fossils 目录,用来存放 fossil 的仓库文件,并设置权限,让 tommy 用户可写

$ cd /var/www/yourdomain.com/
$ sudo mkdir fossils
$ sudo chown tommy:apache fossils/
$ cd /var/www/yourdomain.com/fossils
$ fossil init tommy.fossil

把界面提示的初始访问密码记下来,再修改权限,让 apache 可以执行

$ chmod 664 tommy.fossil 
$ sudo chown tommy:apache tommy.fossil

在 public_html 目录下创建一个 fossil.cgi 脚本

$ sudo vi /var/www/yourdomain.com/public_html/fossil.cgi
 
#!/usr/local/bin/fossil
directory: /var/www/yourdomain.com/fossils
notfound: http://www.yourdomain.com/tommy

访问测试
~~~~~~~
所有都配置好以后,重启 apache

$ sudo service httpd restart

访问 http://www.yourdomain.com/tommy 测试,域名记得要改为自己的哦。

Tags: ,
Posted in Technology | No Comments »

PostgreSQL 的 autovacuum 运行机制

June 1st, 2013

最近有个同事的一个查询 SQL 运行很慢,后来发现是有张表很久没有做过 vacuum 和 analyze 了,由于自从我将公司数据库版本升级到 9.1 以后,就不再通过 crontab 来执行 vacuum ,而是使用系统默认的 autovacuum 功能。先解释一下 autovacuum 的运行机制。

PostgreSQL 9.x 默认会开启 autovacuum,有一堆默认的参数,可以在 postgresql.conf 配置文件中修改,有关参数的含义可以参考官方文档。

autoanalyze 触发条件
~~~~~~~~~~~~~~~~~~~
如果从最后一次 autoanalyze 后, 插入,更新,删除的记录数之和大于阀值,将触发 autoanalyze,阀值计算公式如下
阀值 = autovacuum_analyze_threshold + autovacuum_analyze_scale_factor * pg_class.reltuples
如果使用默认值,计算公式为 (50 + pg_class.reltuples * 0.1)
由于还没搞清楚系统如何统计最后一次 autoanalyze 以来变动的总记录数,所以暂时手动修改,见下面 SQL 语句的 a_n 字段注释。

autovacuum 触发条件
~~~~~~~~~~~~~~~~~~~
如果从最后一次 autovacuum 后, pg_stat_user_tables.n_dead_tup大于阀值,将触发 autovacuum,阀值计算公式如下
阀值 = autovacuum_vacuum_threshold +autovacuum_vacuum_scale_factor * pg_class.reltuples
如果使用默认值,计算公式为 (50 + pg_class.reltuples * 0.2)
一旦 autovacuum 执行,pg_stat_user_tables.n_dead_tup 会归零,所以不用像 a_n 字段那样处理。

测试
~~~
先建一张测试表。

CREATE TABLE test (
    id                     serial NOT NULL PRIMARY KEY,
    name                   VARCHAR(80)
);

执行下面的 SQL 语句

SELECT
    t1.relname,
 
    -- 如果不是第一次运行 autoanalyze, 需要减去最后一次 autoanalyze 的 a_n 值
    (t1.n_tup_ins + t1.n_tup_upd + t1.n_tup_del - 0) AS a_n,
    (CAST(current_setting('autovacuum_analyze_threshold') AS BIGINT)
        + t2.reltuples * CAST(current_setting('autovacuum_analyze_scale_factor') AS NUMERIC)
    ) AS a_t, -- default = (50 + t2.reltuples * 0.1)
 
    t1.n_dead_tup AS v_n,
    (CAST(current_setting('autovacuum_vacuum_threshold') AS BIGINT)
        + t2.reltuples * CAST(current_setting('autovacuum_vacuum_scale_factor') AS NUMERIC)
    ) AS v_t,  -- default = (50 + t2.reltuples * 0.2)
 
    date_trunc('second', t1.last_autovacuum)  AS last_av,
    date_trunc('second', t1.last_autoanalyze) AS last_aa
FROM
    pg_stat_user_tables t1,
    pg_class t2
WHERE
    t1.relname = t2.relname
    AND t1.relname = 'test';
 relname | a_n | a_t | v_n | v_t | last_av | last_aa 
---------+-----+-----+-----+-----+---------+---------
 test    |   0 |  50 |   0 |  50 |         | 
(1 row)
 
字段解释
relname: 表名
a_n:     analyze 的当前值,用来和analyze阀值比较
a_t:     analyze 的阀值
v_n:     vacuum 的当前值,用来和vacuum阀值比较
v_t:     vacuum 的阀值
last_av: 最后一次 autovacuum 的时间
last_aa: 最后一次 autoanalyze 的时间

然后不断执行下面的插入语句,并观察上面 SQL 中各个值的变化, a_n 在增加

INSERT INTO test(name) VALUES(NULL);
 relname | a_n | a_t | v_n | v_t | last_av | last_aa 
---------+-----+-----+-----+-----+---------+---------
 test    |  50 |  50 |   0 |  50 |         | 
(1 row)

会发现一旦 a_n > a_t 时,会在 1 分钟内 (autovacuum_naptime 参数的默认配置) 触发 autoanalyze,这时 last_aa 会变化。

 relname | a_n | a_t  | v_n | v_t  | last_av |        last_aa         
---------+-----+------+-----+------+---------+------------------------
 test    |  51 | 55.1 |   0 | 60.2 |         | 2013-05-21 17:40:04+08
(1 row)

修改上面的 SQL ,减去 autoanalyze 触发时的 a_n 的值(假如是 51), 修改如下

....
    (t1.n_tup_ins + t1.n_tup_upd + t1.n_tup_del - 0 - 51) AS a_n,
....
 relname | a_n | a_t  | v_n | v_t  | last_av |        last_aa         
---------+-----+------+-----+------+---------+------------------------
 test    |   0 | 55.1 |   0 | 60.2 |         | 2013-05-21 17:40:04+08
(1 row)

继续执行下面语句,直到触发下一次 autoanalyze

INSERT INTO test(name) VALUES(NULL);
 relname | a_n | a_t  | v_n | v_t  | last_av |        last_aa         
---------+-----+------+-----+------+---------+------------------------
 test    |  56 | 60.7 |   0 | 71.4 |         | 2013-05-21 17:43:04+08
(1 row)

继续修改 SQL, 将其改为

....
    (t1.n_tup_ins + t1.n_tup_upd + t1.n_tup_del - 0 - 51 - 56) AS a_n,
....
 relname | a_n | a_t  | v_n | v_t  | last_av |        last_aa         
---------+-----+------+-----+------+---------+------------------------
 test    |   0 | 60.7 |   0 | 71.4 |         | 2013-05-21 17:43:04+08
(1 row)

下面测试如何触发 autovacuum, 不断执行下面的删除语句,并观察各值的变化

DELETE FROM test WHERE id = (SELECT MAX(id) FROM test)

由于删除也会影响 a_n,所以会先触发 autoanalyze,当执行第 61 次删除时,等一分钟,让其触发 autoanalyze

 relname | a_n | a_t  | v_n | v_t  | last_av |        last_aa         
---------+-----+------+-----+------+---------+------------------------
 test    |  61 | 54.6 |  61 | 59.2 |         | 2013-05-21 17:49:05+08
(1 row)

继续修改 SQL, 将其改为

....
    (t1.n_tup_ins + t1.n_tup_upd + t1.n_tup_del - 0 - 51 - 56 - 61) AS a_n,
....
 relname | a_n | a_t  | v_n | v_t  |        last_av         |        last_aa         
---------+-----+------+-----+------+------------------------+------------------------
 test    |   0 | 54.6 |   0 | 59.2 | 2013-05-21 17:50:05+08 | 2013-05-21 17:49:05+08
(1 row)

这是会发现 last_av 已经有值了,说明 autovacuum 已经被触发了,观察上面的数据,当 autoanalyze 被触发时,
不但 a_t 降低到 54.6,v_t 也从 71.4 降到 59.2,小于 v_n (61),所以被自动触发,同时 v_n 归零

参考文档
~~~~~~~
[1] http://www.network-theory.co.uk/docs/postgresql/vol3/Theautovacuumdaemon.html
[2] http://dba.stackexchange.com/questions/18664/are-regular-vacuum-analyze-stil-recommended-under-9-1
[3] http://serverfault.com/questions/53376/how-do-i-know-if-the-autovacuumer-in-postgres-8-3-is-actually-working

Tags:
Posted in Technology | No Comments »

网站搬家

May 2nd, 2013

虽然 Godaddy 空间还有半年才到期,在经过几天分析后,毅然将网站全部搬到了 Linode,事实证明我的决定没错。虽然每年贵了几百块钱,但我对服务器有完整的 root 权限,可以装任意我想装的软件,只要空间足够。非常适合我这种没事就瞎捣鼓 Linux 的用户。当然访问速度也快了不少,Ping 值从平均 300 降到了不到 200,没敢用东京的机房,虽说速度更快,但风险也高,大家懂得。

网站搬家简单的很,新服务器上先装好什么 Apache , MySQL 等,配好防火墙,把从 Godaddy 备份的数据库恢复过来,然后把相应的 html, php 文件挪过来就 OK 了。

Posted in Inspire | 3 Comments »

放弃的重新开始,开始的再次放弃

April 13th, 2013

生活中有些事本已经放弃,现在却要继续,比如两年前 无奈的放弃,现在在老板的软磨硬泡下又重新开始,但我发誓,决不能让这个项目像两年前那样控制我和折磨我。

还有些事怪我自己毅力不够,只能放弃,如半年前 开始的戒烟, 前些日子在几个饭局的酒后, 我的戒烟失败了。但好的是现在量很小,重新开始的戒烟行动正在策划中。

Posted in Inspire | No Comments »

锻炼身体,少用电脑

February 27th, 2013

春节回家彻底检查了我的颈椎,医生说我的颈椎病已经很严重了,不但有骨质增生,而且已钙化并压迫了手臂的神经。
之前每天早上都会感到四肢无力,酸痛,一直以为是嘌呤高导致的,拼命控制海鲜,啤酒,现在看来颈椎才是真正的病因。可怜我去泰国一趟,什么龙虾、螃蟹都不敢吃。

必须要多锻炼了,医生建议最好的运动是游泳和羽毛球,晚上回家少用电脑,尤其是星际2这种游戏要彻底戒掉。
到今天为止,已经半个多月没用玩游戏,回家也基本不用电脑了,现在早上起来感觉还有有点好转的。关键还是锻炼。

Posted in Inspire | 1 Comment »

如何写一个有用的 commit 信息

January 25th, 2013

网上有关如何写 commit 信息的文章不是很多,中文的就更少了。现在公司基本所有的项目都需要成员之间高度协作,无论项目规模大小,commit 信息都是开发人员了解代码演化的重要途径之一。即使你独自一人负责一个项目,一组好的 commit 也会帮你快速的回忆起几周或几个月以前的代码变动的原因和当时的想法。

我每天都会关注几个项目的代码变化,越来越觉得一个合适的 commit 信息规范简直太重要了。

GIT 现在是我们唯一使用的版本管理工具,如果你使用的是 Subverion, Mercurial 等其它工具,下面的内容仅供参考。

基本约定
~~~~~~~

  • 第一行是commit 信息概述,控制50个字符(或25个汉字)以内。
    • 结尾统一都不要使用句号,因为后面通常都会有详细描述。
    • 如果你有对应的 Bug 管理工具,可以直接以 #BugId 开头,如 “#12034 短信的号码与内容发送前应该 trim() ”
    • 如果项目很大,可以用子系统名来开头,方便分类,如 “security: 增加 ajax 登录方式,在不退出系统的情况下,重新登录”
  • 第二行是空行,一些处理工具会把第一行作为邮件的标题,其它的作为正文,所以需要一个空行来区分标题和正文。如果后面没有信息详述,此空行可以省略。
  • 第三行开始是commit 信息详述,每行的长度控制在 72个字符(或 36个汉字)之内。

50/72 是一个老外定的标准,并有不少追随者,为了能在各种终端设备上方便阅读,我们也采用此约定。

提高 commit 质量
~~~~~~~
一个 commit 应该包括一个准确的逻辑改动。一个逻辑改动包括增加新的特性或修改特定的 Bug,如果不能在较高层次用简短语句描述这个改动,就说明它对于单个 commit 来说太复杂了,拆分它!如果是 git ,你可以使用 git add -p" (or -i) 命令把你的代码改动拆分拆分为多个 commit.

每次 commit 的差异应该尽可能的简练,每个人都更愿意阅读一串连续的、逻辑清楚、差异精炼的补丁,反正我是一看到那种大段大段复杂改动的 commit 就头疼。

这里有个管用的法则: 其他开发开发人员只看你提交的 commit 信息(不看具体的代码),应该可以在合理的时间内实现几乎相同的补丁程序。

还有一些不好的习惯,一定要从现在开始,立刻改掉它们:

  • 总是在每天下班前 commit 今天所有的改动。这种 commit 信息基本没用。
  • 懒汉式的 commit 信息,如 “修改了几个 Bug”,这种信息毫无意义。
  • 两个改动放在一次 commit 中。 如 “修改传真下载 conn 未关闭及缺少 memo 的异常”,请把它拆开分别 commit。
  • 有些动词用现在时,而不是完成时,例如不要说“改好了…,解决了…, 增加了…,修改了…”,而应该说“增加…,修改…,解决…”,用一种已经完成的口气,总是让人感觉有点自大。

最后给个我觉得还不错的 commit ,供大家参考:

guice: 解决当容器 reload 时,guice 已拿不到实例的问题
 
Web容器在启动时,会恢复之前的session,这样用户可以直接进入Servlet,
但这是之前Guice拿的 service实例可能已经被回收,这时需要强制用户重新
登录,产生新的session。

当你看到下面的 commit,心情是不是都会好一点?

config: 配置文件增加注释区分生产与测试环境
 
安全目的,配置文件默认使用测试环境进行配置,发布到生产环境后,
再修改配置文件,应用到生产环境。
 
尤其注意别将生产环境的一些敏感信息,如 数据库帐号 提交到代码库中。
      ,           ,  
     /             \
    ((__-^^-,-^^-__))
     `-_---' `---_-'
      `--|o` 'o|--'
         \  `  /
          ): :(
          :o_o:
           "-"

参考文档
~~~~~~~
[1] http://www.mediawiki.org/wiki/Git/Commit_message_guidelines
[2] http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
[3] http://who-t.blogspot.co.uk/2009/12/on-commit-messages.html
[4] https://github.com/erlang/otp/wiki/Writing-good-commit-messages
[5] http://lbrandy.com/blog/2009/03/writing-better-commit-messages/
[6] http://wiki.octave.org/Commit_message_guidelines
[7] http://psung.blogspot.co.uk/2007/12/version-control-with-git-git-add.html

Tags: ,
Posted in Technology | No Comments »

将 windows 文件格式转换为 unix

January 5th, 2013

偶尔会收到 windows 文件格式的程序,需要统一转化为 unix 文件格式,否则可能会在显示、运行时出现问题。
最常碰到的问题就是 windows 的换行符是 \r\n, 而 unix 是 \n,这时有些工具显示文件时,每一行后面都有一个 \r,看起来十分怪异。
在 linux 下,可以直接通过 cat 命令查看那些特殊字符。

$ cat -A example.txt

可以通过 emacs 直接 C-x RET f 或者 M-x set-buffer-file-coding-system ,然后输入 unix

Tags:
Posted in Technology | No Comments »

Java 程序检查远程服务器状态

December 3rd, 2012

通常我们以命令的方式判断远程服务器是否正常运行有两种方式,pingtelnet 一个远程端口。假设我们要检查的远程服务器都是 Linux 系统。

从 JDK 1.5 以后, InetAddress 类提供一个 isReachable() 方法,用来判断远程主机是否可以到达,这个方法的使用的时候一定要注意以下几点:如果客户端是 Windows 系统,JDK 不会向服务器发送 ICMP 的请求,因为 Windows 没有提供可被调用的底层接口,这时 JDK 会自动向服务器的 Port 7 发起一个 Socket 连接,如果连接也失败,则认为服务器无法到达,返回 False。 注意在客户端执行此程序无须管理员用户,但要求服务器端的防火墙放行 Port 7 的访问(大部分 Linux 发行版此端口默认是禁止访问的)。

如果客户端是 Linux 系统, JDK 可以使用底层接口来向服务器发送 ICMP 请求,但执行此程序的用户必须拥有 root 权限,否则 JDK 也会像 Widnows 一样以 Socket 方式进行验证,即向服务器的 Port 7 发起一个 Socket 连接来进行判断。

如果不用 isReachable(),比较简单的方法是 Socket 连接一个指定的端口,但前提是确定此端口可以被访问,如 Web Server 的 80 端口。这种方式对用户权限没有要求,唯一要注意的是,如果无法连接此端口,有可能只是应用服务出现问题,不一定是服务器无法连接。

    private static boolean isReachable(String ip, String port, int timeout) {
        boolean reachable = false;
        // 如果端口为空,使用 isReachable 检测,非空使用 socket 检测
        if(port == null) {
            try {
                InetAddress address = InetAddress.getByName(ip);
                reachable = address.isReachable(timeout);
            } catch (Exception e) {
                logger.error(e.getMessage());
                reachable = false;
            }
        } else {
            Socket socket = new Socket();
            try {
                socket.connect(new InetSocketAddress(ip, Integer.parseInt(port)), timeout);
                reachable = true;
            } catch (Exception e) {
                logger.error(e.getMessage());
                reachable = false;
            } finally {
                try {
                    if(socket != null) socket.close();
                }catch (Exception e) {}
            }
        }
        return reachable;
    }

Tags:
Posted in Technology | No Comments »

开始戒烟 (2012-11-24)

November 24th, 2012

今天开始戒烟, 原因很多,但已经不重要了,35岁的我开始改变自己生活的第一步。

Posted in Changes | No Comments »

让 PPTP 穿越 PIX

September 4th, 2012

最近把公司淘汰的一台 PIX 用于部门内,发现之前配置的一些 PPTP 不能成功连接外部的 Server,查后发现需要在 PIX 上增加一个配置,允许其穿过。

fixup protocol pptp 1723

Tags:
Posted in Technology | No Comments »

Previous page