鹏 的个人资料皓月清风照片日志列表更多 工具 帮助

日志


9月7日

linux系统字符集影响TOMCAT运行,造成页面HTML代码输出不全

OS:REDHAT LINUX AS4
TOMCAT:5.5.3
JDK:1.5.0_12
  随着网站访问量的加大,一个奇怪的现象引起我的注意。某些页面下部的文字或者按钮没有显示,有时候页面中会显示出一些HTML标签。这种情况并不是每次都出现,在访问量比较大的时候可能性大一些。查看页面代码发现HTML代码在某个位置被截断了,后面的都没有输出?!于是写在未输出部分里的文字或者按钮自然不能显示,而一些标记因为缺少对应的结束标记,所以被显示出来。
  这个问题我觉得很诡异。作为TOMCAT这么一个长期发展项目,出现这种情况而没人发现是不应该的。但是用GOOGLE查遍中文、英文站点,都没有找到合理的解释。一般都是说因为程序出错,所以JSP页面不能完全输出。但是在TOMCAT的日志里没有任何异常。
  为了能稳定的重现这种问题,我找到一个根本不用动态输出的页面(也是JSP)来研究,发现如果使用80端口访问,经由APACHE转给TOMCAT处理,输出的页面完整;如果使用8080端口直接提交给TOMCAT,输出的HTML就不全;如果将页面内的JAVA代码全部去掉,只保留<%@ page language="java"%>,则页面显示乱码,HTML输出完全,且可以通过修改浏览器编码的方式显示出正常的中文。
  通过这些现象,我判断应该是TOMCAT的问题,而且和编码方式有关系。我们知道JSP页面上的pageEncodingcontentTypecharset 是指定不同的编码方式,前者指定JSP在被编译时使用的编码,后者指定JSP被输出时使用的编码。因为开发都在WINDOWS上做的,所以JSP文件的编码都是GB2312,指定pageEncoding=GB2312或者pageEncoding=GBK 应该都没问题,但只要一加上就会输出不全,页面被截断。我因此怀疑TOMCAT和JVM不能完全协调,很可能就是在对中文的编码方面。比如JVM便宜JSP的时候根据指定的pageEncoding进行了编码,而TOMCAT则对JSP采用另一种编码,二者计算出来的页面字节数不同,TOMCAT按照自己计算的字节输出,结果没输出完就给截断了。当然这只是我的推测了,国外的人很少有遇到中文编码的情况,国内的资料又没有,就先提出这么个假设吧。
  然后我去研究TOMCAT的配置文件server.xml,里面说TOMCAT运行时使用的是与操作系统相同的字符集。我忽然想到这个LINUX不是我亲手装的,是让一个新手装的,本来想让新人练练手,后来发现他把LINUX的默认字符集设置成中文了。打开/etc/sysconfig/i18n查看:
[root@localhost ~]# vi /etc/sysconfig/i18n                                                           
                                                                                                                     
LANG="zh_CN.UTF-8"                                                                                        
SUPPORTED="zh_HK.UTF-8:zh_HK:zh:zh_CN.UTF-8:zh_CN:zh:en_US.UTF-8:en_US:en" 
SYSFONT="latarcyrheb-sun16"                                                                            
把LANG="zh_CN.UTF-8"改成LANG="en_US.UTF-8"
重起服务器之后再测试那JSP,发现80端口访问依然正常,但是8080访问的时候超级慢,而其他页面在访问量很大的时候也不出现页面显示不全的现象了。
  老问题解决了,新问题出现了,实在是很让人诧异。为什么TOMCAT会出这种问题呢?也许是我用的版本不好,5.5.3有些低了,以后用高版本或者用6.x试试。如果还出这种问题,也给写个BUG REPORT吧。让TOMCAT项目组重视一下中文问题!
  如果哪位也遇到了类似的问题,希望能交流一下解决的经验。
7月27日

IPTABLES配置举例

     上一篇已经写了怎么编译内核增加connlimit模块,这里给一个IPTABLES的配置文件作为例子,类似的功能可以自己加。
     需要注意的是,如果给WEB服务器配置了比较高的安全级别,比如对于输入包(INPUT)默认拒绝接受(DROP),则必须考虑到连接的双向问题。也就是说,如果服务器主动与另外一台服务器建立连接,比如连接数据库,或者用域名访问其他站点时需要与DNS服务器建立连接,类似这些情况都需要接收对方服务器的数据,会受到防火墙的影响,必须考虑是否需要专门配置过滤通道,免得无法接收数据。
     另外还要注意各种过滤规则的匹配流程,整体的顺序是在配置文件中设置的条件按照从上到下逐一匹配。我们常用的接受(-j ACCEPT)和拒绝(-j DROP)这种类型的过滤条件都是匹配到即执行,然后跳出流程不做其他条件的匹配了。有些规则的操作执行后并不跳出流程,而是继续向下进行匹配,比如-j LOG和-j MARK,只是目前我还没有用到。如果所有规则都没有匹配到,就执行默认的操作。具体流程可以参考下图:
bridge3b
 
     以下是我做的配置,首先打开配置文件,直接对内容进行编辑:
vi /etc/sysconfig/iptables                                                             
###########以下为文件内容,IP地址数值使用☆代替###########
# Generated by iptables-save v1.2.11 on Thu Jul 17 19:39:41 2008                  
*filter                                                                                                    
:INPUT DROP [488:44294]                                                                         
#对接收到的输入包,默认为不接受,除非符合下面的规则。                                      
#[xxx:xxx]代表[符合规则的包数:符合规则的包的字节数]                                         
:FORWARD DROP [0:0]                                                                              
#默认转发包都做转发                                                                                   
:OUTPUT ACCEPT [3348:636912]                                                                
#默认输出包都输出                                                                                     
:RH-Firewall-1-INPUT - [0:0]                                                                      
-A INPUT -s .☆.☆.☆/32 -j ACCEPT                                                           
#接收到的来自☆.☆.☆.☆的所有包都接受,保证连接数据库服务器正常                          
-A INPUT -s ☆.☆.☆.☆/32 -j ACCEPT                                                           
#将接收到的来自☆.☆.☆.☆的所有包都接受,保证连接DNS服务器正常                         
-A INPUT -s 127.0.0.1 -j ACCEPT                                                                
#将接收到的本机自己发送的包都接受                                                                 
-A INPUT -p tcp -m tcp -s ☆.☆.☆.☆ -j ACCEPT                                            
#将接收到的☆.☆.☆.☆所有的TCP包都接受,保证本公司连接不受限制                          
-A INPUT -m conntrack --ctstate INVALID -j DROP                                         
#将所有不正常的包都丢弃                                                                              
-A INPUT -p tcp -m connlimit --connlimit-above 10 --connlimit-mask 24 -j DROP  
#限制所有C类IP的TCP连接数最大为10个,超过的丢弃                                             
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT                                               
#接受80端口的TCP包,开通WEB服务                                                                
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT                                               
#接受22端口的TCP包,开通SSH                                                                     
-A INPUT -p tcp -m tcp --dport 8080 -j ACCEPT                                            
#接受8080端口的TCP包,可以直接调用TOMCAT                                                  
-A INPUT -f -m limit --limit 100/sec --limit-burst 100 -j ACCEPT                        
#限制IP碎片输入为每秒100个包,防止IP碎片攻击                                                  
-A INPUT -p icmp -m limit --limit 1/sec --limit-burst 10 -j ACCEPT                     
#连续接受10个PING包以后,限制每秒只接受1个PING包                                           
-A FORWARD -m conntrack --ctstate INVALID -j DROP                                    
#不转发不正常的包                                                                                      
-A FORWARD -p icmp -m limit --limit 1/sec --limit-burst 10 -j ACCEPT                
#连续转发10个PING包以后,限制每秒只转发1个PING包                                          
-A OUTPUT -p tcp -m tcp --sport 31337 -j DROP                                           
#禁止31337端口发送TCP包                                                                           
-A OUTPUT -p tcp -m tcp --dport 31337 -j DROP                                           
#禁止朝其他机器的31337发送包                                                                      
COMMIT                                                                                                 
# Completed on Thu Jul 17 19:39:41 2008                                                    
IPTABLES的配置文件修改以后需要重起服务使规则生效:
service iptables restart         
查看当前过滤规则可以用以下命令:
iptables -v -n -L                  
查看设置的规则,以及符合各个规则的包、数据流量。
以上配置可以在一定程度上防御DOS攻击。

