JVM调优总结 -Xms -Xmx -Xmn -Xss

三、常见配置举例


  1. 堆大小设置
    JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制;系统的可用虚拟内存限制;系统的可用物理内存限制。32位系统下,一般限制在1.5G~2G;64为操作系统对内存无限制。我在Windows Server 2003 系统,3.5G物理内存,JDK5.0下测试,最大可设置为1478m。
    典型设置:

    • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
      Xmx3550m:设置JVM最大可用内存为3550M。
      -Xms3550m
      :设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
      -Xmn2g
      :设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
      -Xss128k
      :设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

    • java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
      -XX:NewRatio=4
      :设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
      -XX:SurvivorRatio=4
      :设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
      -XX:MaxPermSize=16m:设置持久代大小为16m。
      -XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。

  2. 回收器选择
    JVM给了三种选择:串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。默认情况下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后,JVM会根据当前系统配置进行判断。

    1. 吞吐量优先的并行收集器
      如上文所述,并行收集器主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等。
      典型配置

      • java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20
        -XX:+UseParallelGC
        :选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。
        -XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。

      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC
        -XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集。

      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100
        -XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。

      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy
        -XX:+UseAdaptiveSizePolicy
        :设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

    2. 响应时间优先的并发收集器
      如上文所述,并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。
      典型配置

      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
        -XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。
        -XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。

      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection
        -XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。
        -XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

  3. 辅助信息
    JVM提供了大量命令行参数,打印信息,供调试使用。主要有以下一些:

    • -XX:+PrintGC
      输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs]

                      [Full GC 121376K->10414K(130112K), 0.0650971 secs]


    • -XX:+PrintGCDetails
      输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]

                      [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]


    • -XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可与上面两个混合使用
      输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]

    • -XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用
      输出形式:Application time: 0.5291524 seconds

    • -XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用
      输出形式:Total time for which application threads were stopped: 0.0468229 seconds

    • -XX:PrintHeapAtGC:打印GC前后的详细堆栈信息
      输出形式:
      34.702: [GC {Heap before gc invocations=7:
       def new generation   total 55296K, used 52568K [0x1ebd0000, 0x227d0000, 0x227d0000)
      eden space 49152K,  99% used [0x1ebd0000, 0x21bce430, 0x21bd0000)
      from space 6144K,  55% used [0x221d0000, 0x22527e10, 0x227d0000)
        to   space 6144K,   0% used [0x21bd0000, 0x21bd0000, 0x221d0000)
       tenured generation   total 69632K, used 2696K [0x227d0000, 0x26bd0000, 0x26bd0000)
      the space 69632K,   3% used [0x227d0000, 0x22a720f8, 0x22a72200, 0x26bd0000)
       compacting perm gen  total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
         the space 8192K,  35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
          ro space 8192K,  66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
          rw space 12288K,  46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
      34.735: [DefNew: 52568K->3433K(55296K), 0.0072126 secs] 55264K->6615K(124928K)Heap after gc invocations=8:
       def new generation   total 55296K, used 3433K [0x1ebd0000, 0x227d0000, 0x227d0000)
      eden space 49152K,   0% used [0x1ebd0000, 0x1ebd0000, 0x21bd0000)
        from space 6144K,  55% used [0x21bd0000, 0x21f2a5e8, 0x221d0000)
        to   space 6144K,   0% used [0x221d0000, 0x221d0000, 0x227d0000)
       tenured generation   total 69632K, used 3182K [0x227d0000, 0x26bd0000, 0x26bd0000)
      the space 69632K,   4% used [0x227d0000, 0x22aeb958, 0x22aeba00, 0x26bd0000)
       compacting perm gen  total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
         the space 8192K,  35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
          ro space 8192K,  66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
          rw space 12288K,  46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
      }
      , 0.0757599 secs]

    • -Xloggc:filename:与上面几个配合使用,把相关日志信息记录到文件以便分析。

  4. 常见配置汇总

    1. 堆设置

      • -Xms:初始堆大小
      • -Xmx:最大堆大小
      • -XX:NewSize=n:设置年轻代大小
      • -XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
      • -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
      • -XX:MaxPermSize=n:设置持久代大小

    2. 收集器设置

      • -XX:+UseSerialGC:设置串行收集器
      • -XX:+UseParallelGC:设置并行收集器
      • -XX:+UseParalledlOldGC:设置并行年老代收集器
      • -XX:+UseConcMarkSweepGC:设置并发收集器

    3. 垃圾回收统计信息

      • -XX:+PrintGC
      • -XX:+PrintGCDetails
      • -XX:+PrintGCTimeStamps
      • -Xloggc:filename

    4. 并行收集器设置

      • -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
      • -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
      • -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)

    5. 并发收集器设置

      • -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
      • -XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

四、调优总结

  1. 年轻代大小选择

    • 响应时间优先的应用尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
    • 吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。

  2. 年老代大小选择

    • 响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:

      • 并发垃圾收集信息
      • 持久代并发收集次数
      • 传统GC信息
      • 花在年轻代和年老代回收上的时间比例
      减少年轻代和年老代花费的时间,一般会提高应用的效率

    • 吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。

  3. 较小堆引起的碎片问题
    因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:

    • -XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。
    • -XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩

五、参考文献

Resin 3 pro高并发,响应性与稳定性方案

以下方案我是在Intel xeon(至强) 3.2G 2个双核物理CPU+2G内存(Ecc)上进行:

resin版本为resin-pro-3.0.21,JVM为Jrockit 1.5_06, resin java 启动参数 -Xms256m -Xmx512m

1. 以下为resin.conf配置
<!–

– Resin 3.0 configuration file.

–>

<resin xmlns=“http://caucho.com/ns/resin”
xmlns:resin=“http://caucho.com/ns/resin/core”>

<!–

– Logging configuration for the JDK logging API.

–>

<log name=“” level=“all” path=“stdout:” timestamp=“[%H:%M:%S.%s] “/>



<logger name=“com.caucho.java” level=“config”/>

<logger name=“com.caucho.loader” level=“config”/>



<dependency-check-interval>600s</dependency-check-interval>



<javac compiler=“internal” args=“”/>



<thread-pool>

<thread-max>10240</thread-max>

<spare-thread-min>50</spare-thread-min>

</thread-pool>



<min-free-memory>5M</min-free-memory>



<server>

<class-loader>

<tree-loader path=“${resin.home}/lib”/>

<tree-loader path=“${server.root}/lib”/>

</class-loader>



<keepalive-max>1024</keepalive-max>

<keepalive-timeout>60s</keepalive-timeout>



<resin:if test=“${resin.isProfessional()}”>

<select-manager enable=“true”/>

</resin:if>



<bind-ports-after-start/>



<http server-id=“” host=“*” port=“80”/>



<cluster>

<srun server-id=“” host=“127.0.0.1” port=“6802”/>

</cluster>



<resin:if test=“${resin.isProfessional()}”>

<persistent-store type=“cluster”>

<init path=“session”/>

</persistent-store>

</resin:if>



<ignore-client-disconnect>true</ignore-client-disconnect>



<resin:if test=“${isResinProfessional}”>

<cache path=“cache” memory-size=“20M”/>

</resin:if>



<web-app-default>

<class-loader>

<tree-loader path=“${server.root}/ext-webapp”/>

</class-loader>



<cache-mapping url-pattern=“/” expires=“60s”/>

<cache-mapping url-pattern=“*.gif” expires=“600s”/>

<cache-mapping url-pattern=“*.jpg” expires=“600s”/>



<servlet servlet-name=“directory”
servlet-class=“com.caucho.servlets.DirectoryServlet”>

<init enable=“false”/>

</servlet>



<allow-servlet-el/>



<session-config>

<enable-url-rewriting>false</enable-url-rewriting>

</session-config>



</web-app-default>



<host-default>

<class-loader>

<compiling-loader path=“webapps/WEB-INF/classes”/>

<library-loader path=“webapps/WEB-INF/lib”/>

</class-loader>



<!–access-log path=“logs/access.log”

format=‘%h %l %u %t “%r” %s %b “%{Referer}i” “%{User-Agent}i”‘
rollover-period=“1W”/–>



<web-app-deploy path=“webapps”/>



<ear-deploy path=“deploy”>

<ear-default>

<!– Configure this for the ejb server



– <ejb-server>

– <config-directory>WEB-INF</config-directory>

– <data-source>jdbc/test</data-source>

– </ejb-server>

–>

</ear-default>

</ear-deploy>



<resource-deploy path=“deploy”/>



<web-app-deploy path=“deploy”/>

</host-default>



<resin:import path=“${resin.home}/conf/app-default.xml”/>



<host-deploy path=“hosts”>

<host-default>

<resin:import path=“host.xml” optional=“true”/>

</host-default>

</host-deploy>



<host id=“” root-directory=“.”>

<web-app id=“/” document-directory=“d:\website\chat”>

</web-app>

</host>



</server>

</resin>





2. 在应用的web.xml中加入resin status查看servlet映射

      <servlet-mapping servlet-class=‘com.caucho.servlets.ResinStatusServlet’>

<url-pattern>/resin-status</url-pattern>

<init enable=“read”/>

</servlet-mapping>



3. 启动resin,确认应用正常启动。

4. 写访问测试程序

import java.io.InputStream;

import java.net.URL;


public class TestURL

