试用Nginx

Nginx是一款性能非常强劲的Web服务器,也可以用来做反向代理服务器,现在用Nginx做服务器站点越来越多了。

netcraft_chart_with_lighttpd

上图是Nginx和Lighttpd的主机数量比较

最近我试用了一下Nginx,做了一个Nginx+Tomcat的测试,具体步骤如下:

groupadd www
useradd -g www www

wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-7.8.tar.gz
wget http://sysoev.ru/nginx/nginx-0.7.39.tar.gz

tar zxvf pcre-7.8.tar.gz
cd pcre-7.8
./configure
make
make install
cd ..

tar zxvf nginx-0.7.39.tar.gz
cd nginx-0.7.39
./configure –user=www –group=www –prefix=/usr/local/nginx –with-http_stub_status_module –with-http_ssl_module
make
make install

cd /usr/local/nginx/conf/

我们编辑nginx.conf文件,内容如下

user  www www;
worker_processes  3;

error_log  logs/error.log  notice;

pid        logs/nginx.pid;

events {
use epoll;
worker_connections  1024;
}

http {
include       mime.types;
default_type  application/octet-stream;

include    /usr/local/nginx/conf/proxy.conf;

log_format  main  ‘$remote_addr – $remote_user [$time_local] “$request” ‘
‘”$status” $body_bytes_sent “$http_referer” ‘
‘”$http_user_agent” “$http_x_forwarded_for”‘;

#access_log  logs/access.log  main;

sendfile        on;
tcp_nopush     on;

#keepalive_timeout  0;
keepalive_timeout  65;

#gzip  on;

server {
listen       80;
server_name  localhost;

#charset koi8-r;

access_log  logs/localhost.access.log  main;

#location / {
#    root   html;
#    index  index.html index.htm;
#}

location / {
proxy_pass http://localhost:8080;
}

#location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
#    expires      30d;
#}

#location ~ .*\.(js|css)?$ {
#   expires      1h;
#}

location /NginxStatus {
stub_status             on;
access_log              on;
auth_basic              “NginxStatus”;
}

#error_page  404              /404.html;

# redirect server error pages to the static page /50x.html
#
error_page   500 502 503 504  /50x.html;
location = /50x.html {
root   html;
}

}

}

同时我们要在/usr/local/nginx/conf下创建proxy.conf文件,内容如下

proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffers 32 4k;

保存之后,进入/usr/local/nginx/目录下,执行sbin/nginx,即可启动Nginx。

客户端浏览器输入http://ServerIP,可以看到Tomcat的欢迎页面,证明Ngnix已经转发HTTP请求给后端的Tomcat了,打开http://ServerIP/NginxStatus可以查看Nginx的状态,在Shell下我们还可以查看一下HTTP头

curl -I http://localhost
HTTP/1.1 200 OK
Server: nginx/0.7.39
Date: Thu, 05 Mar 2009 02:08:54 GMT
Content-Type: text/html;charset=ISO-8859-1
Connection: keep-alive
Content-Length: 8132

关于Nginx的配置,还是需要看它的文档http://wiki.nginx.org/Main,国内张宴的《Nginx 0.7.x + PHP 5.2.8(FastCGI)搭建胜过Apache十倍的Web服务器(第4版)[原创]》非常不错,可以参考。

追逐互联网的梦想

离第一次上网已有10年时间了,我想还没有一样东西像互联网一样,如此深刻的改变着我们的生活,互联网带给了我梦想,并在着10年中不懈的追逐着这个梦想。

1998-2000是一波互联网浪潮的年代,99年的时候我创建了自己的网站–“爱情工作室”。

2000年,郑州,之所以在郑州,是因为周爱民(如今中国软件响当当的人物)忽悠我来做AV95互联网方面的工作,互联网在中国那时也算是刚起步,专业杀毒和互联网结合的前途也是不可限量,当时就来了AV95公司的所在地郑州,当时和爱民住在一个屋子里,几乎是晚上工作,白天休息,不得不承认,爱民兄绝对是为软件而生的,当时已经把JavaScript搞的是如火纯清,Delphi、win32更是不在话下,让我顶礼膜拜,不过爱民兄忠实的很随M$,而我后来却选择了JAVA。