LINUX_ AS4编译内核connlimit模块,用IPTABLES防御DOS攻击

     上上周服务器总是受到流量攻击,查看连接数都比较大:
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'                                            
查看各种TCP连接的数量
netstat -an|grep ESTABLISHED|awk '{print $5}'|awk -F: '{print $4}'|sort |uniq -c|sort -r +0n          
查看各个IP地址ESTABLISHED(已经建立的)连接的数量
netstat -an|grep SYN|awk '{print $5}'|awk -F: '{print $1}'|sort |uniq -c|sort -r +0n                       
查看各个IP地址SYN(正在建立中的)连接的数量
 
     对于这种情况,短期内可以采用禁止连接数太大的IP访问的方法,但需要管理员随时注意监控。如果遇到大规模的DOS(拒绝服务)或者DDOS(分布式拒绝服务)攻击,正规一些的做法需要配置流量防火墙。但是对于拒绝服务类的攻击,目前没有特别有效的解决方案,只能是对访问请求包进行过滤,对于连接数量过大的IP加以阻止。拒绝服务类攻击也有多种方式,具体区别请自行查阅。
    要提高防御DDOS攻击的效果需要软件、硬件防火墙相互配合,所以最好联系机房做硬件防火墙配置。这里只提供本机用IPTABLES做软件防火墙的方法。
    目前的LINUX内核版本已经是2.6.x,防火墙工具也改为iptables,以下是各个内核对应的防火墙工具
Linux 2.4.x and 2.6.x iptables
Linux 2.2.x  ipchains
Linux 2.0.x  ipfwadm
     需要理解的一点是,IPTABLES是设置工具,真正的防火墙是netfilter,是包含在内核中的一个模块,IPTABLES是用来配置内核中这部分功能的一个工具。正是因为防火墙集成在内核中,效率比较高,但升级和修改比较困难,需要升级内核和设置工具,也就是重新编译内核、升级IPTABLES。
准备工作:
     确保已经安装各种编译工具,建议在安装LINUX的时候将所有开发工具都选上。
     最重要的是研究内核版本、connlimit模块版本、IPTABLES版本之间的兼容性。在LINUX系统中,兼容性是很难掌握的,即使微小的版本差异,也可能造成编译失败等情况。而版本的更新更是相当频繁,IPTABLES基本上每天都更新……
     本次的升级操作是以REDHAT LINUX AS4为基础(内核版本2.6.9-22.ELsmp),connlimit需要2.6.11以上的内核。实际采用的搭配是:linux-2.6.15(内核)+iptables-1.3.5-20061006(IPTABLES)+patch-o-matic-ng-20060725(包含connlimit模块的补丁集合)。
下载地址:
ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.15.tar.gz
ftp.netfilter.org/pub/iptables/snapshot/iptables-1.3.5-20061006.tar.bz2
http://ftp.netfilter.org/pub/patch-o-matic-ng/snapshot/patch-o-matic-ng-20060725.tar.bz2
注意所有地址都不要死记硬背,最好从根目录逐步找进去,因为目录结构可能修改。
压缩包下载以后放在/usr/src/目录下解开
tar -gxf linux-2.6.15.tar.gz                             
bunzip2 -d iptables-1.3.5-20061006.tar.bz2       
tar -xf iptables-1.3.5-20061006.tar                  
bunzip2 -d patch-o-matic-ng-20060725.tar.bz2 
tar -xf patch-o-matic-ng-20060725.tar            
修改补丁里的配置文件
vi patch-o-matic-ng-20060725/patchlets/connlimit/linux-2.6.11/net/ipv4/netfilter/Makefile.ladd           
obj-$(CONFIG_IP_NF_MATCH_STATE) += ipt_state.o
改为obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o
给内核以及IPTABLES的源码打补丁
进入补丁包patch-o-matic-ng-20060725的目录,里面有runme脚本。然后给出内核源码和IPTABLES源码的路径再执行:
KERNEL_DIR=/usr/src/linux-2.6.15 IPTABLES_DIR=/usr/src/iptables-1.3.5-20060725 ./runme connlimit   
会显示一个字符界面的提示信息,如下:
Welcome to Patch-o-matic ($Revision: 6577 $)!                                              
Kernel:   2.6.18, /usr/src/linux-2.6.18.8                                                         
Iptables: 1.3.5, /usr/src/iptables-1.3.5-20060725                                            
Each patch is a new feature: many have minimal impact, some do not.                
Almost every one has bugs, so don't apply what you don't need!                        
-------------------------------------------------------                                
Already applied:                                                                                        
Testing connlimit... not applied                                                                    
The connlimit patch:                                                                                 
   Author: Gerd Knorr <
kraxel@bytesex.org>                                                  
   Status: ItWorksForMe[tm]                                                                      
This adds an iptables match which allows you to restrict the                            
number of parallel TCP connections to a server per client IP address                  
(or address block).                                                                                    
Examples:                                                                                               
# allow 2 telnet connections per client host                                                  
iptables -p tcp --syn --dport 23 -m connlimit --connlimit-above 2 -j REJECT       
# you can also match the other way around:                                                 
iptables -p tcp --syn --dport 23 -m connlimit ! --connlimit-above 2 -j ACCEPT    
# limit the nr of parallel http requests to 16 per class C sized                           
# network (24 bit netmask)                                                                        
iptables -p tcp --syn --dport 80 -m connlimit --connlimit-above 16 \                 
        --connlimit-mask 24 -j REJECT                                                            
-----------------------------------------------------------------                  
Do you want to apply this patch [N/y/t/f/a/r/b/w/q/?]                                    
输入y,回车,即可在内核与IPTABLES的源文件里加入connlimit模块代码。
然后编译内核。进入内核源码目录linux-2.6.15,然后执行
make mrproper            
make clean                 
make menuconfig         

    这时会出现一个内核模块选择的界面,用空格可以切换模块编译形式,[*]为加入内核,[M]为以模块形式使用。有些是有详细设置的,按Enter进入,双击Esc可以返回上一级。
