維護人員應該掌握的代碼

很多公司在招聘高級系統維護人員的時候,希望應聘者有一些開發經驗或者要會寫程序,至少也要讀得懂用某些語言寫的程序並能作簡單修改。這並非是無事找事,故意擡高門檻。實際上,有的時候,爲了能夠更好地監控我們的系統,或者驗證我們安裝的系統滿足應用的要求,我們必須要使用一些代碼。比如,我們的應用服務器 運行正常嗎?我們的數據庫連接正常嗎?我們的服務器把 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 應用服務器裏面。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章