在AV95时间不长,北京的一家互联网公司就来收购“爱情工作室”,收购金额不高,并且收购之后我要继续为其工作,我想了想,这也算是一个机会,就离开了AV95,也算是开始了一段创业生活,我找来一个在网上认识的朋友–一叶飞(这家伙现在已经不知去向了),说服他和我一起做,他当时刚刚大学毕业,郑州也没什么好工作,他就和我一起开始开始了这番“事业”。

我们在友爱路租了一处房子,是顶楼,阳台正对着碧沙岗公园,很快就到了盛夏季节,中原的夏天是很热的,空气中没有一丝风,我们租的房子是两间,其中一间有空调,我们就把电脑放在这间,早上我们起来洗漱好,就坐到电脑面前了,一叶飞会打开音箱,播放MP3,我们的MP3里的歌有很多,伍佰、动力火车、唐朝、黑豹、王菲等等,歌曲从早放到晚也不会重复,我们便在音乐的伴随下开始了工作,网站开发、编辑、美工都是自己在做,中午和傍晚,我们两个就会忍受着酷暑出去吃饭,回到屋子里都是一身大汗,要在空调下猛吹,日子一天一天,很充实、也很快乐,当时并没有考虑过太多的盈利模式、网站的未来,只是为了自己的兴趣在做这些事情,当时我已经想到了将网站做成完全的社区模式,所有内容由网友发起,也曾经计划个人日记这些产品,现在看来不就是Web2.0、博客吗?

2001年随着第一波互联网泡沫的破灭,我也结束了和北京网站的合作,爱情工作室也终结了,一叶飞去了北京,由于我父母是上海人,所以我选择了上海,开始了软件、互联网的另一段生活,如今8、9年过去了,我仍然不能忘记那段日子,因为对于我,互联网的梦想从未停止过,我将永远做互联网的追梦人。

用jQuery Form Plugin实现Ajax无刷新的文件上传

在我以前的意识里,觉得用JavaScript或Ajax提交文件表单似乎是一件麻烦的事情,也没有太仔细的研究,后来看jQuery的时候,发现了jQuery Form Plugin这个插件,它方便的实现了Ajax方式的表单提交,例子里也包括了文件表单的提交,那我也来试一下吧,还是用Kohana(最近专注于这个,而且PHP开发要快多了,要是用Struts,要费好多功夫)。

要Kohana支持上传,先要配置一些upload的参数,把system\config\upload.php拷贝到application/config下,里面的参数做好设置,具体参考Kohana的文档吧,之后创建一个Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Myupload_Controller extends Top_Controller {
 
	public function index() {
		$view = new View ("upload");		
		$view->msg = "Upload File";
 
		$view->render ( TRUE );
	}
 
	public function upload() {		
		$filename = upload::save('myfile');
		echo $filename;
 
		exit ();
	}
 
}

Top_Controller为我自己创建的类,也继承自Kohana的Controller,之后创建upload.php的view文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<html>
<head>
<title><?php echo $msg;?></title>
<script type="text/javascript" src="<?=url::base()?>js/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="<?=url::base()?>js/jquery.form.js"></script>
<script type="text/javascript"> 
        // wait for the DOM to be loaded 
        $(document).ready(function() { 
        	var options = { 
        		target:        '#fileinfo'   // target element(s) to be updated with server response         
        	};
 
        	$('#myForm').submit(function() { 
        		$(this).ajaxSubmit(options);
        		return false; 
        	});
 
        }); 
</script>
</head>
 
<body>
<h2><?php echo $msg;?></h2>
<form id="myForm" action="<?=url::base()?>myupload/upload" method="post"
	enctype="multipart/form-data">
	<input name="myfile" type="file" />
	<input type="submit" value="Submit Comment" />
</form>
<div id="fileinfo"></div>
</body>
</html>

在这里我们用jQuery Form Plugin来提交表单,之后将信显示在“fileinfo”这个div里,去试试效果吧。

http://malsup.com/jquery/form/里有详细的API和例子,同时http://malsup.com/jquery/里也有其他的一些不错的jQuery的插件。

Kohana的Events和Hooks研究

Kohana是一个使用PHP5的面向对象的MVC框架,是从CodeIgniter衍生出来,研究了几天,感觉还不错,就是它的文档实在太简陋了,在看到它的Events机制和Hooks机制时,确实有点不明白,文档上说的实在太简单了,后来我在Google上搜索了一下,找到两篇文章《Events and hooks in Kohana》《Practical Kohana Hooks example: PHPIDS》,上面讲了一下Kohana的Events和Hooks,并举了例子,我也开始慢慢理解了它的含义。