我们需要依次进入
Networking                                                           
-->Networking options                                            
   -->Network packet filtering(replaces ipchains)          
      -->IP: Netfilter Configuration                              
找到Connections/IP limit match support项(应该在最下方),并设置为[*]
然后保存退出,继续执行编译
make bzImage                      
make modules                      
make modules_install             
make install                         
    如果没有报错(warning“警告”问题不大),在/boot下就已经有了编译过的内核程序,在重新启动时会增加内核选项,可以选择新内核或者老内核启动。万一新内核不能启动,记得选回老的,设法重新编译。
    然后编译新的IPTABLES。进入源码目录iptables-1.3.5-20060725执行:
make KERNEL_DIR=/usr/src/linux-2.6.15                           
make install KERNEL_DIR=/usr/src/linux-2.6.15                   
make install-devel                                                        
    这样所有编译工作都结束了。但如果选择新内核启动会失败,需要把SELinux禁止掉。这可能是老的SELinux版本不能与新内核兼容造成的,以后还是用LINUX AS5做操作系统比较保险。
禁止SELinux的方法:
vi /etc/selinux/config                   
把SELinux的选项改为disable或者permissive。
将新内核作为默认启动内核方法:(注意,一定要确定新内核可以启动再执行)
vi /boot/grub/grub.conf                
找到在开头部分的default=x(x是一个数字),数字代表下面的内核序号,由上到下,从0开始。默认的是老内核,新内核一般加在最前面,也就是把default=0就可以了。
     重新启动之后,就可以使用IPTABLES来设置访问限制了,可以限制同一IP的连接数。
4月24日

webbrowser控件缓存问题邪道解决方法

  最近使用VB6做一个简单的小软件,使用到WEBBROWSER控件来做个浏览器。但是缓存问题一直很难控制,总是显示缓存过的页面。查了很多资料,有关的信息就这么几条:
1,WEBBOWSER控件的缓存方式可以用IE来控制,也就是在IE[Internet选项]->[常规]->[Internet临时文件]的设置里去更改。但是作为一个独立运行的软件,不能让用户去IE里做设置。
2,本来WEBBROWSER的Navigate2方法提供了缓存控制参数:1,2,4,8,但实际上并没有实现这些参数的控制,所以是无效的。
3,可以用WINDOWS的API来删除缓存,但WEBBROWSER控件的缓存文件和IE放在一起,分不出来。API好象是根据条目序号来定位删除的文件,所以这种方法会把IE的缓存也删了。
  这样一来,问题就不好解决了。但好在用这个软件主要浏览的站点是可以控制的,所以可以在两方面用邪道方法绕开缓存。主要是两步:
1,在VB程序里,执行WEBBROWSER的Navigate方法之前,把URL后面加一个CACHE参数,参数值用个随机数。这样一来,所有从VB程序直接写URL访问的地址都不一样了,从而避免缓存。函数如下:
==============================
Public Function ranURL(ByVal strURL As String) As String
    Dim IMFlag As Integer
    Dim cacheNU As Long
   
    IMFlag = InStr(strURL, "?") '判断URL是否带参数
    cacheNU = Fix(Timer()) '取时间做随机数
    If IMFlag > 0 Then
        ranURL = strURL & "&cache=" & cacheNU
    Else
        ranURL = strURL & "?cache=" & cacheNU
    End If
End Function
==============================
2,在服务器端页面里,所有页面上的链接全在后面加一个CACHE参数,也用一个随机数。这样保证在VB程序里点击页面链接访问的地址也都不一样。
3月4日

VB6使用Toolbar控件制作工具栏,按钮图片不透明的问题

     最近用VB6开发一个小软件,做好了菜单条和工具栏(toolbar控件+imagelist控件),发现在imagelist控件里加入的图片使用透明背景显示正常,一放到toolbar控件里就显示为白色了。查了很多地方都没有解决,后来研究出这么种方法:
1,制作透明背景的ICO图片
2,设置imagelist控件的usemaskcolor属性为true
3,将所有ICO文件加入到imagelist里之后,设置backcolor属性为&H00FF00FF&,maskcolor属性为&H00FF00FF&
4,将这个imagelist控件关联到toolbar控件里,设置好toolbar控件按钮索引和imagelist控件图片索引的对应关系,按钮就显示为透明背景。
2月21日

mysql数据库同步校准问题

     MYSQL进行同步的文章不少,这里不多说了,反正就是配置一个MASTER给SLAVE设置好帐户、密码;多个SLAVE设置好MASTER地址、访问帐户、密码、同步哪个数据库等等。现在要说的是如何让两个以前不同步的MYSQL开始同步,并且保留以前的数据。
     首先要了解MYSQL同步的原理。其实就是MASTER上生成二进制LOG,里面记录了所有对MASTER进行数据修改的SQL语句。然后SLAVE从MASTER取这些LOG,在自己这边重新执行一下。这个过程中,SLAVE会记录最后一次同步从MASTER的哪个LOG里取的哪个编号的SQL语句,以便再同步的时候接着去取。如果设置了SLAVE的同步LOG(和设置二进制LOG不同,会生成两套LOG),则还会记录应该把MASTER的SQL语句保存到哪个文件里。
     知道了这些以后就好办了,手动修改、删除相应的记录就可以控制MYSQL从哪里开始同步了。我采用的方法以删除为主。
1,把MASTER上需要导入SLAVE的数据都导过去,然后关闭MYSQL,备份二进制LOG和INDEX,再把LOG和LOG的INDEX文件删除。比如我的LOG文件是localhost-bin.xxx(xxx是一个3位序号),INDEX文件是localhost-bin.index。最后启动MYSQL,会自动生成新的LOG(序号从001开始)和INDEX文件。
2,关闭SLAVE的MYSQL,把LOG和INDEX文件备份后删除。比如我的SLAVE上的LOG是localhost-relay-bin.xxx(xxx是一个3位序号),INDEX是localhost-relay-bin.index。如果以前没配置过同步,则不会有这些文件。
3,在SLAVE上的master.info也要删除,因为里面记录了从MASTER最后一次同步的LOG文件名,MASTER都从新从001开始记录LOG了,这里也要重新开始。如果以前没配置过同步,则不会有这个文件。
4,确定在MASTER和SLAVE的/etc/my.cnf(这是LINUX的路径)里都配置好同步的参数以后,启动SLAVE的MYSQL,进入控制台(mysql -u root -p),用show slave status\G看看同步的情况。
           Slave_IO_Running: Yes
          Slave_SQL_Running: Yes
有这两项说明同步进程已经启动了,MASTER上再有数据更改,在SLAVE上会同样更改。
 
     但是这种操作是有隐患的,因为MASTER和SLAVE上的数据是不统一的,主键数值不同。如果修改了MASTER上的数据,SQL语句会记录一个UPDATE....WHERE.....语句,其中的WHERE后面会指定主键值,这条SQL语句被存入LOG,拿到SLAVE去执行的时候,可能修改了不主键数值相同但其他字段不同的记录。
2月18日

