2007年11月21日星期三

Tomcat 6下NIO及SSL配置

Tomcat 6支持三种HTTP connector,分别是:
  1. Http11Protocol 传统的Blocking IO,也是缺省配置     
  2. Http11NioProtocol 使用JDK 1.4以后的NIO包,提供Non-Blocking的HTTP引擎
  3. Http11AprProtocol 使用Apache Portable Runtime库,利用本地代码提供高性能的(仍然是Blocking方式)HTTP引擎
以下是三种方式的比较:

                  Java Blocking Connector       Java Nio Blocking Connector       APR Connector

    Classname                 Http11Protocol                  Http11NioProtocol         Http11AprProtocol
    Tomcat Version           3.x 4.x 5.x 6.x                       6.x                              5.5.x 6.x
    Support Polling            NO                                      YES                               YES

    Polling Size                 N/A                   Unlimited - Restricted by mem        Unlimited
    Read HTTP Request     Blocking                     Non Blocking                        Blocking
    Read HTTP Body         Blocking                     Blocking                                Blocking

    Write HTTP Response   Blocking                     Blocking                              Blocking
    SSL Support                 Java SSL                    Java SSL                             Open SSL
    SSL Handshake            Blocking                     Non blocking                        Blocking

    Max Connections          maxThreads               See polling size                 See polling size

由于Tomcat 6的缺省配置还是Blocking IO,在AprListener找到tcnative库后自动改为APR connector。因此需要修改配置使用NIO connector。修改server.xml,将原来的

    <Connector executor="tomcatThreadPool"
               port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

改为

     <Connector port="8080" minSpareThreads="5" maxSpareThreads="75"
      maxThreads="1000" strategy="ms" maxHttpHeaderSize="8192" acceptorThreadCount="2"
      emptySessionPath="true" protocol=" org.apache.coyote.http11.Http11NioProtocol"
      enableLookups="false" redirectPort="8443" acceptCount="100"
      connectionTimeout="20000" disableUploadTimeout="true" />

以使用NIO connector,注意红色的部分。

