鹏 的个人资料皓月清风照片日志列表更多 ![]() | 帮助 |
|
|
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页面上的pageEncoding和contentType的charset 是指定不同的编码方式,前者指定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,只是目前我还没有用到。如果所有规则都没有匹配到,就执行默认的操作。具体流程可以参考下图:
以下是我做的配置,首先打开配置文件,直接对内容进行编辑:
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的时候,PHP和APACHE是默认安装的,而且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.2和APACHE还是以旧的方式来调用,因此就找不到libmysqlclient.so 了。 而默认安装的PHP是4.x的,MYSQL是5.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条的插入,速度无法接受。 JDK提供的方法就是Statement对象以及addBatch(sql)、executeBatch()方法,就是建一个Statement对象,把要执行的SQL语句ADD进1批里,然后执行批处理。但要先取消数据库连接的AutoCommit,执行完之后再恢复。还有就是设置一下每批的数量,用循环控制。我设置的是1000条SQL/批。 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,删除补丁。因为是补丁造成的问题,所以删掉就可以了。但是这要冒一定的风险,毕竟是把系统漏洞给敞开了,最好配合病毒防火墙。网上提到的可能造成我这类问题的补丁有KB913807和2007年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的设置。 |
|
|