UTF-8编码页面不显示的问题

      今天同事遇到个奇怪现象,程序生成的HTML页面使用UTF-8编码,奇怪的是有些可以正常显示,有些就显示空白页,而查看源代码则显示所有页面字符已经下载下来了。更奇怪的是,如果在不显示的页面代码里加1个空格,就可以正常显示了?!真真奇哉怪也!
     后来终于在GOOGLE上找到这么一个文章,解释了这个现象。原页面地址为http://bbs.mambochina.net/viewthread.php?tid=15339
================引用===================
在windows操作系统上使用IE作为浏览器时。常常会发生这样的问题:在浏览使用UTF-8编码的网页时,浏览器无法自动侦测(即没有设定“自动选择”编码格式时)该页面所用的编码。即使网页已经声明过编码格式:
<meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″ />
由此造成某些含有中文UTF-8编码的页面产生空白输出。
如果使用的是Mozilla、Mozilla Firefox、Sarafi的浏览器这不会造成这个问题。这是由于IE解析网页编码时以HTML内的标签优先,而后才是HTTP header内的讯息;而mozilla系列的浏览器则刚刚相反。
由于UTF-8为3个字节表示一个汉子,而普通的GB2312或BIG5是两个。页面输出时,由于上述原因,使浏览器解析、输出<title> </title>的内容时,如果在</title>前有奇数个全角字符时,IE把UTF-8当作两个字节解析时出现半个汉字的情况,这时该半个汉字会和</title>的<结合成一个乱码字,导致IE无法读完<title>部分,使整个页面为空百输出。而这个时候如果察看源文件的话,会发现实际上整个页面全部已经输出了。
因此最简单的解决办法是再网页文件的
标签中一定要把字符定义<meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″ />放在<title></title>之前。
===============引用结束================
     修改之后果然可以正常显示了。
1月30日

Red Hat Linux AS4 安装PHP5

为了将公司原有的论坛移植到新服务器上,需要在新服务器上安装论坛程序。但是在安装时报出不支持MYSQL的错误信息。

当初在新服务器安装LINUX AS4的时候,PHPAPACHE是默认安装的,而且PHP脚本可以被解释。试着写了几个PHP页面,发现在连接MYSQL的时候报错:

Call to undefined function: mysql_connect()

这说明数据库连接方法出错。到/etc/php.ini里去看,发现默认端口和默认SOCKET都没有设置,把3306/var/lib/mysql/mysql.sock写上去,重起APACHE,还是不行,很奇怪。   

经过查询,找到这么一篇文章:

http://blog.chinaunix.net/u/15010/showart_395725.html

MYSQL4版本以上,调用libmysqlclient.so 的方式改了,跟MYSQL3.23不一样,而PHP4.3.2APACHE还是以旧的方式来调用,因此就找不到libmysqlclient.so 了。
英文原文如下:
-------------------------------------------
Ensure that you don't have any MySQL clients that use shared libraries (like the Perl DBD::mysql module). If you do, you should recompile them, because the data structures used in `libmysqlclient.so' have changed. The same applies to other MySQL interfaces as well, such as the Python MySQLdb module.
-------------------------------------------

而默认安装的PHP4.x的,MYSQL5.x的,所以说还是版本冲突造成的无法连接数据库,需要安装PHP5

 

先卸载PHP,在执行rpm –e 的时候报错,有其他包依赖PHP包,不能卸载。这时记录下有依赖关系的包,逐个强行卸载。

PHP提供的是源码,需要自己编译,所以安装PHP5之前先安装MYSQL的开发包:MySQL-devel-community-5.0.45-0.rhel4.i386.rpm然后在/usr/include/mysql/目录下找到mysql.h文件。

/home/bet310下把PHP的源码包解压,目录是php-5.2.5

然后进入cd php-5.2.5

配置 ./configure \

--prefix=/usr/local/php5.2.5 \

--with-mysql=/usr/include/mysql/ \

--with-apxs2=/usr/sbin/apxs

       这里设置了PHP安装路径:/usr/local/php5.2.5;指定了mysql.h路径:/usr/include/mysql/;指定了APX2路径:/usr/sbin/apxs

配置完成以后 make

                          make install

复制PHP配置文件到安装目录 cp /home/bet310/php-5.2.5/php.ini.dist /usr/local/php5.2.5/lib/php.ini

    安装完毕。

 

    然后开始配置PHP。首先在/etc/httpd/conf/httpd.conf文件里找到这样一段:

#

# AddType allows you to add to or override the MIME configuration

# file mime.types for specific file types.

#

在下面加上:

AddType application/x-httpd-php .php .phtml

AddType application/x-httpd-php-source .phps

 

然后找到这里:

#

# Dynamic Shared Object (DSO) Support

#

发现在加载模块列表的最后已经自动加上了这样一行:

LoadModule php5_module        /usr/lib/httpd/modules/libphp5.so

最后重起APACHE就可以了。

12月23日

批量执行SQL语句

    最近工作方面遇到这么个问题,向数据库里一次插入数千到数万条记录,如果直接用INSERT INTO的方式,1条1条的插入,速度无法接受。
    遇到这种情况一般应该设置一个缓存,先向缓存里写,然后把缓存里的记录一次性写入到目标空间内。

    查了一下JAVA以及MYSQL的资料,两方面都提供了解决方法。MYSQL的是可以在1条INSERT语句中写入多条需要插入的数据,当然数据顺序要和字段顺序一致,最好给出字段顺序先。
    例如:INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6);
    这样的话可以控制一下,每1000条(或者根据总数制定缓存空间)数据写一个SQL。如果还是太慢,可以用INSERT DELAYED语句,先返回成功信息,由MYSQL自己缓存插入操作,等数据库空闲时进行插入。
    但这样会让SQL语句特别长,数年前(2000年)使用MYSQL的时候,在服务器负荷特别大的时候,出现过SQL语句被截断执行的现象。可惜那时候还轮不到我去查原因,只留下了这么个心理阴影,以后尽量不用MYSQL做压力特别大的应用,尽量缩短SQL语句长度。如果用INSERT DELAYED,这个只是MYSQL支持,以后移植还可能有麻烦。所以我想最好找JDK提供的方法。

    JDK提供的方法就是Statement对象以及addBatch(sql)、executeBatch()方法,就是建一个Statement对象,把要执行的SQL语句ADD进1批里,然后执行批处理。但要先取消数据库连接的AutoCommit,执行完之后再恢复。还有就是设置一下每批的数量,用循环控制。我设置的是1000条SQL/批。