支持SSL的步骤(转贴自:http://www.xyhot.com/article.asp?id=280):

   1. 生成server key
      以命令行方式切换到目录%TOMCAT_HOME%,在command命令行输入如下命令( jdk1.4以上带的工具):
      keytool -genkey -alias tomcat -keyalg RSA -keypass changeit -storepass changeit -keystore server.keystore -validity 3600
      用户名输入域名,如localhost(开发或测试用) 或hostname.domainname(用户拥有的域名),其它全部以 enter 跳过,最后确认,此时会在%TOMCAT_HOME%下生成server.keystore 文件。
      注:参数 -validity 指证书的有效期(天),缺省有效期很短,只有90天。
   2. 将证书导入的JDK的证书信任库中:
      这步对于Tomcat的SSL配置不是必须。
      导入过程分2步,第一步是导出证书,第二步是导入到证书信任库,命令如下:
      keytool -export -trustcacerts -alias tomcat -file  server.cer -keystore  server.keystore -storepass changeit
      keytool -import -trustcacerts -alias tomcat -file server.cer -keystore  %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit
      如果有提示,输入Y就可以了。
      其他有用keytool命令(列出信任证书库中所有已有证书,删除库中某个证书):
      keytool -list -v -keystore D:/sdks/jdk1.5.0_11/jre/lib/security/cacerts
      keytool -delete -trustcacerts -alias tomcat  -keystore  %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit
   3.  配置TOMCAT :
      修改%TOMCAT_HOME%\conf\server.xml,以文字编辑器打开,查找这一行:
      xml 代码
      <!-- Define a SSL HTTP/1.1 Connector on port 8443 -->  

      将之后的那段的注释去掉,并加上 keystorePass及keystoreFile属性。
          <Connector protocol="org.apache.coyote.http11.Http11NioProtocol" 
                 port="8443" minSpareThreads="5" maxSpareThreads="75" 
                 enableLookups="true" disableUploadTimeout="true"   
                 acceptCount="100"  maxThreads="200" 
                 scheme="https" secure="true" SSLEnabled="true" 
                 clientAuth="false" sslProtocol="TLS" 
                 keystoreFile="D:/workbench/apache-tomcat-6.0.14/server.keystore"   
                 keystorePass="changeit"/>
   4. 访问 https://localhost:8443/ 验证配置。

2007年11月16日星期五

使用JConsole远程监控Java程序

  1. 首先程序必须运行在Java SE 5.0以上,在启动参数中加入:-Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false 9999表示监控的端口号,要确保可以访问。上面的设置将口令验证与ssl访问关闭。
  2. 在客户机上启动jconsole,弹出对话框的"远程 "属性页中输入host_IP:9999 选择"连接" ,就能查看到远程程序的运行情况了。

解释一下JConsole可以监控的常用项:
内存:
  1. Eden Space (heap): 内存最初从这个线程池分配给大部分对象。
  2. Survivor Space (heap):用于保存在eden space内存池中经过垃圾回收后没有被回收的对象。
  3. Tenured Generation (heap):用于保持已经在 survivor space内存池中存在了一段时间的对象。
  4. Permanent Generation (non-heap): 保存虚拟机自己的静态(refective)数据,例如类(class)和方法(method)对象。Java虚拟机共享这些类数据。
  5. Code Cache (non-heap): HotSpot Java虚拟机包括一个用于编译和保存本地代码(native code)的内存,叫做"代码缓存区"(code cache)
线程:
  1. 名称:线程的名称
  2. 状态:WAITING,RUNNABLE,TIMED_WAITING,BLOCKED
  3. 阻塞总数:
  4. 等待总数:
  5. 堆栈
VM摘要信息等。

2007年11月14日星期三

使用RMI方式调用JBoss的MBean


JBoss采用JMX的MBean来进行资源管理及监控的任务,用户可以自行定义自己的MBean,在容器内执行。JBoss提供了HTTP和RMI两种接口来调用MBean,HTTP方式用于jmx-console,可以在网页上查看MBean信息并执行其中的方法。RMI接口用于远程调用,具体步骤是:
  1. 通过JNDI查找jmx/invoker/RMIAdaptor得到RMIAdaptor的实例
  2. 通过构造ObjectName对象,传入RMIAdaptor定位具体的MBean实例
  3. 调用RMIAdaptor的invoke()方法执行MBean的方法
代码清单如下:

import java.util.Properties;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.jboss.jmx.adaptor.rmi.RMIAdaptor;

public class JbossJMXBrowser {
   
    private ObjectName name;
    private RMIAdaptor server;

    public JbossJMXBrowser(String host, String objectName) throws Exception {
        Properties props = new Properties();
        props.put("java.naming.factory.initial", " org.jnp.interfaces.NamingContextFactory");
        props.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces ");
        props.put("java.naming.provider.url", host);
        Context ctx = new InitialContext(props);
        server = (RMIAdaptor) ctx.lookup("jmx/invoker/RMIAdaptor");
        name = new ObjectName(objectName);
    }
   
    public Object invoke(String operationName, Object[] params, String[] signatures) throws Exception{
        return server.invoke(name, operationName, params, signatures);
    }
   
    public MBeanInfo getMBeanInfo() throws Exception{
        return server.getMBeanInfo(name);
    }

    private void printOperationInfo(MBeanInfo info) {
        MBeanOperationInfo[] oprerations = info.getOperations ();
        MBeanOperationInfo op = null;
        for (MBeanOperationInfo mBeanOperationInfo : oprerations) {
                op = mBeanOperationInfo;
                System.out.println(op.getName() + " " + op.getReturnType());
                for (MBeanParameterInfo paramInfo : op.getSignature()) {
                    if (paramInfo.getName().equals(paramInfo.getType())) {
                        System.out.println(paramInfo.getType());
                    } else {
                        System.out.println(paramInfo.getType() + " " + paramInfo.getName());
                    }
                }
        }
    }
   
    public static void main(String[] args) throws Exception {
        JbossJMXBrowser browser = new JbossJMXBrowser("10.100.1.156:1099", "jboss.system:type=ServerInfo ");
        MBeanInfo info = browser.getMBeanInfo();
        System.out.println("MBean Name: " + info.getClassName());
        browser.printOperationInfo(info);
        String[] sig = {"boolean"};
        Object[] opArgs = {Boolean.TRUE};
        Object result = browser.invoke("listMemoryPools", opArgs, sig);
        System.out.println (result);
    }
}

以上代码编译运行需要jbossall-client.jar 。

2007年11月6日星期二

配置GlassFish双机集群

有两台物理计算机,主机名为host151与host164,注意GlassFish是根据主机名进行集群通信,因此在两台机各自的hosts文件中必须配置IP与主机名的映射,并保持一致。
host151作为DAS管理端,配置domain和cluster,具体步骤如下:
  1. ant -f setup-cluster.xml 安装GlassFish为cluster模式
  2. asadmin start-domain domain1 启动主域
  3. asadmin create-cluster --host host151 --port 4848 ejb-cluster 创建一个名为ejb-cluster的集群
  4. asadmin create-node-agent --host host151 --port 4848 agent151 创建一个名为agent151的节点代理,靠它来管理集群里的实例,必须单独启动
  5. asadmin create-instance --host host151 --port 4848 --nodeagent agent151 --cluster ejb-cluster instance151 创建一个名为instance151的集群实例,由agent151管理,加入ejb-cluster集群
  6. asadmin start-node-agent --syncinstances=true agent151 启动agent151,syncinstances设为true是为了同时启动集群和下面的实例instance151,缺省为false
host164的配置步骤:
  1. ant -f setup-cluster.xml
  2. asadmin create-node-agent --host host151 --port 4848 agent164 创建名为agent164的节点代理,因为host151作为集群DAS管理端,因此本机不用启动domain
  3. asadmin create-instance --host host151 --port 4848 --nodeagent agent164 --cluster ejb-cluster instance164 创建一个名为instance164的集群实例,由agent164管理,加入ejb-cluster集群,注意host和port选项是指管理主机名和端口号,不是物理主机,物理主机由所属agent决定
  4. asadmin start-node-agent --syncinstances=true agent164 启动agent164
打开host151的管理端界面,可以看到集群下面有两个实例,都已经运行。可以在集群的管理界面上部署应用程序,会自动分发到两个节点中。
EJB的客户端访问代码为了支持IIOP Load-balancing,需要做一些另外的配置,示例如下:

    public void sayHello() throws NamingException {
        Properties props = new Properties();
        props.setProperty("java.naming.factory.initial", "com.sun.enterprise.naming.SerialInitContextFactory");
        props.setProperty("java.naming.factory.url.pkgs", "com.sun.enterprise.naming");
        props.setProperty("java.naming.factory.state", "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
        System.setProperty("com.sun.appserv.iiop.endpoints", "10.100.1.164:33700,10.100.1.151:33700");
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ": "+getExBean(props).sayHello("Herculesx"));
        }
    }

    private ExBeanRemote getExBean(Properties props) throws NamingException {
        Context ctx = new InitialContext(props);
        ExBeanRemote exBean = (ExBeanRemote) ctx.lookup("com.xued.clusteredejb.ExBeanRemote");
        return exBean;
    }

上面的代码有两点需要注意:
  1. 想要支持IIOP Load-balancing,必须配置系统环境变量com.sun.appserv.iiop.endpoints=host1:port ,host2:port...,host是物理主机IP或主机名,port是IIOP端口号,在管理端可以看到不同实例具体的值。使用org.omg.CORBA.ORBInitialHost和org.omg.CORBA.ORBInitialPort就会关闭IIOP Load-balancing。
  2. 客户端选择不同的主机是在lookup的时候进行的,如果想每次调用都平均分配到不同主机上,就必须每次都重新lookup,这会降低一部分性能。