{
public static void main(String[] args) throws Exception

{
long a = System.currentTimeMillis();

System.out.println(“Starting request url:”);

for(int i = 0; i < 10000; i++){
URL url = new URL(“http://192.168.1.200/main.jsp”);



InputStream is = url.openStream();

is.close();

System.out.println(“Starting request url:”+i);

}
System.out.println(“request url end.take “(System.currentTimeMillis()-a)“ms”);

}


}



5. 在Jbuilder中执行TestURL

在执行过程中,一边刷新http://192.168.1.200/resin-status,查看resin状态,在http://*:80 中的 Active Threads 和 Total,会一直增长,当长到512的时候不再增长,这时再刷新resin-status页面时,会发现打开很慢。原因是服务器已经达到最大连接数,在等待前面连接的释放而不能接受新的连接。

于是下载Resin 3.0.21源码,搜索 512,发现com.caucho.server.port.Port类中有以下代码:
// default timeout


private long _timeout = 65000L;



private int _connectionMax = 512;//就是这行,查找resin所有源码后,发现没有对这个值进行设置

private int _minSpareConnection = 16;



private int _keepaliveMax = -1;



private int _minSpareListen = 5;

private int _maxSpareListen = 10;


将_connectionMax 改为 20480,然后重新编译并替换resin.jar中的Port类。

6. 重新启动Resin,再次运行TestURL进行测试,这次你会发现Threads Active 和 Total 一直变大,且可以超过512一直增大,在测试程序运行过程中刷新页面,页面响应性能还是不错的.

另,测试过程中Resin会打印出 1-3次 强制执行GC的信息,属于正常。

7.待测试完毕,Threads Active 和 Total 马上降为1.Idle为9,总内存为536.87Meg 空闲内存为480.33M

再经多次测试,结果一致,内存回收正常,表明当前 resin 稳定性和响应性可靠。

出自 JAVA开发者(http://www.chinajavaworld.com)

resin服务器突然变慢问题解决办法.

  本人负责的一个网站不知道为什么,由周一开始,突然间变得很慢,有时则很快.搞来搞去,搞了差不多一个星期(部份时间开发新功能去了).终于找到原因了.

 

服务器情况:

linux as4

Intel(R) Xeon(R) CPU            5130  @ 2.00GHz

4G内存,

300Gscsi+320GSATA硬盘.

resin 3.0.23+ lighttpd

 

resin启动参数

-server -verbose:gc -Xdebug -Xloggc:gc.log -Djava.awt.headless=true -XX:PermSize=128M -XX:NewRatio=4 -XX:+UseConcMarkSweepGC -XX:MaxPermSize=200m -XX:NewSize=100 -XX:MaxNewSize=128m -Xmn256m -Xms512m -Xmx512m

 

  服务器运行一直很正常,到了周一,网站变慢,CPU占用50%以上,但内存点使用50%,其中大部分是mysql用去了.load average: 10.02, 10.03, 10.00 .

 

  全部程序都测试过.底层代码也查过.JDK换过,RESIN版本换过,mysql配置也换过.问题依然存在.

 

  利用resin-admin查看情况.

 









Total memory: 501.44Meg
Free memory:

350.37Meg


  

  看上去也正常.不过free memory 变化的很快,由100多M,突然变到300多M.一开始还以为是程序释放了内存.就不去管他了.

 

  经过好多晚的调度,发现一个规律.当启动resin时,一切正常.证明跟jdk版本无关.

 

  今天早上更新服务器程序时,发现resin目录有个gc.log文件.打开一看.全部是gc的执行记录.很多.于是使用tail命令一下.

 

        tail -f gc.log

 

  吓了我一跳,平均每秒就会执行一次gc.问题就在这里.是gc把服务器拖慢了.

 

  于是就把resin的启动参数改一下,把内存改大,gc就不会这么容易触发.

 

        -server -verbose:gc -Xdebug -Xloggc:gc.log -Djava.awt.headless=true -XX:PermSize=128M -XX:NewRatio=4 -XX:+UseConcMarkSweepGC -XX:MaxPermSize=200m -XX:NewSize=100 -XX:MaxNewSize=128m -Xmn256m -Xms1024m -Xmx1024m

 

 

  现在好了,CPU占用只有0.1%左右,有时上到30%多,但很快降下来.load average: 0.05, 0.01, 0.00 <—服务器很健康.

 

  利用resin-admin查看情况.









Total memory: 998.44Meg
Free memory: 607.76Meg

      resin平均使用400M左右的内存,一开始只配置了512给她,怪不得老是gc.呵.

使用ping功能使resin更健壮

由于种种原因,resin有时会遇到resin down机的情况,比如:java.lang.OutOfMemoryError。这样的问题如果从代码角度来解决问题,也不是一时半会能解决的,应急解决方案是启用resin-pro的ping功能。
    <resin:if test=”${isResinProfessional}”>
      <ping>
        <url>http://localhost:8080/ping.jsp</url>
        <sleep-time>1m</sleep-time>
        <try-count>5</try-count>
        <socket-timeout>30s</socket-timeout>
      </ping>
    </resin:if>


ping.jsp可随意写一些代码,纯html代码也行,只能服务器能响应返回200。


如果访问resin访问http://localhost:8080/ping.jsp返回不是200 code,然后再重试try-count次,如果依然不响应200,则将自动重启resin服务。


如果在resin tag下配置了:


  <system-property mail.smtp.host=”127.0.0.1″/>
  <system-property mail.smtp.port=”25″/>


还可以让resin自动给你发个邮件通知你它自动重启过了:


    <ping resin:type=”com.caucho.server.admin.PingMailer”>
      <url>http://localhost:8080/ping.jsp</url>
      <mail-to>[email protected]</mail-to>
      <mail-from>[email protected]</mail-from>
      <mail-subject>Resin ping has failed for server ${’${’}server.name}</mail-subject>
    </ping>


这样配置好了,不管resin出现什么故障,只要resin进程不死,就可以放心睡大觉了。


如果还启用了resin的session持久化存储,用户基本感觉不到服务已经重启过了。


如果启用了resin的cluster,那么请修改httpd.sh,将-server参数加上,不然重启resin后是没有cluster的。


破解resin-pro-3.1

在网上查看了很多关于resin性能的文章,比较公认的说法是resin比tomcat的性能要高很多。


但最近在csdn上看了一篇文章,说resin 3.x的免费版与tomcat性能已经不相上下了。原因有两个:


1、tomcat从5.0后就开始在性能上不断改进,以达到生产环境的要求;


2、resin从3.0开始分为免费版与pro版,更多的性能改进被放到了pro版中了,所以免费版的性能就下降了。


基于这样的一些原因,更由于resin的pro版支持session持久化存储、负载均衡及cache等高端特性,于是对破解resin pro版产生了兴趣。


经过N个夜晚的奋战,终于把这个pro版给破解了。


网上也有人说破解得不好的resin pro版可能会使性能下降,个人感觉不存在这样的问题,只要破解了即可享用pro版的各种特性,性能也有大幅提高。


以下的resin启动的部分输出:


Resin Professional 3.1.4 (built Tue, 04 Dec 2007 11:27:12 PST)
Copyright(c) 1998-2007 Caucho Technology.  All rights reserved.


Starting Resin on Mon, 31 Dec 2007 17:18:52 +0800 (CST)


[17:18:53.640] {main} Proxy Cache disk-size=1024M memory-size=64M
[17:18:53.656] {main} PingThread[] starting, checking []
[17:18:54.109] {main}
[17:18:54.234] {main} Windows XP 5.1 x86
[17:18:54.343] {main} Java 1.5.0_11-b03, 32, mixed mode, GBK, zh, Sun Microsystems Inc.
[17:18:54.468] {main} user.name: dingl
[17:18:54.593] {main} resin.home = C:\resin-pro-3.1.4
[17:18:54.718] {main} resin.root = C:\resin-pro-3.1.4
[17:18:54.828] {main} resin.conf = /D:/workspace/project/resin-3.1.conf
[17:18:54.828] {main}
[17:18:55.734] {main} WebApp[http://localhost:80] Set web app root system property: ‘webapp.root’ = [D:\workspace\project\WebContent\]
[17:18:59.515] {main} WebApp[http://localhost:80] active
[17:19:00.171] {main} WebApp[http://localhost:80/resin-doc] active
[17:19:00.390] {main} WebApp[http://localhost:80/resin-admin] active
[17:19:00.500] {main} Host[] active
[17:19:00.640] {main} Socket JNI library is not available.
[17:19:00.640] {main} Resin will still run but performance will be slower.
[17:19:00.640] {main} To compile the Socket JNI library on Unix, use ./configure; make; make install.
[17:19:00.796] {main} hmux listening to localhost:6800
[17:19:00.921] {main} http listening to *:80
[17:19:01.140] {main} Server[id=,cluster=app-tier] active
[17:19:01.281] {main} Resin started in 9547ms


 可以看到已经没有关于license的提示了,并且增加了Proxy Cache disk-size=1024M memory-size=64M的输出,说明已经使用了cache来提高性能。


 值得一提的是,要想获得更好的性能,请使用linux/unix系统,因为在windows下,默认安装的resin是不能使用JNI库的,需要手工编译。而在linux/unix下,make源码后就已经将JNI库编译好了。这个特性可以从输出中看出来:


[17:19:00.640] {main} Socket JNI library is not available.
[17:19:00.640] {main} Resin will still run but performance will be slower.


linux/unix系统中没有这样的提示。


目前本blog提供resin-pro-3.1.3版的下载,地址为:


resin-pro-3.1.3 for windows 下载


resin-pro-3.1.3 for linux/unix 下载


resin-pro-3.1.4由于目前标记为dev,暂不破解此版本。3.1.5目前正在开发,尚未发布,目前已经可以看到3.1.5的change log了。


lighttpd configure java web server

我的apache 2.061在window 2003 下老是报告错误,虽然也可以使用但感觉很不爽.
错误入下,找了很久也没有办法,包括修复sockt也没用.
报告队列中的错误: 错误应用程序 Apache.exe,版本 2.0.61.200,错误模块 ntdll.dll,版本 5.2.3790.3959,错误地

一直想找一个替代apache的软件,最近看了lighttpd想用lighttpd来替代apache,先下放过资料.
下载地址.
http://ftp.dtech.hu/pub/WLMP-Project/LightTPD-Win32/



. When to use lighttpd


You can use lighttpd to



  • secure access to your application server
  • reduce load on your server by offloading static requests
  • load balance your application servers
  • use lighttpd’s spambot and bad bot blocking capabilities
  • get more request rewriting and redirecting flexibility
  • use the above flexibility to improve your search engine rankings
  • profit.

2. When not to use lighttpd


You might not like lighttpd if you



  • don’t like configuring software
  • use URL rewriting and ;jsessionid (though a patch is available for this problem).

3. lighttpd modules you need


The following lighty modules are needed:



  • mod_access
  • mod_redirect
  • mod_rewrite
  • mod_proxy

Add them to your server.modules section:

server.modules = (
“mod_accesslog”,
“mod_access”,
“mod_redirect”,
“mod_rewrite”,
“mod_proxy”
,
“mod_status”,
“mod_evhost”,
“mod_expire”
)

4. Denying access to JEE directories


The WEB-INF and META-INF directories shouldn’t be accessible through lighttpd. Files from your development environment also shouldn’t be visible.

url.access-deny = ( “WEB-INF”, “.classpath”, “.project”, “META-INF” )

5. Binding your application server to localhost


To prevent duplicate content penalties, your application server shouldn’t be visible from the web. Even if you run it on a high port, someone might eventually find it.


Binding a web site to localhost looks like this in Orion’s <name>-web-site.xml:

<web-site host=”127.0.0.1″ port=”12345″>
<frontend host=”johannburkard.de” port=”80″/>

Consult your documentation if you aren’t using Orion.


6. Redirecting www. to non-www. hosts


Even if you don’t really need to do this, I recommend doing so. Removing duplicate content will improve your rankings.


The following snippet redirects all visitors from www.<domain> to <domain> with a 301 permanent redirect.

$HTTP[“host”] =~ “^www\.(.*)___FCKpd___3quot; {
url.redirect = ( “^/(.*)” => “http://%1/$1” )
}

You should also redirect all additional domains (johannburkard.com, johann-burkard.org) to your main domain.


7. Proxying dynamic requests


We will use mod_proxy to proxy some requests to your Java application server.


Depending on your site’s structure, one of the following approaches will work better.


Simple JSP


If all you have is a bunch of Java Server Pages, the following mod_proxy rule is sufficient:

proxy.server = ( “.jsp” =>
(
( “host” => “127.0.0.1”,
“port” => “12345”
)
)
)

Note that the JSP must be actual files. You cannot use Servlets mapped to these URIs.


Applications


If you use Servlets or more complex applications, you can proxy URIs by prefix:

proxy.server = ( “/blog/” =>
(
( “host” => “127.0.0.1”,
“port” => “12345”
)
)
)

Proxying with exceptions


If most of your site is dynamic and you have a directory for static content (/assets, /static or so), you can proxy all requests except requests for static files:

$HTTP[“url”] !~ “^/static” {
proxy.server = ( “” =>
(
( “host” => “127.0.0.1”,
“port” => “12345”
)
)
)
}

8. Rewriting requests


lighttpd can dynamically rewrite requests. I mostly use this to use default.jsp as dynamic index file instead of index.html. Here’s an example:

url.rewrite-once = ( “^(.*)/___FCKpd___7quot; => “$1/default.jsp”,
“^(.*)/([;?]+.*)___FCKpd___7quot; => “$1/default.jsp$2” )

This is visible at gra0.com and internally rewrites all requests from / to /default.jsp (including jsessionid and query string).


mod_rewrite can also be used to make URLs shorter. For example, to remove the ?page=comments query string, I use the following:

url.rewrite-once = (
“^/blog/(.*)\.html___FCKpd___8quot; => “/blog/$1.html?page=comments”
)

9. Redirecting requests


You can use mod_redirect to redirect the user to a different URL. Contrary to mod_rewrite where the request is rewritten, a 301 permanent redirect will be sent to the browser.


In this example, I’m redirecting requests to an old domain to a new domain:

$HTTP[“host”] == “olddomain.com” {
url.redirect = (
“^/(.*)___FCKpd___9quot; => “http://newdomain.com/$1”
)
}

10. More things to be aware of



  • The only IP address in your application server log files should be 127.0.0.1. If you need the original address, log the X-FORWARDED-FOR header.
  • Don’t analyze both lighttpd and application server logs – lighty’s log files already contain all requests.
  • You might want to set up virtual hosts sooner or later.
  • Use mod_expire to make resources cacheable. Doing so can make your site a lot faster and save you money.

Configure lighttpd alias (mod_alias)


This lighttpd module provides for mapping different parts of the host filesystem in the document tree. You can use it for mapping various directories. For example cgi-bin directory mapped to /var/lib/cgi-bin.


Configuration


Open your lighttpd configuration file:


vi /etc/lighttpd/lighttpd.conf


Append/add mod_ alias to list of server modules:
server.modules += ( “mod_alias” )


Examples


Add cgi-bin alias for doamin theos.in
alias.url = ( “/cgi-bin/” => “/home/lighttpd/theos.in/cgi-bin/” )


Browse all documents installed at /usr/share/doc/ directory with following alias:
alias.url = ( “/docs/” => “/usr/share/doc/” )
alias.url += ( “/stats/” => “/home/theos.in/http/webalizer/” )


Open a browser and type url http://theos.in/docs/ or http://your-domain.com/docs/


巧用tmpfs加速你的linux服务器

巧用tmpfs加速你的linux服务器


今天从朋友高春辉那里又学了一招,就是使用tmpfs,我把他消化后用来实现虚拟磁盘来存放squid的缓存文件和php的seesion。速度快不少哦!


默认系统就会加载/dev/shm ,它就是所谓的tmpfs,有人说跟ramdisk(虚拟磁盘),但不一样。象虚拟磁盘一样,tmpfs 可以使用您的 RAM,但它也可以使用您的交换分区来存储。而且传统的虚拟磁盘是个块设备,并需要一个 mkfs 之类的命令才能真正地使用它,tmpfs 是一个文件系统,而不是块设备;您只是安装它,它就可以使用了。


tmpfs有以下优势:
1。动态文件系统的大小,
2。tmpfs 的另一个主要的好处是它闪电般的速度。因为典型的 tmpfs 文件系统会完全驻留在 RAM 中,读写几乎可以是瞬间的。
3。tmpfs 数据在重新启动之后不会保留,因为虚拟内存本质上就是易失的。所以有必要做一些脚本做诸如加载,绑定的操作。


好了讲了一些大道理,大家看的烦了吧,还是讲讲我的应用吧:)


首先在/dev/stm建个tmp文件夹,然后与实际/tmp绑定


mkdir /dev/shm/tmp
chmod 1777 /dev/shm/tmp
mount –bind /dev/shm/tmp /tmp



1。squid的缓存目录设置


vi /etc/squid/squid.conf


修改成
cache_dir ufs /tmp 256 16 256
这里的第一个256表示使用256M内存,我觉得高性能LINUX双效防火墙HOWTO使用ramdisk的方法还不如直接使用tmpfs,至少每次启动不用mkfs,还可以动态改变大小。


然后重启一下服务,ok,现在所有的squid缓存文件都保存倒tmpfs文件系统里了,很快哦。



2。对php性能的优化


对于一个访问量大的以apache+php的网站,可能tmp下的临时文件都会很多,比如seesion或者一些缓存文件,那么你可以把它保存到tmpfs文件。


保存seesion的方法很简单了只要修改php.ini就行了,由于我已经把/dev/stm/tmp与/tmp绑定,所以不改写也行,至于php程序产生的缓存文件那只能改自己的php程序了:)


希望我的这个方法,能对你有所启发。


参考文档:使用虚拟内存(virtual memory,VM)文件系统和绑定安装


lucene简单实例<二>

 写文章的时候,感觉比较难写的就是标题,有时候不知道起什么名字好,反正这里写的都是关于lucene的一些简单的实例,就随便起啦.

Lucene 其实很简单的,它最主要就是做两件事:建立索引和进行搜索
来看一些在lucene中使用的术语,这里并不打算作详细的介绍,只是点一下而已—-因为这一个世界有一种好东西,叫搜索。

IndexWriter:lucene中最重要的的类之一,它主要是用来将文档加入索引,同时控制索引过程中的一些参数使用。

Analyzer:分析器,主要用于分析搜索引擎遇到的各种文本。常用的有StandardAnalyzer分析器,StopAnalyzer分析器,WhitespaceAnalyzer分析器等。

Directory:索引存放的位置;lucene提供了两种索引存放的位置,一种是磁盘,一种是内存。一般情况将索引放在磁盘上;相应地lucene提供了FSDirectory和RAMDirectory两个类。

Document:文档;Document相当于一个要进行索引的单元,任何可以想要被索引的文件都必须转化为Document对象才能进行索引。

Field:字段。

IndexSearcher:是lucene中最基本的检索工具,所有的检索都会用到IndexSearcher工具;

Query:查询,lucene中支持模糊查询,语义查询,短语查询,组合查询等等,如有TermQuery,BooleanQuery,RangeQuery,WildcardQuery等一些类。

QueryParser: 是一个解析用户输入的工具,可以通过扫描用户输入的字符串,生成Query对象。

Hits:在搜索完成之后,需要把搜索结果返回并显示给用户,只有这样才算是完成搜索的目的。在lucene中,搜索的结果的集合是用Hits类的实例来表示的。

上面作了一大堆名词解释,下面就看几个简单的实例吧:
1、简单的的StandardAnalyzer测试例子



Java代码


  1. package lighter.javaeye.com;  

  2.   

  3. import java.io.IOException;  

  4. import java.io.StringReader;  

  5.   

  6. import org.apache.lucene.analysis.Analyzer;  

  7. import org.apache.lucene.analysis.Token;  

  8. import org.apache.lucene.analysis.TokenStream;  

  9. import org.apache.lucene.analysis.standard.StandardAnalyzer;  

  10.   

  11. public class StandardAnalyzerTest   

  12. {  

  13.     //构造函数,  

  14.     public StandardAnalyzerTest()  

  15.     {  

  16.     }  

  17.     public static void main(String[] args)   

  18.     {  

  19.         //生成一个StandardAnalyzer对象  

  20.         Analyzer aAnalyzer = new StandardAnalyzer();  

  21.         //测试字符串  

  22.         StringReader sr = new StringReader(“lighter javaeye com is the are on”);  

  23.         //生成TokenStream对象  

  24.         TokenStream ts = aAnalyzer.tokenStream(“name”, sr);   

  25.         try {  

  26.             int i=0;  

  27.             Token t = ts.next();  

  28.             while(t!=null)  

  29.             {  

  30.                 //辅助输出时显示行号  

  31.                 i++;  

  32.                 //输出处理后的字符  

  33.                 System.out.println(“第”+i+“行:”+t.termText());  

  34.                 //取得下一个字符  

  35.                 t=ts.next();  

  36.             }  

  37.         } catch (IOException e) {  

  38.             e.printStackTrace();  

  39.         }  

  40.     }  

  41. }  

显示结果:

引用

第1行:lighter
第2行:javaeye
第3行:com

提示一下:
StandardAnalyzer是lucene中内置的”标准分析器”,可以做如下功能:
1、对原有句子按照空格进行了分词
2、所有的大写字母都可以能转换为小写的字母
3、可以去掉一些没有用处的单词,例如”is”,”the”,”are”等单词,也删除了所有的标点
查看一下结果与”new StringReader(“lighter javaeye com is the are on”)”作一个比较就清楚明了。
这里不对其API进行解释了,具体见lucene的官方文档。需要注意一点,这里的代码使用的是lucene2的API,与1.43版有一些明显的差别。

2、看另一个实例,简单地建立索引,进行搜索



Java代码


  1. package lighter.javaeye.com;  

  2. import org.apache.lucene.analysis.standard.StandardAnalyzer;  

  3. import org.apache.lucene.document.Document;  

  4. import org.apache.lucene.document.Field;  

  5. import org.apache.lucene.index.IndexWriter;  

  6. import org.apache.lucene.queryParser.QueryParser;  

  7. import org.apache.lucene.search.Hits;  

  8. import org.apache.lucene.search.IndexSearcher;  

  9. import org.apache.lucene.search.Query;  

  10. import org.apache.lucene.store.FSDirectory;  

  11.   

  12. public class FSDirectoryTest {  

  13.   

  14.     //建立索引的路径  

  15.     public static final String path = “c:\\index2”;  

  16.   

  17.     public static void main(String[] args) throws Exception {  

  18.         Document doc1 = new Document();  

  19.         doc1.add( new Field(“name”“lighter javaeye com”,Field.Store.YES,Field.Index.TOKENIZED));  

  20.   

  21.         Document doc2 = new Document();  

  22.         doc2.add(new Field(“name”“lighter blog”,Field.Store.YES,Field.Index.TOKENIZED));  

  23.   

  24.         IndexWriter writer = new IndexWriter(FSDirectory.getDirectory(path, true), new StandardAnalyzer(), true);  

  25.         writer.setMaxFieldLength(3);  

  26.         writer.addDocument(doc1);  

  27.         writer.setMaxFieldLength(3);  

  28.         writer.addDocument(doc2);  

  29.         writer.close();  

  30.   

  31.         IndexSearcher searcher = new IndexSearcher(path);  

  32.         Hits hits = null;  

  33.         Query query = null;  

  34.         QueryParser qp = new QueryParser(“name”,new StandardAnalyzer());  

  35.           

  36.         query = qp.parse(“lighter”);  

  37.         hits = searcher.search(query);  

  38.         System.out.println(“查找\”lighter\” 共” + hits.length() + “个结果”);  

  39.   

  40.         query = qp.parse(“javaeye”);  

  41.         hits = searcher.search(query);  

  42.         System.out.println(“查找\”javaeye\” 共” + hits.length() + “个结果”);  

  43.   

  44.     }  

  45.   

  46. }  

运行结果:



Java代码


  1. 查找“lighter” 共2个结果  

  2. 查找“javaeye” 共1个结果  

lucene的简单实例<一>

 说明一下,这一篇文章的用到的lucene,是用2.0版本的,主要在查询的时候2.0版本的lucene与以前的版本有了一些区别.
其实这一些代码都是早几个月写的,自己很懒,所以到今天才写到自己的博客上,高深的文章自己写不了,只能记录下一些简单的记录与点滴,其中的代码算是自娱自乐的,希望高手不要把重构之类的砸下来…

1、在windows系统下的的C盘,建一个名叫s的文件夹,在该文件夹里面随便建三个txt文件,随便起名啦,就叫”1.txt”,”2.txt”和”3.txt”啦
其中1.txt的内容如下:



Java代码


  1. 中华人民共和国  

  2. 全国人民  

  3. 2006年  

而”2.txt”和”3.txt”的内容也可以随便写几写,这里懒写,就复制一个和1.txt文件的内容一样吧

2、下载lucene包,放在classpath路径中
建立索引:



Java代码


  1. package lighter.javaeye.com;  

  2.   

  3. import java.io.BufferedReader;  

  4. import java.io.File;  

  5. import java.io.FileInputStream;  

  6. import java.io.IOException;  

  7. import java.io.InputStreamReader;  

  8. import java.util.Date;  

  9.   

  10. import org.apache.lucene.analysis.Analyzer;  

  11. import org.apache.lucene.analysis.standard.StandardAnalyzer;  

  12. import org.apache.lucene.document.Document;  

  13. import org.apache.lucene.document.Field;  

  14. import org.apache.lucene.index.IndexWriter;  

  15.   

  16. /** 

  17.  * author lighter date 2006-8-7 

  18.  */  

  19. public class TextFileIndexer {  

  20.     public static void main(String[] args) throws Exception {  

  21.         /* 指明要索引文件夹的位置,这里是C盘的S文件夹下 */  

  22.         File fileDir = new File(“c:\\s”);  

  23.   

  24.         /* 这里放索引文件的位置 */  

  25.         File indexDir = new File(“c:\\index”);  

  26.         Analyzer luceneAnalyzer = new StandardAnalyzer();  

  27.         IndexWriter indexWriter = new IndexWriter(indexDir, luceneAnalyzer,  

  28.                 true);  

  29.         File[] textFiles = fileDir.listFiles();  

  30.         long startTime = new Date().getTime();  

  31.           

  32.         //增加document到索引去  

  33.         for (int i = 0; i < textFiles.length; i++) {  

  34.             if (textFiles[i].isFile()  

  35.                     && textFiles[i].getName().endsWith(“.txt”)) {  

  36.                 System.out.println(“File “ + textFiles[i].getCanonicalPath()  

  37.                         + “正在被索引….”);  

  38.                 String temp = FileReaderAll(textFiles[i].getCanonicalPath(),  

  39.                         “GBK”);  

  40.                 System.out.println(temp);  

  41.                 Document document = new Document();  

  42.                 Field FieldPath = new Field(“path”, textFiles[i].getPath(),  

  43.                         Field.Store.YES, Field.Index.NO);  

  44.                 Field FieldBody = new Field(“body”, temp, Field.Store.YES,  

  45.                         Field.Index.TOKENIZED,  

  46.                         Field.TermVector.WITH_POSITIONS_OFFSETS);  

  47.                 document.add(FieldPath);  

  48.                 document.add(FieldBody);  

  49.                 indexWriter.addDocument(document);  

  50.             }  

  51.         }  

  52.         //optimize()方法是对索引进行优化  

  53.         indexWriter.optimize();  

  54.         indexWriter.close();  

  55.           

  56.         //测试一下索引的时间  

  57.         long endTime = new Date().getTime();  

  58.         System.out  

  59.                 .println(“这花费了”  

  60.                         + (endTime – startTime)  

  61.                         + ” 毫秒来把文档增加到索引里面去!”  

  62.                         + fileDir.getPath());  

  63.     }  

  64.   

  65.     public static String FileReaderAll(String FileName, String charset)  

  66.             throws IOException {  

  67.         BufferedReader reader = new BufferedReader(new InputStreamReader(  

  68.                 new FileInputStream(FileName), charset));  

  69.         String line = new String();  

  70.         String temp = new String();  

  71.           

  72.         while ((line = reader.readLine()) != null) {  

  73.             temp += line;  

  74.         }  

  75.         reader.close();  

  76.         return temp;  

  77.     }  

  78. }  


索引的结果:



Java代码


  1. File C:\s\1.txt正在被索引….  

  2. 中华人民共和国全国人民2006年  

  3. File C:\s\2.txt正在被索引….  

  4. 中华人民共和国全国人民2006年  

  5. File C:\s\3.txt正在被索引….  

  6. 中华人民共和国全国人民2006年  

  7. 这花费了297 毫秒来把文档增加到索引里面去!c:\s  


3、建立了索引之后,查询啦….



Java代码


  1. package lighter.javaeye.com;  

  2.   

  3. import java.io.IOException;  

  4.   

  5. import org.apache.lucene.analysis.Analyzer;  

  6. import org.apache.lucene.analysis.standard.StandardAnalyzer;  

  7. import org.apache.lucene.queryParser.ParseException;  

  8. import org.apache.lucene.queryParser.QueryParser;  

  9. import org.apache.lucene.search.Hits;  

  10. import org.apache.lucene.search.IndexSearcher;  

  11. import org.apache.lucene.search.Query;  

  12.   

  13. public class TestQuery {  

  14.     public static void main(String[] args) throws IOException, ParseException {  

  15.         Hits hits = null;  

  16.         String queryString = “中华”;  

  17.         Query query = null;  

  18.         IndexSearcher searcher = new IndexSearcher(“c:\\index”);  

  19.   

  20.         Analyzer analyzer = new StandardAnalyzer();  

  21.         try {  

  22.             QueryParser qp = new QueryParser(“body”, analyzer);  

  23.             query = qp.parse(queryString);  

  24.         } catch (ParseException e) {  

  25.         }  

  26.         if (searcher != null) {  

  27.             hits = searcher.search(query);  

  28.             if (hits.length() > 0) {  

  29.                 System.out.println(“找到:” + hits.length() + ” 个结果!”);  

  30.             }  

  31.         }  

  32.     }  

  33.   

  34. }  


其运行结果:

引用

找到:3 个结果!

具体的API的用法,这里就不说了,具体的做法参考lucene的官方文档吧…