程序如下:
    /**
     * SQL批处理,每1000条提交1次,适用于不接收返回值的SQL语句
     * @param sqls 需要执行的SQL语句列表
     * @return 是否成功
     * @throws Exception
     */
    public static boolean doBatch(List sqls) throws Exception {
        Connection conn = null;
        Statement stmt = null;
        boolean result = false;
        if (!loadConfig()) {    //获取数据库连接配置,方法已经实现
            return false;
        }
        if (sqls == null || sqls.isEmpty()) {
            showDebugInfo("sql list empty!");    //DEBUG用的方法,已经实现
            return false;
        }
        int looplen = sqls.size()/1000;    //每1000条执行一次
        conn = getConnection();    //获取数据库连接方法,已经实现
        try {
            conn.setAutoCommit(false);    //停止自动提交
            stmt = conn.createStatement();
            int i=0;
            int nextstop=1000;
            String sql="";
            for (int x=0; x<looplen; x++){
             while (i<nextstop){
              sql = sqls.get(i).toString();
                    if (debug.equals("true")) {
                        showDebugInfo(sql);
                    }
                    stmt.addBatch(sql);
              i++;
             }
             nextstop=nextstop+1000;
             stmt.executeBatch();
            }    //1000整数倍条SQL已经执行完毕
            while(i<sqls.size()){    //处理零头SQL
             sql = sqls.get(i).toString();
                if (debug.equals("true")) {
                    showDebugInfo(sql);
                }
                stmt.addBatch(sql);
             i++;
            }
            stmt.executeBatch();
            conn.commit();    //最后执行提交
            result = true;
        }
        catch (Exception e) {
            conn.rollback();    //如果执行过程中出现异常,进行回滚操作
            result = false;
            throw e;
        }
        finally {
            try {
                conn.setAutoCommit(true);    //恢复自动提交
            }
            catch (SQLException ex) {
                throw ex;
            }
            close(null, stmt, conn);    //连接关闭方法,已经实现
        }
        return result;
    }
//========================================================
    使用以上方法,10万条数据可以在20多秒时间内完成插入。可能还有优化空间,我会继续研究。

9月20日

填补服务器配置的一个漏洞

之前配置服务器的时候,忘记把WEB-INF目录保护起来,这样APACHE可以访问里面,把CLASS文件下载。 
今天想起来了,就利用SELinux的权限系统把WEB-INF目录设置为非APACHE内容的类型,也就下载不了了。
9月13日

TOMCAT5.5.16开始出现的无法找到包、类的问题

参考资料:
 
     这个问题弄的我挺郁闷的,不过TOMCAT是免费的,出这种问题也算是一种代价吧。
 
     现象是在WEB发布目录下建立的目录里的JSP找不到指定的包、类。比如/home/abc/web作为发布目录,/home/abc/web/xyz目录下的JSP如果使用某个包,会报找不到类的错误。但是如果把报错的JSP放到/home/abc/web下就可以正常显示。或者把/home/abc/web/WEB-INF/classes里面需要的包拷贝到/home/abc/web/xyz/WEB-INF/classes也正常。真见鬼。
     多方查找之后,发现是5.5.16开始的一个新设定,把WEB发布目录下的各个目录自动认为是多个WEB发布目录,如果里面没有WEB-INF目录,那就找不到要用的包或类。真不明白那些开发人是怎么想的,这种变化将让很多WEB服务在升级之后无法正常运转。最要命的是,现在过去1年了,版本到了5.5.25,这个设定依然没有改。还是靠自己解决吧,别等了。
     解决方案1:降回5.5.15,虽然可能某些功能被削弱,总比不能正常使用强。
     解决方案2:在WEB发布目录里不要建立其他目录。虽然这不会出问题了,但是会比较混乱,不提倡。
     解决方案3:在原先设置的WEB发布目录下多建1级目录,然后把原来所有文件和目录都放进去。比如建立/home/abc/web/web目录,把/home/abc/web下的东西都放到/home/abc/web/web下。修改server.xml,把原先配置的虚拟主机的Context改成<Context path="" docBase="/home/abc/web/web/" workDir=""  reloadable="true"/>这样原先的访问路径也不用变了。
8月30日

一篇有关JAVA线程的文章(转载)

 
Java 多线程入门大全(适用于有一定基础者)
 
先从线程的创建说起.线程的创建一共有两种形式:
--------------------------------------------------------------------------------
    一种是继承自Thread类.Thread 类是一个具体的类,即不是抽象类,该类封装了线程的行为.要创建一个线程,程序员必须创建一个从 Thread 类导出的新类.程序员通过覆盖 Thread 的 run() 函数来完成有用的工作.用户并不直接调用此函数;而是通过调用 Thread 的 start() 函数,该函数再调用 run().
    
    例如: 
    public class Test extends Thread{
      public Test(){
      }
      public static void main(String args[]){
        Test t1 = new Test();
        Test t2 = new Test();
        t1.start();
        t2.start();
      }
      public void run(){
        //do thread's things
      }
    }
--------------------------------------------------------------------------------
    另一种是实现Runnable接口,此接口只有一个函数,run(),此函数必须由实现了此接口的类实现.
    
    例如: 
    public class Test implements Runnable{
      Thread thread1;
      Thread thread2;
      public Test(){
        thread1 = new Thread(this,"1");
        thread2 = new Thread(this,"2");
      }
      public static void main(String args[]){
        Test t = new Test();
        t.startThreads();
      }
      public void run(){
        //do thread's things
      }
      public void startThreads(){
        thread1.start();
        thread2.start();
      }
    }
    两种创建方式看起来差别不大,但是弄不清楚的话,也许会将你的程序弄得一团糟.两者区别有以下几点:
1.当你想继承某一其它类时,你只能用后一种方式.
2.第一种因为继承自Thread,只创建了自身对象,但是在数量上,需要几个线程,就得创建几个自身对象;第二种只创建一个自身对象,却创建几个Thread对象.而两种方法重大的区别就在于此,请你考虑:如果你在第一种里创建数个自身对象并且start()后,你会发现好像synchronized不起作用了,已经加锁的代码块或者方法居然同时可以有几个线程进去,而且同样一个变量,居然可以有好几个线程同时可以去更改它.(例如下面的代码)这是因为,在这个程序中,虽然你起了数个线程,可是你也创建了数个对象,而且,每个线程对应了每个对象也就是说,每个线程更改和占有的对象都不一样,所以就出现了同时有几个线程进入一个方法的现象,其实,那也不是一个方法,而是不同对象的相同的方法.所以,这时候你要加锁的话,只能将方法或者变量声明为静态,将static加上后,你就会发现,线程又能管住方法了,同时不可能有两个线程进入同样一个方法,那是因为,现在不是每个对象都拥有一个方法了,而是所有的对象共同拥有一个方法,这个方法就是静态方法.
    而你如果用第二种方法使用线程的话,就不会有上述的情况,因为此时,你只创建了一个自身对象,所以,自身对象的属性和方法对于线程来说是共有的.
    因此,我建议,最好用后一种方法来使用线程.
public class mainThread extends Thread{
  int i=0;
  public static void main(String args[]){
    mainThread m1 = new mainThread();
    mainThread m2 = new mainThread();
    mainThread m3 = new mainThread();
    mainThread m4 = new mainThread();
    mainThread m5 = new mainThread();
    mainThread m6 = new mainThread();
    m1.start();
    m2.start();
    m3.start();
    m4.start();
    m5.start();
    m6.start();
  }
  public synchronized void t1(){
    i=++i;
    try{
        Thread.sleep(500);
    }
    catch(Exception e){}
    //每个线程都进入各自的t1()方法,分别打印各自的i
    System.out.println(Thread.currentThread().getName()+" "+i);
  }
  public void run(){
    synchronized(this){
      while (true) {
        t1();
      }
    }
  }
}
--------------------------------------------------------------------------------
    下面我们来讲synchronized的4种用法吧:
    1.方法声明时使用,放在范围操作符(public等)之后,返回类型声明(void等)之前.即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入.
      例如:
      public synchronized void synMethod() {
        //方法体
      }
    2.对某一代码块使用,synchronized后跟括号,括号里是变量,这样,一次只有一个线程进入该代码块.例如:
      public int synMethod(int a1){
        synchronized(a1) {
          //一次只能有一个线程进入
        }
      }
    3.synchronized后面括号里是一对象,此时,线程获得的是对象锁.例如:
public class MyThread implements Runnable {
  public static void main(String args[]) {
    MyThread mt = new MyThread();
    Thread t1 = new Thread(mt, "t1");
    Thread t2 = new Thread(mt, "t2");
    Thread t3 = new Thread(mt, "t3");
    Thread t4 = new Thread(mt, "t4");
    Thread t5 = new Thread(mt, "t5");
    Thread t6 = new Thread(mt, "t6");
    t1.start();
    t2.start();
    t3.start();
    t4.start();
    t5.start();
    t6.start();
  }
  public void run() {
    synchronized (this) {
      System.out.println(Thread.currentThread().getName());
    }
  }

 
    对于3,如果线程进入,则得到对象锁,那么别的线程在该类所有对象上的任何操作都不能进行.在对象级使用锁通常是一种比较粗糙的方法.为什么要将整个对象都上锁,而不允许其他线程短暂地使用对象中其他同步方法来访问共享资源?如果一个对象拥有多个资源,就不需要只为了让一个线程使用其中一部分资源,就将所有线程都锁在外面.由于每个对象都有锁,可以如下所示使用虚拟对象来上锁:
class FineGrainLock {
   MyMemberClass x, y;
   Object xlock = new Object(), ylock = new Object();
   public void foo() {
      synchronized(xlock) {
         //access x here
      }
      //do something here - but don't use shared resources
      synchronized(ylock) {
         //access y here
      }
   }
   public void bar() {
      synchronized(this) {
         //access both x and y here
      }
      //do something here - but don't use shared resources
   }
}
    4.synchronized后面括号里是类.例如:
class ArrayWithLockOrder{
  private static long num_locks = 0;
  private long lock_order;
  private int[] arr;
  public ArrayWithLockOrder(int[] a)
  {
    arr = a;
    synchronized(ArrayWithLockOrder.class) {//-----------------------------------------这里
      num_locks++;             // 锁数加 1.
      lock_order = num_locks;  // 为此对象实例设置唯一的 lock_order.
    }
  }
  public long lockOrder()
  {
    return lock_order;
  }
  public int[] array()
  {
    return arr;
  }
}
class SomeClass implements Runnable
  {
  public int sumArrays(ArrayWithLockOrder a1,
                       ArrayWithLockOrder a2)
  {
    int value = 0;
    ArrayWithLockOrder first = a1;       // 保留数组引用的一个
    ArrayWithLockOrder last = a2;        // 本地副本.
    int size = a1.array().length;
    if (size == a2.array().length)
    {
      if (a1.lockOrder() > a2.lockOrder())  // 确定并设置对象的锁定
      {                                     // 顺序.
        first = a2;
        last = a1;
      }
      synchronized(first) {              // 按正确的顺序锁定对象.
        synchronized(last) {
          int[] arr1 = a1.array();
          int[] arr2 = a2.array();
          for (int i=0; i<size; i++)
            value += arr1[i] + arr2[i];
        }
      }
    }
    return value;
  }
  public void run() {
    //...
  }
}
    对于4,如果线程进入,则线程在该类中所有操作不能进行,包括静态变量和静态方法,实际上,对于含有静态方法和静态变量的代码块的同步,我们通常用4来加锁.
以上4种之间的关系:
    锁是和对象相关联的,每个对象有一把锁,为了执行synchronized语句,线程必须能够获得synchronized语句中表达式指定的对象的锁,一个对象只有一把锁,被一个线程获得之后它就不再拥有这把锁,线程在执行完synchronized语句后,将获得锁交还给对象.
    在方法前面加上synchronized修饰符即可以将一个方法声明为同步化方法.同步化方法在执行之前获得一个锁.如果这是一个类方法,那么获得的锁是和声明方法的类相关的Class类对象的锁.如果这是一个实例方法,那么此锁是this对象的锁.
  下面谈一谈一些常用的方法:
  wait(),wait(long),notify(),notifyAll()等方法是当前类的实例方法,
        wait()是使持有对象锁的线程释放锁;
        wait(long)是使持有对象锁的线程释放锁时间为long(毫秒)后,再次获得锁,wait()和wait(0)等价;
        notify()是唤醒一个正在等待该对象锁的线程,如果等待的线程不止一个,那么被唤醒的线程由jvm确定;
        notifyAll是唤醒所有正在等待该对象锁的线程.
        在这里我也重申一下,我们应该优先使用notifyAll()方法,因为唤醒所有线程比唤醒一个线程更容易让jvm找到最适合被唤醒的线程.
    对于上述方法,只有在当前线程中才能使用,否则报运行时错误java.lang.IllegalMonitorStateException: current thread not owner.
--------------------------------------------------------------------------------
    下面,我谈一下synchronized和wait(),notify()等的关系:
1.有synchronized的地方不一定有wait,notify
2.有wait,notify的地方必有synchronized.这是因为wait和notify不是属于线程类,而是每一个对象都具有的方法,而且,这两个方法都和对象锁有关,有锁的地方,必有synchronized.
另外,请注意一点:如果要把notify和wait方法放在一起用的话,必须先调用notify后调用wait,因为如果调用完wait,该线程就已经不是current thread了.如下例:
/**
 * Title:        Jdeveloper's Java Projdect
 * Description:  n/a
 * Copyright:    Copyright ? 2001
 * Company:      soho  http://www.chinajavaworld.com/
 * @author jdeveloper@21cn.com
 * @version 1.0
 */
import java.lang.Runnable;
import java.lang.Thread;
public class DemoThread
    implements Runnable {
  public DemoThread() {
    TestThread testthread1 = new TestThread(this, "1");
    TestThread testthread2 = new TestThread(this, "2");
    testthread2.start();
    testthread1.start();
  }
  public static void main(String[] args) {
    DemoThread demoThread1 = new DemoThread();
  }
  public void run() {
    TestThread t = (TestThread) Thread.currentThread();
    try {
      if (!t.getName().equalsIgnoreCase("1")) {
        synchronized (this) {
          wait();
        }
      }
      while (true) {
        System.out.println("@time in thread" + t.getName() + "=" +
                           t.increaseTime());
        if (t.getTime() % 10 == 0) {
          synchronized (this) {
            System.out.println("****************************************");
            notify();
            if (t.getTime() == 100)
              break;
            wait();
          }
        }
      }
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}
class TestThread
    extends Thread {
  private int time = 0;
  public TestThread(Runnable r, String name) {
    super(r, name);
  }
  public int getTime() {
    return time;
  }
  public int increaseTime() {
    return++time;
  }
}
    下面我们用生产者/消费者这个例子来说明他们之间的关系:
    public class test {
     public static void main(String args[]) {
     Semaphore s = new Semaphore(1);
     Thread t1 = new Thread(s, "producer1");
     Thread t2 = new Thread(s, "producer2");
     Thread t3 = new Thread(s, "producer3");
     Thread t4 = new Thread(s, "consumer1");
     Thread t5 = new Thread(s, "consumer2");
     Thread t6 = new Thread(s, "consumer3");
     t1.start();
     t2.start();
     t3.start();
     t4.start();
     t5.start();
     t6.start();
   }
    }
class Semaphore implements Runnable {
  private int count;
  public Semaphore(int n) {
    this.count = n;
  }
  public synchronized void acquire() {
    while (count == 0) {
      try {
        wait();
      }
      catch (InterruptedException e) {
        //keep trying
      }
    }
    count--;
  }
  public synchronized void release() {
    while (count == 10) {
      try {
        wait();
      }
      catch (InterruptedException e) {
        //keep trying
      }
    }
    count++;
    notifyAll(); //alert a thread that's blocking on this semaphore
  }
  public void run() {
    while (true) {
      if (Thread.currentThread().getName().substring(0,8).equalsIgnoreCase("consumer")) {
        acquire();
      }
      else if (Thread.currentThread().getName().substring(0,8).equalsIgnoreCase("producer")) {
        release();
      }
      System.out.println(Thread.currentThread().getName() + " " + count);
    }
  }
}
    生产者生产,消费者消费,一般没有冲突,但当库存为0时,消费者要消费是不行的,但当库存为上限(这里是10)时,生产者也不能生产.请好好研读上面的程序,你一定会比以前进步很多.
    上面的代码说明了synchronized和wait,notify没有绝对的关系,在synchronized声明的方法,代码块中,你完全可以不用wait,notify等方法,但是,如果当线程对某一资源存在某种争用的情况下,你必须适时得将线程放入等待或者唤醒.  
8月28日

SELinux的奇怪现象,CSS样式表不正常

昨天遇到一个奇怪现象,首先是WEB发布目录下的CSS样式表不能使用,直接输入URL显示没有权限。
但是CSS所在的include目录已经设置为httpd_user_content_t的类型,重新启动APACHE也一样。
然后把*.CSS改成httpd_sys_content_t类型,访问正常了。
今天把*.css改回httpd_user_content_t类型,重起APACHE,访问也正常。真是见鬼。
也许是IE的缓存问题,或者是SELinux和APACHE有那么点儿冲突,我打算还是用httpd_user_content_t类型,免得和系统页面混淆。
记录有用的页面地址:
http://www.mitre.org/tech/selinux/apache.txt(SELinux对于APACHE的配置策略,英文)
8月27日

TOMCAT启动时报Can't detect initial thread stack location

经过GOOGLE,觉得这个错误不影响使用。 有一些人遇到相同的问题,没有影响正常应用。
这个错误原因似乎是因为启动TOMCAT的用户没有访问和操作/proc的权限,不过我不想修改了,以后也许启动脚本和执行程序会升级,解决这个问题。
8月26日

低级错误:index.jsp页面显示为空白

 今天遇到个奇怪问题,以前做的那个服务器,用目录访问的时候显示空白页面。
/etc/httpd/conf/httpd.conf
这里把index.jsp加到DirectoryIndex后面了,可还是显示空白页面。
发布目录的WEB-INF下的web.xml里已经加了
  <welcome-file-list>
    <!-- Redirects to "welcome.htm" for dispatcher handling -->
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
还是不行。
但是写上index.jsp就可以正常显示。
 
反复查找没找到原因,后来发现其他没有index.jsp页面的路径下直接目录访问会报404错误,所以之前那里应该已经识别到有index.jsp了。到目录下仔细一看,原来有一个index.html存在,而且是0字节的空文件。就是它让APACHE先处理了(index.html排在index.jsp前面),而且是空的,自然就是空白页面。实在是个低级错误。
 
教训是,以后看文件列表要仔细。而且避免使用一些开发软件的自动生成功能,这个空文件不知道是哪个人给弄上去的。
8月19日

OUTLOOK2003无法接受任务和进度无法更新问题及解决

最近遇到这么一个奇怪问题:
想用OUTLOOK2003的任务功能做开发项目管理,但是其他人分派任务和更新任务进度都正常,就是我这里不行。接收到的任务和任务进度更新能在OUTLOOK的阅读窗格里看到内容,但不能双击打开;别人分派的任务我这里虽然可以看到“接受”、“拒绝”两个选择,但是点击以后没有任何反映,既不会弹出立即发送确认消息的窗口,也不会出现错误信息。
用GOOGLE搜索,发现也有个别人有类似问题,或者是接收到的会议安排出现类似现象。但是中文页面上没有提供解决方法。
我怀疑是补丁没有打全,通过自动更新找到一些OUTLOOK的补丁打上,问题依旧。
接着GOOGLE英文站点,发现类似问题可以找到很多,而且解决方法也提了很多,主要分成两种:
1,继续打补丁。我这种情况是由于以前的补丁不能兼容所有计算机造成的(也许是和某个软件冲突,或者和微软其他补丁冲突),现在有一个Hotfix KB899340的新补丁来做补丁的补丁。但是这个新补丁不能直接下载,要北美用户和微软的技术支持联系,然后发送。我想包括我在内的绝大多数大陆用户都不会考虑这种解决方法的。
2,删除补丁。因为是补丁造成的问题,所以删掉就可以了。但是这要冒一定的风险,毕竟是把系统漏洞给敞开了,最好配合病毒防火墙。网上提到的可能造成我这类问题的补丁有KB9138072007年1月9日和1月10日两天发布的补丁,还有Adobe Reader 8也可能造成无法接受会议通知。
 
我是把有关OUTLOOK的所有补丁,以及Adobe Reader 8都卸载了才解决。也许卸多了,以后再说吧。
进入“控制面板”的“添加删除程序”,点上“显示更新”,等一会儿,安装的补丁包就显示出来了。OUTLOOK的和OFFICE的在一起。
卸栽补丁以后,以前的任务还是不能接受和更新,但是新的任务就可以了。仔细看前后两种任务的邮件,以前的被认为有附件,而正常的就不显示有附件。
8月6日

Linux AS4、Apache、Tomcat、Mysql、SELinux安装与配置

从来不爱写日记,现在想记录一些工作上总结的经验,不知道能坚持多久。
 
本次LINUX安装过程。
Red Hat Enterprise Linux Advance Server 4 U2
Apache2.0.52(系统自带安装)
Tomcat5.5.23
Mysql5.0.45
以架设WEB服务器为目标。
 
     首先Linux AS会自动安装Apache、PHP、Mysql等WEB服务软件和数据库,即使在安装时候不选择安装也没用(我认为这是个BUG),而且安装之后不能用rpm -e xxxxx(模块名)的方式卸载,因为缺少其他模块。使用最小安装可以防止自动安装,但是没有XWINDOW等,自己再去找安装包很不方便,所以只好在安装好之后用rpm -e --nodeps xxxxx的方式强制卸载。本次安装Mysql就是在强行卸载系统自带的之后安装的(rpm -e xxxxxxx然后rpm -ivh MySQL-server-community-5.0.45-0.rhel4.i386.rpm以及rpm -ivh MySQL-client-community-5.0.45-0.rhel4.i386.rpm)。
     Mysql之后是安装JDK和Tomcat,注意先卸载系统自带的JDK,先rpm -qa|grep java查看已经安装的JAVA模块,然后rpm -e xxxx卸载之。JDK是执行文件,增加执行权限之后即可安装
chmod +x jdk-1_5_0_12-linux-i586-rpm.bin(我用的版本)
./jdk-1_5_0_12-linux-i586-rpm.bin
后面很多版权信息,注意别按太多次键盘,最后要输入yes表示同意的。然后编辑环境变量(vi /etc/profile)增加如下内容:
JAVA_HOME=/usr/java/jdk1.5.0_12
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/jre/lib/tools.jar
PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin
修改以下部分:
export JAVA_HOME CLASSPATH PATH USER LOGNAME MAIL HOSTNAME HISTSIZE INPUTRC
然后把JAVA_HOME、CLASSPATH、PATH都export一下。
     TOMCAT把包解开就可以,不过要设置成系统服务,自动运行,并且不用root身份运行,需要做一些编译操作。在把解开的包移动到/usr/local/tomcat5之后,在/usr/local/tomcat5/bin/jsvc-src/下面有jsvc的源程序,需要编译。
chmod +x configure(增加可执行权限)
./configure --with-java=/usr/java/java/jdk1.5.0_12(我的JDK安装位置)
make
进入下一级native目录,里面有Tomcat5.sh和Tomcat.sh两个脚本,编辑前者,在开头部分增加两行
#chkconfig: 2345 85 15
#description: apache-tomcat.5
在下面根据自己的路径修改变量,我的是:
# Adapt the following lines to your configuration
JAVA_HOME=/usr/java/jdk1.5.0_12
CATALINA_HOME=/usr/local/tomcat5
DAEMON_HOME=/usr/local/tomcat5
TOMCAT_USER=tomcat
# for multi instances adapt those lines.
TMP_DIR=/var/tmp
PID_FILE=/var/run/jsvc.pid
CATALINA_BASE=$CATALINA_HOME
CATALINA_OPTS=
CLASSPATH=\
$JAVA_HOME/lib/tools.jar:\
$CATALINA_HOME/bin/commons-daemon.jar:\
$CATALINA_HOME/bin/bootstrap.jar
case "$1" in
  start)
    #
    # Start Tomcat
    #
    $DAEMON_HOME/bin/jsvc-src/jsvc \
    -user $TOMCAT_USER \
    -home $JAVA_HOME \
    -Dcatalina.home=$CATALINA_HOME \
    -Dcatalina.base=$CATALINA_BASE \
    -Djava.io.tmpdir=$TMP_DIR \
    -wait 10 \
    -pidfile $PID_FILE \
    -outfile $CATALINA_HOME/logs/catalina.out \
    -errfile '&1' \
    $CATALINA_OPTS \
    -cp $CLASSPATH \
    org.apache.catalina.startup.Bootstrap
    #
    # To get a verbose JVM
    #-verbose \
    # To get a debug of jsvc.
    #-debug \
    exit $?
    ;;
  stop)
    #
    # Stop Tomcat
    #
    $DAEMON_HOME/bin/jsvc-src/jsvc \
    -stop \
    -pidfile $PID_FILE \
    org.apache.catalina.startup.Bootstrap
    exit $?
    ;;
  *)
    echo "Usage tomcat.sh start/stop"
    exit 1;;
