2009年12月22日 | 分类: PostgreSQL

pgsql允许管理员在文件系统里定义表空间存储位置,这样创建数据库对象时候就可以引用这个表空间了。好处就不用多说了,可以把数据库对象存储到不同的分区上,比如更好的存储之类。默认initdb之后会有两个表空间pg_global和pg_default。

查看pgsql当前表空间有哪些可以试试下面:

postgres=> SELECT spcname FROM pg_tablespace;
  spcname
------------
 pg_default
 pg_global
(2 rows)

或:

postgres=> \db
    Name    |  Owner   | Location
------------+----------+----------
 pg_default | postgres |
 pg_global  | postgres |

建立表空间需要注意的主要的是权限问题,而且要在新的空目录上建立,权限属于数据库管理员比如默认postgres。

1. 建立目录

$ mkdir /home/smallfish/pgdata
$ sudo chown -R postgres:postgres /home/smallfish/pgdata

2. 进入psql

$ psql -U postgres -h 192.168.0.122

如果权限没设置好下面语句会报错

postgres=> CREATE TABLESPACE space1 LOCATION '/home/smallfish/pgdata';

建测试表

postgres=> CREATE TABLE foo(i int) TABLESPACE space1;

可以查看表空间目录下多了文件

postgres=> \! ls /home/smallfish/pgdata

删除表空间,需要注意的是先要删除所有该表空间里的对象

postgres=> DROP TABLESPACE space1;

ok,到这里已经建立好表空间了。当然每次建表都指定TABLESPACE也有点麻烦,来点默认的把。

postgres=> SET default_tablespace = space1;
postgres=> CREATE TABLE foo(i int);
2009年12月16日 | 分类: Apache

前言:

扩展Apache模块开发网上大部分教程都是围绕Perl语言,老外的《Writing Apache Modules with Perl and C》可以算是经典之作了,可惜一直都是针对老版本开发,而且主力语言是Perl,C语言部分只是略有介绍。不过相比较而言用Perl来扩展模块功能确实比 C语言来的快速以及便捷多了,也简单容易。我自己也在工作里应用了一部分,主要是在防盗链上面写了两个简单都模块,可以参考我写的另外两篇文章:apache+mod_perl防盗链以及apache+mod_perl实现url rewrite。说了那么多题外话,回到正题,这里只是用C语言实现一个简单的hello模块,模块功能是查询MySQL自带mysql数据库里都user表。

系统环境:

ArchLinux Apache2.2 MySQL 5.0

具体开发步骤:

1. 利用Apache自带都apxs建立hello模块:

[root#localhost] apxs -g -n hello

这样就会在当前目录下新建一个hello模块的文件目录,可以看到里面有:Makefile mod_hello.c modules.mk这样的文件,具体apxs路径查询下本机apache/bin目录。

2. 预览下mod_hello.c,可以看到里面apxs自动帮你生成一堆代码了,我们需要的只是修改里面的代码部分,先简单都介绍下里面的函数说明。

include 部分就是引入了一些必要都头文件
hello_handler 这个就是hello模块都主体部分,所有的显示、处理请求什么的都在这里。
hello_register_hooks hello_module 这俩个是需要导出的函数所必须的,先可以不管他们,按照生成的不动即可。

3. 修改hello_handler函数,里面可以看到request_rec *r,r有很多函数和变量,具体要参见文档了。里面的ap_rputs是输出,可以简单的理解为把字符串输出到r。

static int hello_handler(request_rec *r)
{
if (strcmp(r->handler, "hello")) { // 判断apache配置文件里handler是否等于hello,不是就跳过
          return DECLINED;
     }
     r->content_type = "text/html"; // 设置content-type
     if (!r->header_only)
          ap_rputs("The sample page from mod_hello.c\n", r); // 输出一段文字
     return OK;// 返回 200 OK状态
}

增加#include “mysq.h”,查询需要用到这个头文件。
具体代码参见本文结尾部分。

4. 编译模块

[root#localhost] apxs -c -a -i -I/usr/include/mysql/ -lmysqlclient mod_hello.c

可以看到一堆编译指令,加上-I和-l是编译mysql必须的,编译完会自动在httpd.conf加上 LoadModule hello_module modules/mod_hello.so

5. 修改httpd.conf
<Location /hello>
SetHandler hello
</Location
6. 重启apache,访问http://localhost/hello,看是否成功。

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

完整代码:

#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"
/* 头文件,本文用到了ap_rprintf函数 */
#include "apr.h"
#include "apr_lib.h"
#include "apr_strings.h"
#include "apr_want.h"
#include "mysql.h"

/* 定义mysql数据变量 */
const char *host = "localhost";
const char *user = "root";
const char *pass = "smallfish";
const char *db    = "mysql";

/* The sample content handler */
static int hello_handler(request_rec *r)
{
    if (strcmp(r->handler, "hello")) {
        return DECLINED;
    }
    r->content_type = "text/html";
    /* 定义mysql变量 */
    MYSQL mysql;
    MYSQL_RES *rs;
    MYSQL_ROW row;
    mysql_init(&mysql); /* 初始化 */
    if (!mysql_real_connect(&mysql, host, user, pass, db, 0, NULL, 0)) {/* 连接数据库 */
        ap_rprintf(r, "<li>Error:%d %s</li>\n", mysql_errno(&mysql), mysql_error(&mysql));
        return OK;
    }
    char *sql = "select host,user from user order by rand()";
    if (mysql_query(&mysql, sql)!=0) { /* 查询 */
        ap_rprintf(r, "<li>Error : %d %s</li>\n", mysql_errno(&mysql), mysql_error(&mysql));
        return OK;
    }
    rs = mysql_store_result(&mysql); /* 获取查询结果 */
    while ((row = mysql_fetch_row(rs))) { /* 获取每一行记录 */
        ap_rprintf(r, "<li>%s - %s</li>\n", row[0], row[1]);
    }
    mysql_free_result(rs); /* 释放结果集 */
    mysql_close(&mysql); /* 关闭连接 */
    return OK;
}

static void hello_register_hooks(apr_pool_t *p)
{
    ap_hook_handler(hello_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA hello_module = {
    STANDARD20_MODULE_STUFF,
    NULL,                            /* create per-dir              config structures */
    NULL,                            /* merge  per-dir              config structures */
    NULL,                            /* create per-server config structures */
    NULL,                            /* merge  per-server config structures */
    NULL,                            /* table of config file commands                 */
    hello_register_hooks  /* register hooks                                */
};
2009年12月16日 | 分类: Apache, Python

环境:Linux Apache Python(mod_python)

换了一台新机器,没有配置Mod_Python了,在一些应用里import MySQLdb出现了下面错误:

ExtractionError: Can't extract file(s) to egg cache
The following error occurred while trying to extract file(s) to the Python egg
cache:
  [Errno 13] Permission denied: '/root/.python-eggs'
The Python egg cache directory is currently set to:
  /root/.python-eggs
Perhaps your account does not have write access to this directory?  You can
change the cache directory by setting the PYTHON_EGG_CACHE environment
variable to point to an accessible directory.

解决办法有两种:

1.设置PYTHON_EGG_CACHE环境变量

$ SetEnv PYTHON_EGG_CACHE /tmp/aaa/

目录权限注意要是apache用户,或者简单点就777

2.把egg格式转成目录

$ cd /python-path/site-packages/
$ mv MySQL_python-1.2.3c1-py2.5-linux-x86_64.egg foo.zip
$ mkdir MySQL_python-1.2.3c1-py2.5-linux-x86_64.egg
$ cd MySQL_python-1.2.3c1-py2.5-linux-x86_64.egg
$ unzip ../foo.zip
$ rm ../foo.zip
2009年12月16日 | 分类: Java

先来回顾下linux下scp命令的用法:

[shell $] scp -r /本地目录或文件 [email protected]:/远程目录

这条命令是把本地的目录或者文件拷贝到远程192.168.0.110一个目录下,如果是从远程拷到本地,则反一下ip和目录。-r则是递归目录。更多参见scp –help

最近在Java里调用scp,是通过一个JSP页面来触发。为了在调用系统命令时候不出现提示密码,两台机器配置好了信任关系,可以参考ssh, scp不输入密码,大致代码如下:

Runtime.getRuntime().exec("scp /aa.txt [email protected]:/bb");

try时候也没任何异常,但是文件没拷贝过去,最后根据Process的waitFor()获取命令返回值是1。

这下可以肯定的是调用系统命令失败,在System.out.println里打印出command,linux下运行是没错的。为何呢?

后来发现原来是用户权限的问题,默认apache运行用户是nobody,根本没权限调用scp命令,配置的信任关系也是本机的root用户。

那就重新加一个user把,adduser…到配置好信任关系,在scp -i 指定一个rsa文件,并把rsa文件复制到/tmp目录下,权限为0755,继续刷新,后台可以看到提示输入密码之类的output了。

貌似还比较棘手,最后还是搜了下,发现有关Java scp的库,Ganymed SSH-2 for Java。貌似比较老,先来测试一下把。

Connection conn = new Connection(“192.168.0.110”);
conn.connect();
boolean isAuthenticated = conn.authenticateWithPassword(“root”, "***********");
if (isAuthenticated == false)
    throw new IOException("Authentication failed.");
SCPClient client = new SCPClient(conn);
client.put("/aa.txt", "/bb");
conn.close();

OK!发现竟然可以一次运行了。算了就不调用系统命令了,直接使用这个库把。

client.put方法第一个参数可以是个数组,即文件名的数组。暂时没找到整个目录的方法,就自己手动获取下目录文件列表把。

2009年12月16日 | 分类: Linux

经常在不同linux机器之间互相scp拷文件,每次总是要输入密码才可行。

通过ssh-keygen生成公钥,在两台机器之间互相建立信任通道即可。假设本地机器client,远程机器为server。

1. 生成rsa keygen

[winter@client winter] $ ssh-keygen -b 1024 -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/winter/.ssh/id_rsa): <Enter>
Enter passphrase (empty for no passphrase): <Enter>
Enter same passphrase again: <Enter>
Your identification has been saved in /home/winter/.ssh/id_rsa.
Your public key has been saved in /home/winter/.ssh/id_rsa.pub.
The key fingerprint is:
33:d4:7b:9c:87:04:cf:14:40:22:6d:c7:15:78:97:6a winter@client

直接上面公钥和私钥存放地址可以直接回车,私钥密码可以直接回车,也可以输入。

2. 查看.ssh目录下了多私钥和公钥文件

[winter@client winter] $ ls .ssh/
id_rsa  id_rsa.pub  known_hosts

3. 拷贝公钥到目标机器上,并改名成authorized_keys

首次scp命令时候还是会提示输入密码,还有是否继续链接的提示,以后

4. 测试ssh进入

[winter@client winter] $ ssh 192.168.0.110

5. ok,搞定!

[winter@server winter] # it's ok!
2009年12月15日 | 分类: Apache

环境:Linux Apache2.2 (路径 /usr/local/apache)

步骤:

1. 修改 conf/httpd.conf,找到如下位置,去除 # 注释符

# Virtual hosts
Include conf/extra/httpd-vhosts.conf

2.修改 conf/extra/httpd-vhosts.conf

<VirtualHost *:80>
    ServerAdmin [email protected]
    DocumentRoot "/usr/aa"
    ServerName ww.aa.com
    ServerAlias ww.aa.com
    ErrorLog "logs/ww.aa.com-error_log"
    CustomLog "logs/ww.aa.com-access_log" common
</VirtualHost>

注意CustomLog这行,默认给的配置是:”logs/dummy-host.example.com-access_log common”

这个其实是错误的,Apache启动时候会报错,common这个应该放在双引号外面:

Syntax error on line 32 of /usr/local/apache/conf/extra/httpd-vhosts.conf:
CustomLog takes two or three arguments, a file name, a custom log format string or format name, and an optional "env=" clause (see docs)

另外还有个问题,在Apache+Tomcat时候出现的,在配置好mod_jk之后,通过默认80端口访问jsp总会提示403禁止访问,纳闷了!后来 才发现是Directory配置问题,因为每个VirtualHost配置的目录不是默认的DocumentRoot目录,在Apache2以后对于权限 和安全有了更高的要求,所以必须手动配置下每个Directory属性。

<Directory "/usr/aa">
    Options Indexes FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
</Directory>

OK,到这里虚拟主机部分已经配置完成!

补充一点,加上日志按天保存,避免一个巨大无比的文件!

CustomLog "|/usr/local/apache/bin/rotatelogs /usr/local/apache/logs/ww.aa.com-access-%y%m%d.log 86400 480 " combined
2009年12月15日 | 分类: Apache, Perl

相信apache的mod_rewrite模块都很熟悉了,今天这儿换个思路,利用mod_perl来实现下,发现竟然是如此的简单!

首先得保证apache已经安装了mod_perl模块,具体安装配置可以看上一篇文章哦。

修改下http.conf配置,添加一下内容:

PerlTransHandler MyTrans # MyTrans 这个是自己添加的处理模块名

具体MyTrans.pm脚本如下:

package MyTrans;

use strict;
use Apache2::Const qw(DECLINED);

sub handler {
    my $r = shift;
    my $uri = $r->uri;
    my ($id) = ($url =~ m|^/news/(.*)\.html|);
    $r->uri("/news.php");
    $r->args("id=$id");
    return Apache2::Const::DECLINED;
}
1;

实现就是:/news/12345.html  => /news.php?id=12345

2009年12月15日 | 分类: Apache, Perl

大体思路是这样的,比如有 一个地址:http://www.aa.com/down/1.mp3,不幸搜索引擎或者迅雷扒到了,就无偿为他们奉献流量了。 但是假如在http://www.aa.com/down/1.mp3?key=123,key参数每天变化或者几分钟变化一次,在apache服务端校 验下这个参数,不正确则显示拒绝访问或者找不到的话,那样防盗链的效果就算达到了把。

modperl强大到可以任意应用apache内部API,官方地址是:http://perl.apache.org 。根据apache版本选择相应的modperl版本,现在大部分都apache2了,就选择modperl2把。具体安装配置可以看官方文档。

先建立/home/httpd/modperl/startup.pl(目录请自行修改),内容如下:

use strict;

use lib qw(/home/httpd/modperl); # 把这个路径加入到perl lib路径

use Apache2::RequestRec ();
use Apache2::RequestIO ();
use Apache2::Connection ();
use Apache2::RequestUtil ();
use Apache2::ServerUtil ();
use Apache2::Log ();
use Apache2::Request ();

1;

部分本机httpd.conf配置:

LoadModule perl_module                modules/mod_perl.so
LoadModule apreq_module             modules/mod_apreq2.so
PerlPostConfigRequire /home/httpd/modperl/startup.pl
<Location /down >
    SetHandler modperl # 设置该目录交给modper处理
    PerlAccessHandler Down # Down是模块名称
    PerlSetVar key 123 # 设置校验参数值
</Location>

mod_apreq2.so这个模块需要安装Apache2::Request,具体安装:http://pyh7.spaces.live.com/blog/cns!47D8D44208AC51E5!128.entry

startup.pl文件一般modperl应用都有,加载一些常用库,可以在apache启动时预先载入,避免重复加载。

修改这些后可以重启下apache,看下logs/error_log里最后是否有mod_apreq和mod_perl字样,如果有就说明成功了。剩下都就是写校验的perl脚本了,/home/httpd/modperl/Down.pm,内容如下:

package Down;
use strict;
use Apache2::RequestRec ();
use Apache2::RequestIO ();
use Apache2::Connection ();
use Apache2::RequestUtil ();
use Apache2::ServerUtil ();
use Apache2::Log ();
use Apache2::Const -compile => qw(OK FORBIDDEN);
use Apache2::Request ();

sub handler {
    my $r = shift;
    my $req = Apache2::Request->new($r);
    my $ip = $r->connection->remote_ip;
    my $k = $req->param('key') || ''; # 判断访问时是否带key参数
    my $key = $r->dir_config('key') || '123'; # 加载httpd.conf配置中的key值
    if ($key eq $k) { # 相等可以正常访问
        return Apache2::Const::OK;
    } else { # 否则显示拒绝访问
        my $s = Apache2::ServerUtil->server;
        $s->log_error("[$ip--------forbidden.]");
        return Apache2::Const::FORBIDDEN;
    }
}

1;

提示一下,以上两个perl脚本文件末尾记得加上1;

重启下apache。现在可以通过http://www.aa.com/down/1.mp3和http://www.aa.com/down /1.mp3?key=123测试下。可以看见不加参数拒绝访问了把。这里只是简单的判断,实际上可以根据日期或者IP加密生成一个字符串来判断。

写这个帖子完全是无意中搜索modperl应用时候发现了,具体可以参见:
http://pyh7.spaces.live.com/blog/cns!47D8D44208AC51E5!140.entry

上面的文档已经写都很详细了,包括怎么安装modperl、Apache2::Request等模块以及配置apache的http.conf就不在累赘都重复了。

2009年12月15日 | 分类: Python

pexpect是python一个模块,可以通过:easy_install pexpect 来安装。

这里主要是用pexpect执行ssh,查看远程uptime和df -h看硬盘状况。

#ssh_cmd.py
#coding:utf-8
import pexpect

def ssh_cmd(ip, user, passwd, cmd):
    ssh = pexpect.spawn('ssh %s@%s "%s"' % (user, ip, cmd))
    r = ''
    try:
        i = ssh.expect(['password: ', 'continue connecting (yes/no)?'])
        if i == 0 :
            ssh.sendline(passwd)
        elif i == 1:
            ssh.sendline('yes')
    except pexpect.EOF:
        ssh.close()
    else:
        r = ssh.read()
        ssh.expect(pexpect.EOF)
        ssh.close()
    return r

hosts = '''
192.168.0.12:smallfish:1234:df -h,uptime
192.168.0.13:smallfish:1234:df -h,uptime
'''

for host in hosts.split("\n"):
    if host:
        ip, user, passwd, cmds = host.split(":")
        for cmd in cmds.split(","):
            print "-- %s run:%s --" % (ip, cmd)
            print ssh_cmd(ip, user, passwd, cmd)

hosts数组格式是:主机IP:用户名:密码:命令 (多个命令用逗号, 隔开)
可以看出打印出相应的结果了,可以拼成html发送mail看起来比较美观些咯!

2009年12月15日 | 分类: Java, Memcached, Python

用Python写了一个计划任务,定时更新Memcached中一个key值,写的很happy,几分钟搞定。

然后在Java Servlet测试,代码写的也很happy,编译 – 刷新,一气呵成。

然后发现值一直是null,再tail日志看看,异常不断:

com.danga.MemCached.MemCachedClient Mon Jul 20 09:37:04 CST 2009 - ++++ exception thrown while trying to get object from cache for key: test_num
 com.danga.MemCached.MemCachedClient Mon Jul 20 09:37:04 CST 2009 - 3
 com.danga.MemCached.NestedIOException: 3
 at com.danga.MemCached.MemCachedClient.get(MemCachedClient.java:1408)
 at com.danga.MemCached.MemCachedClient.get(MemCachedClient.java:1270)

晕倒,记得以前为了让两个语言实现API读写共享,手动去修改了两个的API包,实现了中文互读写。难不成今儿个还要手动去搞一把?

然后手动试了下:

shell> telnet xxxxxx 11211
get test_num
VALUE test_num 4 2
23

经查证VALUE协议返回的是 key flags len \r\n value 这样的格式,大悟:原来flags不一样啊,Java里面对int型赋值以后flags是0,而Python里则不一样,两者序列化的东西不同啊。懒得去 折腾两者序列化有啥不同。来点直接的把。

然后打开Python Memcached API,大概578行_val_to_store_info方法里,可以看到flags部分,是根据变量类型进行定义的,isinstance(val, str) 如果是str则pass。

到这里就简单了,直接在py代码里:mc.set(‘test_num’, str(num))

Java读取OK。