在Kohana的Events中,默认定义了很多的Events(例如system.ready、system.pre_controller等等),中文理解就是事件,也就是触发点,在程序在运行到某个位置时,会被触发,具体就是调用Event::run方法,我们在Kohana.php里可以看到在不同的位置执行了不同的Event::run方法,那么Hooks的意义在于当一个事件被触发之前,可以通过已加载的Hooks来修改事件的回调,有点绕口,说白了就是在事件发生之前,做点事情,举个例子,你安排了今天的日程,下午3点要开会,那么在3点开会就是一个Event(事件),那么到3点的时刻,你希望提醒你一下,你在你的手机里定了一个闹钟,在3点的时候会响,这就是一个Hook,可以看出Hook是基于Event的。

我们可以创建自己的Hook程序,并加载如默认的Events里,但是默认的Events不一定能满足我们的需要,比如我们在Web应用中经常使用的权限校验,判断这个用户是否登陆过,我们虽然可以使用默认Events里的system.pre_controller,但是这个Event是针对所有的Controller的,有些Controller是不需要校验用户的,还好Kohana允许自己定义Event,下面我们还是举例说明吧。

要使用Hooks,首先要在application/config/config.php里将$config[‘enable_hooks’]置为TRUE。

之后我们定义自己的Event,我们在application/controllers目录下建立base.php,代码如下:

1
2
3
4
5
6
7
class Base_Controller extends Controller  {
 
	public function __construct() {
		parent::__construct();
		Event::run("base.construct");
	}	
}

我们继承了Kohana的Controller,在构造函数里定义了Event,名字叫“base.construct”,以后我们的Controller都继承自Base_Controller,那么在对象创建的时候都会触发base.construct事件。

Hook的文件放在application/hooks下,我们就创建一个hook文件sessioncheck.php,代码如下:

1
2
3
4
5
6
7
8
9
class SessionCheck {
 
	public function check() {
		echo "check session ...";
	}
 
}
 
Event::add('base.construct', array('SessionCheck','check'));

我们将SessionCheck的check方法加载到了base.construct事件上,也就是在触发base.construct事件之前,会执行SessionCheck的check方法。

接下来我们写一个Controller,在application/controllers目录下建立first.php,代码如下:

1
2
3
4
5
6
7
class First_Controller extends Base_Controller  {
 
	public function index() {
		echo "First - index";
		exit();
	}
}

我们执行一下看看会有什么提示,http://localhost/kohana/first,显示

check session …First – index

没问题了,在Controller构造时,执行了Hook里的方法。

从JavaEye上找的Google翻译JavaScript脚本

在Blog或新闻发布的时候,我们希望URL是一段有意义的字串,例如我Blog里的《Linux下切分Tomcat的catalina.out日志文件》文章链接是https://i.laoer.com/2009/02/18/rotating-catalina-out-in-tomcat-using-cronolog/,这样有利于SEO(搜索引擎优化),对于我们母语为中文的小虾,有时候要翻译标题还是有点头疼的,还好有了Google翻译,让翻译的事情变得简单了一点,但对于开发者,怎么使用户也方便的翻译,特别是在写Blog的时候就能把标题翻译好,还要动点脑筋,我看到JavaEye里发布新闻的地方有个“让Google帮助生成永久链接”的功能挺好的,看了一下他们的源码,得到以下这段,顺别也解释一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script type="text/javascript" src="http://www.google.com/jsapi"></script> //载入Google的jsapi
 
<script type="text/javascript">  
 
  function slugify(str) { //这个方法应该是将字符转为小写,并把一些特殊字符转为"-"
    return str.toLowerCase().replace(/[^a-z0-9-_]+/g, '-').replace(/^-|-$/g, '');
  }
 
  google.load("language", "1"); //载入Google的language Ajax库
 
  function translate_title() { //这里就是翻译文章标题了,里面的方法需要jQuery支持,之前要引入jQuery的包,这里就省略了
    if($F("news_title").blank()) {
      alert("请先填写标题");
      return;
    }
    $("news_slug_url").value = "正在翻译中...";
    google.language.translate($F("news_title"), "zh", "en", function(result) {
      if (!result.error) {
        $("news_slug_url").value = slugify(result.translation);
      }
    });
  }  
</script>

这里有个地方要说一下,Google的Ajax库API还是挺好的,可以load几种Ajax的库,另外Google的Web 工具包也可以看看。