esac
     接下来是Apache和Tomcat的整合,注意现在最好先把SELinux停止(在安装linux的时候会问是否开启,如果开启了可以在图形环境下设置关闭或者在/etc/selinux/config里设置为禁止),然后重起一下让SELinux失效,以后再配置让它生效。
     从http://tomcat.apache.org下载tomcat-connectors-1.2.23-src.tar.gz,解开包之后在native目录里进行编译(如果下载了编译好的2进制文件有可能不被认为是Apache的模块)。
chmod +x configure(增加可执行权限)
./configure --with-apxs=/xxxx(apxs位置,可以用whereis apxs来获得)
make
     如果使用自带的Apache,会没有apxs这个模块,需要从安装光盘里找到httpd-devel-2.0.52-19.ent.i386.rpm文件自己安装,然后再编译。完成以后mod_jk.so文件会放到Apache的模块目录中(我的是/usr/lib/httpd/modules)。然后在Apache的配置文件目录里新建一个mod_jk.conf文件,内容为:
#
#add by liupeng. install apache-tomcat-connector
#
LoadModule jk_module modules/mod_jk.so
# Where to find workers.properties
JkWorkersFile /etc/httpd/conf/workers.properties
# Where to put jk shared memory
JkShmFile /var/log/httpd/mod_jk.shm
# Where to put jk logs
JkLogFile /var/log/httpd/mod_jk.log
# Set the jk log level [debug/error/info]
JkLogLevel info
# Select the timestamp log format
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
# JkOptions indicate to send SSL KEY SIZE,
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
# JkRequestLogFormat set the request format
JkRequestLogFormat "%w %V %T"
# Send servlet for context /examples to worker named worker1
JkMount /*/servlet/* worker1
# Send JSPs  for context /examples to worker named worker1
JkMount /*.jsp worker1
新建一个workers.properties文件,内容为
# Define 1 real worker using ajp13
worker.list=worker1
# Set properties for worker1 (ajp13)
worker.worker1.type=ajp13
worker.worker1.host=localhost
worker.worker1.port=8009
worker.worker1.lbfactor=50
这样Apache会把这个目录下所有.conf文件作为配置文件加载,如果不行,就在http.conf里加1行
include /etc/httpd/conf/mod_jk.conf(mod_jk.conf的路径)
     完成以后重起Tomcat和Apache,有可能会报无法找到主机域名。把Apache和Tomcat的配置文件里ServerName那项设置为localhost(默认是被注释掉的)可以解决。至于如何配置WEB发布目录和测试连接器,网上文章很多
     最后是配置SELinux。先把它设置为起用,然后重起,很可能Apache已经不能启动。这时检查一下Apache配置文件的目录(/etc/httpd/conf)下的文件:
ls -Z
可能新建的mod_jk.conf和workers.properties两个文件的用户、规则、类型与其他文件不同,或者根本是空,用以下方法修改:
chcon -R -h root:object_r:httpd_config_t mod_jk.conf
chcon -R -h root:object_r:httpd_config_t workers.properties
如果要使用自己新建的目录作为WEB发布目录,也要针对新目录做类似操作,否则Apache不能访问。
chcon -R -t httpd_user_content_t /xxxxxx(自己随便建立的目录)
如果某个文件或目录的用户、规则、类型为空,则不能只设定一个,需要
chcon -R -h 用户:规则:类型 文件或目录名
这样一次完成设定。
SELinux也配置了一些默认的可以作为WEB发布的目录,在/etc/selinux/targeted目录下记录着这些信息。比如/home下的各个用户目录下的www或public_html或web目录等。如果发现某个页面无法访问,除了检查LINUX的权限设置以外,也要注意检查SELinux的设置。