很多公司在招聘高级系统维护人员的时候,希望应聘者有一些开发经验或者要会写程序,至少也要读得懂用某些语言写的程序并能作简单修改。这并非是无事找事,故意抬高门槛。实际上,有的时候,为了能够更好地监控我们的系统,或者验证我们安装的系统满足应用的要求,我们必须要使用一些代码。比如,我们的应用服务器 运行正常吗?我们的数据库连接正常吗?我们的服务器把 HTTP request 正确地传给了我们的应用服务器吗?我们的MySQL + Resin 能正常处理汉字吗? 这些在实践中都是常常遇到的问题,下面我们来仔细说说。
一. 应用服务器运行正常吗?
相对于其它应用来说,WEB 应用比较特殊,因为它需要放到应用服务器或者说 WEB 容器里面来运行。由于既有 WEB 应用,又有 WEB 应用服务器,两者交织在了一起,当出现问题的时候,可能就会出现不能快速准确定位到底是 WEB 应用的问题还是 WEB 应用服务器的问题。所有,我们应该有相应的措施来区分问题到底出现在哪个身上。笔者在实践中采取的措施是,用了一个简单的 JSP 页面来实时验证 WEB 应用服务器的状况,当监控系统(比如what'sup)保障的时候,监控人员只需要通过查看 WEB 应用服务器监控页面,就知道到底是哪个的问题了,然后采取相应的措施。笔者所用的代码可以说是再简单不过了:
# cat $RESIN_HOME/webapps/ROOT/test_resin.jsp
<%= 1 + 1 %>
然后,在 What'sup 里面进行配置,让它去 GET 该页面,如果能够取到值2, 就显示正常,否则,报警。
二. 数据库连接正常吗?
有一段时间,数据库老出问题,监控人员分不清到底是系统问题还是数据库问题,就老是“骚扰”我,比较痛苦。为此,笔者“凑”了下面的代码,来监控数据库:
# cat $RESIN_HOME/webapps/ROOT/test_oracle.jsp
<%@ page contentType="text/html;charset=gb2312" import="java.sql.*" %> <% // define variables String CLASS="oracle.jdbc.driver.OracleDriver"; String NODE1_AND_DB="jdbc:oracle:thin:@(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 172.17.1.3)(PORT = 1521))(CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME =RACDB.GLOBAL)))"; String NODE2_AND_DB="jdbc:oracle:thin:@(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 172.17.1.4)(PORT = 1521))(CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME =RACDB.GLOBAL)))"; String USER="<username>"; String PASSWORD="<password>"; String SQL="select count(*) from tab";
// init Connection conn = null; Statement stmt = null; ResultSet rs = null; // check NODE1 try { Class.forName(CLASS); conn = DriverManager.getConnection(NODE1_AND_DB,USER,PASSWORD); stmt = conn.createStatement(); rs=stmt.executeQuery(SQL); out.println("<br>"); out.println("172.17.1.3 is OK"); } catch(SQLException e) { out.println("172.17.1.3 is not OK"); } finally { try { if(rs != null) rs.close(); if(stmt != null) stmt.close(); if(conn != null) conn.close(); } catch(SQLException e) { out.println("DB close error " + getClass()); } }
// check NODE2 try { Class.forName(CLASS); conn = DriverManager.getConnection(NODE2_AND_DB,USER,PASSWORD); stmt = conn.createStatement(); rs=stmt.executeQuery(SQL); out.println("<br>"); out.println("172.17.1.4 is OK"); } catch(SQLException e) { out.println("172.17.1.4 is not OK"); } finally { try { if(rs != null) rs.close(); if(stmt != null) stmt.close(); if(conn != null) conn.close(); } catch(SQLException e) { out.println("DB close error " + getClass()); } } %>
由于我们的数据库都是 RAC 来的,所以需要监控两个结点,从上面的代码可以看出,该页面能够监控RAC的两个结点。 然后,在 What'sup 里面配置,让它去 GET 该页面,如果返回"is OK",则显示正常,否则,报警。
注意,尽管该代码是监控 ORACLE RAC 的,但是只要经过简单修改,就可以用来监控其它数据库了。相信读者有修改这点简单代码的功力,所以笔者就不具体阐述了。
三. 服务器把 HTTP request 正确地传给了我们的应用服务器吗?
曾经遇到过一个怪事情,笔者维护的一台服务器的 RESIN 不能正常处理 HTTP GET 请求,但是 telnet 8080 端口又没有问题。开发人员怀疑,是不是笔者的服务器有没有把 HTTP GET 请求正确地发给 RESIN,或者是 RESIN 不能正确处理 HTTP GET 请求。于是,笔者搞了下面的代码,来表明自己的“清白”,呵呵呵呵。
# cat $RESIN_HOME/webapps/ROOT/disp_http_request_header.jsp
<%@ page contentType="text/html; charset=gb2312" language="java" import="java.sql.*,java.util.*"%> <% out.print("1.Local Address: " + request.getLocalAddr() + "<br>"); out.print("2.Local Name: " + request.getLocalName() + "<br>"); out.print("3.Server Name: " + request.getServerName() + "<br>"); out.print("4.Server Port: " + request.getServerPort() + "<br>"); out.print("5.Remote Host: " + request.getRemoteHost() + "<br>"); out.print("6.User Agent: " + request.getHeader("user-agent") + "<br>"); out.print("7.User Agent: " + request.getHeader("referer") + "<br>"); out.print("8.Protocol: " + request.getProtocol() + "<br>"); out.print("9.Method: " + request.getMethod() + "<br>"); out.print("10.Request URI: " + request.getRequestURI() + "<br>"); out.print("11.Servlet Path: " + request.getServletPath()+ "<br>"); out.print("12.Path Info: " + request.getPathInfo() + "<br>"); out.print("13.Real Path: " + request.getRealPath("/")+ "<br>"); %>
然后,再从浏览器里面请求该页面,一切正常。看来,不是笔者服务器或者 RESIN 的问题。后来,经过开发的反复折腾,重要找到了问题,都是 filter 惹的祸 !!!
四. MySQL + Resin 能正常处理汉字吗?
摆弄过 MySQL 5.0.x + Resin 3.0.x 的读者可能遇到过,这种比较优秀的搭配,有时候在处理汉字的时候会“闹鬼”,不能正常处理汉字。所以,我们在安装了这个配置之后,最好是用代码来验证一下,自己的安装配置有没有问题。当然,首先你得保证,你的 LINUX 系统的字符集没有问题,这样配置 i18n 文件可以保证你的 LINUX 系统字符集没问题。
# cat /etc/sysconfig/i18n LANG="zh_CN.GB18030" LANGUAGE="zh_CN.GB18030:zh_CN.GB2312:zh_CN" SUPPORTED="zh_CN.UTF-8:zh_CN:zh:en_US.UTF-8:en_US:en" SYSFONT="lat0-sun16"
接下来,在安装MySQL 5.0.x 的时候,选择默认字符集为 gb2312。然后,就可以用下面的代码来验证了,
# cat $RESIN_HOME/webapps/ROOT/test_resin_mysql_cn.jsp
<%@ page contentType="text/html;charset=gb2312" import="java.sql.*" %> <% // define variables String CLASS="com.mysql.jdbc.Driver"; // note: default character set of mysql is gb2312 // version of connectorJ is greater 5.0 String dbString = "jdbc:mysql://localhost/test?user=admin&password=password&useUnicode=true&characterEncoding=gb2312"; String SQL_create_table = "create table test.cn(col1 varchar(10))"; String SQL_insert_cn = "insert into test.cn values('中文')"; String SQL_select_cn = "select * from test.cn";
// init Connection conn = null; Statement stmt = null; ResultSet rs = null; // check try { Class.forName(CLASS); conn = DriverManager.getConnection(dbString); stmt = conn.createStatement(); stmt.execute(SQL_create_table); stmt.execute(SQL_insert_cn); rs = stmt.executeQuery(SQL_select_cn); while(rs.next()) { out.println(rs.getString("col1")); out.println("<br>"); } } catch(SQLException e) { out.println("DB operation error " + getClass()); } finally { try { if(rs != null) rs.close(); if(stmt != null) stmt.close(); if(conn != null) conn.close(); } catch(SQLException e) { out.println("DB close error " + getClass()); } } %>
然后,用浏览器去请求该页面,如果你看到了“中文”这两个字,恭喜你,你的安装配置没问题。
总之,上面的这些代码并不复杂。但是,在实践中,它们确实有用,所有,学学还是有好处的。
注意,尽管笔者是在 RESIN 里面使用这些代码,但是,毫无疑问,只要稍做改变(甚至不需做任何改变),这些代码也可以用在其它 WEB 应用服务器里面。