修改Oracle连接数

在SQL/PLUS下,先检查一下当前连接数
show parameter process;
show parameter session;

修改命令,根据自己实际情况修改数值
alter system set processes=300 scope = spfile;
alter system set sessions=350 scope = spfile;

之后重启Oracle

Linux下切分Tomcat的catalina.out日志文件

随着Tomcat的运行,catalina.out文件会越来越大,虽然Tomcat每日会生成一个catalina.ymd.log的文件,但catalina.out主文件仍然不断增加,需要对catalina.out按日切分才好,在网上找了一下,看到一篇《rotating catalina.out in tomcat 5.5 using cronolog》,就用公司的Tomcat配置一下。

cronolog工具已经在服务器上装过,一个对日志切分的小工具,其主页在http://cronolog.org/,我们也用它来切分Apache的日志。

进入Tomcat的bin目录,打开catalina.sh文件,找到tomcat启动的相关行,或者你直接查找catalina.out,一般我们修改下面行中的内容(因为我们一般不会在-security条件下运行),

1
2
3
4
5
6
7
8
9
10
11
12
13
    else$_RUNJAVA$JAVA_OPTS $CATALINA_OPTS \
    -Djava.endorsed.dirs=”$JAVA_ENDORSED_DIRS-classpath$CLASSPATH” \
    -Dcatalina.base=”$CATALINA_BASE” \
    -Dcatalina.home=”$CATALINA_HOME” \
    -Djava.io.tmpdir=”$CATALINA_TMPDIR” \
    org.apache.catalina.startup.Bootstrap “$@” start  \
    >>$CATALINA_BASE/logs/catalina.out 2>&1 &
 
    if [ ! -z "$CATALINA_PID" ]; then
    echo $! > $CATALINA_PID
    fi
    fi

修改
org.apache.catalina.startup.Bootstrap “$@” start  \
>> “$CATALINA_BASE”/logs/catalina.out 2>&1 &

org.apache.catalina.startup.Bootstrap “$@” start 2>&1 \
| /usr/local/sbin/cronolog “$CATALINA_BASE”/logs/catalina.%Y-%m-%d.out >> /dev/null &
同时,上面有一行
touch “$CATALINA_BASE”/logs/catalina.out
可以注释掉,完成之后重起Tomcat就可以了,在logs目录下可以看到catalina.2009-02-18.out的日志,是按日生成的。

Mysql的一些常用命令

自定义Mysql启动命令

/usr/local/mysql/bin/mysqld –defaults-extra-file=/usr/local/mysql/my.cnf –basedir=/usr/local/mysql –datadir=/usr/local/mysql/data –user=mysql –log-error=/usr/local/mysql/data/localhost.localdomain.err –pid-file=/usr/local/mysql/data/localhost.localdomain.pid –socket=/tmp/mysql.sock –port=3306

通过定义里面的参数,可以在一个机子上启动多个Mysql实例在不同的端口

CREATE USER ‘username’@’%’ IDENTIFIED BY ‘password’;
创建用户,%代表可以来自任何主机,如果是本机,可以写localhost

GRANT ALL PRIVILEGES ON * . * TO ‘username’@’%’ IDENTIFIED BY ‘password’ WITH GRANT OPTION MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0
给用户赋所有权限,也可以根据需要,赋特定的权限

导出一个库
mysql/bin/mysqldump -u user -p password dbname > /full/path/dbname.sql

导入一个库
mysql -u user -p password –default-character-set=utf8 dbname < /full/path/dbname.sql --default-character-set是指定数据库编码

Redhat Linux下src rpm的安装方法

方法一:
1. 执行rpm -i your-package.src.rpm
2. cd /usr/src/redhat/SPECS
3. rpmbuild -bp your-package.specs 一个和你的软件包同名的specs文件
4. cd /usr/src/redhat/BUILD/your-package/ 一个和你的软件包同名的目录
5. ./configure 这一步和编译普通的源码软件一样,可以加上参数
6. make
7. make install

方法二:
1. 执行rpm -i you-package.src.rpm
2. cd /usr/src/redhat/SPECS (前两步和方法一相同)
3. rpmbuild -bb your-package.specs 一个和你的软件包同名的specs文件

这时,在/usr/src/redhat/RPM/i386/ (根据具体包的不同,也可能是i686,noarch等等) 在这个目录下,有一个新的rpm包,这个是编译好的二进制文件。执行:

rpm -i new-package.rpm即可安装完成。

关于Struts2里使用EL或JSTL

最近在公司里做一些Java Web开发的培训,同时对已经做的一些工程做一些ReView,现在的工程里,工程师直接使用JSTL取得Action里的属性,这个用法我以前到真的没有用过,因为在我印象中,Struts2的这些Action属性,应该是在ValueStack中,而在某些情况下,从ValueStack取值是件挺麻烦的事情,在做天乙社区8时,我就参考Struts2的标记库,自己扩展标记库,从而取得ValueStack里的值,而JSTL应该是从Page、Request、Session和Application里顺序取值,莫非Struts2将ValueStack里的值也放入了Request?同时我们直接用EL标签也直接取出了Action的属性值,莫非真的放入了Request?但是打开Struts2的Debug,发现Request里并没有值。

带着这个疑问,我Google了一下,很快找到答案:

提问:在Struts2中,如何使用JSTL来读取Action中的变量?

这是一个历史悠久的问题。因为事实上,很多朋友(包括我在内)是不使用Struts2自身的标签库,而是使用JSTL的,可能因为JSTL标签库比较少,简单易用的原因吧。

我们知道,JSTL默认是从page,request,session,application这四个Scope逐次查找相应的EL表达式所对应 的对象的值。那么如果要使用JSTL来读取Action中的变量,就需要把Action中的变量,放到request域中才行。所以,早在 Webwork2.1.X的年代,我们会编写一个拦截器来做这个事情的。大致的原理是:在Action执行完返回之前,依次读取Action中的所有的变 量,并依次调用request.setAttribute()来进行设置。具体的整合方式,请参考以下这篇文档:http://wiki.opensymphony.com/display/WW/Using+WebWork+and+XWork+with+JSP+2.0+and+JSTL+1.1

不过随着时代的发展,上面的这种方式,已经不再被推荐使用了。(虽然如此,我们依然可以学习它的一个解决问题的思路)目前来说,自从 Webwork2.2以后,包括Struts2,都使用另外一种整合方式:对HttpServletRequest进行装饰。让我们来看一下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 public class StrutsRequestWrapper extends HttpServletRequestWrapper {
 
     /**
      * The constructor
      * @param req The request
      */
     public StrutsRequestWrapper(HttpServletRequest req) {
         super(req);
     }
 
     /**
      * Gets the object, looking in the value stack if not found
      *
      * @param s The attribute key
      */
     public Object getAttribute(String s) {
         if (s != null && s.startsWith("javax.servlet")) {
             // don't bother with the standard javax.servlet attributes, we can short-circuit this
             // see WW-953 and the forums post linked in that issue for more info
             return super.getAttribute(s);
         }
 
         ActionContext ctx = ActionContext.getContext();
         Object attribute = super.getAttribute(s);
 
         boolean alreadyIn = false;
         Boolean b = (Boolean) ctx.get("__requestWrapper.getAttribute");
         if (b != null) {
             alreadyIn = b.booleanValue();
         }
 
         // note: we don't let # come through or else a request for
         // #attr.foo or #request.foo could cause an endless loop
         if (!alreadyIn && attribute == null && s.indexOf("#") == -1) {
             try {
                 // If not found, then try the ValueStack
                 ctx.put("__requestWrapper.getAttribute", Boolean.TRUE);
                 ValueStack stack = ctx.getValueStack();
                 if (stack != null) {
                     attribute = stack.findValue(s);
                 }
             } finally {
                 ctx.put("__requestWrapper.getAttribute", Boolean.FALSE);
             }
         }
         return attribute;
    }
}

看到了嘛?这个类会在Struts2初始化的时候,替换HttpServletRequest,运行于整个Struts2的运行过程中,当我们试 图调用request.getAttribute()的时候,就会执行上面的这个方法。(这是一个典型的装饰器模式)在执行上面的方法时,会首先调用 HttpServletRequest中原本的request.getAttribute(),如果没有找到,它会继续到ValueStack中去查找, 而action在ValueStack中,所以action中的变量通过OGNL表达式,就能找到对应的值了。

在这里,在el表达式广泛使用的今天,JSTL1.1以后,也支持直接使用el表达式。注意与直接使用struts2的tag的区别,这里需要使用el的表示符号:${}

例如:${user.name}, <c:out value=”${department.name}” />

原来是这样,学无止境啊,很多东西需要仔细去研究。