﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-jinfeng_wang-随笔分类-ZZ</title><link>http://www.blogjava.net/jinfeng_wang/category/672.html</link><description>G-G-S,D-D-U!</description><language>zh-cn</language><lastBuildDate>Fri, 30 Apr 2010 22:30:34 GMT</lastBuildDate><pubDate>Fri, 30 Apr 2010 22:30:34 GMT</pubDate><ttl>60</ttl><item><title>log4jdbc and hibernate</title><link>http://www.blogjava.net/jinfeng_wang/archive/2010/04/30/319762.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 30 Apr 2010 02:53:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2010/04/30/319762.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/319762.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2010/04/30/319762.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/319762.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/319762.html</trackback:ping><description><![CDATA[1)&nbsp; SLF4J user manual<br />
<a href="http://www.slf4j.org/manual.html">http://www.slf4j.org/manual.html</a><br />
<br />
source:<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #008080">&nbsp;1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #0000ff">package</span><span style="color: #000000">&nbsp;test;<br />
</span><span style="color: #008080">&nbsp;2</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">&nbsp;3</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;org.slf4j.Logger;<br />
</span><span style="color: #008080">&nbsp;4</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;org.slf4j.LoggerFactory;<br />
</span><span style="color: #008080">&nbsp;5</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">&nbsp;6</span><span style="color: #000000"><img id="Codehighlighter1_91_232_Open_Image" onclick="this.style.display='none'; Codehighlighter1_91_232_Open_Text.style.display='none'; Codehighlighter1_91_232_Closed_Image.style.display='inline'; Codehighlighter1_91_232_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_91_232_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_91_232_Closed_Text.style.display='none'; Codehighlighter1_91_232_Open_Image.style.display='inline'; Codehighlighter1_91_232_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top"  alt="" /></span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Test&nbsp;</span><span id="Codehighlighter1_91_232_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_91_232_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">&nbsp;7</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">final</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;Logger&nbsp;logger&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;LoggerFactory.getLogger(Test.</span><span style="color: #0000ff">class</span><span style="color: #000000">);<br />
</span><span style="color: #008080">&nbsp;8</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">&nbsp;9</span><span style="color: #000000"><img id="Codehighlighter1_202_229_Open_Image" onclick="this.style.display='none'; Codehighlighter1_202_229_Open_Text.style.display='none'; Codehighlighter1_202_229_Closed_Image.style.display='inline'; Codehighlighter1_202_229_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_202_229_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_202_229_Closed_Text.style.display='none'; Codehighlighter1_202_229_Open_Image.style.display='inline'; Codehighlighter1_202_229_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span id="Codehighlighter1_202_229_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_202_229_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">10</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.error(</span><span style="color: #000000">"</span><span style="color: #000000">test</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">11</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">12</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">13</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top"  alt="" />}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">14</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span></div>
<br />
jar file:<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #000000">slf4j</span><span style="color: #000000">-</span><span style="color: #000000">log4j12</span><span style="color: #000000">-</span><span style="color: #000000">1.5</span><span style="color: #000000">.</span><span style="color: #000000">8</span><span style="color: #000000">.jar<br />
<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" />slf4j</span><span style="color: #000000">-</span><span style="color: #000000">api</span><span style="color: #000000">-</span><span style="color: #000000">1.5</span><span style="color: #000000">.</span><span style="color: #000000">8</span><span style="color: #000000">.jar<br />
<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" />log4j</span><span style="color: #000000">-</span><span style="color: #000000">1.2</span><span style="color: #000000">.</span><span style="color: #000000">15</span><span style="color: #000000">.jar</span></div>
<br />
2) Analyzing JDBC Logs with LOG4JDBC<br />
http://www.cubrid.org/analyzing_jdbc_logs#_Toc234038379<br />
<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #008080">&nbsp;1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #0000ff">package</span><span style="color: #000000">&nbsp;test;<br />
</span><span style="color: #008080">&nbsp;2</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">&nbsp;3</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;java.sql.Connection;<br />
</span><span style="color: #008080">&nbsp;4</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;java.sql.DriverManager;<br />
</span><span style="color: #008080">&nbsp;5</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;java.sql.ResultSet;<br />
</span><span style="color: #008080">&nbsp;6</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">import</span><span style="color: #000000">&nbsp;java.sql.Statement;<br />
</span><span style="color: #008080">&nbsp;7</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">&nbsp;8</span><span style="color: #000000"><img id="Codehighlighter1_153_647_Open_Image" onclick="this.style.display='none'; Codehighlighter1_153_647_Open_Text.style.display='none'; Codehighlighter1_153_647_Closed_Image.style.display='inline'; Codehighlighter1_153_647_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_153_647_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_153_647_Closed_Text.style.display='none'; Codehighlighter1_153_647_Open_Image.style.display='inline'; Codehighlighter1_153_647_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top"  alt="" /></span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;jdbcsample&nbsp;</span><span id="Codehighlighter1_153_647_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_153_647_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">&nbsp;9</span><span style="color: #000000"><img id="Codehighlighter1_212_645_Open_Image" onclick="this.style.display='none'; Codehighlighter1_212_645_Open_Text.style.display='none'; Codehighlighter1_212_645_Closed_Image.style.display='inline'; Codehighlighter1_212_645_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_212_645_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_212_645_Closed_Text.style.display='none'; Codehighlighter1_212_645_Open_Image.style.display='inline'; Codehighlighter1_212_645_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;main(String[]&nbsp;argc)&nbsp;</span><span style="color: #0000ff">throws</span><span style="color: #000000">&nbsp;Exception&nbsp;</span><span id="Codehighlighter1_212_645_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_212_645_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">10</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;connectionURL&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">jdbc:log4jdbc:mysql://127.0.0.1:3306/sampledb</span><span style="color: #000000">"</span><span style="color: #000000">;<br />
</span><span style="color: #008080">11</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">12</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class.forName(</span><span style="color: #000000">"</span><span style="color: #000000">net.sf.log4jdbc.DriverSpy</span><span style="color: #000000">"</span><span style="color: #000000">).newInstance();<br />
</span><span style="color: #008080">13</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Connection&nbsp;connection&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;DriverManager.getConnection(connectionURL,<br />
</span><span style="color: #008080">14</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">root</span><span style="color: #000000">"</span><span style="color: #000000">,&nbsp;</span><span style="color: #000000">"</span><span style="color: #000000">1234</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">15</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">16</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Statement&nbsp;statement&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;connection.createStatement();<br />
</span><span style="color: #008080">17</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;rs&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;statement.executeQuery(</span><span style="color: #000000">"</span><span style="color: #000000">SELECT&nbsp;*&nbsp;FROM&nbsp;customers</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
</span><span style="color: #008080">18</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">19</span><span style="color: #000000"><img id="Codehighlighter1_583_627_Open_Image" onclick="this.style.display='none'; Codehighlighter1_583_627_Open_Text.style.display='none'; Codehighlighter1_583_627_Closed_Image.style.display='inline'; Codehighlighter1_583_627_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_583_627_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_583_627_Closed_Text.style.display='none'; Codehighlighter1_583_627_Open_Image.style.display='inline'; Codehighlighter1_583_627_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">while</span><span style="color: #000000">&nbsp;(rs.next())&nbsp;</span><span id="Codehighlighter1_583_627_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_583_627_Open_Text"><span style="color: #000000">{<br />
</span><span style="color: #008080">20</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(rs.getString(</span><span style="color: #000000">1</span><span style="color: #000000">));<br />
</span><span style="color: #008080">21</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">22</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rs.close();<br />
</span><span style="color: #008080">23</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /><br />
</span><span style="color: #008080">24</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">25</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top"  alt="" />}</span></span><span style="color: #000000"><br />
</span><span style="color: #008080">26</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span></div>
<br />
jar file:<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #000000">add&nbsp;the&nbsp;jar&nbsp;file:<br />
<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" />log4jdbc3</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">.2beta1.jar<br />
<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" />mysql</span><span style="color: #000000">-</span><span style="color: #000000">connector</span><span style="color: #000000">-</span><span style="color: #000000">java</span><span style="color: #000000">-</span><span style="color: #000000">5.1</span><span style="color: #000000">.</span><span style="color: #000000">12</span><span style="color: #000000">-</span><span style="color: #000000">bin.jar</span></div>
<br />
3)Hibernate SQL Logging: log4jdbc / Haciendo log de SQL con Hibernate: log4jdbc<br />
<a href="http://softdevbuilttolast.wordpress.com/2010/02/22/hibernate-sql-logging-log4jdbc-haciendo-log-de-sql-con-hibernate-log4jdbc/">http://softdevbuilttolast.wordpress.com/2010/02/22/hibernate-sql-logging-log4jdbc-haciendo-log-de-sql-con-hibernate-log4jdbc/<br />
</a><br />
//confgi the hibernate with the proxy connection<br />
<p>hibernate.dialect=net.sf.hibernate.dialect.MySQLDialect<br />
hibernate.connection.driver_class=net.sf.log4jdbc.DriverSpy<br />
hibernate.connection.url=jdbc:log4jdbc:mysql://localhost:3306/SAMPLEDB<br />
hibernate.connection.username=root<br />
hibernate.connection.password=1234<br />
hibernate.show_sql=false</p>
<p>&nbsp;</p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/319762.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2010-04-30 10:53 <a href="http://www.blogjava.net/jinfeng_wang/archive/2010/04/30/319762.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谈谈网站静态化 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318519.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 16 Apr 2010 06:34:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318519.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/318519.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318519.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/318519.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/318519.html</trackback:ping><description><![CDATA[<h2 style="margin: 13pt 0cm"><span style="font-family: 黑体"><font style="font-size: 18pt" color="#000000">写在前头</font></span></h2>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">静态化是解决减轻网站压力</span>,<span style="font-family: 宋体">提高网站访问速度的常用方案</span>,<span style="font-family: 宋体">但在强调交互的</span>We2.0 <span style="font-family: 宋体">时代</span>,<span style="font-family: 宋体">对静态化提出了更高的要求</span>,<span style="font-family: 宋体">静态不仅要能静</span>,<span style="font-family: 宋体">还要能动</span>,<span style="font-family: 宋体">下面我通过一个项目</span>,<span style="font-family: 宋体">谈谈网站静态化后的架构设计方案</span>,<span style="font-family: 宋体">同时和大家探讨一下</span>,<span style="font-family: 宋体">在开源产品大行其道</span>,<span style="font-family: 宋体">言架构必称</span>MemberCache, Nginx,<span style="font-family: 宋体">的时代</span>,<span style="font-family: 宋体">微软技术在网站架构设计中的运用</span>.</font></font></p>
<h2 style="margin: 13pt 0cm"><span style="font-family: 黑体"><font style="font-size: 18pt" color="#000000">静态化的设计原则和步骤</font></span></h2>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">静态化是解决减轻网站压力</span>,<span style="font-family: 宋体">但是静态化也会带来一系列的问题</span>,<span style="font-family: 宋体">包括开发上复杂度的增加</span>,<span style="font-family: 宋体">维护难度的增加</span>,<span style="font-family: 宋体">运用不的当</span>,<span style="font-family: 宋体">更可能适得其反</span>,<span style="font-family: 宋体">而许多替代方案</span>,<span style="font-family: 宋体">比如页面缓存</span>,<span style="font-family: 宋体">如果运用得当</span>,<span style="font-family: 宋体">也能起到很好的效果</span>,<span style="font-family: 宋体">所以在开始之前</span>,<span style="font-family: 宋体">必须进行详细的考察</span>,<span style="font-family: 宋体">确定是否适合静态化</span>,<span style="font-family: 宋体">并制定适合的静态化方式</span>,<span style="font-family: 宋体">下面先介绍一下</span></font></font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><strong><span style="font-family: 宋体"><font size="3">考查读写比</font></span></strong><font size="3">:</font></font></p>
<p style="margin: 0cm 0cm 0pt 21pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">读写比</span>,<span style="font-family: 宋体">准确的说是读写负荷比</span>,<span style="font-family: 宋体">是否值得静态化的最终考虑</span>,<span style="font-family: 宋体">由于一般写入的压力明显大于读出的压力</span>,<span style="font-family: 宋体">如果写入太频繁</span>,<span style="font-family: 宋体">或者每次写入消耗的资源太多</span>,<span style="font-family: 宋体">都不能达到效果</span>,<span style="font-family: 宋体">我觉得读写比例</span>10:1<span style="font-family: 宋体">应该是个上限</span>.<span style="font-family: 宋体">具体情况需要根据自己的业务逻辑判断</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><strong><span style="font-family: 宋体"><font size="3">确定页面呈现的内容是否适合静态化</font></span></strong><font size="3">:</font></font></p>
<p style="margin: 0cm 0cm 0pt 21pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">在设计方案时</span>,<span style="font-family: 宋体">必须详细考虑每个原型页面</span>,<span style="font-family: 宋体">找到页面上展示的信息</span>,<span style="font-family: 宋体">和他的更新方式</span>,<span style="font-family: 宋体">更新时机</span>,<span style="font-family: 宋体">更新频率</span>,<span style="font-family: 宋体">一定要注意那些不起眼的信息</span>,<span style="font-family: 宋体">他们可能左右你的设计</span>,</font></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">比如</span>:<span style="font-family: 宋体">我们以</span>CSDN<span style="font-family: 宋体">的论坛的任意一篇帖子为例</span>,<span style="font-family: 宋体">进行分析</span></font></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><font size="3"><font color="#000000"><span style="font-family: 宋体"><span style="font-family: 黑体"><font color="#000000"><img style="width: 361px; height: 385px" height="466" alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/yizhu2000/EntryImages/20080901/topic.JPG" width="512" /></font></span></span></font></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">上面的帖子中呈现的内容主要是这样几块</span>,<span style="font-family: 宋体">帖子内容</span>,<span style="font-family: 宋体">回复内容</span>,<span style="font-family: 宋体">发帖人回复人的用户信息</span></font></font></p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">n</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体"><font size="3">帖子内容和回复内容在发帖时更新</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">发帖后用户可以修改其内容</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">更新频率高</font></span></font></p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">n</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体"><font size="3">用户信息</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">用户修改个人信息时可能会发生更改</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">用户等级增加时也可能发生更改</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">比如加星</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">更新频率低</font></span></font></p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">n</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体"><font size="3">回复数将每次回复后都要更改</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">更新频率高</font></span></font></p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">n</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体"><font size="3">设计时要注意细节</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">如上图中圈出来的部分</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">这些部分是怎么修改的</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">频率有多大</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">一个都不能放过</font></span><font size="3">.</font></font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><strong><font size="3"><span style="font-family: 宋体">确定生成方式</span>:</font></strong></font></p>
<p style="margin: 0cm 0cm 0pt 21pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">在上面帖子一例中</span>.<span style="font-family: 宋体">每次更改都重新生成页面是不可取的</span>,<span style="font-family: 宋体">一篇比回复数多的帖子</span>,<span style="font-family: 宋体">需要的数据量是巨大的</span>(<span style="font-family: 宋体">每层楼的用户信息</span>,<span style="font-family: 宋体">回复内容</span>),<span style="font-family: 宋体">任何修改</span>,<span style="font-family: 宋体">都需要重新取出数据进行生成是不能允许的</span>.<span style="font-family: 宋体">一般除非你的页面基本不用更新</span>,<span style="font-family: 宋体">或者更新开销极小</span>,(<span style="font-family: 宋体">比如一段嵌入的广告代码</span>)<span style="font-family: 宋体">才能采用整体更新的方式</span>,<span style="font-family: 宋体">不然就需要我们找到合适的更新页面局部区域的方法</span>:</font></font></p>
<p style="margin: 0cm 0cm 0pt 21pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">一般有下面两个方法</span>:</font></font></p>
<p style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt"><font color="#000000"><strong><font size="3">1)</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></strong><strong><font size="3"><span style="font-family: 宋体">正则修改法</span>:</font></strong></font></p>
<pre><font size="3"><font color="#000000"><font face="宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 比如,如果帖子中的回复数,html代码是这样</font></font></font></pre>
<pre><font size="3"><font color="#000000"><font face="宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;label&gt;回复数&lt;var id="replyCount"&gt;34&lt;/var&gt;&lt;/label&gt;</font></font></font></pre>
<pre><font size="3"><font color="#000000"><font face="宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们可以通过用下面正则来查找并替换计数</font></font></font></pre>
<pre><font size="3"><font color="#000000"><font face="宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  (?&lt;=id="replyCount"&gt;)"d{1,}</font></font></font></pre>
<p style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt"><font color="#000000"><strong><font size="3">2)</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></strong><strong><font size="3"><span style="font-family: 宋体">页面区域分块</span>:</font></strong></font></p>
<p style="margin: 0cm 0cm 0pt 21pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">把页面分成很多小块</span>,<span style="font-family: 宋体">在显示时组装起来</span>,<span style="font-family: 宋体">比如</span>DotText<span style="font-family: 宋体">就采用这个方法</span></font></font></p>
<p style="margin: 0cm 0cm 0pt 21pt"><font size="3"><font color="#000000"><span style="font-family: 宋体"><span style="font-family: 宋体"><img height="441" alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/yizhu2000/EntryImages/20080901/blog.JPG" width="379" /></span></span></font></font></p>
<p style="margin: 0cm 0cm 0pt 21pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">这是一篇典型的</span>Dottext blog<span style="font-family: 宋体">页面</span>,<span style="font-family: 宋体">其中红色标定部分是一个独立的文件</span>,<span style="font-family: 宋体">而黄色框内的是脚本动态加载</span>,<span style="font-family: 宋体">这些部分在最终显示的时候组合起来</span>,<span style="font-family: 宋体">最终构成了一篇</span>Blog,<span style="font-family: 宋体">具体的组合方法也有多种</span>,<span style="font-family: 宋体">可以使用</span>Include,<span style="font-family: 宋体">也可以自己来实现</span>.DotText<span style="font-family: 宋体">就自己实现了一套加载机制</span></font></font></p>
<p style="margin: 0cm 0cm 0pt 21pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt 21pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">上面的两种方法并不孤立</span>,<span style="font-family: 宋体">并可以根据需要</span>,<span style="font-family: 宋体">配合使用</span></font></font></p>
<p style="margin: 0cm 0cm 0pt 21pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><strong><font size="3"><span style="font-family: 宋体">确定需要动态加载的信息</span>:</font></strong></font></p>
<p style="margin: 0cm 0cm 0pt 21pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">页面上总有一些内容看起来不太适合静态化</span>,<span style="font-family: 宋体">最典型的是一些统计结果</span>,<span style="font-family: 宋体">比如如果你在做一个图书介绍页面</span>,<span style="font-family: 宋体">可能就会需要展示图书的当天综合评分</span>,<span style="font-family: 宋体">或者书籍排名</span>,<span style="font-family: 宋体">这些内容需要用脚本进行动态加载</span></font></font></p>
<p style="margin: 0cm 0cm 0pt 21pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">既然做了静态化</span>,<span style="font-family: 宋体">就是希望减少服务器负载</span>,<span style="font-family: 宋体">动态加载的数据总是不得已而为之</span>,<span style="font-family: 宋体">有的时候在需求允许的情况下</span>,<span style="font-family: 宋体">我们在数据在实时性和性能方面做一些妥协</span>,<span style="font-family: 宋体">比如上面帖子中的用户星级和昵称</span>,<span style="font-family: 宋体">从数据实时性上说</span>,<span style="font-family: 宋体">当用户的星级增长</span>,<span style="font-family: 宋体">他发言的所有帖子都应该发生变化</span>,<span style="font-family: 宋体">所以应该用动态加载</span>.<span style="font-family: 宋体">然而其实上这些信息如果不发生变化</span>,<span style="font-family: 宋体">也无伤大雅</span>,<span style="font-family: 宋体">用户反而能够看到自己在多年前发帖时的级别和昵称</span>.</font></font></p>
<h2 style="margin: 13pt 0cm"><span style="font-family: 黑体"><font style="font-size: 18pt" color="#000000">现实中的项目</font></span></h2>
<p style="margin: 0cm 0cm 0pt; text-indent: 21.75pt"><font size="3"><font color="#000000">X<span style="font-family: 宋体">网站是大型的电影资讯</span>,<span style="font-family: 宋体">电影社区</span>,<span style="font-family: 宋体">向外提供电影相关信息服务</span>,<span style="font-family: 宋体">以及用户社区</span>,<span style="font-family: 宋体">其中信息服务部分</span>, <span style="font-family: 宋体">其中大部分页面属于信息呈现页</span>,<span style="font-family: 宋体">读取量比较大</span>,<span style="font-family: 宋体">百万级别</span>pv,<span style="font-family: 宋体">信息主要由编辑在后台发布</span>,<span style="font-family: 宋体">更新较少</span>,<span style="font-family: 宋体">但其页面上有大量的交互性的内容</span>,<span style="font-family: 宋体">比如评论</span>,<span style="font-family: 宋体">收藏列表</span>,<span style="font-family: 宋体">同时许多内容允许用户创造</span>,<span style="font-family: 宋体">比如上传图片</span>,<span style="font-family: 宋体">添加注释</span>.<span style="font-family: 宋体">交互内容的数量和交互的频繁程度</span>,<span style="font-family: 宋体">都超过了普通的咨询页面</span>,<span style="font-family: 宋体">这次调整</span>,<span style="font-family: 宋体">准备将其中访问量最大的几块</span>:<span style="font-family: 宋体">电影资料页</span>,<span style="font-family: 宋体">影人资料页</span>,<span style="font-family: 宋体">进行静态化</span>,<span style="font-family: 宋体">如果成功</span>,<span style="font-family: 宋体">还将运用到更多的频道</span>,<span style="font-family: 宋体">基本实现全站静态化</span></font></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21.75pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">通过对页面设计和前一版本的分析</span>,<span style="font-family: 宋体">下面是具有挑战性的地方</span>.<span style="font-family: 宋体">这些特点基本使用于大多数</span>web2.0<span style="font-family: 宋体">的站点</span>,<span style="font-family: 宋体">很具有典型意义</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><strong><font size="3"><span style="font-family: 宋体">页面生成的触发条件复杂</span></font></strong></font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">一般论坛中的帖子或者</span>blog,<span style="font-family: 宋体">更新方式比较单一</span>:<span style="font-family: 宋体">主要是由回复进行触发还有少数的修改动作</span>,<span style="font-family: 宋体">然而该网站一个页面上需要根据不同触发条件就有</span>20<span style="font-family: 宋体">多个</span>, <span style="font-family: 宋体">比如光二级菜单</span>:<span style="font-family: 宋体">用户发布图片</span>,<span style="font-family: 宋体">删除图片</span>,<span style="font-family: 宋体">发布或者删除影片信息</span>,<span style="font-family: 宋体">发布或者修改视频</span>,<span style="font-family: 宋体">后台修改电影信息</span>,<span style="font-family: 宋体">都有可能触发</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><strong><font size="3"><span style="font-family: 宋体">一个动作触发生成的页面可能很多而且相互交叠</span></font></strong></font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">每一个动作都会触发一系列的生成</span>,<span style="font-family: 宋体">并且不同动作可能都会涉及同一个页面或者区域的生成</span>.</font></font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">比如</span>:<span style="font-family: 宋体">用户给一步电影评分</span>,<span style="font-family: 宋体">需要生成评分更多页</span>,<span style="font-family: 宋体">评分统计更多页</span>,<span style="font-family: 宋体">首页右侧谁还关注此影片小区域</span>,<span style="font-family: 宋体">等等</span>.<span style="font-family: 宋体">用户收藏一个影片</span>,<span style="font-family: 宋体">也需要更新首页右侧谁还关注此影片小区域</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><strong><font size="3"><span style="font-family: 宋体">触发频繁</span>:</font></strong></font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">虽然不及某些更大规模的网站</span>,<span style="font-family: 宋体">但是由于涉及众多用户参与的内容</span>,<span style="font-family: 宋体">评论</span>,<span style="font-family: 宋体">收藏等等</span>,<span style="font-family: 宋体">触发点多</span>,<span style="font-family: 宋体">发生频度相当频繁</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><strong><font size="3"><span style="font-family: 宋体">页面多</span>,</font></strong><strong><font size="3"><span style="font-family: 宋体">结构复杂</span>,</font></strong><strong><font size="3"><span style="font-family: 宋体">空间占用大</span>:</font></strong></font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">通常</span>,<span style="font-family: 宋体">需要生成的页面规模是这样粗略估算的</span>,Rn*P,Rn<span style="font-family: 宋体">为资源数</span>,P<span style="font-family: 宋体">为每个资源的页面数</span>,<span style="font-family: 宋体">所谓资源</span>,<span style="font-family: 宋体">可以看做一个生成单位</span>,<span style="font-family: 宋体">其页面数可以简单看做发布一个资源</span>,<span style="font-family: 宋体">就需要生成其所有相关页面数量</span>,<span style="font-family: 宋体">比如</span>:<span style="font-family: 宋体">发布一个</span>blog,<span style="font-family: 宋体">就需要生成一个</span>Blog<span style="font-family: 宋体">页</span>,<span style="font-family: 宋体">同时还需要生成或者更新个人主页的</span>blog<span style="font-family: 宋体">列表</span>,<span style="font-family: 宋体">算上个人主页右侧的分类文章数的小块</span>,<span style="font-family: 宋体">也就是最多</span>10<span style="font-family: 宋体">来个页面或者区域</span>,<span style="font-family: 宋体">但是发布一个电影</span>,<span style="font-family: 宋体">其相关的页面至少有</span>50<span style="font-family: 宋体">个以上</span>,<span style="font-family: 宋体">而且有的页面还带有分页</span>,<span style="font-family: 宋体">一个信息比较丰富的电影</span>,<span style="font-family: 宋体">其页面竟可以达到千个以上</span>,<span style="font-family: 宋体">空间</span>10~20M,<span style="font-family: 宋体">而且资源总数也不少</span>,<span style="font-family: 宋体">电影</span>80000<span style="font-family: 宋体">左右</span>,<span style="font-family: 宋体">电影人虽然</span>P<span style="font-family: 宋体">值较少</span>,<span style="font-family: 宋体">但是总量确有几十万之巨</span>,<span style="font-family: 宋体">估计静态页面磁盘占用量几百个</span>G</font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><strong><font size="3"><span style="font-family: 宋体">向下兼容</span></font></strong></font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">这是一个已有系统</span>,<span style="font-family: 宋体">旧系统的框框需要突破</span>,<span style="font-family: 宋体">但又没有时间</span>,<span style="font-family: 宋体">或者不能完全突破</span>,<span style="font-family: 宋体">比如</span>Url,<span style="font-family: 宋体">已经被收录到搜索引擎</span>,<span style="font-family: 宋体">就不能随便调整</span>,<span style="font-family: 宋体">还有一些地方</span>,<span style="font-family: 宋体">原本没有为静态生成考虑</span>,<span style="font-family: 宋体">另一些地方又需要兼容旧的设计</span>. </font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><strong><font size="3"><span style="font-family: 宋体">多台前端</span>Web</font></strong></font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">这种结构要求生成的文件可能需要分布到多个服务器</span>(<span style="font-family: 宋体">另一个方案是放在几台专用的机器上</span>,<span style="font-family: 宋体">等前端来取</span>)</font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><strong><font size="3"><span style="font-family: 宋体">任务紧迫</span></font></strong></font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">架构讨论结束仪式六月初</span>,<span style="font-family: 宋体">离奥运开幕上线只有两月</span>,<span style="font-family: 宋体">也就是说所有底层框架实现</span>,<span style="font-family: 宋体">页面模板开发</span>,<span style="font-family: 宋体">调试测试</span>,<span style="font-family: 宋体">动作的整理</span>,<span style="font-family: 宋体">必须在</span>7<span style="font-family: 宋体">月底全部完成</span>,<span style="font-family: 宋体">按我原来估计</span>,<span style="font-family: 宋体">光实现这几块的上百个页面模板和填充方法</span>,<span style="font-family: 宋体">也需要那么长的时间</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">综合考虑上述因素</span>,<span style="font-family: 宋体">架构必须要有以下几个方面的特点</span></font></font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体"><font size="3">动作可以灵活扩展配置</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">某个动作对应哪些生成</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">应该可以配置</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">并且可以分组</font></span></font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体"><font size="3">文件必须有分发机制</font></span></font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体"><font size="3">分发和生成必须独立出来</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">并且支持分布式</font></span></font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体"><font size="3">各种的动作</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">必须转化为消息</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">发送到生成和分发服务器进行处理</font></span></font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体"><font size="3">针对同意资源频繁动作</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">在变量相同的情况下能够具有合并的能力</font></span></font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体"><font size="3">动作必须有记录</font></span></font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt"><font color="#000000"><span style="font-family: Wingdings"><font size="3">l</font><span style="font: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal; x-system-font: none">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体"><font size="3">尽量考虑使用已有成熟技术</font></span><font size="3">,</font><span style="font-family: 宋体"><font size="3">节省开发时间</font></span></font></p>
<h2 style="margin: 13pt 0cm"><span style="font-family: 黑体"><font style="font-size: 18pt" color="#000000">下面是设计的第一个架构</font></span></h2>
<h2 style="margin: 13pt 0cm"><span style="font-family: 黑体"><span style="font-family: 宋体"><font color="#000000" size="3"><img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/yizhu2000/EntryImages/20080901/first.JPG" /></font></span></span></h2>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font color="#000000" size="3">用户的动作经过</font></span><font color="#000000" size="3">MSMQ</font><a title="" href="http://blog.csdn.net/PostEditPlain.aspx#_edn1" name="_ednref1"><span style="font-size: 10.5pt; font-family: 'Times New Roman'"><u><font color="#0000ff">[1]</font></u></a></span><font size="3"><font color="#000000"><span style="font-family: 宋体">传入到生成分发中心</span>(<span style="font-family: 宋体">途中绿色箭头</span>)<span style="font-family: 宋体">进行处理</span>,,<span style="font-family: 宋体">处理中心接受到消息后</span>,<span style="font-family: 宋体">负责生成对应的页面或者页面区域</span>,<span style="font-family: 宋体">并将页面分发到各个服务器</span>,<span style="font-family: 宋体">负载均衡沿用以前的架构</span>,<span style="font-family: 宋体">采用微软的</span></font></font><font color="#000000" size="3">NLB</font><a title="" href="http://blog.csdn.net/PostEditPlain.aspx#_edn2" name="_ednref2"><span style="font-size: 10.5pt; font-family: 'Times New Roman'"><u><font color="#0000ff">[2]</font></u></a></span></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">之所以用</span>MSMQ,<span style="font-family: 宋体">就是看上了他提供的完整的消息存储恢复机制</span>,<span style="font-family: 宋体">这样我们能确保即使服务器</span>down<span style="font-family: 宋体">掉重启后</span>,<span style="font-family: 宋体">消息依然能正常处理</span>,<span style="font-family: 宋体">碰巧我们</span>cms<span style="font-family: 宋体">组的同事</span>MSMQ<span style="font-family: 宋体">非常熟悉</span>,<span style="font-family: 宋体">并且真准备在另外一个项目中使用类似的架构</span>—<span style="font-family: 宋体">于是一拍即合</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">页面采用分块存储</span>,<span style="font-family: 宋体">这样能保证生成时目标小</span>,<span style="font-family: 宋体">开销小</span>,<span style="font-family: 宋体">也能重用性</span>,<span style="font-family: 宋体">然后再藉由</span></font></font><font color="#000000" size="3">SSI</font><a title="" href="http://blog.csdn.net/PostEditPlain.aspx#_edn3" name="_ednref3"><span style="font-size: 10.5pt; font-family: 'Times New Roman'"><u><font color="#0000ff">[3]</font></u></a><font color="#000000" size="3">(shtml include)</font></span><font size="3"><font color="#000000"><span style="font-family: 宋体">进行整合</span>,<span style="font-family: 宋体">之所以采取这样的方案</span>,<span style="font-family: 宋体">而不采用</span>Dottext<span style="font-family: 宋体">的整合方式</span>,<span style="font-family: 宋体">是因为如果采用</span>Dottext<span style="font-family: 宋体">的方式</span>,<span style="font-family: 宋体">就必须走</span>IIS<span style="font-family: 宋体">和</span>.Net<span style="font-family: 宋体">的管道</span></font></font><a title="" href="http://blog.csdn.net/PostEditPlain.aspx#_edn4" name="_ednref4"><span style="font-size: 10.5pt; font-family: 'Times New Roman'"><u><font color="#0000ff">[4]</font></u></span></a><font size="3"><font color="#000000">,<span style="font-family: 宋体">而据测试</span>,<span style="font-family: 宋体">经过管道和直接返回</span>html<span style="font-family: 宋体">性能有非常大的差异</span>,<span style="font-family: 宋体">而使用</span>ssi,<span style="font-family: 宋体">在性能上是一个折中</span>,<span style="font-family: 宋体">并且可以</span>Light HTTPd<span style="font-family: 宋体">等高性能</span>web<span style="font-family: 宋体">服务器</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">模板生成方式</span>,<span style="font-family: 宋体">采用了</span>XSLT<span style="font-family: 宋体">和另外一种自定义的模板</span>(<span style="font-family: 宋体">我的同事开发的机制</span>,<span style="font-family: 宋体">很有趣</span>, <span style="font-family: 宋体">理论上能把传统模板替换的性能开销全部消除</span>),<span style="font-family: 宋体">生成的最终产物是</span>shtml,<span style="font-family: 宋体">之所以生成</span>shtml<span style="font-family: 宋体">是为了使用其</span>ssi(Server Side Include)<span style="font-family: 宋体">的特性</span>,<span style="font-family: 宋体">保证一定的灵活性</span>,<span style="font-family: 宋体">并实现热点数据的分离</span>:<span style="font-family: 宋体">某些页面上的部分可能会频繁更新和生成</span>,<span style="font-family: 宋体">而其它地方不变</span>,<span style="font-family: 宋体">或者某个部分是所有页面通用的</span>(<span style="font-family: 宋体">比如页头和页脚</span>),<span style="font-family: 宋体">较之</span>php<span style="font-family: 宋体">下常常使用</span>smarty,<span style="font-family: 宋体">生成</span>php<span style="font-family: 宋体">文件</span>,<span style="font-family: 宋体">虽然灵活性不如</span>php,<span style="font-family: 宋体">但是性能上不相上下</span>,<span style="font-family: 宋体">还略高</span>.</font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">但是这个设计的问题是动态内容和静态内容没有分开</span>,<span style="font-family: 宋体">生成的</span>html<span style="font-family: 宋体">页面</span>,<span style="font-family: 宋体">和动态页面都放在前端服务器上</span>,<span style="font-family: 宋体">通过负载均衡访问</span>,<span style="font-family: 宋体">也造成了分发服务器需要分发到多台服务器</span>,<span style="font-family: 宋体">网络</span>IO<span style="font-family: 宋体">效率较低</span>,<span style="font-family: 宋体">而且静态内容需要的磁盘空间很大</span>,<span style="font-family: 宋体">且小文件非常多</span>,<span style="font-family: 宋体">和动态页面混在一起不便于优化</span>,<span style="font-family: 宋体">所以第二个方案对生成的静态内容与动态内容使用不同的服务器</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<h2 style="margin: 13pt 0cm"><font style="font-size: 18pt" color="#000000"><span style="font-family: 黑体">方案二</span><font face="Arial">:</font></font></h2>
<h2 style="margin: 13pt 0cm"><font color="#000000"><span style="font-family: 宋体"><img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/yizhu2000/EntryImages/20080901/second.JPG" /></span></font></h2>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">我们把生成的静态文件单独放置</span>,<span style="font-family: 宋体">可以看到</span>,<span style="font-family: 宋体">前端增加</span>Nginx,<span style="font-family: 宋体">作为跳转</span>,<span style="font-family: 宋体">把电影</span>,<span style="font-family: 宋体">影人资料库的页面转向静态服务器</span>,<span style="font-family: 宋体">其他的调用转向动态服务器</span>,<span style="font-family: 宋体">这样我们就可以单独为静态服务器进行优化</span>,<span style="font-family: 宋体">比如采用更高效的服务器等等</span>.</font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">同时减少了文件分发的次数</span>(<span style="font-family: 宋体">甚至可以只分发到本机</span>),<span style="font-family: 宋体">提高生成分发的处理能力</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">更进一步</span>,<span style="font-family: 宋体">可以把图片服务分到另外一组机器上</span>,<span style="font-family: 宋体">使用独立的域名</span>,<span style="font-family: 宋体">比如</span>img.xxx.com,<span style="font-family: 宋体">这样可以有效的减少带宽</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<h2 style="margin: 13pt 0cm"><font style="font-size: 18pt" color="#000000"><span style="font-size: 18pt"><span style="font-family: 黑体">最终完整架构</span>:</span></font></h2>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3"><img height="458" alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/yizhu2000/EntryImages/20080901/final.JPG" width="554" />&nbsp;</font></p>
<h2 style="margin: 13pt 0cm"><span style="font-family: 黑体"><font color="#000000"><span style="font-size: 18pt"><span style="font-family: 黑体"><font color="#000000"><span style="font-size: 18pt"><span style="font-family: 黑体"><font color="#000000"><span style="font-family: 黑体"><font color="#000000">文件生成分发中心</font></span></font></span></span></font></span></span></font></span></h2>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font color="#000000" size="3">下图是文件生成分发中心的工作流程图</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><img height="293" alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/yizhu2000/EntryImages/20080901/generator.JPG" width="526" /><br />
</span><font size="3"><font color="#000000"><span style="font-family: 宋体">生成服务对外只有一个输入</span>,<span style="font-family: 宋体">就是消息</span>,<span style="font-family: 宋体">一个输出</span>:<span style="font-family: 宋体">静态文件</span>,<span style="font-family: 宋体">内部根据消息</span>,<span style="font-family: 宋体">从配置文件中找到对应的生成方法</span>,<span style="font-family: 宋体">取出相应的模板</span>,<span style="font-family: 宋体">进行数据填充</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">分发服务主要吧生成服务产生的文件进行分发</span>,<span style="font-family: 宋体">分发到前端的</span>N<span style="font-family: 宋体">台服务器上</span>,<span style="font-family: 宋体">开始考虑得比较复杂</span>,<span style="font-family: 宋体">希望分发服务可以跨越协议</span>(<span style="font-family: 宋体">本地文件系统</span>,<span style="font-family: 宋体">局域网</span>,http<span style="font-family: 宋体">协议</span>),<span style="font-family: 宋体">跨越多种存储介质</span>(<span style="font-family: 宋体">文件系统</span>,<span style="font-family: 宋体">数据库</span>),<span style="font-family: 宋体">实际最后定下来基本是本地文件系统或者局域网传输</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000"><strong><span style="font-size: 9pt; font-family: 宋体">注</span></strong><strong><span style="font-size: 9pt">:</span></strong><strong><span style="font-size: 9pt; font-family: 宋体">上图中文件分发的部分也可以通过定制</span></strong><strong><span style="font-size: 9pt">MogileFS,</span></strong><strong><span style="font-size: 9pt; font-family: 宋体">来实现分布式文件系统</span></strong></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<h2 style="margin: 13pt 0cm"><font style="font-size: 18pt" color="#000000"><span style="font-family: 黑体">马后炮</span><font face="Arial">:</font></font></h2>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">总结起来</span>,<span style="font-family: 宋体">静态化除了对架构方面的影响</span>,<span style="font-family: 宋体">对开发和测试流程也有影响</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><strong><font size="3"><font color="#000000"><span style="font-family: 宋体">对测试提出更高的要求</span>:</font></font></strong></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">因为一旦上线后</span>,<span style="font-family: 宋体">某个页面发现问题</span>,<span style="font-family: 宋体">即使是文字的修改</span>,<span style="font-family: 宋体">也需要重新生成许多页面</span>,<span style="font-family: 宋体">所以测试人员必须非常仔细</span>,<span style="font-family: 宋体">测试周期也需要延长</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt"><strong><font size="3"><font color="#000000"><span style="font-family: 宋体">开发人员需要掌握模板语言</span></font></font></strong></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">需要掌握一种模板预言</span>,<span style="font-family: 宋体">无论是</span>Xslt<span style="font-family: 宋体">还是自己开发的模板语言</span>,<span style="font-family: 宋体">都需要花一定的时间掌握</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt"><strong><font size="3"><font color="#000000"><span style="font-family: 宋体">需要给第一次生成腾出足够时间</span>:</font></font></strong></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font color="#000000"><span style="font-family: 宋体">如果不是新系统</span>,<span style="font-family: 宋体">那么数据迁移和生成的过程就比较痛苦</span>,<span style="font-family: 宋体">由于页面众多</span>,<span style="font-family: 宋体">第一次生成的过程可能需要以天来计算</span>,<span style="font-family: 宋体">在制定上线方案是就需要考虑到这个方面</span></font></font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">&nbsp;</font></p>
<p style="margin: 0cm 0cm 0pt"><font color="#000000" size="3">Nginx</font><font size="3"><font color="#000000"><span style="font-family: 宋体">作为前端的跳转</span>,<span style="font-family: 宋体">根据其他网站的经验</span>,<span style="font-family: 宋体">应该可以达到</span>2-3<span style="font-family: 宋体">万并发连接</span>,<span style="font-family: 宋体">但是使用之后</span>,<span style="font-family: 宋体">常常有卡壳的情况发生</span>,<span style="font-family: 宋体">具体症状为在浏览器中访问页面时</span>,<span style="font-family: 宋体">连接超时</span>,<span style="font-family: 宋体">或者一直不响应</span>,<span style="font-family: 宋体">此时</span>Nginx<span style="font-family: 宋体">连接数并不高</span>,<span style="font-family: 宋体">好在还有第一套方案可以备用</span>,<span style="font-family: 宋体">让我们有时间去解决这个问题</span>,<span style="font-family: 宋体">如果大家对这个问题有什么心得</span>,<span style="font-family: 宋体">欢迎交流</span></font></font></p>
 <img src ="http://www.blogjava.net/jinfeng_wang/aggbug/318519.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2010-04-16 14:34 <a href="http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318519.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>亿万用户网站MySpace的成功秘密 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318514.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 16 Apr 2010 06:22:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318514.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/318514.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318514.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/318514.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/318514.html</trackback:ping><description><![CDATA[http://www.yaosansi.com/post/1130.html<br />
<br />
<br />
高速增长的访问量给社区网络的技术体系带来了巨大挑战。MySpace的开发者多年来不断重构站点软件、数据库和存储系统，以期与自身的成长同步——目前，该站点月访问量已达400亿。绝大多数网站需要应对的流量都不及MySpace的一小部分，但那些指望迈入庞大在线市场的人，可以从MySpace的成长过程学到知识。<br />
<br />
<span style="font-weight: bold">用户的烦恼</span><br />
Drew，是个来自达拉斯的17岁小伙子，在他的MySpace个人资料页上，可以看到他的袒胸照，看样子是自己够着手拍的。他的好友栏全是漂亮姑娘和靓车的链接，另外还说自己参加了学校田径队，爱好吉他，开一辆蓝色福特野马。<br />
不过在用户反映问题的论坛里，似乎他的火气很大。&#8220;赶紧弄好这该死的收件箱！&#8221;他大写了所有单词。使用MySpace的用户个人消息系统可以收发信息，但当他要查看一条消息时，页面却出现提示：&#8220;非常抱歉&#8230;&#8230;消息错误&#8221;。<br />
<br />
Drew的抱怨说明1.4亿用户非常重视在线交流系统，这对MySpace来说是个好消息。但也恰是这点让MySpace成了全世界最繁忙的站点之一。<br />
11月，MySpace的美国国内互联网用户访问流量首次超过Yahoo。comScore Media Metrix公司提供的资料显示，MySpace当月访问量为387亿，而Yahoo是380.5亿。<br />
显然，MySpace的成长太快了——从2003年11月正式上线到现在不过三年。这使它很早就要面对只有极少数公司才会遇到的高可扩展性问题的严峻挑战。<br />
事实上，MySpace的Web服务器和数据库经常性超负荷，其用户频繁遭遇&#8220;意外错误&#8221;和&#8220;站点离线维护&#8221;等告示。包括Drew在内的MySpace用户经常无法收发消息、更新个人资料或处理其他日常事务，他们不得不在论坛抱怨不停。<br />
<br />
尤其是最近，MySpace可能经常性超负荷。因为Keynote Systems公司性能监测服务机构负责人Shawn White说，&#8220;难以想象，在有些时候，我们发现20%的错误日志都来自MySpace，有时候甚至达到30%以至40%&#8230;&#8230;而Yahoo、Salesforce.com和其他提供商用服务的站点，从来不会出现这样的数字。&#8221;他告诉我们，其他大型站点的日错误率一般就1%多点。<br />
<br />
顺便提及，MySpace在2006年7月24号晚上开始了长达12小时的瘫痪，期间只有一个可访问页面——该页面解释说位于洛杉矶的主数据中心发生故障。为了让大家耐心等待服务恢复，该页面提供了用Flash开发的派克人（Pac-Man）游戏。Web站点跟踪服务研究公司总经理Bill Tancer说，尤其有趣的是，MySpace瘫痪期间，访问量不降反升，&#8220;这说明了人们对MySpace的痴迷——所有人都拥在它的门口等着放行&#8221;。<br />
现Nielsen Norman Group 咨询公司负责人、原Sun Microsystems公司工程师，因在Web站点方面的评论而闻名的Jakob Nielsen说，MySpace的系统构建方法显然与Yahoo、eBay以及Google都不相同。和很多观察家一样，他相信MySpace对其成长速度始料未及。&#8220;虽然我不认为他们必须在计算机科学领域全面创新，但他们面对的的确是一个巨大的科学难题。&#8221;他说。<br />
<br />
MySpace开发人员已经多次重构站点软件、数据库和存储系统，以满足爆炸性的成长需要，但此工作永不会停息。&#8220;就像粉刷金门大桥，工作完成之时，就是重新来过之日。&#8221;（译者注：意指工人从桥头开始粉刷，当到达桥尾时，桥头涂料已经剥落，必须重新开始）MySpace技术副总裁Jim Benedetto说。<br />
既然如此，MySpace的技术还有何可学之处？因为MySpace事实上已经解决了很多系统扩展性问题，才能走到今天。<br />
<br />
Benedetto说他的项目组有很多教训必须总结，他们仍在学习，路漫漫而修远。他们当前需要改进的工作包括实现更灵活的数据缓存系统，以及为避免再次出现类似7月瘫痪事件的地理上分布式架构。<br />
<br style="font-weight: bold" />
<span style="font-weight: bold">背景知识</span><br />
MySpace目前的努力方向是解决扩展性问题，但其领导人最初关注的是系统性能。<br />
3年多前，一家叫做Intermix Media（早先叫eUniverse。这家公司从事各类电子邮件营销和网上商务）的公司推出了MySpace。而其创建人是Chris DeWolfe和Tom Anderson，他们原来也有一家叫做ResponseBase的电子邮件营销公司，后于2002年出售给Intermix。据Brad Greenspan（Intermix前CEO）运作的一个网站披露，ResponseBase团队为此获得2百万美金外加分红。Intermix是一家颇具侵略性的互联网商务公司——部分做法可能有点过头。2005年，纽约总检察长Eliot Spitzer——现在是纽约州长——起诉Intermix使用恶意广告软件推广业务，Intermix最后以790万美元的代价达成和解。<br />
<br />
2003年，美国国会通过《反垃圾邮件法》（CAN-SPAM Act），意在控制滥发邮件的营销行为。Intermix领导人DeWolfe和Anderson意识到新法案将严重打击公司的电子邮件营销业务，&#8220;因此必须寻找新的方向。&#8221;受聘于Intermix负责重写公司邮件营销软件的Duc Chau说。<br />
当时有个叫Friendster的交友网站，Anderson和DeWolfe很早就是它的会员。于是他们决定创建自己的网上社区。他们去除了Friendster在用户自我表述方面的诸多限制，并重点突出音乐（尤其是重金属乐），希望以此吸引用户。Chau使用Perl开发了最初的MySpace版本，运行于Apache Web服务器，后台使用MySQL数据库。但它没有通过终审，因为Intermix的多数开发人员对ColdFusion（一个Web应用程序环境，最初由Allaire开发，现为Adobe所有）更为熟悉。因此，最后发布的产品采用ColdFusion开发，运行在Windows上，并使用MS SQL Server作为数据库服务器。<br />
Chau就在那时离开了公司，将开发工作交给其他人，包括Aber Whitcomb（Intermix的技术专家，现在是MySpace技术总监）和Benedetto（MySpace现技术副总裁，大概于MySpace上线一个月后加入）。<br />
<br />
MySpace上线的2003年，恰恰是Friendster在满足日益增长的用户需求问题上遭遇麻烦的时期。在财富杂志最近的一次采访中，Friendster总裁Kent Lindstrom承认他们的服务出现问题选错了时候。那时，Friendster传输一个页面需要20到30秒，而MySpace只需2到3秒。<br />
<br />
结果，Friendster用户开始转投MySpace，他们认为后者更为可靠。<br />
今天，MySpace无疑已是社区网站之王。社区网站是指那些帮助用户彼此保持联系、通过介绍或搜索、基于共同爱好或教育经历交友的Web站点。在这个领域比较有名的还有最初面向大学生的Facebook、侧重职业交流的LinkedIn，当然还少不了Friendster。MySpace宣称自己是&#8220;下一代门户&#8221;，强调内容的丰富多彩（如音乐、趣事和视频等）。其运作方式颇似一个虚拟的夜总会——为未成年人在边上安排一个果汁吧，而显著位置则是以性为目的的约会，和寻找刺激派对气氛的年轻人的搜索服务。<br />
<br />
用户注册时，需要提供个人基本信息，主要包括籍贯、性取向和婚姻状况。虽然MySpace屡遭批评，指其为网上性犯罪提供了温床，但对于未成年人，有些功能还是不予提供的。<br />
MySpace的个人资料页上表述自己的方式很多，如文本式&#8220;关于本人&#8221;栏、选择加载入MySpace音乐播放器的歌曲，以及视频、交友要求等。它还允许用户使用CSS（一种Web标准格式语言，用户以此可设置页面元素的字体、颜色和页面背景图像）自由设计个人页面，这也提升了人气。不过结果是五花八门——很多用户的页面布局粗野、颜色迷乱，进去后找不到东南西北，不忍卒读；而有些人则使用了专业设计的模版（可阅读《Too Much of a Good Thing?》第49页），页面效果很好。<br />
在网站上线8个月后，开始有大量用户邀请朋友注册MySpace，因此用户量大增。&#8220;这就是网络的力量，这种趋势一直没有停止。&#8221;Chau说。<br />
<br />
拥有Fox电视网络和20th Century Fox影业公司的媒体帝国——新闻集团，看到了他们在互联网用户中的机会，于是在2005年斥资5.8亿美元收购了MySpace。新闻集团董事局主席Rupert Murdoch最近向一个投资团透露，他认为MySpace目前是世界主要Web门户之一，如果现在出售MySpace，那么可获60亿美元——这比2005年收购价格的10倍还多！新闻集团还惊人地宣称，MySpace在2006年7月结束的财政年度里总收入约2亿美元，而且预期在2007年度，Fox Interactive公司总收入将达到5亿美元，其中4亿来自MySpace。<br />
然而MySpace还在继续成长。12月份，它的注册账户达到1.4亿，而2005年11月时不过4千万。当然，这个数字并不等于真实的用户个体数，因为有些人可能有多个帐号，而且个人资料也表明有些是乐队，或者是虚构的名字，如波拉特（译者注：喜剧电影《Borat》主角），还有像Burger King（译者注：美国最大的汉堡连锁集团）这样的品牌名。<br />
<br />
当然，这么多的用户不停发布消息、撰写评论或者更新个人资料，甚至一些人整天都泡在Space上，必然给MySpace的技术工作带来前所未有的挑战。而传统新闻站点的绝大多数内容都是由编辑团队整理后主动提供给用户消费，它们的内容数据库通常可以优化为只读模式，因为用户评论等引起的增加和更新操作很少。而MySpace是由用户提供内容，数据库很大比例的操作都是插入和更新，而非读取。<br />
浏览MySpace上的任何个人资料时，系统都必须先查询数据库，然后动态创建页面。当然，通过数据缓存，可以减轻数据库的压力，但这种方案必须解决原始数据被用户频繁更新带来的同步问题。<br />
<br />
MySpace的站点架构已经历了5个版本——每次都是用户数达到一个里程碑后，必须做大量的调整和优化。Benedetto说，&#8220;但我们始终跟不上形势的发展速度。我们重构重构再重构，一步步挪到今天&#8221;。<br />
尽管MySpace拒绝了正式采访，但Benedetto在参加11月于拉斯维加斯召开的SQL Server Connections会议时还是回答了Baseline的问题。本文的不少技术信息还来源于另一次重要会议——Benedetto和他的老板——技术总监Whitcomb今年3月出席的Microsoft MIX Web开发者大会。<br />
据他们讲，MySpace很多大的架构变动都发生在2004和2005年早期——用户数在当时从几十万迅速攀升到了几百万。<br />
<br />
在每个里程碑，站点负担都会超过底层系统部分组件的最大载荷，特别是数据库和存储系统。接着，功能出现问题，用户失声尖叫。最后，技术团队必须为此修订系统策略。<br />
虽然自2005年早期，站点账户数超过7百万后，系统架构到目前为止保持了相对稳定，但MySpace仍然在为SQL Server支持的同时连接数等方面继续攻坚，Benedetto说，&#8220;我们已经尽可能把事情做到最好&#8221;。<br />
<br style="font-weight: bold" />
<span style="font-weight: bold">里程碑一：50万账户</span><br />
按Benedetto 的说法，MySpace最初的系统很小，只有两台Web服务器和一个数据库服务器。那时使用的是Dell双CPU、4G内存的系统。<br />
单个数据库就意味着所有数据都存储在一个地方，再由两台Web服务器分担处理用户请求的工作量。但就像MySpace后来的几次底层系统修订时的情况一样，三服务器架构很快不堪重负。此后一个时期内，MySpace基本是通过添置更多Web服务器来对付用户暴增问题的。<br />
但到在2004年早期，MySpace用户数增长到50万后，数据库服务器也已开始汗流浃背。<br />
但和Web服务器不同，增加数据库可没那么简单。如果一个站点由多个数据库支持，设计者必须考虑的是，如何在保证数据一致性的前提下，让多个数据库分担压力。<br />
在第二代架构中，MySpace运行在3个SQL Server数据库服务器上——一个为主，所有的新数据都向它提交，然后由它复制到其他两个；另两个全力向用户供给数据，用以在博客和个人资料栏显示。这种方式在一段时间内效果很好——只要增加数据库服务器，加大硬盘，就可以应对用户数和访问量的增加。<br />
<br />
<span style="font-weight: bold">里程碑二：1-2百万账户</span><br />
MySpace注册数到达1百万至2百万区间后，数据库服务器开始受制于I/O容量——即它们存取数据的速度。而当时才是2004年中，距离上次数据库系统调整不过数月。用户的提交请求被阻塞，就像千人乐迷要挤进只能容纳几百人的夜总会，站点开始遭遇&#8220;主要矛盾&#8221;，Benedetto说，这意味着MySpace永远都会轻度落后于用户需求。<br />
&#8220;有人花5分钟都无法完成留言，因此用户总是抱怨说网站已经完蛋了。&#8221;他补充道。<br />
这一次的数据库架构按照垂直分割模式设计，不同的数据库服务于站点的不同功能，如登录、用户资料和博客。于是，站点的扩展性问题看似又可以告一段落了，可以歇一阵子。<br />
垂直分割策略利于多个数据库分担访问压力，当用户要求增加新功能时，MySpace将投入新的数据库予以支持它。账户到达2百万后，MySpace还从存储设备与数据库服务器直接交互的方式切换到SAN（Storage Area Network，存储区域网络）——用高带宽、专门设计的网络将大量磁盘存储设备连接在一起，而数据库连接到SAN。这项措施极大提升了系统性能、正常运行时间和可靠性，Benedetto说。<br />
<br />
<span style="font-weight: bold">里程碑三：3百万账户</span><br />
当用户继续增加到3百万后，垂直分割策略也开始难以为继。尽管站点的各个应用被设计得高度独立，但有些信息必须共享。在这个架构里，每个数据库必须有各自的用户表副本——MySpace授权用户的电子花名册。这就意味着一个用户注册时，该条账户记录必须在9个不同数据库上分别创建。但在个别情况下，如果其中某台数据库服务器临时不可到达，对应事务就会失败，从而造成账户非完全创建，最终导致此用户的该项服务无效。<br />
另外一个问题是，个别应用如博客增长太快，那么专门为它服务的数据库就有巨大压力。<br />
2004年中，MySpace面临Web开发者称之为&#8220;向上扩展&#8221;对&#8220;向外扩展&#8221;（译者注：Scale Up和Scale Out，也称硬件扩展和软件扩展）的抉择——要么扩展到更大更强、也更昂贵的服务器上，要么部署大量相对便宜的服务器来分担数据库压力。一般来说，大型站点倾向于向外扩展，因为这将让它们得以保留通过增加服务器以提升系统能力的后路。<br />
但成功地向外扩展架构必须解决复杂的分布式计算问题，大型站点如Google、Yahoo和Amazon.com，都必须自行研发大量相关技术。以Google为例，它构建了自己的分布式文件系统。<br />
另外，向外扩展策略还需要大量重写原来软件，以保证系统能在分布式服务器上运行。&#8220;搞不好，开发人员的所有工作都将白费&#8221;，Benedetto说。<br />
因此，MySpace首先将重点放在了向上扩展上，花费了大约1个半月时间研究升级到32CPU服务器以管理更大数据库的问题。Benedetto说，&#8220;那时候，这个方案看似可能解决一切问题。&#8221;如稳定性，更棒的是对现有软件几乎没有改动要求。<br />
糟糕的是，高端服务器极其昂贵，是购置同样处理能力和内存速度的多台服务器总和的很多倍。而且，站点架构师预测，从长期来看，即便是巨型数据库，最后也会不堪重负，Benedetto说，&#8220;换句话讲，只要增长趋势存在，我们最后无论如何都要走上向外扩展的道路。&#8221;<br />
因此，MySpace最终将目光移到分布式计算架构——它在物理上分布的众多服务器，整体必须逻辑上等同于单台机器。拿数据库来说，就不能再像过去那样将应用拆分，再以不同数据库分别支持，而必须将整个站点看作一个应用。现在，数据库模型里只有一个用户表，支持博客、个人资料和其他核心功能的数据都存储在相同数据库。<br />
既然所有的核心数据逻辑上都组织到一个数据库，那么MySpace必须找到新的办法以分担负荷——显然，运行在普通硬件上的单个数据库服务器是无能为力的。这次，不再按站点功能和应用分割数据库，MySpace开始将它的用户按每百万一组分割，然后将各组的全部数据分别存入独立的SQL Server实例。目前，MySpace的每台数据库服务器实际运行两个SQL Server实例，也就是说每台服务器服务大约2百万用户。Benedetto指出，以后还可以按照这种模式以更小粒度划分架构，从而优化负荷分担。<br />
当然，还是有一个特殊数据库保存了所有账户的名称和密码。用户登录后，保存了他们其他数据的数据库再接管服务。特殊数据库的用户表虽然庞大，但它只负责用户登录，功能单一，所以负荷还是比较容易控制的。<br />
<br />
<span style="font-weight: bold">里程碑四：9百万到1千7百万账户</span><br />
2005年早期，账户达到9百万后，MySpace开始用Microsoft的C#编写ASP.NET程序。C#是C语言的最新派生语言，吸收了C++和Java的优点，依托于Microsoft .NET框架（Microsoft为软件组件化和分布式计算而设计的模型架构）。ASP.NET则由编写Web站点脚本的ASP技术演化而来，是Microsoft目前主推的Web站点编程环境。<br />
可以说是立竿见影，MySpace马上就发现ASP.NET程序运行更有效率，与ColdFusion相比，完成同样任务需消耗的处理器能力更小。据技术总监Whitcomb说，新代码需要150台服务器完成的工作，如果用ColdFusion则需要246台。Benedetto还指出，性能上升的另一个原因可能是在变换软件平台，并用新语言重写代码的过程中，程序员复审并优化了一些功能流程。<br />
<br />
最终，MySpace开始大规模迁移到ASP.NET。即便剩余的少部分ColdFusion代码，也从Cold-Fusion服务器搬到了ASP.NET，因为他们得到了BlueDragon.NET（乔治亚州阿尔法利塔New Atlanta Communications公司的产品，它能将ColdFusion代码自动重新编译到Microsoft平台）的帮助。<br />
账户达到1千万时，MySpace再次遭遇存储瓶颈问题。SAN的引入解决了早期一些性能问题，但站点目前的要求已经开始周期性超越SAN的I/O容量——即它从磁盘存储系统读写数据的极限速度。<br />
原因之一是每数据库1百万账户的分割策略，通常情况下的确可以将压力均分到各台服务器，但现实并非一成不变。比如第七台账户数据库上线后，仅仅7天就被塞满了，主要原因是佛罗里达一个乐队的歌迷疯狂注册。<br />
某个数据库可能因为任何原因，在任何时候遭遇主要负荷，这时，SAN中绑定到该数据库的磁盘存储设备簇就可能过载。&#8220;SAN让磁盘I/O能力大幅提升了，但将它们绑定到特定数据库的做法是错误的。&#8221;Benedetto说。<br />
最初，MySpace通过定期重新分配SAN中数据，以让其更为均衡的方法基本解决了这个问题，但这是一个人工过程，&#8220;大概需要两个人全职工作。&#8221;Benedetto说。<br />
长期解决方案是迁移到虚拟存储体系上，这样，整个SAN被当作一个巨型存储池，不再要求每个磁盘为特定应用服务。MySpace目前采用了一种新型SAN设备——来自加利福尼亚州弗里蒙特的3PARdata。<br />
在3PAR的系统里，仍能在逻辑上按容量划分数据存储，但它不再被绑定到特定磁盘或磁盘簇，而是散布于大量磁盘。这就使均分数据访问负荷成为可能。当数据库需要写入一组数据时，任何空闲磁盘都可以马上完成这项工作，而不再像以前那样阻塞在可能已经过载的磁盘阵列处。而且，因为多个磁盘都有数据副本，读取数据时，也不会使SAN的任何组件过载。<br />
当2005年春天账户数达到1千7百万时，MySpace又启用了新的策略以减轻存储系统压力，即增加数据缓存层——位于Web服务器和数据库服务器之间，其唯一职能是在内存中建立被频繁请求数据对象的副本，如此一来，不访问数据库也可以向Web应用供给数据。换句话说，100个用户请求同一份资料，以前需要查询数据库100次，而现在只需1次，其余都可从缓存数据中获得。当然如果页面变化，缓存的数据必须从内存擦除，然后重新从数据库获取——但在此之前，数据库的压力已经大大减轻，整个站点的性能得到提升。<br />
缓存区还为那些不需要记入数据库的数据提供了驿站，比如为跟踪用户会话而创建的临时文件——Benedetto坦言他需要在这方面补课，&#8220;我是数据库存储狂热分子，因此我总是想着将万事万物都存到数据库。&#8221;但将像会话跟踪这类的数据也存到数据库，站点将陷入泥沼。<br />
增加缓存服务器是&#8220;一开始就应该做的事情，但我们成长太快，以致于没有时间坐下来好好研究这件事情。&#8221;Benedetto补充道。<br />
<br />
<span style="font-weight: bold">里程碑五：2千6百万账户</span><br />
2005年中期，服务账户数达到2千6百万时，MySpace切换到了还处于beta测试的SQL Server 2005。转换何太急？主流看法是2005版支持64位处理器。但Benedetto说，&#8220;这不是主要原因，尽管这也很重要；主要还是因为我们对内存的渴求。&#8221;支持64位的数据库可以管理更多内存。<br />
更多内存就意味着更高的性能和更大的容量。原来运行32位版本的SQL Server服务器，能同时使用的内存最多只有4G。切换到64位，就好像加粗了输水管的直径。升级到SQL Server 2005和64位Windows Server 2003后，MySpace每台服务器配备了32G内存，后于2006年再次将配置标准提升到64G。<br />
<br />
<span style="font-weight: bold">意外错误</span><br />
如果没有对系统架构的历次修改与升级，MySpace根本不可能走到今天。但是，为什么系统还经常吃撑着了？很多用户抱怨的&#8220;意外错误&#8221;是怎么引起的呢？<br />
原因之一是MySpace对Microsoft的Web技术的应用已经进入连Microsoft自己也才刚刚开始探索的领域。比如11月，超出SQL Server最大同时连接数，MySpace系统崩溃。Benedetto说，这类可能引发系统崩溃的情况大概三天才会出现一次，但仍然过于频繁了，以致惹人恼怒。一旦数据库罢工，&#8220;无论这种情况什么时候发生，未缓存的数据都不能从SQL Server获得，那么你就必然看到一个&#8216;意外错误&#8217;提示。&#8221;他解释说。<br />
去年夏天，MySpace的Windows 2003多次自动停止服务。后来发现是操作系统一个内置功能惹的祸——预防分布式拒绝服务攻击（黑客使用很多客户机向服务器发起大量连接请求，以致服务器瘫痪）。MySpace和其他很多顶级大站点一样，肯定会经常遭受攻击，但它应该从网络级而不是依靠Windows本身的功能来解决问题——否则，大量MySpace合法用户连接时也会引起服务器反击。<br />
&#8220;我们花了大约一个月时间寻找Windows 2003服务器自动停止的原因。&#8221;Benedetto说。最后，通过Microsoft的帮助，他们才知道该怎么通知服务器：&#8220;别开枪，是友军。&#8221;<br />
紧接着是在去年7月某个周日晚上，MySpace总部所在地洛杉矶停电，造成整个系统停运12小时。大型Web站点通常要在地理上分布配置多个数据中心以预防单点故障。本来，MySpace还有其他两个数据中心以应对突发事件，但Web服务器都依赖于部署在洛杉矶的SAN。没有洛杉矶的SAN，Web服务器除了恳求你耐心等待，不能提供任何服务。<br />
Benedetto说，主数据中心的可靠性通过下列措施保证：可接入两张不同电网，另有后备电源和一台储备有30天燃料的发电机。但在这次事故中，不仅两张电网失效，而且在切换到备份电源的过程中，操作员烧掉了主动力线路。<br />
2007年中，MySpace在另两个后备站点上也建设了SAN。这对分担负荷大有帮助——正常情况下，每个SAN都能负担三分之一的数据访问量。而在紧急情况下，任何一个站点都可以独立支撑整个服务，Benedetto说。<br />
MySpace仍然在为提高稳定性奋斗，虽然很多用户表示了足够信任且能原谅偶现的错误页面。<br />
&#8220;作为开发人员，我憎恶Bug，它太气人了。&#8221;Dan Tanner这个31岁的德克萨斯软件工程师说，他通过MySpace重新联系到了高中和大学同学。&#8220;不过，MySpace对我们的用处很大，因此我们可以原谅偶发的故障和错误。&#8221; Tanner说，如果站点某天出现故障甚至崩溃，恢复以后他还是会继续使用。<br />
这就是为什么Drew在论坛里咆哮时，大部分用户都告诉他应该保持平静，如果等几分钟，问题就会解决的原因。Drew无法平静，他写道，&#8220;我已经两次给MySpace发邮件，而它说一小时前还是正常的，现在出了点问题&#8230;&#8230;完全是一堆废话。&#8221;另一个用户回复说，&#8220;毕竟它是免费的。&#8221;Benedetto坦承100%的可靠性不是他的目标。&#8220;它不是银行，而是一个免费的服务。&#8221;他说。<br />
换句话说，MySpace的偶发故障可能造成某人最后更新的个人资料丢失，但并不意味着网站弄丢了用户的钱财。&#8220;关键是要认识到，与保证站点性能相比，丢失少许数据的故障是可接受的。&#8221;Benedetto说。所以，MySpace甘冒丢失2分钟到2小时内任意点数据的危险，在SQL Server配置里延长了&#8220;checkpoint&#8221;操作——它将待更新数据永久记录到磁盘——的间隔时间，因为这样做可以加快数据库的运行。<br />
Benedetto说，同样，开发人员还经常在几个小时内就完成构思、编码、测试和发布全过程。这有引入Bug的风险，但这样做可以更快实现新功能。而且，因为进行大规模真实测试不具可行性，他们的测试通常是在仅以部分活跃用户为对象，且用户对软件新功能和改进不知就里的情况下进行的。因为事实上不可能做真实的加载测试，他们做的测试通常都是针对站点。<br />
&#8220;我们犯过大量错误，&#8221;Benedetto说，&#8220;但到头来，我认为我们做对的还是比做错的多。&#8221;<br />
<script src="http://www.yaosansi.com/PLUGIN/copytofriends/copy.js" type="text/javascript"></script>
 <img src ="http://www.blogjava.net/jinfeng_wang/aggbug/318514.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2010-04-16 14:22 <a href="http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318514.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Web 2.0网站性能调优实践 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318511.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 16 Apr 2010 05:58:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318511.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/318511.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318511.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/318511.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/318511.html</trackback:ping><description><![CDATA[<a href="http://www.yaosansi.com/post/1150.html">http://www.yaosansi.com/post/1150.html</a>&nbsp; <br />
<br />
<br />
<h3>Web2.0网站性能调优实践</h3>
<p style="text-align: right" align="right">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 文/王宗义</p>
<p style="text-indent: 21pt">当前web2.0革命风起云涌，web2.0强调服务，而服务最基本的要求是速度快和稳定，离开这两个谈功能强大和易用性都没有任何意义。本文介绍一些关于笔者运营一个web2.0网站的优化心得和经验，希望能够和大家共同探讨。</p>
<p style="text-indent: 21pt">Web2.0网站不同于以往以静态信息为主的网站架构，以往的结构大体分为2层，一个是客户端浏览器，一个就是web服务器；而web2.0以动态和交互为主，一般是3层或者4层，在静态信息网站的结构上的web服务器后端会增加应用服务器和数据库。一般会把浏览器和web服务器归为最上一层即为web层，应用服务器为中间一层，数据库为最底层。从优化角度来讲，越上层优化获得益处越大，优化也是从上自下而来。</p>
<h3 style="margin: 15.6pt 0cm 0pt; line-height: normal">Web层优化</h3>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">DNS的解析时间</h4>
<p style="text-indent: 21pt">这个时间就是在用户第一次访问网站的时候产生，解析时间会影响用户的访问感受，因此想要网站响应速度快，第一就是不要在DNS解析上产生问题。另外DNS的TTL时间也要考量，IE的DNS过期时间是30分钟，TTL设置的比这个长一点就可以。另外在web服务器上使用keep-live也会减少DNS查询次数。</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">降低浏览器发起请求的数量</h4>
<p style="text-indent: 21pt">尽量降低浏览器发起请求的数量，就是说尽量能够让浏览器缓存任何可以缓存的东西。这样当用户访问过一次后，第二次访问可能会使得发起的请求数趋近1或者等于1，如果是静态的页面则可能是0。方法包括：</p>
<p style="text-indent: 21pt">把所有的样式表文件并为1个</p>
<p style="text-indent: 21pt">把所有的js文件并成1个</p>
<p style="text-indent: 21pt">图片尽量能够合成1张，这个跟以前不一样，现在大多数是adsl上网，反而是大量的零碎图片能够影响速度</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">页面布局与样式</h4>
<p style="text-indent: 21pt">页面采用xhtml，采用div+css布局，而把样式表和xhtml文件分开，一则能够降低xhtml文件大小，二则能够对样式表文件进行其他缓存处理。这里还有个ui设计的原则，ui跟系统结构一样，越简洁越好，这样整体页面代码会比较少，速度也会比较不错。</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">JavaScript文件</h4>
<p style="text-indent: 21pt">JavaScript文件也最好放到html文件外，原因同上。</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">静态文件的优化方法</h4>
<p style="text-indent: 21pt">a)目前大多数的浏览器都支持gzip压缩文件，因此为文本、静态页面、样式表、JavaScript文件等可以压缩处理的文件进行压缩处理能够减少内容获取时间，一般压缩完的大小为原大小的10-30%。这个在apache等web服务器上进行设置，笔者使用lighttpd的设置为：</p>
<p style="text-indent: 21pt">server.modules = ( </p>
<p style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8230;.</p>
<p style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "mod_compress",</p>
<p style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8230;</p>
<p style="text-indent: 21pt">)</p>
<p style="text-indent: 21pt">compress.cache-dir="/usr/local/lighttpd/cache"</p>
<p style="text-indent: 21pt">compress.filetype = ("text/plain", "text/html", "text/css", "text/javascript")</p>
<p style="text-indent: 21pt">b)还可以在静态文件服务器前面增加缓存服务器比如squid，进一步增强客户端的访问性能。如果有好的财力，还可以使用一些商业的CDN加速服务。</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">应用Cookie的注意事项</h4>
<p style="text-indent: 21pt">Cookie的应用要注意，要限制cookie的应用域和应用的目录以及过期时间。不然如果用户是第一次访问的话，可能连一个小小的静态图片都要发送cookie到服务器，这样增加了通信负载。另外要限制cookie的大小，一个3k的cookie能够增加延迟达到80ms。</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">提高页面速度</h4>
<p style="text-indent: 21pt">页面由2-4个不同域名的服务器提供服务能够提高速度，这个国外也有研究证明。比如主html文件由app.domain.com提供，样式表由style.domain.com提供，图片等由img.domain.com提供，这样浏览器可以同时从多个服务器下载文件，速度就能够上去。但是最好不要超过4个。</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">样式表文件位置</h4>
<p style="text-indent: 21pt">把样式表文件放在页面的&lt;head&gt;，这样能够先读取。因为在ie中有个样式表的问题，样式表如果没有加载完会影响后面的html内容的页面显示，因此虽然html文件都已经在浏览器了，但是页面还是显示不出来。</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">把JavaScript移到html文件末尾</h4>
<p style="text-indent: 21pt">把JavaScript移到html文件末尾。为什么这么做呢，因为JavaScript处理的过程中会阻塞后面的页面显示，并且也会使得http请求也被阻止。笔者的网站就有过这样的例子，网站上放了一个合作方的JavaScript，结果每次访问时候感觉页面都停滞，用户体验特别差，后来让同事处理了一下，放到末尾等页面加载完了再显示在原有位置，一下子就好了。</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">尽量避免跳转</h4>
<p style="text-indent: 21pt">尽量避免跳转比如301和302。如果必须的话，对301和302的页面添加过期头。笔者原来的单点登录就需要进行跳转，后来改进了不需要跳转，整体的速度效果就出来了。</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">移除重复的脚本</h4>
<p style="text-indent: 21pt">要移除重复的脚本，ie会对重复的脚本发起重复的http请求，大多数网站在运营一段时间都有可能出现这个情况，笔者的网站中就经常有市场人员添加的重复的广告脚本。</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">AJAX内容</h4>
<p style="text-indent: 21pt">AJAX内容也是可以进行缓存的，同样可以压缩和缓存异步调用的xml、json等数据。</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">对爬虫进行限制</h4>
<p style="text-indent: 21pt">对爬虫进行限制，国内的一些爬虫非常厉害，并且不遵守robots规矩，经常有反应某某厉害爬虫把网站搞瘫的事件。怎么对爬虫进行限制呢，只能在web服务器上下功夫了，apache等服务器都能够进行限制，笔者的lighttpd限制10个并发的配置如下：&nbsp; </p>
<p style="text-indent: 21pt">&nbsp;evasive.max-conns-per-ip = 10</p>
<p style="text-indent: 21pt">web层的优化目的就是极大的利用了浏览器的缓存特性，从而达到几乎是本地访问的速度，下图是笔者访问douban.com首页的效果图对比：</p>
<p style="text-indent: 21pt"><img id="图片 2" height="189" alt="" src="http://book.csdn.net/BookFiles/569/img/image032.jpg" width="420" border="0" /></p>
<p style="text-indent: 21pt">前一列数据是空的缓存所需要下载的文件大小和http请求数量，后面是真实访问的带cache的情况，效果特别明显。http请求减少了95%，内容cache了82%。</p>
<h3 style="margin: 15.6pt 0cm 0pt; line-height: normal">应用程序层优化</h3>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">应用服务器的优化</h4>
<p style="text-indent: 21pt">Php的可以采用一些优化手段比如Zend Optimizer、eAccelerator、MMCache、Zend Performance Suite等</p>
<p style="text-indent: 21pt">Java的可以采用一些性能强的jdk、应用服务器，对jdk参数进行优化等等</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">使用ETag</h4>
<p style="text-indent: 21pt">ETag就像版本控制服务器中的版本号一样，每次更新后的ETag是不一样的，而浏览器处理就类似版本服务器的客户端一样，先把版本号发到服务器请求。ETag的处理过程，先是Web服务器在响应的http头中发送ETag，比如这样：</p>
<p style="text-indent: 21pt">ETag: "1111-2222-3333"</p>
<p style="text-indent: 21pt">Last-Modified: Thu, 07 Oct 2007 15:20:18 GMT</p>
<p style="text-indent: 21pt">而浏览器如果再次请求该页面就会发送类似如下的头：</p>
<p style="text-indent: 21pt">If-None-Match: "1111-2222-3333"</p>
<p style="text-indent: 21pt">If-Modified-Since: Thu, 07 Oct 2007 15:20:18 GMT</p>
<p style="text-indent: 21pt">此时，如果该页面没有任何变更，则web服务器会响应一个304的头，并且不需要附带页面内容给浏览器（即不需要再动态生成页面内容），这样就大大减少了服务器的处理和网路通信负载。</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">同步变异步</h4>
<p style="text-indent: 21pt">同步变异步，在web2.0网站中经常有很复杂的处理，比如一个用户的注册还需要发邮件等操作，有时候可能还有其他的处理，这样用户的等待时间比较长，并且容易出现错误。此种情况下，把其他处理变成异步的，从而直接把页面尽快响应给用户。笔者的一个数据上传的程序也是如此处理，一大堆数据，上传时间可能就1-2秒，而处理时间可能长的需要接近10秒（需要在数据库中进行上千次的插入操作），而在应用服务器容器内处理耗时则更长，笔者后来改成异步处理以后，用户满意度则大幅上升。</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">使用缓存</h4>
<p style="text-indent: 21pt">还是缓存，可能的情况下尽量使用缓存，毕竟现在内存非常便宜，用空间换取时间效率应该是非常划算的。尤其是对耗时比较长的、需要建立网络链接的，方法：采用memcached进行数据库或者常用数据的缓存；应用数据库缓冲池减少建立数据库连接的时间</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">采用gzip压缩动态页面</h4>
<p style="text-indent: 21pt">可能情况下，也可以采用gzip压缩动态页面。如果服务器较多，cpu负载不高，则可以考虑对动态页面增加gzip压缩功能</p>
<h4 style="margin: 15.6pt 0cm 0pt; line-height: normal">集群处理</h4>
<p style="text-indent: 21pt">访问压力大的时候，对应用服务器采用集群处理。</p>
<p style="text-indent: 21pt">应用服务器的优化主要是减少程序处理的时间，提高运行效率。</p>
<h3 style="margin: 15.6pt 0cm 0pt; line-height: normal">数据库优化</h3>
<p style="text-indent: 21pt">这个议题跟具体数据库关系比较大，议题也比较广泛，笔者就只简要列举一下：</p>
<p style="text-indent: 21pt">设置专门的DBA，专门负责数据库的安装、优化；对sql进行优化采用数据库集群和复制功能分担数据库压力。</p>
<h3 style="margin: 15.6pt 0cm 0pt; line-height: normal">其他优化措施</h3>
<p style="text-indent: 21pt">网站的优化涉及的方面比较多，其他方面涉及的还包括网站架构、操作系统、服务器硬件、网络设备、isp机房网络等等的调优。</p>
<h3 style="margin: 15.6pt 0cm 0pt; line-height: normal">工具</h3>
<p style="text-indent: 21pt">笔者用到的工具，都是firefox插件，所以firefox是必备的了：</p>
<p style="text-indent: 21pt">1、LiveHTTPHeaders (<a href="http://livehttpheaders.mozdev.org/"><font color="#336699">http://livehttpheaders.mozdev.org/</font></a>)</p>
<p style="text-indent: 21pt">2、Firebug (<a href="http://getfirebug.com/"><font color="#336699">http://getfirebug.com</font></a>)</p>
<p style="text-indent: 21pt">3、YSlow (<a href="http://developer.yahoo.com/yslow/"><font color="#336699">http://developer.yahoo.com/yslow/</font></a>)，要先装Firebug</p>
<p style="text-indent: 21pt">4、Web Developer (<a href="http://chrispederick.com/work/web-developer/"><font color="#336699">http://chrispederick.com/work/web-developer/</font></a>)</p>
<p style="text-indent: 21pt">除了这些免费的工具外，还可以采用一些商业的网站性能监测服务。一般网站性能监测服务商都会在不同的isp设置数据采集点，然后会定期模拟浏览器的访问对网站进行访问获取各种数据，比如dsn查询时间、第一个包获取时间、整个页面加载时间等等，然后汇总到数据中心。数据中心则可以产生性能报表、不同时间的可访问率、哪个isp容易出问题、发出警报等等。如果预算足够的话，可以采用这个服务。国外的有keynote、ip-label等，功能比较齐全，但是服务费用比较贵而且国内的点比较少。国内近些年也开始涌现出一些厂商，比如基调网络。笔者使用的监测系统的图例：</p>
<p style="margin-left: 21pt"><img id="图片 1" height="202" alt="" src="http://book.csdn.net/BookFiles/569/img/image033.jpg" width="553" border="0" /></p>
<h3 style="margin: 15.6pt 0cm 0pt; line-height: normal">笔者网站3年不同阶段的优化过程</h3>
<p style="text-indent: 21pt">优化的原则是尽量不去优化，在未发生性能问题的时候，没有必要去专门考虑细节的性能问题，当然大的结构应该是能够适应网站不断发展变化的。笔者的网站近3年的优化过程如下：</p>
<p style="text-indent: 21pt">1、开发完成，刚上线的时候，不做优化，用户量少，用了3台服务器。</p>
<p style="text-indent: 21pt">2、10万用户的时候，主要对sql进行了优化，还是3台服务器。</p>
<p style="text-indent: 21pt">3、10万用户到100万的过程中，采用了AJAX等，因此开始关注JavaScript的优化手段，访问量也快速上去，因此对静态文件进行分离并优化。服务器也进行了扩展，扩展到5台服务器。</p>
<p style="text-indent: 21pt">4、100万-200万用户，业务系统增加了很多，因此数据库采用了复制，程序方面应用了各种缓存处理，在数据库和程序之间增加了memcached进行数据缓存。</p>
<p style="text-indent: 21pt">5、在200万用户以上，主要在程序架构上做文章，对某些服务使用了集群。另外为了监测国内不同城市、isp的网络状况，使用了商业化的网站性能监测服务。</p>
 <img src ="http://www.blogjava.net/jinfeng_wang/aggbug/318511.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2010-04-16 13:58 <a href="http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318511.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Network Programming Using Libevent zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318479.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 16 Apr 2010 02:29:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318479.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/318479.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318479.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/318479.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/318479.html</trackback:ping><description><![CDATA[http://blog.gslin.org/archives/2005/11/24/220/<br />
<br />
<br />
在課堂上學過 Unix Network Programming 後，我們知道在處理多 User 時會有幾種方法解決：<br />
<ol>
    <li>一個新的 Connection 進來，用 <code>fork()</code> 產生一個 Process 處理。
    <li>一個新的 Connection 進來，用 <code>pthread_create()</code> 產生一個 Thread 處理。
    <li>一個新的 Connection 進來，丟入 Event-based Array，由 Main Process 以 Nonblocking 的方式處理所有的 I/O。<br />
    </li>
</ol>
這三種方法當然也都有各自的缺點：<br />
<ol>
    <li>用 <code>fork()</code> 的問題在於每一個 Connection 進來時的成本太高。
    <li>用 Multi-thread 的問題在於 Thread-safe 與 Deadlock 問題難以解決，另外有 Memory-leak 的問題要處理。
    <li>用 Event-based 的方式在於實做上不好寫，尤其是要注意到事件產生時必須 Nonblocking，於是會需要實做 Buffering 的問題，而 Multi-thread 所會遇到的 Memory-leak 問題在這邊會更嚴重。而在多 CPU 的系統上沒有辦法使用到所有的 CPU resource。<br />
    </li>
</ol>
當然，針對前面兩項有各自的解法：<br />
<ol>
    <li>以 Poll 的方式解決：當一個 Process 處理完一個 Connection 後，不直接死掉，而繼續回到 accept() 的狀態繼續處理，但這樣會遇到 Memory-leak 的問題，於是採用這種方式的人通常會再加上「處理過 N 個 Connection 後死掉，由 Parent Process 再 <code>fork()</code> 一隻新的」。最有名的例子是 <a href="http://httpd.apache.org/">Apache</a> 1.3。
    <li>Thread-safe 的問題可以透過自己撰寫，或是尋找其他 Thread-safe Library 直接使用。Memory-leak 的問題可以試著透過 Garbage Collection Library 分析出來。<a href="http://httpd.apache.org/">Apache</a> 2.0 的 Thread MPM 就是使用這個模式。<br />
    </li>
</ol>
然而，目前高效率的 Server 都偏好採用 Event-based，一方面是沒有 Create Process/Thread 所造成的 Overhead，另外一方面是不需要透過 Shared Memory 或是 Mutex 在不同的 Process/Thread 之間交換資料。<br />
<br />
然而，Event-based 在實做上的幾個複雜的地方在於：<br />
<ol>
    <li><code>select()</code> 與 <code>poll()</code> 的效率過慢，造成每次要判斷「有哪些 Event 發生」這件事情的成本很高，這在 BSD 支援 <code>kqueue()</code>、Linux 支援 <code>epoll()</code>、Solaris 支援 <code>/dev/poll</code> 後就解決了，但這兩組 Function 都不是 Standard，於是在不同的平台上就必須再改一次。<br />
    <li>因為 Nonblocking，所以在 <code>write()</code> 或是 <code>send()</code> 時滿了需要自己 Buffering。
    <li>因為 Nonblocking，所以不能使用 <code>fgets()</code> 或是其他類似的 function，於是需要自己刻一個 Nonblocking 的 <code>fgets()</code>。但是使用者所丟過來的資料又不能保證在一次 <code>read()</code> 或 <code>recv()</code> 就有一行，於是要自己做 Buffering。</li>
</ol>
實際上這三件事情在 <a href="http://www.monkey.org/%7Eprovos/libevent/">libevent</a> 都有 Library 處理掉了。<br />
<br />
另外值得注意的一點在於 <a href="http://www.monkey.org/%7Eprovos/libevent/">libevent</a> 使用的是 3-clause BSD license 而非 GPL，所以在不想公開程式碼 (像是商業用途) 的情況下會比其他的 Library 適合。<br />
<br />
<br />
<br />
<div class="post-body">
<p>
<div style="clear: both"></div>
接下來要談的是 <a href="http://www.monkey.org/%7Eprovos/libevent/">libevent</a> 要如何使用，不過為了方便起見，我們直接寫一個很簡單的 Time Server 來當作例子：當你連上去以後 Server 端直接提供時間，然後結束連線。<br />
<br />
在這些例子裡面我以 <a href="http://www.tw.freebsd.org/">FreeBSD</a> 6.0 當作測試的平台，另外使用 <a href="http://www.monkey.org/%7Eprovos/libevent/">libevent</a> 1.1a 當作 Event-based Library，Compile 時請使用 <code>gcc -I/usr/local/include -o timeserver timeserver.c -L/usr/local/lib -levent</code> (如果 <a href="http://www.monkey.org/%7Eprovos/libevent/">libevent</a> 的 Header 與 Library 放在 <code>/usr/include</code> 與 <code>/usr/lib</code> 下的話可以省略這兩個參數)。<br />
<br />
原始程式碼在文章的最後頭。<br />
<br />
<code>event_init()</code> 表示初始化 <a href="http://www.monkey.org/%7Eprovos/libevent/">libevent</a> 所使用到的變數。<br />
<br />
<code>event_set(&amp;ev, s, EV_READ | EV_PERSIST, connection_accept, &amp;ev)</code> 把 <code>s</code> 這個 File Description 放入 <code>ev</code> (第一個參數與第二個參數)，並且告知當事件 (第三個參數的 <code>EV_READ</code>) 發生時要呼叫 <code>connection_accept()</code> (第四個參數)，呼叫時要把 <code>ev</code> 當作參數丟進去 (第五個參數)。<br />
<br />
其中的 <code>EV_PERSIST</code> 表示當呼叫進去的時候不要把這個 event 拿掉 (繼續保留在 Event Queue 裡面)，這點可以跟 <code>connection_accept()</code> 內在註冊 <code>connection_time()</code> 的程式碼做比較。<br />
<br />
而 <code>event_add(&amp;ev, NULL)</code> 就是把 <code>ev</code> 註冊到 event queue 裡面，第二個參數指定的是 Timeout 時間，設定成 <code>NULL</code> 表示忽略這項設定。<br />
<br />
最後的 <code>event_dispatch()</code> 表示進入 event loop，當 Queue 裡面的任何一個 File Description 發生事件的時候就會進入 callback function 執行。<br />
<br />
這隻程式非常粗糙，有很多地方沒有注意到 Blocking 的問題，這點我們就先不管了。當跑起來以後你就可以連到 port 7000，就會出現類似下面的結果：gslin@netnews [~] [3:14/W5] t 0 7000<br />
<blockquote><code><br />
gslin@netnews [~/work/C] [3:15/W3] t 0 7000<br />
Trying 0.0.0.0...<br />
Connected to 0.<br />
Escape character is '^]'.<br />
Fri Nov 25 03:15:10 2005<br />
Connection closed by foreign host.<br />
</code></blockquote><br />
最基本的使用就是這樣了，你可以 <code>man event</code> 看到完整的說明。<br />
<br />
這是 <code>timeserver.c</code>：<br />
<pre><code><br />
#include &lt;netinet/in.h&gt;<br />
#include &lt;sys/socket.h&gt;<br />
#include &lt;sys/types.h&gt;<br />
#include &lt;event.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;time.h&gt;<br />
<br />
void connection_time(int fd, short event, struct event *arg)<br />
{<br />
char buf[32];<br />
struct tm t;<br />
time_t now;<br />
<br />
time(&amp;now);<br />
localtime_r(&amp;now, &amp;t);<br />
asctime_r(&amp;t, buf);<br />
<br />
write(fd, buf, strlen(buf));<br />
shutdown(fd, SHUT_RDWR);<br />
<br />
free(arg);<br />
}<br />
<br />
void connection_accept(int fd, short event, void *arg)<br />
{<br />
/* for debugging */<br />
fprintf(stderr, "%s(): fd = %d, event = %d.\n", __func__, fd, event);<br />
<br />
/* Accept a new connection. */<br />
struct sockaddr_in s_in;<br />
socklen_t len = sizeof(s_in);<br />
int ns = accept(fd, (struct sockaddr *) &amp;s_in, &amp;len);<br />
if (ns &lt; 0) {<br />
perror("accept");<br />
return;<br />
}<br />
<br />
/* Install time server. */<br />
struct event *ev = malloc(sizeof(struct event));<br />
event_set(ev, ns, EV_WRITE, (void *) connection_time, ev);<br />
event_add(ev, NULL);<br />
}<br />
<br />
int main(void)<br />
{<br />
/* Request socket. */<br />
int s = socket(PF_INET, SOCK_STREAM, 0);<br />
if (s &lt; 0) {<br />
perror("socket");<br />
exit(1);<br />
}<br />
<br />
/* bind() */<br />
struct sockaddr_in s_in;<br />
bzero(&amp;s_in, sizeof(s_in));<br />
s_in.sin_family = AF_INET;<br />
s_in.sin_port = htons(7000);<br />
s_in.sin_addr.s_addr = INADDR_ANY;<br />
if (bind(s, (struct sockaddr *) &amp;s_in, sizeof(s_in)) &lt; 0) {<br />
perror("bind");<br />
exit(1);<br />
}<br />
<br />
/* listen() */<br />
if (listen(s, 5) &lt; 0) {<br />
perror("listen");<br />
exit(1);<br />
}<br />
<br />
/* Initial libevent. */<br />
event_init();<br />
<br />
/* Create event. */<br />
struct event ev;<br />
event_set(&amp;ev, s, EV_READ | EV_PERSIST, connection_accept, &amp;ev);<br />
<br />
/* Add event. */<br />
event_add(&amp;ev, NULL);<br />
<br />
event_dispatch();<br />
<br />
return 0;<br />
}<br />
</code></pre>
<div style="clear: both; padding-bottom: 0.25em"></div>
<p>&nbsp;</p>
</div>
<div class="post-body">
<p>
<div style="clear: both"></div>
這次要談的跟 Network Programming 沒有直接的關係。<br />
<br />
在寫 Nonblocking Network Program 通常要處理 Buffering 的問題，但並不好寫，主要是因為 <code>read()</code> 或 <code>recv()</code> 不保證可以一次讀到一行的份量進來。<br />
<br />
在 <a href="http://www.monkey.org/%7Eprovos/libevent/">libevent</a> 裡面提供相當不錯的 Buffer Library 可以用，完整的說明在 <code>man event</code> 的時候可以看到，最常用的應該就是以 <code>evbuffer_add()</code>、<code>evbuffer_readline()</code> 這兩個 Function，其他的知道存在就可以了，需要的時候再去看詳細的用法。<br />
<br />
下面直接提供 <code>libevent-buff.c</code> 當作範例，編譯後看執行結果，再回頭來看 source code 應該就有感覺了：<br />
<pre><code><br />
#include &lt;sys/time.h&gt;<br />
#include &lt;event.h&gt;<br />
#include &lt;stdio.h&gt;<br />
<br />
void printbuf(struct evbuffer *evbuf)<br />
{<br />
for (;;) {<br />
char *buf = evbuffer_readline(evbuf);<br />
printf("* buf = %p, the string = \"\e[1;33m%s\e[m\"\n", buf, buf);<br />
if (buf == NULL)<br />
break;<br />
free(buf);<br />
}<br />
}<br />
<br />
int main(void)<br />
{<br />
struct evbuffer *evbuf;<br />
<br />
evbuf = evbuffer_new();<br />
if (evbuf == NULL) {<br />
fprintf(stderr, "%s(): evbuffer_new() failed.\n", __func__);<br />
exit(1);<br />
}<br />
<br />
/* Add "gslin" into buffer. */<br />
u_char *buf1 = "gslin";<br />
printf("* Add \"\e[1;33m%s\e[m\".\n", buf1);<br />
evbuffer_add(evbuf, buf1, strlen(buf1));<br />
printbuf(evbuf);<br />
<br />
u_char *buf2 = " is reading.\nAnd he is at home.\nLast.";<br />
printf("* Add \"\e[1;33m%s\e[m\".\n", buf2);<br />
evbuffer_add(evbuf, buf2, strlen(buf2));<br />
printbuf(evbuf);<br />
<br />
evbuffer_free(evbuf);<br />
}<br />
</code></pre>
<div style="clear: both; padding-bottom: 0.25em"></div>
<p>&nbsp;</p>
</div>
 <img src ="http://www.blogjava.net/jinfeng_wang/aggbug/318479.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2010-04-16 10:29 <a href="http://www.blogjava.net/jinfeng_wang/archive/2010/04/16/318479.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>亿级数据的高并发通用搜索引擎架构设计zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2010/04/15/318434.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Thu, 15 Apr 2010 08:35:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2010/04/15/318434.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/318434.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2010/04/15/318434.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/318434.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/318434.html</trackback:ping><description><![CDATA[http://blog.s135.com/post/385/<br />
http://blog.s135.com/post/360/<br />
<br />
<br />
曾经在七月，写过一篇文章──《<a href="http://blog.s135.com/post/360.htm" target="_blank">基于Sphinx+MySQL的千万级数据全文检索（搜索引擎）架构设计</a>》，前公司的分类信息搜索基于此架构，效果明显，甚至将很大一部分带Where条件的MySQL SQL查询，都改用了Sphinx+MySQL搜索。但是，这套架构仍存在局限：一是MySQL本身的并发能力有限，在200～300个并发连接下，查询和更新就比较慢了；二是由于MySQL表的主键与Sphinx索引的ID一一对应，从而无法跨多表建立整站查询，而且新增加类别还得修改配置文件，比较麻烦；三是因为和MySQL集成，无法发挥出Sphinx的优势。<br />
<br />
　　最近，我设计出了下列这套最新的搜索引擎架构，目前已经写出&#8220;搜索查询接口&#8221;和&#8220;索引更新接口&#8221;的beta版。经测试，在一台&#8220;奔腾四 3.6GHz 双核CPU、2GB内存&#8221;的普通PC机，7000万条索引记录的条件下，&#8220;搜索查询接口&#8221;平均查询速度为0.0XX秒（查询速度已经达到百度、谷歌、搜狗、中国雅虎等搜索引擎的水平，详见文章末尾的&#8220;附2&#8221;），并且能够支撑高达5000的并发连接；而&#8220;索引更新接口&#8221;进行数据分析、入队列、返回信息给用户的全过程，高达1500 Requests/Sec。<br />
<br />
　　&#8220;队列控制器&#8221;这一部分是核心，它要控制队列读取，更新MySQL主表与增量表，更新搜索引擎数据存储层Tokyo Tyrant，准实时（1分钟内）完成更新Sphinx增量索引，定期合并Sphinx索引。我预计在这周写出beta版。<br />
<br />
<a href="http://blog.s135.com/attachment/200812/search.gif" target="_blank"><img class="insertimage" title="点击在新窗口中浏览此图片" alt="点击在新窗口中浏览此图片" src="http://blog.s135.com/attachment/200812/search.gif" border="0" /></a><br />
<br />
　　<span style="font-size: 14px"><strong>图示说明：</strong></span><br />
　　<strong>1、搜索查询接口：</strong><br />
<a name="entrymore"></a>　　①、Web应用服务器通过HTTP POST/GET方式，将搜索关键字等条件，传递给搜索引擎服务器的search.php接口；<br />
　　②③、search.php通过Sphinx的API（我根据最新的Sphinx 0.9.9-rc1 API，改写了一个C语言的PHP扩展sphinx.so），查询Sphinx索引服务，取得满足查询条件的搜索引擎唯一ID（15位搜索唯一ID：前5位类别ID+后10位原数据表主键ID）列表；<br />
　　④⑤、search.php将这些ID号作为key，通过Memcache协议一次性从Tokyo Tyrant中mget取回ID号对应的文本数据。<br />
　　⑥⑦、search.php将搜索结果集，按查询条件，进行摘要和关键字高亮显示处理，以JSON格式或XML格式返回给Web应用服务器。<br />
<br />
　　<strong>2、索引更新接口：</strong><br />
　　⑴、Web应用服务器通过HTTP POST/GET方式，将要增加、删除、更新的内容告知搜索服务器的update.php接口；<br />
　　⑵、update.php将接收到的信息处理后，写入TT高速队列（我基于Tokyo Tyrant做的一个队列系统）；<br />
　　注：这两步的速度可达到1500次请求/秒以上，可应对6000万PV的搜索索引更新调用。<br />
<br />
　　<strong>3、搜索索引与数据存储控制：</strong><br />
　　㈠、&#8220;队列控制器&#8221;守护进程从TT高速队列中循环读取信息（每次50条，直到末尾）；<br />
　　㈡、&#8220;队列控制器&#8221;将读取出的信息写入搜索引擎数据存储层Tokyo Tyrant；<br />
　　㈢、&#8220;队列控制器&#8221;将读取出的信息<strong>异步</strong>写入MySQL主表（这张主表按500万条记录进行分区，仅作为数据永久性备份用）；<br />
　　㈣、&#8220;队列控制器&#8221;将读取出的信息写入MySQL增量表；<br />
　　㈤、&#8220;队列控制器&#8221;在1分钟内，触发Sphinx更新增量索引，Sphinx的indexer会将MySQL增量表作为数据源，建立增量索引。Sphinx的增量索引和作为数据源的MySQL增量表成对应关系；<br />
　　㈥、&#8220;队列控制器&#8221;每间隔3小时，短暂停止从TT高速队列中读取信息，并触发Sphinx将增量索引合并入主索引（这个过程非常快），同时清空MySQL增量表（保证了MySQL增量表的记录数始终只有几千条至几十万条，大大加快Sphinx增量索引更新速度），然后恢复从TT高速队列中取出数据，写入MySQL增量表。<br />
<br />
　　<span style="font-size: 14px"><strong>本架构使用的开源软件：</strong></span><br />
　　1、Sphinx 0.9.9-rc1<br />
　　2、Tokyo Tyrant 1.1.9<br />
　　3、MySQL 5.1.30<br />
　　4、Nginx 0.7.22<br />
　　5、PHP 5.2.6<br />
<br />
　　<span style="font-size: 14px"><strong>本架构自主研发的程序：</strong></span><br />
　　1、搜索查询接口（search.php）<br />
　　2、索引更新接口（update.php）<br />
　　3、队列控制器<br />
　　4、Sphinx 0.9.9-rc1 API的PHP扩展（sphinx.so）<br />
　　5、基于Tokyo Tyrant的高速队列系统<br />
<br />
<br />
<br />
<br />
<br />
在DELL PowerEdge 6850服务器（四颗64 位Inter Xeon MP 7110N处理器 / 8GB内存）、RedHat AS4 Linux操作系统、MySQL 5.1.26、MyISAM存储引擎、key_buffer=1024M环境下实测，单表1000万条记录的数据量（这张MySQL表拥有int、datetime、varchar、text等类型的10多个字段，只有主键，无其它索引），用主键（PRIMARY KEY）作为WHERE条件进行SQL查询，速度非常之快，只耗费0.01秒。<br />
<br />
　　出自俄罗斯的开源全文搜索引擎软件<a href="http://www.sphinxsearch.com/" target="_blank">Sphinx</a>，单一索引最大可包含1亿条记录，在1千万条记录情况下的查询速度为0.x秒（毫秒级）。Sphinx创建索引的速度为：创建100万条记录的索引只需3～4分钟，创建1000万条记录的索引可以在50分钟内完成，而只包含最新10万条记录的增量索引，重建一次只需几十秒。<br />
<br />
　　基于以上几点，我设计出了这套搜索引擎架构。在生产环境运行了一周，效果非常不错。有时间我会专为配合Sphinx搜索引擎，开发一个逻辑简单、速度快、占用内存低、非表锁的MySQL存储引擎插件，用来代替MyISAM引擎，以解决MyISAM存储引擎在频繁更新操作时的锁表延迟问题。另外，分布式搜索技术上已无任何问题。<br />
<br />
<hr />
<br />
　　<span style="font-size: 14px"><strong>一、搜索引擎架构设计：</strong></span><br />
　　<strong>1、搜索引擎架构图：</strong><br />
　　<a href="http://blog.s135.com/attachment/200807/sphinx.png" target="_blank"><img class="insertimage" title="点击在新窗口中浏览此图片" alt="点击在新窗口中浏览此图片" src="http://blog.s135.com/attachment/200807/sphinx.png" border="0" /></a><br />
<br />
　　<strong>2、搜索引擎架构设计思路：</strong><br />
　　<strong>(1)、调用方式最简化：</strong><br />
　　尽量方便前端Web工程师，只需要一条简单的SQL语句&#8220;SELECT ... FROM myisam_table JOIN sphinx_table ON (sphinx_table.sphinx_id=myisam_table.id) WHERE query='...';&#8221;即可实现高效搜索。<br />
<br />
　　<strong>(2)、创建索引、查询速度快：</strong><br />
　　①、Sphinx Search 是由俄罗斯人Andrew Aksyonoff 开发的高性能全文搜索软件包，在GPL与商业协议双许可协议下发行。<br />
　　<strong>Sphinx的特征：</strong><br />
　　&#8226;Sphinx支持高速建立索引（可达10MB/秒，而Lucene建立索引的速度是1.8MB/秒）<br />
　　&#8226;高性能搜索（在2-4 GB的文本上搜索，平均0.1秒内获得结果）<br />
　　&#8226;高扩展性（实测最高可对100GB的文本建立索引，单一索引可包含1亿条记录）<br />
　　&#8226;支持分布式检索<br />
　　&#8226;支持基于短语和基于统计的复合结果排序机制<br />
　　&#8226;支持任意数量的文件字段（数值属性或全文检索属性）<br />
　　&#8226;支持不同的搜索模式（&#8220;完全匹配&#8221;，&#8220;短语匹配&#8221;和&#8220;任一匹配&#8221;）<br />
　　&#8226;支持作为Mysql的存储引擎<br />
<br />
　　②、通过国外《High Performance MySQL》专家组的测试可以看出，根据主键进行查询的类似&#8220;SELECT ... FROM ... WHERE id = ...&#8221;的SQL语句（其中id为PRIMARY KEY），每秒钟能够处理10000次以上的查询，而普通的SELECT查询每秒只能处理几十次到几百次：<br />
　　<a href="http://blog.s135.com/attachment/200807/mysqlselect.png" target="_blank"><img class="insertimage" title="点击在新窗口中浏览此图片" alt="点击在新窗口中浏览此图片" src="http://blog.s135.com/attachment/200807/mysqlselect.png" border="0" /></a><br />
<br />
　　③、Sphinx不负责文本字段的存储。假设将数据库的id、date、title、body字段，用sphinx建立搜索索引。根据关键字、时间、类别、范围等信息查询一下sphinx，sphinx只会将查询结果的ID号等非文本信息告诉我们。要显示title、body等信息，还需要根据此ID号去查询MySQL数据库，或者从Memcachedb等其他的存储中取得。安装SphinxSE作为MySQL的存储引擎，将MySQL与Sphinx结合起来，是一种便捷的方法。<br />
　　创建一张Sphinx类型表，将MyISAM表的主键ID和Sphinx表的ID作一个JOIN联合查询。这样，对于MyISAM表来所，只相当于一个WHERE id=...的主键查询，WHERE后的条件都交给Sphinx去处理，可以充分发挥两者的优势，实现高速搜索查询。<br />
<br />
　　<strong>(3)、按服务类型进行分离：</strong><br />
　　为了保证数据的一致性，我在配置Sphinx读取索引源的MySQL数据库时，进行了锁表。Sphinx读取索引源的过程会耗费一定时间，由于MyISAM存储引擎的读锁和写锁是互斥的，为了避免写操作被长时间阻塞，导致数据库同步落后跟不上，我将提供&#8220;搜索查询服务&#8221;的和提供&#8220;索引源服务&#8221;的MySQL数据库进行了分开。监听3306端口的MySQL提供&#8220;搜索查询服务&#8221;，监听3406端口的MySQL提供&#8220;索引源服务&#8221;。<br />
<br />
　　<strong>(4)、&#8220;主索引＋增量索引&#8221;更新方式：</strong><br />
　　一般网站的特征：信息发布较为频繁；刚发布完的信息被编辑、修改的可能性大；两天以前的老帖变动性较小。<br />
　　基于这个特征，我设计了Sphinx主索引和增量索引。对于前天17:00之前的记录建立主索引，每天凌晨自动重建一次主索引；对于前天17:00之后到当前最新的记录，间隔3分钟自动重建一次增量索引。<br />
<br />
　　<strong>(5)、&#8220;Ext3文件系统＋tmpfs内存文件系统&#8221;相结合：</strong><br />
　　为了避免每3分钟重建增量索引导致磁盘IO较重，从而引起系统负载上升，我将主索引文件创建在磁盘，增量索引文件创建在tmpfs内存文件系统&#8220;/dev/shm/&#8221;内。&#8220;/dev/shm/&#8221;内的文件全部驻留在内存中，读写速度非常快。但是，重启服务器会导致&#8220;/dev/shm/&#8221;内的文件丢失，针对这个问题，我会在服务器开机时自动创建&#8220;/dev/shm/&#8221;内目录结构和Sphinx增量索引。<br />
<br />
　　<strong>(6)、中文分词词库：</strong><br />
　　我根据&#8220;自整理的中文分词库&#8221;＋&#8220;搜狗拼音输入法细胞词库&#8221;＋&#8220;LibMMSeg高频字库&#8221;＋... 综合整理成一份中文分词词库，出于某些考虑暂不提供。你可以使用LibMMSeg自带的中文分词词库。
 <img src ="http://www.blogjava.net/jinfeng_wang/aggbug/318434.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2010-04-15 16:35 <a href="http://www.blogjava.net/jinfeng_wang/archive/2010/04/15/318434.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用rsync实现网站镜像和备份 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2010/04/14/318279.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Wed, 14 Apr 2010 09:14:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2010/04/14/318279.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/318279.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2010/04/14/318279.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/318279.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/318279.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="word-spacing: 0px; font: 12px 'MS PGothic'; text-transform: none; color: rgb(0,0,0); text-indent: 0px; white-space: normal; letter-spacing: normal; border-collapse: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class="Apple-style-span" style="line-height: 18px; border-collapse: collapse">
<p><br class="Apple-interchange-newline" />
2、rsync的配置</p>
<p>对于rsync服务器来说，最重要和复杂的就是它的配置了。rsync服务器的配置文件为/etc/rsyncd.conf，其控制认证、访问、日志记录等等。</p>
<p>该文件是由一个或多个模块结构组成。一个模块定义以方括弧中的模块名开始，直到下一个模块定义开始或者文件结束，模块中包含格式为name = value的参数定义。每个模块其实就对应需要备份的一个目录树，比方说在我们的实例环境中，有三个目录树需要备份：/www/、/home/web_user1/和/home/web_user2/，那么就需要在配置文件中定义三个模块，分别对应三个目录树。</p>
<p>配置文件是行为单位的，也就是每个新行都表示一个新的注释、模块定义或者参数赋值。以#开始的行表示注释，以""结束的行表示下面一行是该行的继续。参数赋值中等号后可能是一个大小写不敏感的字符串、一个以trure/false表示的布尔值。</p>
<p>全局参数</p>
<p>在文件中[modlue]之前的所有参数都是全局参数，当然也可以在全局参数部分定义模块参数，这时候该参数的值就是所有模块的默认值。</p>
<p>motd file</p>
<p>"motd file"参数用来指定一个消息文件，当客户连接服务器时该文件的内容显示给客户，默认是没有motd文件的。</p>
<p>log file</p>
<p>"log file"指定rsync的日志文件，而不将日志发送给syslog。</p>
<p>pid file</p>
<p>指定rsync的pid文件。</p>
<p>syslog facility</p>
<p>指定rsync发送日志消息给syslog时的消息级别，常见的消息级别是：uth, authpriv, cron, daemon, ftp, kern, lpr, mail, news, security, sys-log, user, uucp, local0, local1, local2, local3,local4, local5, local6和local7。默认值是daemon。</p>
<p>模块参数</p>
<p>在全局参数之后就需要定义一个或多个模块了，模块中可以定义以下参数：</p>
<p>comment</p>
<p>给模块指定一个描述，该描述连同模块名在客户连接得到模块列表时显示给客户。默认没有描述定义。</p>
<p>path</p>
<p>指定该模块的供备份的目录树路径，该参数是必须指定的。</p>
<p>use chroot</p>
<p>如果"use chroot"指定为true，那么rsync在传输文件以前首先chroot到path参数所指定的目录下。这样做的原因是实现额外的安全防护，但是缺点是需要以roots权限，并且不能备份指向外部的符号连接所指向的目录文件。默认情况下chroot值为true。</p>
<p>max connections</p>
<p>指定该模块的最大并发连接数量以保护服务器，超过限制的连接请求将被告知随后再试。默认值是0，也就是没有限制。</p>
<p>lock file</p>
<p>指定支持max connections参数的锁文件，默认值是/var/run/rsyncd.lock。</p>
<p>read only</p>
<p>该选项设定是否允许客户上载文件。如果为true那么任何上载请求都会失败，如果为false并且服务器目录读写权限允许那么上载是允许的。默认值为true。</p>
<p>list</p>
<p>该选项设定当客户请求可以使用的模块列表时，该模块是否应该被列出。如果设置该选项为false，可以创建隐藏的模块。默认值是true。</p>
<p>uid</p>
<p>该选项指定当该模块传输文件时守护进程应该具有的uid，配合gid选项使用可以确定哪些可以访问怎么样的文件权限，默认值是"nobody"。</p>
<p>gid</p>
<p>该选项指定当该模块传输文件时守护进程应该具有的gid。默认值为"nobody"。</p>
<p>exlude</p>
<p>用来指定多个由空格隔开的多个模式列表，并将其添加到exclude列表中。这等同于在客户端命令中使用--exclude来指定模式，不过配置文件中指定的exlude模式不会传递给客户端，而仅仅应用于服务器。一个模块只能指定一个exlude选项，但是可以在模式前面使用"-"和"+"来指定是exclude还是include。</p>
<p>但是需要注意的一点是该选项有一定的安全性问题，客户很有可能绕过exlude列表，如果希望确保特定的文件不能被访问，那就最好结合uid/gid选项一起使用。</p>
<p>exlude from</p>
<p>指定一个包含exclude模式的定义的文件名，服务器从该文件中读取exlude列表定义。</p>
<p>include</p>
<p>用来指定多个由空格隔开的多个rsync并应该exlude的模式列表。这等同于在客户端命令中使用--include来指定模式，结合include和exlude可以定义复杂的exlude/include规则 。一个模块只能指定一个include选项，但是可以在模式前面使用"-"和"+"来指定是exclude还是include。</p>
<p>include from</p>
<p>指定一个包含include模式的定义的文件名，服务器从该文件中读取include列表定义。</p>
<p>auth users</p>
<p>该选项指定由空格或逗号分隔的用户名列表，只有这些用户才允许连接该模块。这里的用户和系统用户没有任何关系。如果"auth users"被设置，那么客户端发出对该模块的连接请求以后会被rsync请求challenged进行验证身份这里使用的challenge/response认证协议。用户的名和密码以明文方式存放在"secrets file"选项指定的文件中。默认情况下无需密码就可以连接模块(也就是匿名方式)。</p>
<p>secrets file</p>
<p>该选项指定一个包含定义用户名:密码对的文件。只有在"auth users"被定义时，该文件才有作用。文件每行包含一个username:passwd对。一般来说密码最好不要超过8个字符。没有默认的secures file名，需要限式指定一个。(例如：/etc/rsyncd.secrets)</p>
<p>strict modes</p>
<p>该选项指定是否监测密码文件的权限，如果该选项值为true那么密码文件只能被rsync服务器运行身份的用户访问，其他任何用户不可以访问该文件。默认值为true。</p>
<p>hosts allow</p>
<p>该选项指定哪些IP的客户允许连接该模块。客户模式定义可以是以下形式：</p>
<p>o xxx.xxx.xxx.xxx，客户主机只有完全匹配该IP才允许访问。例如：192.167.0.1</p>
<p>o a.b.c.d/n，属于该网络的客户都允许连接该模块。例如：192.168.0.0/24</p>
<p>o a.b.c.d/e.f.g.h，属于该网络的客户都允许连接该模块。例如：192.168.0.0/255.255.255.0</p>
<p>o 一个主机名，客户主机只有拥有该主机名才允许访问，例如：backup.linuxaid.com.cn。</p>
<p><br />
o *.linuxaid.com.cn，所有属于该域的主机都允许。</p>
<p>默认是允许所有主机连接。</p>
<p>hosts deny</p>
<p>指定不允许连接rsync服务器的机器，可以使用hosts allow的定义方式来进行定义。默认是没有hosts deny定义。</p>
<p>ignore errors</p>
<p>指定rsyncd在判断是否运行传输时的删除操作时忽略server上的IP错误，一般来说rsync在出现IO错误时将将跳过--delete操作，以防止因为暂时的资源不足或其它IO错误导致的严重问题。</p>
<p>ignore nonreadable</p>
<p>指定rysnc服务器完全忽略那些用户没有访问权限的文件。这对于在需要备份的目录中有些文件是不应该被备份者得到的情况是有意义的。</p>
<p>transfer logging</p>
<p>使rsync服务器使用ftp格式的文件来记录下载和上载操作在自己单独的日志中。</p>
<p>log format</p>
<p>通过该选项用户在使用transfer logging可以自己定制日志文件的字段。其格式是一个包含格式定义符的字符串，可以使用的格式定义符如下所示：</p>
<p>o %h 远程主机名</p>
<p>o %a 远程IP地址</p>
<p>o %l 文件长度字符数</p>
<p>o %p 该次rsync会话的进程id</p>
<p>o %o 操作类型："send"或"recv"</p>
<p>o %f 文件名</p>
<p>o %P 模块路径</p>
<p>o %m 模块名</p>
<p>o %t 当前时间</p>
<p>o %u 认证的用户名(匿名时是null)</p>
<p>o %b 实际传输的字节数</p>
<p>o %c 当发送文件时，该字段记录该文件的校验码</p>
<p>默认log格式为："%o %h [%a] %m (%u) %f %l"，一般来说,在每行的头上会添加"%t [%p] "。在源代码中同时发布有一个叫rsyncstats的perl脚本程序来统计这种格式的日志文件。</p>
<p>timeout</p>
<p>通过该选项可以覆盖客户指定的IP超时时间。通过该选项可以确保rsync服务器不会永远等待一个崩溃的客户。超时单位为秒钟，0表示没有超时定义，这也是默认值。对于匿名rsync服务器来说，一个理想的数字是600。</p>
<p>refuse options</p>
<p>通过该选项可以定义一些不允许客户对该模块使用的命令参数列表。这里必须使用命令全名，而不能是简称。但发生拒绝某个命令的情况时服务器将报告错误信息然后退出。如果要防止使用压缩，应该是："dont compress = *"。</p>
<p>dont compress</p>
<p>用来指定那些不进行压缩处理再传输的文件，默认值是</p>
<p>*.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz</p>
<p>rsync客户命令</p>
<p>在对rsync服务器配置结束以后，下一步就需要在客户端发出rsync命令来实现将服务器端的文件备份到客户端来。rsync是一个功能非常强大的工具，其命令也有很多功能特色选项，我们下面就对它的选项一一进行分析说明。</p>
<p>首先，rsync的命令格式可以为：</p>
<p>rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST</p>
<p>rsync [OPTION]... [USER@]HOST:SRC DEST</p>
<p>rsync [OPTION]... SRC [SRC]... DEST</p>
<p>rsync [OPTION]... [USER@]HOST::SRC [DEST]</p>
<p>rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST</p>
<p>rsync [OPTION]... rsync://[USER@]HOST[:PORT]/SRC [DEST]<span class="Apple-converted-space">&nbsp;</span><br />
rsync有六种不同的工作模式：</p>
<p>拷贝本地文件；当SRC和DES路径信息都不包含有单个冒号":"分隔符时就启动这种工作模式。</p>
<p>使用一个远程shell程序（如rsh、ssh）来实现将本地机器的内容拷贝到远程机器。当DST路径地址包含单个冒号":"分隔符时启动该模式。</p>
<p>使用一个远程shell程序（如rsh、ssh）来实现将远程机器的内容拷贝到本地机器。当SRC地址路径包含单个冒号":"分隔符时启动该模式。</p>
<p>从远程rsync服务器中拷贝文件到本地机。当SRC路径信息包含"::"分隔符时启动该模式。</p>
<p>从本地机器拷贝文件到远程rsync服务器中。当DST路径信息包含"::"分隔符时启动该模式。</p>
<p>列远程机的文件列表。这类似于rsync传输，不过只要在命令中省略掉本地机信息即可。</p>
<p>1、用法</p>
<p>在使用rsync传输文件时，需要指定一个源和一个目的，其中一个可能是远程机器的资源信息。例如：</p>
<p>rsync *.c foo:src/</p>
<p>表示将传输当前目录下所有以.c结尾的文件到机器foo的src目录下。如果任何文件已经存在于远程系统，则会调用远程更新协议来实现仅仅传输那些更新过的文件。</p>
<p>rsync -avz foo:src/bar /data/tmp</p>
<p>该命令则递归地传输机器foo上的src/bar目录下的所有内容到本地/data/tmp/bar目录中。文件以归档模式进行传输，以确保符号链结、属性、权限、属主等信息在传输中都被保存。此外，可以使用压缩技术来加快数据传输：</p>
<p>rsync -avz foo:src/bar/ /data/tmp</p>
<p>路径信息以"/"结尾时表示拷贝该目录，而不以"/"结尾表示拷贝该目录。当配合使用--delete选项时这两种情况的区别将会表现出来。</p>
<p>也可以以本地模式来使用rsync，如果SRC和DST路径中都没有任何":"符号则表示该命令运行在本地模式，等同于cp命令。</p>
<p>rsync somehost.mydomain.com::</p>
<p>这种模式则将会列出somehost.mydomain.com.可以访问的所有模块信息。</p>
<p>选项说明</p>
<p>-v, --verbose 详细模式输出<span class="Apple-converted-space">&nbsp;</span><br />
-q, --quiet 精简输出模式<span class="Apple-converted-space">&nbsp;</span><br />
-c, --checksum 打开校验开关，强制对文件传输进行校验<span class="Apple-converted-space">&nbsp;</span><br />
-a, --archive 归档模式，表示以递归方式传输文件，并保持所有文件属性，等于-rlptgoD<span class="Apple-converted-space">&nbsp;</span><br />
-r, --recursive 对子目录以递归模式处理<span class="Apple-converted-space">&nbsp;</span><br />
-R, --relative 使用相对路径信息</p>
<p>rsync foo/bar/foo.c remote:/tmp/</p>
<p>则在/tmp目录下创建foo.c文件，而如果使用-R参数：</p>
<p>rsync -R foo/bar/foo.c remote:/tmp/</p>
<p>则会创建文件/tmp/foo/bar/foo.c，也就是会保持完全路径信息。</p>
<p>-b, --backup 创建备份，也就是对于目的已经存在有同样的文件名时，将老的文件重新命名为~filename。可以使用--suffix选项来指定不同的备份文件前缀。<span class="Apple-converted-space">&nbsp;</span><br />
--backup-dir 将备份文件(如~filename)存放在在目录下。<span class="Apple-converted-space">&nbsp;</span><br />
-suffix=SUFFIX 定义备份文件前缀<span class="Apple-converted-space">&nbsp;</span><br />
-u, --update 仅仅进行更新，也就是跳过所有已经存在于DST，并且文件时间晚于要备份的文件。(不覆盖更新的文件)<span class="Apple-converted-space">&nbsp;</span><br />
-l, --links 保留软链结<span class="Apple-converted-space">&nbsp;</span><br />
-L, --copy-links 想对待常规文件一样处理软链结<span class="Apple-converted-space">&nbsp;</span><br />
--copy-unsafe-links 仅仅拷贝指向SRC路径目录树以外的链结<span class="Apple-converted-space">&nbsp;</span><br />
--safe-links 忽略指向SRC路径目录树以外的链结<span class="Apple-converted-space">&nbsp;</span><br />
-H, --hard-links 保留硬链结<span class="Apple-converted-space">&nbsp;</span><br />
-p, --perms 保持文件权限<span class="Apple-converted-space">&nbsp;</span><br />
-o, --owner 保持文件属主信息<span class="Apple-converted-space">&nbsp;</span><br />
-g, --group 保持文件属组信息<span class="Apple-converted-space">&nbsp;</span><br />
-D, --devices 保持设备文件信息<span class="Apple-converted-space">&nbsp;</span><br />
-t, --times 保持文件时间信息<span class="Apple-converted-space">&nbsp;</span><br />
-S, --sparse 对稀疏文件进行特殊处理以节省DST的空间<span class="Apple-converted-space">&nbsp;</span><br />
-n, --dry-run现实哪些文件将被传输<span class="Apple-converted-space">&nbsp;</span><br />
-W, --whole-file 拷贝文件，不进行增量检测<span class="Apple-converted-space">&nbsp;</span><br />
-x, --one-file-system 不要跨越文件系统边界<span class="Apple-converted-space">&nbsp;</span><br />
-B, --block-size=SIZE 检验算法使用的块尺寸，默认是700字节<span class="Apple-converted-space">&nbsp;</span><br />
-e, --rsh=COMMAND 指定替代rsh的shell程序<span class="Apple-converted-space">&nbsp;</span><br />
--rsync-path=PATH 指定远程服务器上的rsync命令所在路径信息<span class="Apple-converted-space">&nbsp;</span><br />
-C, --cvs-exclude 使用和CVS一样的方法自动忽略文件，用来排除那些不希望传输的文件<span class="Apple-converted-space">&nbsp;</span><br />
--existing 仅仅更新那些已经存在于DST的文件，而不备份那些新创建的文件<span class="Apple-converted-space">&nbsp;</span><br />
--delete 删除那些DST中SRC没有的文件<span class="Apple-converted-space">&nbsp;</span><br />
--delete-excluded 同样删除接收端那些被该选项指定排除的文件<span class="Apple-converted-space">&nbsp;</span><br />
--delete-after 传输结束以后再删除<span class="Apple-converted-space">&nbsp;</span><br />
--ignore-errors 及时出现IO错误也进行删除<span class="Apple-converted-space">&nbsp;</span><br />
--max-delete=NUM 最多删除NUM个文件<span class="Apple-converted-space">&nbsp;</span><br />
--partial 保留那些因故没有完全传输的文件，以是加快随后的再次传输<span class="Apple-converted-space">&nbsp;</span><br />
--force 强制删除目录，即使不为空<span class="Apple-converted-space">&nbsp;</span><br />
--numeric-ids 不将数字的用户和组ID匹配为用户名和组名<span class="Apple-converted-space">&nbsp;</span><br />
--timeout=TIME IP超时时间，单位为秒<span class="Apple-converted-space">&nbsp;</span><br />
-I, --ignore-times 不跳过那些有同样的时间和长度的文件<span class="Apple-converted-space">&nbsp;</span><br />
--size-only 当决定是否要备份文件时，仅仅察看文件大小而不考虑文件时间<span class="Apple-converted-space">&nbsp;</span><br />
--modify-window=NUM 决定文件是否时间相同时使用的时间戳窗口，默认为0<span class="Apple-converted-space">&nbsp;</span><br />
-T --temp-dir=DIR 在DIR中创建临时文件<span class="Apple-converted-space">&nbsp;</span><br />
--compare-dest=DIR 同样比较DIR中的文件来决定是否需要备份<span class="Apple-converted-space">&nbsp;</span><br />
-P 等同于 --partial<span class="Apple-converted-space">&nbsp;</span><br />
--progress 显示备份过程<span class="Apple-converted-space">&nbsp;</span><br />
-z, --compress 对备份的文件在传输时进行压缩处理<span class="Apple-converted-space">&nbsp;</span><br />
--exclude=PATTERN 指定排除不需要传输的文件模式<span class="Apple-converted-space">&nbsp;</span><br />
--include=PATTERN 指定不排除而需要传输的文件模式<span class="Apple-converted-space">&nbsp;</span><br />
--exclude-from=FILE 排除FILE中指定模式的文件<span class="Apple-converted-space">&nbsp;</span><br />
--include-from=FILE 不排除FILE指定模式匹配的文件<span class="Apple-converted-space">&nbsp;</span><br />
--version 打印版本信息<span class="Apple-converted-space">&nbsp;</span><br />
--address 绑定到特定的地址<span class="Apple-converted-space">&nbsp;</span><br />
--config=FILE 指定其他的配置文件，不使用默认的rsyncd.conf文件<span class="Apple-converted-space">&nbsp;</span><br />
--port=PORT 指定其他的rsync服务端口<span class="Apple-converted-space">&nbsp;</span><br />
--blocking-io 对远程shell使用阻塞IO<span class="Apple-converted-space">&nbsp;</span><br />
-stats 给出某些文件的传输状态<span class="Apple-converted-space">&nbsp;</span><br />
--progress 在传输时现实传输过程<span class="Apple-converted-space">&nbsp;</span><br />
--log-format=FORMAT 指定日志文件格式<span class="Apple-converted-space">&nbsp;</span><br />
--password-file=FILE 从FILE中得到密码<span class="Apple-converted-space">&nbsp;</span><br />
--bwlimit=KBPS 限制I/O带宽，KBytes per second<span class="Apple-converted-space">&nbsp;</span><br />
-h, --help 显示帮助信息</p>
<p>实例分析</p>
<p>这里假设有两台服务器：A和B。其中A是主web服务器，具有域名<a style="font-size: 9pt; color: black; text-decoration: none" href="http://www.linuxaid.com.cn(202.99.11.120/">www.linuxaid.com.cn(202.99.11.120</a>)，B服务器是备份机，其域名为backup.linuxaid.com.cn(202.99.11.121)。其中A的web内容存放在以下几个地方：/www/和/home/web_user1/和/home/web_user2/。我们需要在备份机B上建立对这几个目录内容的备份。</p>
<p>服务器配置实例</p>
<p>那么在<a style="font-size: 9pt; color: black; text-decoration: none" href="http://www.linuxaid.com.cn/">www.linuxaid.com.cn</a>上创建rsyncd的配置文件/etc/rsyncd.conf，内容如下：</p>
<p>uid = nobody<span class="Apple-converted-space">&nbsp;</span><br />
gid = nobody<span class="Apple-converted-space">&nbsp;</span><br />
use chroot = no<span class="Apple-converted-space">&nbsp;</span><br />
max connections = 4<span class="Apple-converted-space">&nbsp;</span><br />
pid file = /var/run/rsyncd.pid<span class="Apple-converted-space">&nbsp;</span><br />
lock file = /var/run/rsync.lock<span class="Apple-converted-space">&nbsp;</span><br />
log file = /var/log/rsyncd.log</p>
<p>[www]<span class="Apple-converted-space">&nbsp;</span><br />
path = /www/<span class="Apple-converted-space">&nbsp;</span><br />
ignore errors<span class="Apple-converted-space">&nbsp;</span><br />
read only = true<span class="Apple-converted-space">&nbsp;</span><br />
list = false<span class="Apple-converted-space">&nbsp;</span><br />
hosts allow = 202.99.11.121<span class="Apple-converted-space">&nbsp;</span><br />
hosts deny = 0.0.0.0/32<span class="Apple-converted-space">&nbsp;</span><br />
auth users = backup<span class="Apple-converted-space">&nbsp;</span><br />
secrets file = /etc/backserver.pas</p>
<p>[web_user1]<span class="Apple-converted-space">&nbsp;</span><br />
path = /home/web_user1/<span class="Apple-converted-space">&nbsp;</span><br />
ignore errors<span class="Apple-converted-space">&nbsp;</span><br />
read only = true<span class="Apple-converted-space">&nbsp;</span><br />
list = false<span class="Apple-converted-space">&nbsp;</span><br />
hosts allow = 202.99.11.121<span class="Apple-converted-space">&nbsp;</span><br />
hosts deny = 0.0.0.0/32<span class="Apple-converted-space">&nbsp;</span><br />
uid = web_user1<span class="Apple-converted-space">&nbsp;</span><br />
gid = web_user1<span class="Apple-converted-space">&nbsp;</span><br />
auth users = backup<span class="Apple-converted-space">&nbsp;</span><br />
secrets file = /etc/backserver.pas</p>
<p>[web_user2]<span class="Apple-converted-space">&nbsp;</span><br />
path = /home/web_user2/<span class="Apple-converted-space">&nbsp;</span><br />
ignore errors<span class="Apple-converted-space">&nbsp;</span><br />
read only = true<span class="Apple-converted-space">&nbsp;</span><br />
list = false<span class="Apple-converted-space">&nbsp;</span><br />
hosts allow = 202.99.11.121<span class="Apple-converted-space">&nbsp;</span><br />
hosts deny = 0.0.0.0/32<span class="Apple-converted-space">&nbsp;</span><br />
uid = web_user2<span class="Apple-converted-space">&nbsp;</span><br />
gid = web_user2<span class="Apple-converted-space">&nbsp;</span><br />
auth users = backup<span class="Apple-converted-space">&nbsp;</span><br />
secrets file = /etc/backserver.pas</p>
<p>这里定义有四个三个模块，分别对应于三个需要备份的目录树。这里只允许202.99.11.121备份本机的数据，并且需要认证。三个模块授权的备份用户都为backup，并且用户信息保存在文件/etc/backserver.pas中，其内容如下：</p>
<p>backup:bk_passwd</p>
<p>并且该文件只能是root用户可读写的，否则rsyncd启动时会出错。这些文件配置完毕以后，就需要在A服务器上启动rsyncd服务器：</p>
<p>rsync --daemon</p>
<p>客户命令示例</p>
<p>/usr/local/bin/rsync -vzrtopg --delete --exclude "logs/" --exclude "conf/ssl.*/" --progress<a style="font-size: 9pt; color: black; text-decoration: none" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#98;&#97;&#99;&#107;&#117;&#112;&#64;&#50;&#48;&#50;&#46;&#57;&#57;&#46;&#49;&#49;&#46;&#49;&#50;&#48;&#58;&#58;&#119;&#119;&#119;">backup@202.99.11.120::www</a><span class="Apple-converted-space">&nbsp;</span>/backup/www/ --password-file=/etc/rsync.pass</p>
<p>上面这个命令行中-vzrtopg里的v是verbose，z是压缩，r是recursive，topg都是保持文件原有属性如属主、时间的参数。--progress是指显示出详细的进度情况，--delete是指如果服务器端删除了这一文件，那么客户端也相应把文件删除，保持真正的一致。--exclude "logs/" 表示不对/www/logs目录下的文件进行备份。--exclude "conf/ssl.*/"表示不对/www/conf/ssl.*/目录下的文件进行备份。</p>
<p><a style="font-size: 9pt; color: black; text-decoration: none" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#98;&#97;&#99;&#107;&#117;&#112;&#64;&#50;&#48;&#50;&#46;&#57;&#57;&#46;&#49;&#49;&#46;&#49;&#50;&#48;&#58;&#58;&#119;&#119;&#119;">backup@202.99.11.120::www</a><span class="Apple-converted-space">&nbsp;</span>表示对该命令是对服务器202.99.11.120中的www模块进行备份，backup表示使用backup来对该模块进行备份。</p>
<p>--password-file=/etc/rsync.pass来指定密码文件，这样就可以在脚本中使用而无需交互式地输入验证密码了，这里需要注意的是这份密码文件权限属性要设得只有root可读。</p>
<p>这里将备份的内容存放在备份机的/backup/www/目录下。</p>
<p>[root@linuxaid /]# /usr/local/bin/rsync -vzrtopg --delete --exclude "logs/" --exclude "conf/ssl.*/" --progress<span class="Apple-converted-space">&nbsp;</span><a style="font-size: 9pt; color: black; text-decoration: none" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#98;&#97;&#99;&#107;&#117;&#112;&#64;&#50;&#48;&#50;&#46;&#57;&#57;&#46;&#49;&#49;&#46;&#49;&#50;&#48;&#58;&#58;&#119;&#119;&#119;">backup@202.99.11.120::www</a><span class="Apple-converted-space">&nbsp;</span>/backup/www/ --password-file=/etc/rsync.pass<span class="Apple-converted-space">&nbsp;</span><br />
receiving file list ... done<span class="Apple-converted-space">&nbsp;</span><br />
./<span class="Apple-converted-space">&nbsp;</span><br />
1<span class="Apple-converted-space">&nbsp;</span><br />
785 (100%)<span class="Apple-converted-space">&nbsp;</span><br />
1.py<span class="Apple-converted-space">&nbsp;</span><br />
4086 (100%)<span class="Apple-converted-space">&nbsp;</span><br />
2.py<span class="Apple-converted-space">&nbsp;</span><br />
10680 (100%)<span class="Apple-converted-space">&nbsp;</span><br />
a<span class="Apple-converted-space">&nbsp;</span><br />
0 (100%)<span class="Apple-converted-space">&nbsp;</span><br />
ip<span class="Apple-converted-space">&nbsp;</span><br />
3956 (100%)<span class="Apple-converted-space">&nbsp;</span><br />
./<span class="Apple-converted-space">&nbsp;</span><br />
wrote 2900 bytes read 145499 bytes 576.34 bytes/sec<span class="Apple-converted-space">&nbsp;</span><br />
total size is 2374927 speedup is 45.34</p>
<p>对其它两个模块操作的命令分别为：</p>
<p>/usr/local/bin/rsync -vzrtopg --delete --progress<span class="Apple-converted-space">&nbsp;</span><a style="font-size: 9pt; color: black; text-decoration: none" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#98;&#97;&#99;&#107;&#117;&#112;&#64;&#50;&#48;&#50;&#46;&#57;&#57;&#46;&#49;&#49;&#46;&#49;&#50;&#48;&#58;&#58;&#119;&#101;&#98;&#95;&#117;&#115;&#101;&#114;&#49;">backup@202.99.11.120::web_user1</a><span class="Apple-converted-space">&nbsp;</span>/backup/web_user1/ --password-file=/etc/rsync.pass</p>
<p>/usr/local/bin/rsync -vzrtopg --delete --progress<span class="Apple-converted-space">&nbsp;</span><a style="font-size: 9pt; color: black; text-decoration: none" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#98;&#97;&#99;&#107;&#117;&#112;&#64;&#50;&#48;&#50;&#46;&#57;&#57;&#46;&#49;&#49;&#46;&#49;&#50;&#48;&#58;&#58;&#119;&#101;&#98;&#95;&#117;&#115;&#101;&#114;&#50;">backup@202.99.11.120::web_user2</a><span class="Apple-converted-space">&nbsp;</span>/backup/web_user2/ --password-file=/etc/rsync.pass</p>
<p>可以将客户命令通过crontab -e命令来实现自动备份，如crontab -e：</p>
<p>一些示例脚本</p>
<p>这里这些脚本都是rsync网站上的例子：</p>
<p>1、每隔七天将数据往中心服务器做增量备份</p>
<p>#!/bin/sh</p>
<p># This script does personal backups to a rsync backup server. You will end up<span class="Apple-converted-space">&nbsp;</span><br />
# with a 7 day rotating incremental backup. The incrementals will go<span class="Apple-converted-space">&nbsp;</span><br />
# into subdirectories named after the day of the week, and the current<span class="Apple-converted-space">&nbsp;</span><br />
# full backup goes into a directory called "current"<span class="Apple-converted-space">&nbsp;</span><br />
#<span class="Apple-converted-space">&nbsp;</span><a style="font-size: 9pt; color: black; text-decoration: none" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#116;&#114;&#105;&#100;&#103;&#101;&#64;&#108;&#105;&#110;&#117;&#120;&#99;&#97;&#114;&#101;&#46;&#99;&#111;&#109;">tridge@linuxcare.com</a></p>
<p># directory to backup<span class="Apple-converted-space">&nbsp;</span><br />
BDIR=/home/$USER</p>
<p># excludes file - this contains a wildcard pattern per line of files to exclude<span class="Apple-converted-space">&nbsp;</span><br />
EXCLUDES=$HOME/cron/excludes</p>
<p># the name of the backup machine<span class="Apple-converted-space">&nbsp;</span><br />
BSERVER=owl</p>
<p># your password on the backup server<span class="Apple-converted-space">&nbsp;</span><br />
export RSYNC_PASSWORD=XXXXXX</p>
<p><br />
########################################################################</p>
<p>BACKUPDIR=`date +%A`<span class="Apple-converted-space">&nbsp;</span><br />
OPTS="--force --ignore-errors --delete-excluded --exclude-from=$EXCLUDES<span class="Apple-converted-space">&nbsp;</span><br />
--delete --backup --backup-dir=/$BACKUPDIR -a"</p>
<p>export PATH=$PATH:/bin:/usr/bin:/usr/local/bin</p>
<p># the following line clears the last weeks incremental directory<span class="Apple-converted-space">&nbsp;</span><br />
[ -d $HOME/emptydir ]' 'mkdir $HOME/emptydir<span class="Apple-converted-space">&nbsp;</span><br />
rsync --delete -a $HOME/emptydir/ $BSERVER::$USER/$BACKUPDIR/<span class="Apple-converted-space">&nbsp;</span><br />
rmdir $HOME/emptydir</p>
<p># now the actual transfer<span class="Apple-converted-space">&nbsp;</span><br />
rsync $OPTS $BDIR $BSERVER::$USER/current</p>
<p>2、备份至一个空闲的硬盘</p>
<p>#!/bin/sh</p>
<p>export PATH=/usr/local/bin:/usr/bin:/bin</p>
<p>LIST="rootfs usr data data2"</p>
<p>for d in $LIST; do<span class="Apple-converted-space">&nbsp;</span><br />
mount /backup/$d<span class="Apple-converted-space">&nbsp;</span><br />
rsync -ax --exclude fstab --delete /$d/ /backup/$d/<span class="Apple-converted-space">&nbsp;</span><br />
umount /backup/$d<span class="Apple-converted-space">&nbsp;</span><br />
done</p>
<p>DAY=`date "+%A"`</p>
<p>rsync -a --delete /usr/local/apache /data2/backups/$DAY<span class="Apple-converted-space">&nbsp;</span><br />
rsync -a --delete /data/solid /data2/backups/$DAY</p>
<p>3、对vger.rutgers.edu的cvs树进行镜像</p>
<p>#!/bin/bash</p>
<p>cd /var/www/cvs/vger/<span class="Apple-converted-space">&nbsp;</span><br />
PATH=/usr/local/bin:/usr/freeware/bin:/usr/bin:/bin</p>
<p>RUN=`lps x | grep rsync | grep -v grep | wc -l`<span class="Apple-converted-space">&nbsp;</span><br />
if [ "$RUN" -gt 0 ]; then<span class="Apple-converted-space">&nbsp;</span><br />
echo already running<span class="Apple-converted-space">&nbsp;</span><br />
exit 1<span class="Apple-converted-space">&nbsp;</span><br />
fi</p>
<p>rsync -az vger.rutgers.edu::cvs/CVSROOT/ChangeLog $HOME/ChangeLog</p>
<p>sum1=`sum $HOME/ChangeLog`<span class="Apple-converted-space">&nbsp;</span><br />
sum2=`sum /var/www/cvs/vger/CVSROOT/ChangeLog`</p>
<p>if [ "$sum1" = "$sum2" ]; then<span class="Apple-converted-space">&nbsp;</span><br />
echo nothing to do<span class="Apple-converted-space">&nbsp;</span><br />
exit 0<span class="Apple-converted-space">&nbsp;</span><br />
fi</p>
<p>rsync -az --delete --force vger.rutgers.edu::cvs/ /var/www/cvs/vger/<span class="Apple-converted-space">&nbsp;</span><br />
exit 0</p>
<p>FAQ</p>
<p>Q：如何通过ssh进行rsync，而且无须输入密码？<span class="Apple-converted-space">&nbsp;</span><br />
A：可以通过以下几个步骤</p>
<p>1. 通过ssh-keygen在server A上建立SSH keys，不要指定密码，你会在~/.ssh下看到identity和identity.pub文件<span class="Apple-converted-space">&nbsp;</span><br />
2. 在server B上的home目录建立子目录.ssh<span class="Apple-converted-space">&nbsp;</span><br />
3. 将A的identity.pub拷贝到server B上<span class="Apple-converted-space">&nbsp;</span><br />
4. 将identity.pub加到~[user b]/.ssh/authorized_keys<span class="Apple-converted-space">&nbsp;</span><br />
5. 于是server A上的A用户，可通过下面命令以用户B ssh到server B上了<span class="Apple-converted-space">&nbsp;</span><br />
e.g. ssh -l userB serverB<span class="Apple-converted-space">&nbsp;</span><br />
这样就使server A上的用户A就可以ssh以用户B的身份无需密码登陆到server B上了。</p>
<p>Q：如何通过在不危害安全的情况下通过防火墙使用rsync?<span class="Apple-converted-space">&nbsp;</span><br />
A：解答如下：</p>
<p>这通常有两种情况，一种是服务器在防火墙内，一种是服务器在防火墙外。无论哪种情况，通常还是使用ssh，这时最好新建一个备份用户，并且配置sshd仅允许这个用户通过RSA认证方式进入。 如果服务器在防火墙内，则最好限定客户端的IP地址，拒绝其它所有连接。如果客户机在防火墙内，则可以简单允许防火墙打开TCP端口22的ssh外发连接就ok了。</p>
<p>Q：我能将更改过或者删除的文件也备份上来吗？<span class="Apple-converted-space">&nbsp;</span><br />
A：当然可以：</p>
<p>你可以使用如：rsync -other -options -backupdir = ./backup-2000-2-13 ...这样的命令来实现。<span class="Apple-converted-space">&nbsp;</span><br />
这样如果源文件:/path/to/some/file.c改变了，那么旧的文件就会被移到./backup-2000-2-13/path/to/some/file.c，<span class="Apple-converted-space">&nbsp;</span><br />
这里这个目录需要自己手工建立起来</p>
<p>Q：我需要在防火墙上开放哪些端口以适应rsync？<span class="Apple-converted-space">&nbsp;</span><br />
A：视情况而定</p>
<p>rsync可以直接通过873端口的tcp连接传文件，也可以通过22端口的ssh来进行文件传递，但你也可以通过下列命令改变它的端口：</p>
<p>rsync --port 8730 otherhost::<span class="Apple-converted-space">&nbsp;</span><br />
或者<span class="Apple-converted-space">&nbsp;</span><br />
rsync -e 'ssh -p 2002' otherhost:</p>
<p>Q：我如何通过rsync只复制目录结构，忽略掉文件呢？<span class="Apple-converted-space">&nbsp;</span><br />
A：rsync -av --include '*/' --exclude '*' source-dir dest-dir</p>
<p>Q：为什么我总会出现"Read-only file system"的错误呢？<span class="Apple-converted-space">&nbsp;</span><br />
A：看看是否忘了设"read only = no"了</p>
<p>Q：为什么我会出现<a style="font-size: 9pt; color: black; text-decoration: none" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#39;&#64;&#69;&#82;&#82;&#79;&#82;">'@ERROR</a>: invalid gid'的错误呢？<span class="Apple-converted-space">&nbsp;</span><br />
A：rsync使用时默认是用uid=nobody;gid=nobody来运行的，如果你的系统不存在nobody组的话，就会出现这样的错误，可以试试gid = nogroup或者其它</p>
<p>Q：绑定端口873失败是怎么回事？<span class="Apple-converted-space">&nbsp;</span><br />
A：如果你不是以root权限运行这一守护进程的话，因为1024端口以下是特权端口，会出现这样的错误。你可以用--port参数来改变。</p>
<p>Q：为什么我认证失败？<span class="Apple-converted-space">&nbsp;</span><br />
A：从你的命令行看来：</p>
<p>你用的是：<span class="Apple-converted-space">&nbsp;</span><br />
&gt; bash$ rsync -a 144.16.251.213::test test<span class="Apple-converted-space">&nbsp;</span><br />
&gt; Password:<span class="Apple-converted-space">&nbsp;</span><br />
&gt; @ERROR: auth failed on module test<span class="Apple-converted-space">&nbsp;</span><br />
&gt;<span class="Apple-converted-space">&nbsp;</span><br />
&gt; I dont understand this. Can somebody explain as to how to acomplish this.<span class="Apple-converted-space">&nbsp;</span><br />
&gt; All suggestions are welcome.</p>
<p>应该是没有以你的用户名登陆导致的问题，试试rsync -a<span class="Apple-converted-space">&nbsp;</span><a style="font-size: 9pt; color: black; text-decoration: none" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#109;&#97;&#120;&#64;&#49;&#52;&#52;&#46;&#49;&#54;&#46;&#50;&#53;&#49;&#46;&#50;&#49;&#51;&#58;&#58;&#116;&#101;&#115;&#116;">max@144.16.251.213::test</a><span class="Apple-converted-space">&nbsp;</span>test<span class="Apple-converted-space">&nbsp;</span></p>
</span></span>
 <img src ="http://www.blogjava.net/jinfeng_wang/aggbug/318279.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2010-04-14 17:14 <a href="http://www.blogjava.net/jinfeng_wang/archive/2010/04/14/318279.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IP别名+TCP转发+端口映射实现跨网络访问zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2010/04/14/318264.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Wed, 14 Apr 2010 07:17:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2010/04/14/318264.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/318264.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2010/04/14/318264.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/318264.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/318264.html</trackback:ping><description><![CDATA[<div class="textbox-label">&nbsp;</div>
<div class="textbox-content" id="zoomtext">　　[文章作者：张宴 本文版本：v1.0 最后修改：2007.08.21 转载请注明出处：<a href="http://blog.s135.com/" target="_blank">http://blog.s135.com</a>]<br />
<br />
　　我这两天在工作中遇到了一些问题，在今天下午全部解决，于是决定写一篇文章，将实现方法记录下来：<br />
<br />
　　一、背景环境：<br />
　　1、都是Linux服务器；<br />
　　2、&#8220;服务器A&#8221;与&#8220;服务器C&#8221;不在同一网络，两者之间是不通的；<br />
　　3、&#8220;服务器A&#8221;、&#8220;服务器C&#8221;分别与&#8220;服务器B&#8221;相通。<br />
<br />
　　二、要实现的需求：<br />
　　1、让&#8220;服务器A&#8221;上的PHP程序能够连接&#8220;服务器C&#8221;上的MySQL数据库（IP：10.10.1.4，端口：3306）；<br />
　　2、不允许在&#8220;服务器A&#8221;上的PHP程序中更改MySQL地址（10.10.1.4）和MySQL端口（3306）。<br />
<br />
　　三、实现原理：<br />
　　理论上从&#8220;服务器A&#8221;是无法直接连接&#8220;服务器C&#8221;的IP地址（10.10.1.4）及其3306端口的【图中的虚线】，但通过&#8220;IP别名+TCP转发+端口映射&#8221;，我在&#8220;服务器A&#8221;上的PHP程序无须作任何修改的情况下实现了这项功能【图中的实线】。<br />
　　<span style="color: #ff0000">访问路线：&#8220;服务器A&#8221;上的PHP程序─&#8594;虚拟10.10.1.4:3306─&#8594;192.168.1.3:8520─&#8594;真实10.10.1.4:3306</span><br />
<br />
　　<a href="http://blog.s135.com/attachment/200708/iptables.gif" target="_blank"><img class="insertimage" title="点击在新窗口中浏览此图片" alt="点击在新窗口中浏览此图片" src="http://blog.s135.com/attachment/200708/iptables.gif" border="0" /></a><br />
<br />
　　四、实现方法：<a name="entrymore"></a><br />
　　1、修改&#8220;服务器A&#8221;上的Apache配置文件httpd.conf（以下仅列出要修改的部分，其余部分用......表示）：<br />
<div class="quote">
<div class="quote-title">引用</div>
<div class="quote-content">................<br />
Listen 192.168.1.2:80<br />
................<br />
NameVirtualHost 192.168.1.2:80<br />
&lt;VirtualHost 192.168.1.2:80&gt;<br />
................</div>
</div>
<br />
　　然后重启Apache：<br />
　　<span style="color: #008000">/usr/local/apache/bin/httpd -k restart</span><br />
<br />
　　2、在&#8220;服务器A&#8221;上创建本地回环设备lo（即127.0.0.1）的IP别名10.10.1.4，即虚拟IP：<br />
　　『图中的①』<br />
　　<span style="color: #008000">/sbin/ifconfig lo:0 10.10.1.4 broadcast 10.10.1.4 netmask 255.255.255.255 up<br />
　　/sbin/route add -host 10.10.1.4 dev lo:0</span><br />
<br />
　　3、在&#8220;服务器A&#8221;上编译安装TCP转发软件rinetd（官方网站：<a href="http://www.boutell.com/rinetd/" target="_blank">http://www.boutell.com/rinetd/</a>），将对10.10.1.4:80的TCP请求重定向到192.168.1.3:8520上：<br />
　　『图中的②』<br />
<span style="color: #008000">　　wget <a href="http://www.boutell.com/rinetd/http/rinetd.tar.gz" target="_blank">http://www.boutell.com/rinetd/http/rinetd.tar.gz</a><br />
　　tar zxvf rinetd.tar.gz<br />
　　cd rinetd<br />
　　make &amp;&amp; make install<br />
　　vi /etc/rinetd.conf</span><br />
　　输入以下内容(格式：源地址 源端口 目标地址 目标端口)：<br />
<div class="quote">
<div class="quote-title">引用</div>
<div class="quote-content">10.10.1.4 80 192.168.1.3 8520</div>
</div>
<br />
　　启动rinetd守护进程<br />
<span style="color: #008000">　　/usr/sbin/rinetd -c /etc/rinetd.conf</span><br />
<br />
　　4、在&#8220;服务器B&#8221;上利用iptables配置端口映射，将自身的8520端口映射到10.10.1.4的3306端口上：<br />
　　『图中的③』<br />
<span style="color: #008000">　　echo "1"&gt; /proc/sys/net/ipv4/ip_forward<br />
　　/sbin/iptables -t nat -A PREROUTING -p tcp -s 192.168.1.0/24 -d </span><span style="color: #ff0000">192.168.1.3</span><span style="color: #008000"> --dport </span><span style="color: #ff0000">8520</span><span style="color: #008000"> -j DNAT --to-destination </span><span style="color: #ff0000">10.10.1.4:3306</span><br />
<span style="color: #008000">　　/sbin/iptables -t nat -A POSTROUTING -p tcp -s 192.168.1.0/24 -d </span><span style="color: #ff0000">10.10.1.4</span><span style="color: #008000"> --dport </span><span style="color: #ff0000">3306</span><span style="color: #008000"> -j SNAT --to-source </span><span style="color: #ff0000">10.10.1.3</span><br />
<br />
　　为了防止服务器重启导致TCP转发失效，请：<br />
　　<span style="color: #008000">vi /etc/rc.local</span><br />
　　增加一行：<br />
<div class="quote">
<div class="quote-title">引用</div>
<div class="quote-content">echo "1"&gt; /proc/sys/net/ipv4/ip_forward</div>
</div>
<br />
<br />
　　或者：<br />
　　<span style="color: #008000">vi /etc/sysctl.conf</span><br />
　　增加一行：<br />
<div class="quote">
<div class="quote-title">引用</div>
<div class="quote-content">net.ipv4.ip_forward = 1</div>
</div>
<br />
　　<span style="color: #008000">/sbin/sysctl -p</span> </div>
  <img src ="http://www.blogjava.net/jinfeng_wang/aggbug/318264.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2010-04-14 15:17 <a href="http://www.blogjava.net/jinfeng_wang/archive/2010/04/14/318264.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>文字コードについて（シフトJISの問題） zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/11/27/303975.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 27 Nov 2009 12:51:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/11/27/303975.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/303975.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/11/27/303975.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/303975.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/303975.html</trackback:ping><description><![CDATA[<table cellspacing="5" cellpadding="0" width="100%" bgcolor="#ddffff" border="0">
    <tbody>
        <tr>
            <td width="100%">
            <p>&nbsp;</p>
            <p>http://park3.wakwak.com/~ozashin/sw_tips/webapp_tips/sjis_charset.html<br />
            <br />
            文字コードについて（シフトJISの問題）</p>
            </td>
        </tr>
    </tbody>
</table>
<p>文字コードをシフトJISで開発し、Windowsのサーバで動かす場合の文字コード問題について示します。</p>
<p>厳密にいうとWindowsが扱う文字コードは、シフトＪＩＳでは、ありません。MS932です。または、コードページ CP932ともいいます。MS932は、マ社がシフトＪＩＳを拡張して定義したコード体系です。</p>
<p>で、ここで何が問題になるかというと、クライアントへの出力をcharset="Shift_JIS"とか定義して、アプリケーションサーバーなど が一生懸命シフトＪＩＳで出力しようとすると、ある文字列が文字化けするのです。ようは、MS932にある文字コードはシフトＪＩＳには、ないので、'? 'で出力されるということです。なお、OC4JやJBuilderで利用するtomcatは、MS932で出力しようとするので、あんまり文字化けしませ ん。</p>
<p>Java内部で扱う文字コードは、Unicodeです。ここでシフトＪＩＳとUnicodeのマッピングとMS932とUnicodeのマッピング の違いによって、思わぬ、文字化けが発生するのです。ちなみに、JSPファイルにじか書きしてあるもじは、そのまま出力されるので問題はありません。<br />
Javaプログラムを通して、Unicodeからcharsetで定義された文字が出力されるとき文字化けの対象となります。</p>
<p>具体的には、'～'、'∥'、'－'、'￠'、'￡'、'￢'などの一部の記号類です。<br />
日本語のUnicodeベンダ依存文字表 <a href="http://www.ingrid.org/java/i18n/unicode.html" target="_blank">http://www.ingrid.org/java/i18n/unicode.html</a> を参照してください。</p>
<p>というわけで、Windowsで動かすのであれば、charsetを"Windows-31J"にしましょう！そうすれば、一部の文字コードを除い て解決です。<br />
JSPの場合は、<strong>&lt;%@ page contentType="text/html; charset=Windows-31J" %&gt;</strong>と定義します。<br />
Servletでは、</p>
<pre>	private static final String CONTENT_TYPE = "text/html; charset=Windows-31J";
....
response.setContentType(CONTENT_TYPE);
</pre>
<p>のように実装します。charset="MS932"でもＯＫですが、<a href="http://www.ingrid.org/java/i18n/encoding/shift_jis.html" target="_blank">http://www.ingrid.org/java/i18n/encoding/shift_jis.html</a>に あるとおり、これからは、Windows-31Jでいくようです。</p>
<hr />
<strong>WAVE DASH問題（TILDE問題）</strong>
<p>シフトＪＩＳがらみでもうひとつWAVE DASH問題（TILDE問題）というのがあります。これは、JDBCがらみというか、データベースの文字セットの問題というか、誰が根本的な原因かわか らないところがあります。Unicodeの実装が厳密に定義されていないので、ベンダごとで実装の仕方が違っているそうです。</p>
<p>Oracleを例にとって文字の流れを以下に示します。テスト環境は以下のとおり。</p>
<blockquote>
<table cellspacing="0" cellpadding="3" width="80%" border="1">
    <tbody>
        <tr>
            <td width="21%">OS</td>
            <td width="79%">Windows2000</td>
        </tr>
        <tr>
            <td width="21%">データベースサーバ</td>
            <td width="79%">Oracle8i R8.1.7</td>
        </tr>
        <tr>
            <td width="21%">ＤＢ内部コード</td>
            <td width="79%">JA16SJIS</td>
        </tr>
    </tbody>
</table>
</blockquote>
<p>ある入力フィールドに '～' を含んだ文字列を設定し、それをデータベースへ格納し、参照する場合、文字変換の流れは以下のようになります。</p>
<ol>
    <li>ブラウザ上では、Windows-31J(<strong>MS932</strong>)なので、'～'=<strong>0x8160</strong>
    <li>Javaプログラム内部では、<strong>Unicode</strong>なので、JisAutoDetectなどで変換して、'～'=<strong>0xFF5E (FULL WIDTH TILDE)</strong>
    <li>データベースに格納された結果は、<strong>JA16SJIS</strong>なので、'～'=<strong>0x8160</strong>
    <li>Javaプログラムがデータベースから文字をGETしたとき、'～'=<strong>0x301C (WAVE DASH)</strong>
    <li>0x301Cを応答として出力すると、0x8160にマッピングできないので <strong>'?'</strong>となる。 </li>
</ol>
<p>この問題は、上記のようにcharsetをWindows31Jにしても回避できません。</p>
<p>ちなみに、Unicodeでいうところの '～' は、0x301C (WAVE DASH)になるのが、Unicodeコンソーシアムでは正しい実装方法だそうです。</p>
<p>(4)で、0x301Cに変換されているんだからＯＫじゃん、と思いますが、0x8160を0xFF5Eに変換するんだから、0x8160にするに は、0xFF5Eにしてあげなければならないのです。</p>
<p>この問題を回避するには、２通りあります。</p>
<ol>
    <li>(4)で0x301Cで変換された文字を0xFF5Eに再変換する。
    <li>Oracle9i R9.2以降でMS932がサポートされたデータベース内部コード "JA16SJISTILDE"を利用する。 </li>
</ol>
<p>しかし、(1)では、データベースをアクセスするたび、毎回再変換するので、パフォーマンスが落ちます。(2)では、いまさらOracle9に移行 できる予算がないし、検証しているひまもないという問題があります。<br />
したがって、第３の回避方法として、運用で逃げる（'～'は、システム上扱えないコードとしてユーザに我慢してもらう）という手もあります。</p>
<p>ちなみに、私は、多少パフォーマンスが落ちてもＯＫということで、(1)で再変換の道を選びました。</p>
<p>まぁ、最初からOracle9iR9.2を選んでいれば、ＯＫですけどね。また、検証はしていませんが、EUC-JPでも同様の問題があって、 Oracle9iR9.2からJA16EUCTILDEがサポートされているようです。</p>
<hr />
<strong>参考（Javaのデフォルトコンバータの推移）</strong>
<p>Windowsで動かし、JISAutoで変換した場合のデフォルトコンバータは、誰の都合か知りませんが、以下のように変化しました。<br />
まぁ、Windowsで動かす限り、Windows-31Jをcharsetにしておけば、JDK1.4.1以降でもＯＫのようです。</p>
<table cellspacing="0" cellpadding="3" width="80%" border="1">
    <tbody>
        <tr>
            <td align="center" width="50%" bgcolor="#e6e6e6"><strong>JDKのバージョン</strong></td>
            <td align="center" width="50%" bgcolor="#e6e6e6"><strong>デフォルトコンバータ</strong></td>
            <td align="center" width="50%" bgcolor="#e6e6e6"><strong>実装されたコンバータ</strong></td>
        </tr>
        <tr>
            <td width="50%">JDK 1.1.7以前</td>
            <td width="50%">SJISコンバータ</td>
            <td width="50%">　</td>
        </tr>
        <tr>
            <td width="50%">JDK 1.1.8</td>
            <td width="50%">SJISコンバータ</td>
            <td width="50%">MS932コンバータが実装される</td>
        </tr>
        <tr>
            <td width="50%">JDK 1.2 ～ 1.4.0</td>
            <td width="50%">MS932コンバータ</td>
            <td width="50%">　</td>
        </tr>
        <tr>
            <td width="50%">JDK 1.4.1</td>
            <td width="50%">SJISコンバータ</td>
            <td width="50%">　</td>
        </tr>
        <tr>
            <td width="50%">それ以降</td>
            <td width="50%">保証なし？</td>
            <td width="50%">　</td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/303975.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-11-27 20:51 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/11/27/303975.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL Performance Analyzer in Oracle Database 11g Release 1 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/10/24/299566.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Sat, 24 Oct 2009 05:58:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/10/24/299566.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/299566.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/10/24/299566.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/299566.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/299566.html</trackback:ping><description><![CDATA[<a href="http://www.oracle-base.com/articles/11g/SqlPerformanceAnalyzer_11gR1.php#sql_performance_analyzer_api">http://www.oracle-base.com/articles/11g/SqlPerformanceAnalyzer_11gR1.php#sql_performance_analyzer_api</a>&nbsp; <br />
<br />
The concept of SQL tuning sets, along with the <code>DBMS_SQLTUNE</code> package to manipulate them, was introduced in Oracle 10g as part of the <a href="http://www.oracle-base.com/articles/10g/AutomaticSQLTuning10g.php">Automatic SQL Tuning</a> functionality. Oracle 11g makes further use of SQL tuning sets with the SQL Performance Analyzer, which compares the performance of the statements in a tuning set before and after a database change. The database change can be as major or minor as you like, such as:<br />
<ul>
    <li>Database, operating system, or hardware upgrades.
    <li>Database, operating system, or hardware configuration changes.
    <li>Database initialization parameter changes.
    <li>Schema changes, such as adding indexes or materialized views.
    <li>Refreshing optimizer statistics.
    <li>Creating or changing SQL profiles. </li>
</ul>
Unlike <a href="http://www.oracle-base.com/articles/11g/DatabaseReplay_11gR1.php">Database Replay</a>, the SQL Performance Analyzer does not try and replicate the workload on the system. It just plugs through each statement gathering performance statistics.<br />
<br />
The SQL Performance Analyzer can be run manually using the <code>DBMS_SQLPA</code> package or using Enterprise Manager. This article gives an overview of both methods.<br />
<ul>
    <li><a href="http://www.oracle-base.com/articles/11g/SqlPerformanceAnalyzer_11gR1.php#setting_up">Setting Up the Test</a>
    <li><a href="http://www.oracle-base.com/articles/11g/SqlPerformanceAnalyzer_11gR1.php#tuning_set_api">Creating SQL Tuning Sets using the DBMS_SQLTUNE Package</a>
    <li><a href="http://www.oracle-base.com/articles/11g/SqlPerformanceAnalyzer_11gR1.php#sql_performance_analyzer_api">Running the SQL Performance Analyzer using the DBMS_SQLPA Package</a>
    <li><a href="http://www.oracle-base.com/articles/11g/SqlPerformanceAnalyzer_11gR1.php#tuning_set_em">Creating SQL Tuning Sets using Enterprise Manager</a>
    <li><a href="http://www.oracle-base.com/articles/11g/SqlPerformanceAnalyzer_11gR1.php#sql_performance_analyzer_em">Running the SQL Performance Analyzer using Enterprise Manager</a>
    <li><a href="http://www.oracle-base.com/articles/11g/SqlPerformanceAnalyzer_11gR1.php#optimizer_upgrade_simulation">Optimizer Upgrade Simulation</a>
    <li><a href="http://www.oracle-base.com/articles/11g/SqlPerformanceAnalyzer_11gR1.php#parameter_change">Parameter Change</a>
    <li><a href="http://www.oracle-base.com/articles/11g/SqlPerformanceAnalyzer_11gR1.php#transferring_tuning_sets">Transferring SQL Tuning Sets</a> </li>
</ul>
<a id="setting_up"></a>
<h2>Setting Up the Test</h2>
The SQL performance analyzer requires SQL tuning sets, and SQL tuning sets are pointless unless they contain SQL, so the first task should be to issue some SQL statements. We are only trying to demonstrate the technology, so the example can be really simple. The following code creates a test user called <code>SPA_TEST_USER</code>.<br />
<blockquote>
<pre>CONN sys/password@prod AS SYSDBA
CREATE USER spa_test_user IDENTIFIED BY spa_test_user
QUOTA UNLIMITED ON users;
GRANT CONNECT, CREATE TABLE TO spa_test_user;</pre>
</blockquote>Next, connect to the test user and create a test table called <code>MY_OBJECTS</code> using a query from the <code>ALL_OBJECTS</code> view.<br />
<blockquote>
<pre>CONN spa_test_user/spa_test_user@prod
CREATE TABLE my_objects AS
SELECT * FROM all_objects;
EXEC DBMS_STATS.gather_table_stats(USER, 'MY_OBJECTS', cascade =&gt; TRUE);</pre>
</blockquote>This schema represents our "before" state. Still logged in as the test user, issue the following statements.<br />
<blockquote>
<pre>SELECT COUNT(*) FROM my_objects WHERE object_id &lt;= 100;
SELECT object_name FROM my_objects WHERE object_id = 100;
SELECT COUNT(*) FROM my_objects WHERE object_id &lt;= 1000;
SELECT object_name FROM my_objects WHERE object_id = 1000;
SELECT COUNT(*) FROM my_objects WHERE object_id BETWEEN 100 AND 1000;</pre>
</blockquote>Notice, all statements make reference to the currently unindexed <code>OBJECT_ID</code> column. Later we will be indexing this column to create our changed "after" state.<br />
<br />
The select statements are now in the shared pool, so we can start creating an SQL tuning set.<br />
<a id="tuning_set_api"></a>
<h2>Creating SQL Tuning Sets using the DBMS_SQLTUNE Package</h2>
The <code>DBMS_SQLTUNE</code> package contains procedures and functions that allow us to create, manipulate and drop SQL tuning sets. The first step is to create an SQL tuning set called <code>spa_test_sqlset</code> using the <code>CREATE_SQLSET</code> procedure.<br />
<blockquote>
<pre>CONN sys/password@prod AS SYSDBA
EXEC DBMS_SQLTUNE.create_sqlset(sqlset_name =&gt; 'spa_test_sqlset');</pre>
</blockquote>Next, the <code>SELECT_CURSOR_CACHE</code> table function is used to retrieve a cursor containing all SQL statements that were parsed by the <code>SPA_TEST_USER</code> schema and contain the word "my_objects". The resulting cursor is loaded into the tuning set using the <code>LOAD_SQLSET</code> procedure.<br />
<blockquote>
<pre>DECLARE
l_cursor  DBMS_SQLTUNE.sqlset_cursor;
BEGIN
OPEN l_cursor FOR
SELECT VALUE(a)
FROM   TABLE(
DBMS_SQLTUNE.select_cursor_cache(
basic_filter   =&gt; 'sql_text LIKE ''%my_objects%'' and parsing_schema_name = ''SPA_TEST_USER''',
attribute_list =&gt; 'ALL')
) a;
DBMS_SQLTUNE.load_sqlset(sqlset_name     =&gt; 'spa_test_sqlset',
populate_cursor =&gt; l_cursor);
END;
/</pre>
</blockquote>The <code>DBA_SQLSET_STATEMENTS</code> view allows us to see which statements have been associated with the tuning set.<br />
<blockquote>
<pre>SELECT sql_text
FROM   dba_sqlset_statements
WHERE  sqlset_name = 'spa_test_sqlset';
SQL_TEXT
--------------------------------------------------------------------------------
SELECT object_name FROM my_objects WHERE object_id = 100
SELECT COUNT(*) FROM my_objects WHERE object_id &lt;= 100
SELECT COUNT(*) FROM my_objects WHERE object_id BETWEEN 100 AND 1000
SELECT COUNT(*) FROM my_objects WHERE object_id &lt;= 1000
SELECT object_name FROM my_objects WHERE object_id = 1000
5 rows selected.
SQL&gt;</pre>
</blockquote>Now we have an SQL tuning set, we can start using the SQL performance analyzer.<br />
<a id="sql_performance_analyzer_api"></a>
<h2>Running the SQL Performance Analyzer using the DBMS_SQLPA Package</h2>
The <code>DBMS_SQLPA</code> package is the PL/SQL API used to manage the SQL performance ananlyzer. The first step is to create an analysis task using the <code>CREATE_ANALYSIS_TASK</code> function, passing in the SQL tuning set name and making a note of the resulting task name.<br />
<blockquote>
<pre>CONN sys/password@prod AS SYSDBA
VARIABLE v_task VARCHAR2(64);
EXEC :v_task :=  DBMS_SQLPA.create_analysis_task(sqlset_name =&gt; 'spa_test_sqlset');
PL/SQL procedure successfully completed.
SQL&gt; PRINT :v_task
V_TASK
--------------------------------------------------------------------------------
TASK_122
SQL&gt;</pre>
</blockquote>Next, use the <code>EXECUTE_ANALYSIS_TASK</code> procedure to execute the contents of the SQL tuning set against the current state of the database to gather information about the performance before any modifications are made. This analysis run is named <code>before_change</code>.<br />
<blockquote>
<pre>BEGIN
DBMS_SQLPA.execute_analysis_task(
task_name       =&gt; :v_task,
execution_type  =&gt; 'test execute',
execution_name  =&gt; 'before_change');
END;
/</pre>
</blockquote>Now we have the "before" performance information, we need to make a change so we can test the "after" performance. For this example we will simply add an index to the test table on the <code>OBJECT_ID</code> column. In a new SQL*Plus session create the index using the following statements.<br />
<blockquote>
<pre>CONN spa_test_user/spa_test_user@prod
CREATE INDEX my_objects_index_01 ON my_objects(object_id);
EXEC DBMS_STATS.gather_table_stats(USER, 'MY_OBJECTS', cascade =&gt; TRUE);</pre>
</blockquote>Now, we can return to our original session and test the performance after the database change. Once again use the <code>EXECUTE_ANALYSIS_TASK</code> procedure, naming the analysis task "after_change".<br />
<blockquote>
<pre>BEGIN
DBMS_SQLPA.execute_analysis_task(
task_name       =&gt; :v_task,
execution_type  =&gt; 'test execute',
execution_name  =&gt; 'after_change');
END;
/</pre>
</blockquote>Once the before and after analysis tasks are complete, we must run a comparison analysis task. The following code explicitly names the analysis tasks to compare using name-value pairs in the <code>EXECUTION_PARAMS</code> parameter. If this is ommited, the latest two analysis runs are compared.<br />
<blockquote>
<pre>BEGIN
DBMS_SQLPA.execute_analysis_task(
task_name        =&gt; :v_task,
execution_type   =&gt; 'compare performance',
execution_params =&gt; dbms_advisor.arglist(
'execution_name1',
'before_change',
'execution_name2',
'after_change')
);
END;
/</pre>
</blockquote>With this final analysis run complete, we can check out the comparison report using the <code>REPORT_ANALYSIS_TASK</code> function. The function returns a CLOB containing the report in text or HTML format. The script below spools the default text format report out to a file.<br />
<blockquote>
<pre>SET LONG 1000000
SET PAGESIZE 0
SET LINESIZE 200
SET LONGCHUNKSIZE 200
SET TRIMSPOOL ON
SPOOL /tmp/execute_comparison_report.txt
SELECT DBMS_SQLPA.report_analysis_task(:v_task)
FROM   dual;
SPOOL OFF</pre>
</blockquote>An example of this file can be seen <a href="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/execute_comparison_report.txt">here</a>.<br />
<a id="tuning_set_em"></a>
<h2>Creating SQL Tuning Sets using Enterprise Manager</h2>
Click on the "SQL Tuning Sets" link towards the bottom of the "Performance" tab.<br />
<br />
<img alt="SQL Tuning Sets Link" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/01-SqlTuningSetsLink.jpg" /><br />
<br />
On the "SQL Tuning Sets" screen, click the "Create" button.<br />
<br />
<img alt="SQL Tuning Sets" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/02-SqlTuningSets.jpg" /><br />
<br />
Enter a name for the SQL tuning set and click the "Next" button.<br />
<br />
<img alt="Options" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/03-Options.jpg" /><br />
<br />
Select the "Load SQL statements one time only" option, select the "Cursor Cache" as the data source, then click the "Next" button.<br />
<br />
<img alt="Load Methods" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/04-LoadMethods.jpg" /><br />
<br />
Set the appropriate values for the "Parsing Schema Name" and "SQL Text" filter attributes, remove any extra attributes by clicking their remove icons, then click the "Next" button.<br />
<br />
<img alt="Filter Options" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/05-FilterOptions.jpg" /><br />
<br />
Accept the immediate schedule by clicking the "Next" button.<br />
<br />
<img alt="Schedule" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/06-Schedule.jpg" /><br />
<br />
Assuming the review information looks correct, click the "Submit" button.<br />
<br />
<img alt="Review" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/07-Review.jpg" /><br />
<br />
The "SQL Tuning Sets" screen shows the confirmation of the tuning set creation and the scheduled job to populate it.<br />
<br />
<img alt="Confirmation" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/08-Confirmation.jpg" /><br />
<br />
Once the population job completes, clicking on the SQL tuning set displays its contents.<br />
<br />
<img alt="SQL Tuning Set Contents" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/09-SqlTuningSetContents.jpg" /><br />
<br />
Now we have an SQL tuning set, we can start using the SQL performance analyzer.<br />
<a id="sql_performance_analyzer_em"></a>
<h2>Running the SQL Performance Analyzer using Enterprise Manager</h2>
Click the "SQL Performance Analayzer" link on the "Software and Support" tab.<br />
<br />
<img alt="SQL Performance Analyzer Link" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/10-SqlPerformanceAnalyzerLink.jpg" /><br />
<br />
Click the "Guided Workflow" link on the "SQL Performance Analayzer" screen.<br />
<br />
<img alt="Guided Workflow" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/11-GuidedWorkflow.jpg" /><br />
<br />
Click the execute icon on the first step to create the SQL Performance Analyzer task.<br />
<br />
<img alt="Create SPA Task Icon" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/12-CreateSpaTaskIcon.jpg" /><br />
<br />
Enter a name for the SPA task, select the SQL tuning set to associate with it, then click the "Create" button.<br />
<br />
<img alt="Create SPA Task" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/13-CreateSpaTask.jpg" /><br />
<br />
When the status of the previous step becomes a green tick, click the execute icon on the second step to capture the SQL tuning set performance information of the "before" state.<br />
<br />
<img alt="Replay Before Icon" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/14-ReplayBeforeIcon.jpg" /><br />
<br />
Enter a "Replay Trial Name" of "before_change", check the "Trial environment established" checkbox, then click the "Submit" button.<br />
<br />
<img alt="Replay Before" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/15-ReplayBefore.jpg" /><br />
<br />
When the status of the previous step becomes a green tick, click the execute icon on the third step to capture the SQL tuning set performance information of the "after" state.<br />
<br />
<img alt="Replay After Icon" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/16-ReplayAfterIcon.jpg" /><br />
<br />
Alter the state of the database by creating an index on the <code>OBJECT_ID</code> column of the test table.<br />
<blockquote>
<pre>CONN spa_test_user/spa_test_user@prod
CREATE INDEX my_objects_index_01 ON my_objects(object_id);
EXEC DBMS_STATS.gather_table_stats(USER, 'MY_OBJECTS', cascade =&gt; TRUE);</pre>
</blockquote>Enter a "Replay Trial Name" of "after_change", check the "Trial environment established" checkbox, then click the "Submit" button.<br />
<br />
<img alt="Replay After" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/17-ReplayAfter.jpg" /><br />
<br />
When the status of the previous step becomes a green tick, click the execute icon on the forth step to run a comparison analysis task.<br />
<br />
<img alt="Compare Icon" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/18-CompareIcon.jpg" /><br />
<br />
Accept the default "Trial 1 Name" and "Trial 2 Name" settings by clicking the "Submit" button.<br />
<br />
<img alt="Compare" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/19-Compare.jpg" /><br />
<br />
When the status of the previous step becomes a green tick, click the execute icon on the fifth step to view the comparison report.<br />
<br />
<img alt="Report Icon" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/20-ReportIcon.jpg" /><br />
<br />
The resulting page contains the comparison report for the SQL Performance Analyzer task.<br />
<br />
<img alt="Report" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/21-Report.jpg" /><br />
<br />
Clicking on a specific SQL ID displays the statement specific results, along with the before and after execution plans.<br />
<br />
<img alt="SQL Report" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/22-SqlReport.jpg" /><br />
<br />
<a id="optimizer_upgrade_simulation"></a>
<h2>Optimizer Upgrade Simulation</h2>
The SQL Performance Analyzer allows you to test the affects of optimizer version changes on SQL tuning sets. Click the "Optimizer Upgrade Simulation" link on the "SQL Performance Analyzer" page.<br />
<br />
<img alt="Optimizer Upgrade Simulation Link" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/23-OptimizerUpgradeSimulationLink.jpg" /><br />
<br />
Enter a task name, select the two optimizer versions to compare, then click the "Submit" button.<br />
<br />
<img alt="Optimizer Upgrade Simulation" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/24-OptimizerUpgradeSimulation.jpg" /><br />
<br />
The task is listed in the "SQL Performance Analyzer Tasks" section. Refresh the page intermittently until the task status becomes a green tick, then click on the task name.<br />
<br />
<img alt="Task List" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/25-TaskList.jpg" /><br />
<br />
The resulting screen shows details of the selected task. Click on the "Comparison Report" classes icon allows you to view the comparison report.<br />
<br />
<img alt="Task Details" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/26-TaskDetails.jpg" /><br />
<br />
<a id="parameter_change"></a>
<h2>Parameter Change</h2>
The SQL Performance Analyzer provides a shortcut for setting up tests of initialization parameter changes on SQL tuning sets. Click the "Parameter" link on the "SQL Performance Analyzer" page.<br />
<br />
<img alt="Parameter Change Link" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/27-ParameterChangeLink.jpg" /><br />
<br />
Enter a task name and the parameter you wish to test. Enter the base and changed value, then click the "Submit" button.<br />
<br />
<img alt="Parameter Change" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/28-ParameterChange.jpg" /><br />
<br />
The task is listed in the "SQL Performance Analyzer Tasks" section. Refresh the page intermittently until the task status becomes a green tick, then click on the task name.<br />
<br />
<img alt="Task List" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/29-TaskList.jpg" /><br />
<br />
The resulting screen shows details of the selected task. Click on the "Comparison Report" classes icon allows you to view the comparison report.<br />
<br />
<img alt="Task Details" src="http://www.oracle-base.com/articles/11g/images/sql_performance_analyzer/30-TaskDetails.jpg" /><br />
<br />
<a id="transferring_tuning_sets"></a>
<h2>Transferring SQL Tuning Sets</h2>
In the examples listed above, the tests have been performed on the same system. In reality you are more likely to want to create a tuning set on your production system, then run the SQL Performance Analyzer against it on a test system. Fortunately, the <code>DBMS_SQLTUNE</code> package allows you to transport SQL tuning sets by storing them in a staging table.<br />
<br />
First, create the staging table using the <code>CREATE_STGTAB_SQLSET</code> procedure.<br />
<blockquote>
<pre>CONN sys/password@prod AS SYSDBA
BEGIN
DBMS_SQLTUNE.create_stgtab_sqlset(table_name      =&gt; 'SQLSET_TAB',
schema_name     =&gt; 'SPA_TEST_USER',
tablespace_name =&gt; 'USERS');
END;
/</pre>
</blockquote>Next, use the <code>PACK_STGTAB_SQLSET</code> procedure to export SQL tuning set into the staging table.<br />
<blockquote>
<pre>BEGIN
DBMS_SQLTUNE.pack_stgtab_sqlset(sqlset_name          =&gt; 'SPA_TEST_SQLSET',
sqlset_owner         =&gt; 'SYS',
staging_table_name   =&gt; 'SQLSET_TAB',
staging_schema_owner =&gt; 'SPA_TEST_USER');
END;
/</pre>
</blockquote>Once the SQL tuning set is packed into the staging table, the table can be transferred to the test system using Datapump, Export/Import or via a database link. Once on the test system, the SQL tuning set can be imported using the <code>UNPACK_STGTAB_SQLSET</code> procedure.<br />
<blockquote>
<pre>BEGIN
DBMS_SQLTUNE.unpack_stgtab_sqlset(sqlset_name          =&gt; '%',
sqlset_owner         =&gt; 'SYS',
replace              =&gt; TRUE,
staging_table_name   =&gt; 'SQLSET_TAB',
staging_schema_owner =&gt; 'SPA_TEST_USER');
END;
/</pre>
</blockquote>The SQL tuning set can now be used with the SQL Performance Analyzer on the test system.<br />
<br />
For more information see:<br />
<ul>
    <li><a href="http://download.oracle.com/docs/cd/B28359_01/server.111/e12159/spa.htm#RATAD101">SQL Performance Analyzer</a>
    <li><a href="http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28419/d_sqlpa.htm">DBMS_SQLPA</a>
    <li><a href="http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28419/d_sqltun.htm">DBMS_SQLTUNE</a> </li>
</ul>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/299566.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-10-24 13:58 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/10/24/299566.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>  linux 的库操作命令 ar和nm      </title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/06/08/280748.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Mon, 08 Jun 2009 13:57:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/06/08/280748.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/280748.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/06/08/280748.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/280748.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/280748.html</trackback:ping><description><![CDATA[http://dev.csdn.net/article/69/69405.shtm<br />
http://publib.boulder.ibm.com/infocenter/systems/index.jsp?topic=/com.ibm.aix.cmds/doc/aixcmds1/ar.htm<br />
<span id="ArticleContent1_ArticleContent1_lblContent">&nbsp;
<p>当我们的程序中有经常使用的模块，而且这种模块在其他程序中也会用到，这时按照软件重用的思想，我们应该将它们生成库，使得以后编程可以减少开发代码量。这里介绍两个命令ar和nm，用来对库操作。 </p>
<p><strong>1.ar基本用法<br />
2.nm基本用法命令</strong></p>
<p>　当我们的程序中有经常使用的模块，而且这种模块在其他程序中也会用到，这时按照软件重用的思想，我们应该将它们生成库，使得以后编程可以减少开发代码量。这里介绍两个命令ar和nm，用来对库操作。 </p>
<p><strong>1.ar基本用法</strong></p>
<p>　　ar命令可以用来创建、修改库，也可以从库中提出单个模块。库是一单独的文件，里面包含了按照特定的结构组织起来的其它的一些文件（称做此库文件的member）。原始文件的内容、模式、时间戳、属主、组等属性都保留在库文件中。</p>
<p>　　下面是ar命令的格式：</p>
<p>ar [-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files...<br />
</p>
<p>　　例如我们可以用<strong><font color="#ff0000">ar rv libtest.a hello.o hello1.o</font></strong>来生成一个库，库名字是test，链接时可以用-ltest链接。该库中存放了两个模块hello.o和hello1.o。选项前可以有&#8216;-'字符，也可以没有。下面我们来看看命令的操作选项和任选项。现在我们把{dmpqrtx}部分称为操作选项，而[abcfilNoPsSuvV]部分称为任选项。</p>
<p>　　{dmpqrtx}中的操作选项在命令中只能并且必须使用其中一个，它们的含义如下：</p>
<ul>
    <li>d：从库中删除模块。按模块原来的文件名指定要删除的模块。如果使用了任选项v则列出被删除的每个模块。
    <li>m：该操作是在一个库中移动成员。当库中如果有若干模块有相同的符号定义(如函数定义)，则成员的位置顺序很重要。如果没有指定任选项，任何指定的成员将移到库的最后。也可以使用'a'，'b'，或'I'任选项移动到指定的位置。
    <li>p：显示库中指定的成员到标准输出。如果指定任选项v，则在输出成员的内容前，将显示成员的名字。如果没有指定成员的名字，所有库中的文件将显示出来。
    <li>q：快速追加。增加新模块到库的结尾处。并不检查是否需要替换。'a'，'b'，或'I'任选项对此操作没有影响，模块总是追加的库的结尾处。如果使用了任选项v则列出每个模块。 这时，库的符号表没有更新，可以用'ar s'或ranlib来更新库的符号表索引。
    <li>r：在库中插入模块(替换)。当插入的模块名已经在库中存在，则替换同名的模块。如果若干模块中有一个模块在库中不存在，ar显示一个错误消息，并不替换其他同名模块。默认的情况下，新的成员增加在库的结尾处，可以使用其他任选项来改变增加的位置。
    <li>t：显示库的模块表清单。一般只显示模块名。
    <li>x：从库中提取一个成员。如果不指定要提取的模块，则提取库中所有的模块。 </li>
</ul>
<p>　　下面在看看可与操作选项结合使用的任选项：</p>
<ul>
    <li>a：在库的一个已经存在的成员后面增加一个新的文件。如果使用任选项a，则应该为命令行中membername参数指定一个已经存在的成员名。
    <li>b：在库的一个已经存在的成员前面增加一个新的文件。如果使用任选项b，则应该为命令行中membername参数指定一个已经存在的成员名。
    <li>c：创建一个库。不管库是否存在，都将创建。
    <li>f：在库中截短指定的名字。缺省情况下，文件名的长度是不受限制的，可以使用此参数将文件名截短，以保证与其它系统的兼容。
    <li>i：在库的一个已经存在的成员前面增加一个新的文件。如果使用任选项i，则应该为命令行中membername参数指定一个已经存在的成员名(类似任选项b)。
    <li>l：暂未使用
    <li>N：与count参数一起使用，在库中有多个相同的文件名时指定提取或输出的个数。
    <li>o：当提取成员时，保留成员的原始数据。如果不指定该任选项，则提取出的模块的时间将标为提取出的时间。
    <li>P：进行文件名匹配时使用全路径名。ar在创建库时不能使用全路径名（这样的库文件不符合POSIX标准），但是有些工具可以。
    <li>s：写入一个目标文件索引到库中，或者更新一个存在的目标文件索引。甚至对于没有任何变化的库也作该动作。对一个库做ar s等同于对该库做ranlib。
    <li>S：不创建目标文件索引，这在创建较大的库时能加快时间。
    <li>u：一般说来，命令ar r...插入所有列出的文件到库中，如果你只想插入列出文件中那些比库中同名文件新的文件，就可以使用该任选项。该任选项只用于r操作选项。
    <li>v：该选项用来显示执行操作选项的附加信息。
    <li>V：显示ar的版本。 </li>
</ul>
<p><strong>2.nm基本用法命令</strong></p>
<p>　　nm用来列出目标文件的符号清单。下面是nm命令的格式：</p>
<p>nm [-a|--debug-syms] [-g|--extern-only] [-B][-C|--demangle] [-D|--dynamic] [-s|--print-armap][-o|--print-file-name] [-n|--numeric-sort][-p|--no-sort] [-r|--reverse-sort] [--size-sort][-u|--undefined-only] [-l|--line-numbers] [--help][--version] [-t radix|--radix=radix][-P|--portability] [-f format|--format=format][--target=bfdname] [objfile...]<br />
</p>
<p>　　如果没有为nm命令指出目标文件，则nm假定目标文件是a.out。下面列出该命令的任选项，大部分支持"-"开头的短格式和"—"开头的长格式。</p>
<ul>
    <li>-A、-o或--print-file-name：在找到的各个符号的名字前加上文件名，而不是在此文件的所有符号前只出现文件名一次。
    <p>例如nm libtest.a的输出如下：</p>
    CPThread.o:<br />
    00000068 T Main__8CPThreadPv<br />
    00000038 T Start__8CPThread<br />
    00000014 T _._8CPThread<br />
    00000000 T __8CPThread<br />
    00000000 ? __FRAME_BEGIN__<br />
    .......................................<br />
    <p>则nm -A 的输出如下：</p>
    libtest.a:CPThread.o:00000068 T Main__8CPThreadPv<br />
    libtest.a:CPThread.o:00000038 T Start__8CPThread<br />
    libtest.a:CPThread.o:00000014 T _._8CPThread<br />
    libtest.a:CPThread.o:00000000 T __8CPThread<br />
    libtest.a:CPThread.o:00000000 ? __FRAME_BEGIN__<br />
    ..................................................................<br />
    <li>-a或--debug-syms：显示调试符号。
    <li>-B：等同于--format=bsd，用来兼容MIPS的nm。
    <li>-C或--demangle：将低级符号名解码(demangle)成用户级名字。这样可以使得C++函数名具有可读性。
    <li>-D或--dynamic：显示动态符号。该任选项仅对于动态目标(例如特定类型的共享库)有意义。
    <li>-f format：使用format格式输出。format可以选取bsd、sysv或posix，该选项在GNU的nm中有用。默认为bsd。
    <li>-g或--extern-only：仅显示外部符号。
    <li>-n、-v或--numeric-sort：按符号对应地址的顺序排序，而非按符号名的字符顺序。
    <li>-p或--no-sort：按目标文件中遇到的符号顺序显示，不排序。
    <li>-P或--portability：使用POSIX.2标准输出格式代替默认的输出格式。等同于使用任选项-f posix。
    <li>-s或--print-armap：当列出库中成员的符号时，包含索引。索引的内容包含：哪些模块包含哪些名字的映射。
    <li>-r或--reverse-sort：反转排序的顺序(例如，升序变为降序)。
    <li>--size-sort：按大小排列符号顺序。该大小是按照一个符号的值与它下一个符号的值进行计算的。
    <li>-t radix或--radix=radix：使用radix进制显示符号值。radix只能为"d"表示十进制、"o"表示八进制或"x"表示十六进制。
    <li>--target=bfdname：指定一个目标代码的格式，而非使用系统的默认格式。
    <li>-u或--undefined-only：仅显示没有定义的符号(那些外部符号)。
    <li>-l或--line-numbers：对每个符号，使用调试信息来试图找到文件名和行号。对于已定义的符号，查找符号地址的行号。对于未定义符号，查找指向符号重定位入口的行号。如果可以找到行号信息，显示在符号信息之后。
    <li>-V或--version：显示nm的版本号。
    <li>--help：显示nm的任选项。 </li>
</ul>
<p><br />
<br />
<br />
&nbsp;</p>
<h2 id="a0949a5b">ar 命令</h2>
<p><a id="idx111" name="idx111"></a><a id="idx112" name="idx112"></a><a name="a0949a5c"></a></p>
<h3 id="a0949a5c">用途</h3>
<p><a name="eb000d9086mela"></a></p>
<p id="eb000d9086mela">维护链接编辑器使用的索引库。</p>
<p><a name="d720e41316jani"></a></p>
<h3 id="d720e41316jani">语法</h3>
<p><strong>ar</strong> [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#by5zf2d3clif"><strong>-c&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#dy5zf25bclif"><strong>-l&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#i0817971130mkm"><strong>-g</strong></a> | <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#a1z5zf1b0clif"><strong>-o </strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#a0949a6f"><strong>-s&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#zz5zf3d8clif"><strong>-v&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#vhsai81kbar"><strong>-C&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#zcsai194kbar"><strong>-T&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#minusz"><strong>-z&nbsp;</strong></a>] {&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#y36zf371clif"><strong>-h&nbsp;</strong></a>|&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#f46zf2d9clif"><strong>-p&nbsp;</strong></a>|&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#k56zf180clif"><strong>-t&nbsp;</strong></a>|&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#a666zf172clif"><strong>-x&nbsp;</strong></a>} [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#i0817971050mkm"><strong>-X&nbsp;</strong> {<strong>32</strong>|<strong>64</strong>|<strong>32_64</strong>|<strong>d64</strong>| <strong>any</strong>}]</a> <em>ArchiveFile</em> [&nbsp;<em>File&nbsp;</em>...&nbsp;]</p>
<p><strong>ar</strong> [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#by5zf2d3clif"><strong>-c&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#dy5zf25bclif"><strong>-l&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#i0817971130mkm"><strong>-g</strong></a> | <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#a1z5zf1b0clif"><strong>-o</strong></a> ] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#a0949a6f"><strong>-s&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#zz5zf3d8clif"><strong>-v&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#vhsai81kbar"><strong>-C&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#zcsai194kbar"><strong>-T&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#minusz"><strong>-z&nbsp;</strong></a>] {&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#a8q5zf5eclif"><strong>-m&nbsp;</strong></a>|&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#a3d6zf8cclif"><strong>-r&nbsp;</strong></a>[&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#a526zf35cclif"><strong>-u&nbsp;</strong></a>]&nbsp;} [&nbsp;{&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#a0949a63"><strong>-a&nbsp;</strong></a>|&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#azq5zf356clif"><strong>-b&nbsp;</strong></a>|&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#ja5zfe6clif"><strong>-i&nbsp;</strong></a>} <em>&nbsp;PositionName&nbsp;</em>&nbsp;] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#i0817971050mkm"><strong>-X&nbsp;</strong> {<strong>32</strong>|<strong>64</strong>|<strong>32_64</strong>|<strong>d64</strong>|<strong>any</strong>}]</a> &nbsp; <em>ArchiveFile File</em> ...</p>
<p><strong>ar</strong> [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#by5zf2d3clif"><strong>-c&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#dy5zf25bclif"><strong>-l&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#i0817971130mkm"><strong>-g</strong></a> | <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#a1z5zf1b0clif"><strong>-o</strong></a> ] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#a0949a6f"><strong>-s&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#zz5zf3d8clif"><strong>-v&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#vhsai81kbar"><strong>-C&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#zcsai194kbar"><strong>-T&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#minusz"><strong>-z&nbsp;</strong></a>] {&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#q36zf38aclif"><strong>-d&nbsp;</strong></a>|&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#q46zf96clif"><strong>-q&nbsp;</strong></a>} [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#i0817971050mkm"><strong>-X&nbsp;</strong> {<strong>32</strong>|<strong>64</strong>|<strong>32_64</strong>|<strong>d64</strong>|<strong>any</strong>}]</a> <em>ArchiveFile</em><em> File</em> ...</p>
<p><strong>ar</strong> [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#by5zf2d3clif"><strong>-c&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#dy5zf25bclif"><strong>-l&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#zz5zf3d8clif"><strong>-v&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#vhsai81kbar"><strong>-C&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#zcsai194kbar"><strong>-T&nbsp;</strong></a>] [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#minusz"><strong>-z&nbsp;</strong></a>] {&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#i0817971130mkm"><strong>-g</strong></a> | <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#a1z5zf1b0clif"><strong>-o</strong></a> |&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#a0949a6f"><strong>-s&nbsp;</strong></a>|&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#g56zf78clif"><strong>-w&nbsp;</strong></a>} [&nbsp; <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#i0817971050mkm"><strong>-X&nbsp;</strong> {<strong>32</strong>|<strong>64</strong>|<strong>32_64</strong>|<strong>d64</strong>|<strong>any</strong>}]</a> <em>ArchiveFile</em></p>
<p><a name="a0949a5f"></a></p>
<h3 id="a0949a5f">描述</h3>
<p><a name="eb000da227mela"></a></p>
<p id="eb000da227mela"><strong>ar</strong> 命令维护链接编辑器使用的索引库。<strong>ar</strong> 命令将一个或多个指定的文件并入单个写成 <strong>ar</strong> 压缩文档格式的压缩文档文件。当 <strong>ar</strong> 命令创建库时，它创建可传输格式的报头；当它创建或更新库时，它重建符号表。有关格式和索引压缩文档和符号表的结构的信息，请参阅 <strong>ar</strong> 文件格式条目。</p>
<p><strong>ar</strong> 命令识别两种文件格式。&#8220;大压缩文档格式&#8221;<a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.files/doc/aixfiles/ar_big.htm#lawsa072497bcl1245"><strong>ar_big</strong></a>，是缺省文件格式并支持 32 位和 64 位目标文件。&#8220;小压缩文档格式&#8221;可用于创建在比 AIX 4.3 更老的版本上可识别的压缩文档，请参阅 <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#i0817971130mkm"><strong>-g</strong></a> 标志。如果将一个 64 位的对象添加到小格式压缩文档，除非指定了 <strong>-g</strong>，否则 <strong>ar</strong> 首先将它转换为大格式。缺省情况下，<strong>ar</strong> 仅处理 32 位目标文件；压缩文档中任何 64 位目标文件在没有提示的情况下被忽略。要更改此行为，请使用 <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds1/ar.htm#i0817971050mkm"><strong>-X</strong></a> 标志或设置 <strong>OBJECT_MODE</strong> 环境变量。</p>
<p><a name="a0949a61"></a></p>
<h3 id="a0949a61">标志</h3>
<p><a name="eb000da548mela"></a></p>
<p id="eb000da548mela">在 <strong>ar</strong> 命令中，可以从集 <strong>cClosTv</strong> 中指定任何数量的可选标志。必须从标志集 <strong>dhmopqrstwx</strong> 中指定一个标志。如果选择 <strong>-m</strong> 或 <strong>-r</strong> 标志，您可能还要指定一个位置标志（<strong>-a</strong>、<strong>-b</strong> 或 <strong>-i</strong>）；对于 <strong>-a</strong>、<strong>-b</strong> 或 <strong>-i</strong> 标志，您必须还指定在 <em>ArchiveFile</em>（<em>PositionName</em>）中一个文件的名称，此名称紧跟在标志列表后，并由空格隔开。</p>
<p><a name="wq342"></a>
<table id="wq342" rules="none" width="100%" summary="" border="0" frame="void">
    <tbody valign="top">
        <tr>
            <td width="25%"><a id="a0949a63" name="a0949a63"></a><strong>-a</strong><em> PositionName</em></td>
            <td width="75%">在 <em>PositionName</em> 参数标识的现有文件后安置指定的文件。</td>
        </tr>
        <tr>
            <td><a id="azq5zf356clif" name="azq5zf356clif"></a><strong>-b</strong><em> PositionName</em></td>
            <td>在 <em>PositionName</em> 参数标识的现有文件前安置指定的文件。</td>
        </tr>
        <tr>
            <td><a id="by5zf2d3clif" name="by5zf2d3clif"></a><strong>-c</strong></td>
            <td>禁止在创建<em>库</em>时产生的正常消息。</td>
        </tr>
        <tr>
            <td><a id="vhsai81kbar" name="vhsai81kbar"></a><strong>-C</strong></td>
            <td>阻止解压缩的文件替换文件系统中同名的文件。</td>
        </tr>
        <tr>
            <td><a id="q36zf38aclif" name="q36zf38aclif"></a><strong>-d</strong></td>
            <td>从库中删除指定的文件。</td>
        </tr>
        <tr>
            <td><a id="i0817971130mkm" name="i0817971130mkm"></a><strong>-g</strong></td>
            <td>对压缩文档成员进行排序以确保用最小数量的未用空间获得最大的加载效率。在几乎所有情况下，<strong>-g</strong> 标志以压缩文档成员的逻辑链接顺序物理地安置它们。最终生成的压缩文档通常写成小格式，这样该标志可用来将大格式压缩文档转换成小格式压缩文档。包含 64 位 <strong>XCOFF</strong> 对象的压缩文档不能创建成或转换至小格式。</td>
        </tr>
        <tr>
            <td><a id="y36zf371clif" name="y36zf371clif"></a><strong>-h</strong></td>
            <td>将指定的文件的成员报头中的修改时间设置为当前日期和时间。如果不指定任何文件名称，那么 <strong>ar</strong> 命令设置所有成员报头的时间戳记。此标志不能和 <strong>-z</strong> 标志一起使用。</td>
        </tr>
        <tr>
            <td><a id="ja5zfe6clif" name="ja5zfe6clif"></a><strong>-i</strong><em> PositionName</em></td>
            <td>在 <em>PositionName</em> 参数标识的现有文件前安置指定的文件（和 <strong>-b</strong> 相同）。</td>
        </tr>
        <tr>
            <td><a id="dy5zf25bclif" name="dy5zf25bclif"></a><strong>-l</strong></td>
            <td>将临时文件置于当前（本地）目录中，而非 <strong>TMPDIR</strong> 目录中（缺省为 <strong>/tmp</strong>）。</td>
        </tr>
        <tr>
            <td><a id="a8q5zf5eclif" name="a8q5zf5eclif"></a><strong>-m</strong></td>
            <td>将指定的文件移动到库中的某个其他位置。缺省情况下，它将指定的文件移动到库的末尾。使用位置标志（<strong>abi</strong>）来指定某个其他位置。</td>
        </tr>
        <tr>
            <td><a id="a1z5zf1b0clif" name="a1z5zf1b0clif"></a><strong>-o</strong></td>
            <td>对压缩文档成员进行排序以确保用最小数量的未用空间获得最大的加载效率。在几乎所有情况下，<strong>-o</strong> 标志以压缩文档成员的逻辑链接顺序物理地安置它们。最终生成的压缩文档通常写成大格式，这样该标志可用来将小格式压缩文档转换成大格式压缩文档。</td>
        </tr>
        <tr>
            <td><a id="f46zf2d9clif" name="f46zf2d9clif"></a><strong>-p</strong></td>
            <td>将 <em>Files</em> 参数中指定的文件的内容或在 <em>ArchiveFile</em> 参数中指定的所有文件（如果您不指定任何文件）都写至标准输出。</td>
        </tr>
        <tr>
            <td><a id="q46zf96clif" name="q46zf96clif"></a><strong>-q</strong></td>
            <td>将指定的文件添加到库的末尾。另外，如果指定同一个文件两次，它可能被放入库中两次。</td>
        </tr>
        <tr>
            <td><a id="a3d6zf8cclif" name="a3d6zf8cclif"></a><strong>-r</strong></td>
            <td>如果指定的文件已经存在于库中，那么替换它。因为指定的文件在库中占据它们替换的文件的同一个位置，位置标志没有任何附加的影响。当和 <strong>-u</strong> 标志（更新）一起使用时，<strong>-r</strong> 标志仅替换自从最后一次添加到库中以后修改的文件。
            <p>如果指定的文件不存在于库中，那么 <strong>ar</strong> 命令添加它。在这种情况下，位置标志影响放置。如果不指定位置，那么将新文件置于库的末尾。如果指定同一个文件两次，它可能被放入库中两次。</p>
            </td>
        </tr>
        <tr>
            <td><a id="a0949a6f" name="a0949a6f"></a><strong>-s</strong></td>
            <td>无论<strong> ar</strong> 命令是否修改了库内容都强制重新生成库符号表。请在库上使用 <strong>strip</strong> 命令之后，使用此标志来恢复库符号表。</td>
        </tr>
        <tr>
            <td><a id="k56zf180clif" name="k56zf180clif"></a><strong>-t</strong></td>
            <td>将库的目录写至标准输出。如果指定文件名称，那么仅显示指定的那些文件。如果不指定任何文件，<strong>-t</strong> 标志列出库中的所有文件。</td>
        </tr>
        <tr>
            <td><a id="zcsai194kbar" name="zcsai194kbar"></a><strong>-T</strong></td>
            <td>如果压缩文档成员名称比文件系统支持的长，那么允许文件名称截短。此选项无效，因为文件系统支持的名称长度等于 255 个字符的最大压缩文档成员名称。</td>
        </tr>
        <tr>
            <td><a id="a526zf35cclif" name="a526zf35cclif"></a><strong>-u</strong></td>
            <td>仅复制自它们最后一次复制起更改的文件（请参阅先前讨论过的 <strong>-r</strong> 标志）。</td>
        </tr>
        <tr>
            <td><a id="zz5zf3d8clif" name="zz5zf3d8clif"></a><strong>-v</strong></td>
            <td>将建立新库的详细的逐个文件的描述写至标准输出。当和 <strong>-t</strong> 标志一起使用时，它给出类似于 <strong>ls -l</strong> 命令给出的长列表。当和 <strong>-x</strong> 标志一起使用时，它在每个文件前加一个名称。当和 <strong>-h</strong> 标志一起使用，它列出成员名称和更新的修改时间。</td>
        </tr>
        <tr>
            <td><a id="g56zf78clif" name="g56zf78clif"></a><strong>-w</strong></td>
            <td>显示压缩文档符号表。每个符号和其中定义此符号的文件的名称一起列出。</td>
        </tr>
        <tr>
            <td><a id="a666zf172clif" name="a666zf172clif"></a><strong>-x</strong></td>
            <td>通过将指定的文件复制到当前目录来解压缩它们。这些副本和原始文件（保留在库中）具有相同的名称。如果不指定任何文件，<strong>-x</strong> 标志复制库中的所有文件。此过程不会更改库。</td>
        </tr>
        <tr>
            <td><a id="i0817971050mkm" name="i0817971050mkm"></a><strong>-X</strong><em> mode</em></td>
            <td>指定 <strong>ar</strong> 应检查的目标文件的类型。<em>mode</em> 必须是下列其中一项：
            <dl>
            <dt class="bold"><strong>32</strong>
            <dd>仅处理 32 位目标文件
            <dt class="bold"><strong>64</strong>
            <dd>仅处理 64 位目标文件
            <dt class="bold"><strong>32_64</strong>
            <dd>处理 32 位 和 64 位目标文件
            <dt class="bold">d64
            <dd>检测已终止的 64 位 XCOFF 文件（幻数 == U803XTOCMAGIC）。
            <dt class="bold">any
            <dd>处理所有受支持的对象文件。 </dd></dl>缺省值是处理 32 位目标文件（忽略 64 位对象）。<em>mode</em> 还可以用 <strong>OBJECT_MODE</strong> 环境变量来设置。例如，<strong>OBJECT_MODE=64</strong> 使 <strong>ar</strong> 处理任何 64 位对象并忽略 32 位对象。<strong>-X</strong> 标志覆盖 <strong>OBJECT_MODE</strong> 变量。</td>
        </tr>
        <tr>
            <td><a id="minusz" name="minusz"></a><strong>-z</strong></td>
            <td>创建压缩文档的临时副本并对副本执行所有要求的修改。当所有操作成功完成时，压缩文档的工作副本覆盖原始副本。此标志不能和 <strong>-h</strong> 标志一起使用。</td>
        </tr>
        <tr>
            <td><a id="w66zf235clif" name="w66zf235clif"></a><em>ArchiveFile</em></td>
            <td>指定压缩文档文件名称；必需。</td>
        </tr>
        <tr>
            <td><a id="d720e43683jani" name="d720e43683jani"></a><em>MemberName ...</em></td>
            <td>各压缩文档成员的名称。</td>
        </tr>
    </tbody>
</table>
<a name="aaf8f7b553lyn"></a></p>
<h3 id="aaf8f7b553lyn">退出状态</h3>
<p><a name="eb000de534mela"></a></p>
<p id="eb000de534mela">该命令返回以下退出值：</p>
<p><a name="wq343"></a>
<table id="wq343" rules="none" width="100%" summary="" border="0" frame="void">
    <tbody valign="top">
        <tr>
            <td width="5%"><a id="bf2b7b5198joy" name="bf2b7b5198joy"></a><strong>0</strong></td>
            <td width="95%">成功完成。</td>
        </tr>
        <tr>
            <td><a id="bf2b7b5269joy" name="bf2b7b5269joy"></a><strong>&gt;0</strong></td>
            <td>发生错误。</td>
        </tr>
    </tbody>
</table>
<a name="a0949a76"></a></p>
<h3 id="a0949a76">示例</h3>
<ol type="1">
    <li><a name="a0949a77"></a>要创建一个库，请输入：
    <p>&nbsp;</p>
    <a name="a0949a78"></a>
    <pre class="xmp" id="a0949a78">ar -v -q lib.a strlen.o strcpy.o</pre>
    <p class="indatacontent">如果 <tt>lib.a</tt> 库不存在，那么此命令创建它，并将文件 <tt>strlen.o</tt> 和 <tt>strcpy.o</tt> 的副本输入其中。如果 <tt>lib.a</tt> 库存在，那么此命令在不检查相同成员的情况下，将新的成员添加到末尾。<strong>v</strong> 标志设置详细方式，在此方式中 <strong>ar</strong> 命令在其进行时显示进程报告。</p>
    <li><a name="a0949a7a"></a>要显示库的目录，请输入：
    <p>&nbsp;</p>
    <a name="a0949a7b"></a>
    <pre class="xmp" id="a0949a7b">ar -v -t lib.a</pre>
    <p class="indatacontent">此命令列出了 <tt>lib.a</tt> 库的目录，显示类似于 <strong>ls -l</strong> 命令的输出的长列表。要只列出成员文件名称，那么省略 <strong>-v</strong> 标志。</p>
    <li><a name="a0949a7d"></a>要替换或添加新成员到库中，请输入：
    <p>&nbsp;</p>
    <a name="a0949a7e"></a>
    <pre class="xmp" id="a0949a7e">ar -v -r lib.a strlen.o strcat.o</pre>
    <p class="indatacontent">此命令替换成员 <tt>strlen.o</tt> 和 <tt>strcat.o</tt>。如果 <tt>lib.a</tt> 如示例 1 中显示的那样创建，那么替换 <tt>strlen.o</tt> 成员。因为不存在名为 <tt>strcat.o</tt> 的成员，所以它被添加到库的末尾。</p>
    <li><a name="a0949a80"></a>要指定在何处插入新成员，请输入：
    <p>&nbsp;</p>
    <a name="a0949a81"></a>
    <pre class="xmp" id="a0949a81">ar -v -r -b strlen.o lib.a strcmp.o</pre>
    <p class="indatacontent">此命令添加 <tt>strcmp.o</tt> 文件，并将该新成员置于 <tt>strlen.o</tt> 成员之前。</p>
    <li><a name="a0949a83"></a>要更新一个已经更改过的成员，请输入：
    <p>&nbsp;</p>
    <a name="a0949a84"></a>
    <pre class="xmp" id="a0949a84">ar -v -r -u lib.a strcpy.o</pre>
    <p class="indatacontent">此命令替换现有 <tt>strcpy.o</tt> 成员，但仅当文件 <tt>strcpy.o</tt> 自从最后一次添加到库后已经修改时才替换它。</p>
    <li><a name="a0949a86"></a>要更改库成员的顺序，请输入：
    <p>&nbsp;</p>
    <a name="a0949a87"></a>
    <pre class="xmp" id="a0949a87">ar -v -m -a strcmp.o lib.a strcat.o strcpy.o</pre>
    <p class="indatacontent">此命令将成员 <tt>strcat.o</tt> 和 <tt>strcpy.o</tt> 移动到紧跟在 <tt>strcmp.o </tt>成员之后的位置。保留 <tt>strcat.o</tt> 和 <tt>strcpy.o</tt> 成员的相对顺序。换句话说，如果在移动之前 <tt>strcpy.o</tt> 成员在 <tt>strcat.o</tt> 成员之前，那么（移动后）它依旧如此。</p>
    <li><a name="a0949a89"></a>要解压缩库成员，请输入：
    <p>&nbsp;</p>
    <a name="a0949a8a"></a>
    <pre class="xmp" id="a0949a8a">ar -v -x lib.a strcat.o strcpy.o</pre>
    <p class="indatacontent">此命令将成员 <tt>strcat.o</tt> 和 <tt>strcpy.o</tt> 分别复制到名为 <tt>strcat.o</tt> 和 <tt>strcpy.o</tt> 的文件。</p>
    <li><a name="a0949a8c"></a>要解压缩并重命名一个成员，请输入：
    <p>&nbsp;</p>
    <a name="a0949a8d"></a>
    <pre class="xmp" id="a0949a8d">ar -p lib.a strcpy.o &gt;stringcopy.o</pre>
    <p class="indatacontent">此命令将成员 <tt>strcpy.o</tt> 复制到一个名为 <tt>stringcopy.o</tt> 的文件。</p>
    <li><a name="a0949a8f"></a>要删除一个成员，请输入：
    <p>&nbsp;</p>
    <a name="a0949a90"></a>
    <pre class="xmp" id="a0949a90">ar -v -d lib.a strlen.o</pre>
    <p class="indatacontent">此命令从 <tt>lib.a</tt> 库中删除成员 <tt>strlen.o</tt>。</p>
    <li><a name="a0949a58"></a>要从多个用 <a href="http://publib.boulder.ibm.com/infocenter/systems/topic/com.ibm.aix.cmds/doc/aixcmds3/ld.htm"><strong>ld</strong></a> 命令创建的共享模块中创建一个压缩文档库，请输入：
    <p>&nbsp;</p>
    <a name="a144c1689"></a>
    <pre class="xmp" id="a144c1689">ar -r -v libshr.a shrsub.o shrsub2.o&nbsp;shrsub3.o&nbsp;...</pre>
    <p class="indatacontent">此命令从名为 <tt>shrsub.o</tt>、<tt>shrsub2.o</tt>、<tt>shrsub3.o</tt> 等等的共享模块中创建名为 <tt>libshr.a</tt> 的压缩文档库。要编译并链接使用 <tt>libshr.a</tt> 压缩文档库的 <tt>main</tt> 程序，请使用以下命令：</p>
    <a name="a144c168c"></a>
    <pre class="xmp" id="a144c168c">cc -o&nbsp;main&nbsp;main.c&nbsp;-L/u/sharedlib&nbsp;-lshr</pre>
    <p class="indatacontent"><tt>main</tt> 程序现在是可执行的。<tt>main</tt> 程序引用的任何符号（包含在<tt>libshr.a</tt> 压缩文档库中）已经因延迟分辨率而作了标记。<strong>-l</strong> 标志指定应在 <tt>libshr.a</tt> 库中搜索这些符号。</p>
    <li>要列出 <strong>lib.a</strong> 的内容（忽略任何 32 位目标文件），请输入：
    <p>&nbsp;</p>
    <pre class="xmp">ar -X64 -t -v lib.a</pre>
    <li>要从 <strong>lib.a</strong> 解压缩所有 32 位的目标文件，请输入：
    <p>&nbsp;</p>
    <pre class="xmp">ar -X32 -x lib.a</pre>
    <li>要列出 <strong>lib.a</strong> 中的所有文件，无论是 32 位、64 位或非对象，请输入：
    <p>&nbsp;</p>
    <pre class="xmp">ar -X32_64 -t -v lib.a</pre>
    </li>
</ol>
<p><a name="a0949a92"></a></p>
<h3 id="a0949a92">文件</h3>
<p>&nbsp;</p>
<p><a name="wq345"></a>
<table id="wq345" rules="none" width="100%" summary="" border="0" frame="void">
    <tbody valign="top">
        <tr>
            <td width="13%"><a id="a0949a93" name="a0949a93"></a><strong>/tmp/ar*</strong></td>
            <td width="86%">包含临时文件。</td>
        </tr>
    </tbody>
</table>
</p>
</span>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/280748.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-06-08 21:57 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/06/08/280748.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TUXEDO超时控制全功略 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/278089.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 26 May 2009 13:15:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/278089.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/278089.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/278089.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/278089.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/278089.html</trackback:ping><description><![CDATA[http://kimva.blogbus.com/logs/10795776.html<br />
http://kimva.blogbus.com/logs/10795836.html<br />
<br />
<p>摘要：<br />
　　本《功略》集中了TUXEDO应用中，可能涉及到的所有时间参数，并分别对其进行详细描述，不但对其出处、取值等基本属性进行查证，而且，通过分析其内在的控制机制，给出设置建议，以期能够达到透彻理解、方便查阅、准确使用的目的。</p>
<p>1 前言<br />
　　金融、电信等众多行业的综合营业系统中，广泛使用了TUXEDO交易中间件，用来处理大量并发的联机事务处理（OLTP）业务。典型的OLTP业务，每笔业务的信息量较小，而且，具有一定的实时性，对时间的要求非常严格。</p>
<p>　　TUXEDO，联系着DATABASE和客户端软件，凭借其多层次的超时控制机制，达到客户端快速响应，服务器端稳定可靠的效果。</p>
<p>　　TUXEDO的多层次的超时控制机制中，涉及到的时间参数不少于10个，再加上与之紧密联系的DATABASE中的几个超时参数，确实比较复杂。遗憾的是，目前还没有的专门的文档对它们进行详细说明，而是分散在不同的专题中分别说明，而且，不同的专题中，解释的详细程度也不一样，在查阅过程中，多有不便。</p>
<p>　　本文试图将这些参数集中起来，对每一个都加以详细说明，并试图解释每个参数存在的原因。大部分参数时间长短的设置，除个别外，基本没有固定的模式，只要了解它们的具体含义，并结合具体应用系统的实际要求，相信大家都能够作出合理的配置。</p>
<p>2 全功略解读<br />
2.1 SCANUNIT<br />
2.1.1 参数出处<br />
　　UBBCONFIG配置文件 -&gt; RESOURCES -&gt; SCANUNIT 。</p>
<p>2.1.2 时间单位<br />
　　秒,且必须为5的倍数。</p>
<p>2.1.3 取值范围<br />
　　大于0小于等于60中5的倍数，即｛5，10，15，20，25，30，35，40，45，50，55，60｝。</p>
<p>2.1.4 默认取值<br />
　　10 。</p>
<p>2.1.5 用途解释 ⑴<br />
　　这个参数大家都会用，比较好理解，TUXEDO中，BBL是用来对Bulletin Board进行管理和监控的系统进程，它基于时间片的轮询方式，时间片的大小就是SCANUNIT的值，SCANUNIT是Tuxedo对系统进行管理的最基本时间单位。每隔SCANUNIT，BBL对Bulletin Board进行一次检查，看看有没有超时的事务或阻塞的服务请求。后面讲到的很多时间参数都是以SCANUNIT为单位。</p>
<p>2.1.6 超时后果<br />
　　仅仅是个轮询时间单位而已，到时间就轮询，如此而已。</p>
<p>2.1.7 设置考虑<br />
　　作为一个涉及到整个TUXDO系统的基本单位时间，如果业务需要，对时间参数控制比较严格，设置为5也不算小。如果系统业务对时间要求不严格，那就大点儿，60也没什么不可以；毕竟频繁轮询是要耗费更多系统资源的，而任何对资源的不必要的消耗都是浪费。</p>
<p>2.2 SANITYSCAN<br />
2.2.1 参数出处<br />
　　UBBCONFIG配置文件 -&gt; RESOURCES -&gt; SANITYSCAN 。</p>
<p>2.2.2 时间单位<br />
　　SCANUNIT 。</p>
<p>2.2.3 取值范围<br />
　　1 ～32767 。</p>
<p>2.2.4 默认取值<br />
　　大约120/SCANUNIT。</p>
<p>2.2.5 用途解释 ⑵<br />
　　进行系统健全性检查，主要包括Server进程状态和Bulletin Board数据结构，检查Server进程是否存活，如果已经不存在，会清理Bulletin Board中相应的数据项及IPC资源，并根据参数配置决定是否重新启动，如果设了RESTART=Y，所占的Message Queue不会被清除，Queue中的Request得到保留，仍会被处理。如果是MP模式，BBL还会给DBBL发状态消息。</p>
<p>2.2.6 超时后果<br />
　　仅仅是个系统健康检查的间隔时间而已，到时间就检查，如此而已。</p>
<p>2.2.7 设置考虑<br />
　　作为一个涉及到整个TUXDO系统健康检查的间隔时间，如果系统处在一个稳定的运行环境中，网络、数据库、应用都很稳定，那这个参数可以大一些；如果运行环境不稳定，系统繁忙，而且Server进程经常因异常（如超时）而死掉，那就设置小一些。设置的原则和SCANUNIT一样：不要随意浪费系统资源。</p>
<p>2.3 BBLQUERY<br />
2.3.1 参数出处<br />
　　UBBCONFIG配置文件 -&gt; RESOURCES -&gt; BBLQUERY。</p>
<p>2.3.2 时间单位<br />
　　SCANUNIT</p>
<p>2.3.3 取值范围 ⑶<br />
　　BBLQUERY必须大于等于SANITYSCAN，tmloadcf 时会强制检查，如果设的值小于SANITYSCAN，tmloadcf会自动调整为SANITYSCAN。</p>
<p>2.3.4 默认取值<br />
　　大约300/SCANUNIT。</p>
<p>2.3.5 用途解释 ⑷<br />
　　BBL检查，在MP模式下，BBL会每隔一段时间都发送了" I am ok "心跳信息给DBBL，这个间隔就是BBLQUERY。</p>
<p>2.3.6 超时后果 ⑸<br />
　　如果DBBL在规定时间间隔内没有收到某个BBL的信息，DBBL它会主动发送Request给那个BBL，判断其是否正常。（如果等了DBBLWAIT后仍然没有回复，DBBL会认为那台机器有问题，然后，将其隔离。）</p>
<p>2.3.7 设置考虑<br />
　　此设置仅仅在MP模式下才起作用。</p>
<p>　　在MP模式下，如果TUXEDO系统需要对不稳定的运行环境可能发生的故障作出快速的反应，那么BBLQUERY要设置小一些，让系统快速的自我检查。考虑网络传输时间、系统反应速度等因素，网络速度越慢，系统负载越重，取值越大；反之亦然。</p>
<p>　　如果系统运行环境很稳定，利用其默认值即可，甚至可以更大数值。</p>
<p>2.4 DBBLWAIT<br />
2.4.1 参数出处<br />
　　UBBCONFIG配置文件 -&gt; RESOURCES -&gt; DBBLWAIT。</p>
<p>2.4.2 时间单位<br />
　　SCANUNIT。</p>
<p>2.4.3 取值范围<br />
　　大于0。</p>
<p>2.4.4 默认取值<br />
　　1和20/SCANUNIT中的较大者 。</p>
<p>2.4.5 用途解释 ⑹<br />
　　如果DBBL在规定时间间隔BBLQUERY内没有收到某个BBL的"I AM OK"信息，它会发Request给那个BBL，其等待回复的时间就是DBBLWAIT。</p>
<p>2.4.6 超时后果 ⑺<br />
　　DBBL等了DBBLWAIT后仍然没有回复，DBBL会认为相关BBL的机器有问题，然后，将其隔离（partation）。</p>
<p>2.4.7 设置考虑<br />
　　此设置仅仅在MP模式下才起作用。</p>
<p>　　在MP模式下，考虑网络传输时间、系统反应速度等因素，网络速率越大，系统负载越轻，此数值越小；反之亦然。</p>
<p>2.5 BLOCKTIME<br />
2.5.1 参数出处<br />
　　UBBCONFIG配置文件 -&gt; RESOURCES -&gt; BLOCKTIME。</p>
<p>2.5.2 时间单位<br />
　　SCANUNIT。</p>
<p>2.5.3 取值范围<br />
　　大于0。</p>
<p>2.5.4 默认取值<br />
　　大约60/SCANUNIT。</p>
<p>2.5.5 用途解释<br />
　　Client端阻塞请求（Blocking call）服务的超时值，BBL发现有超时的Request时，会给相应的Client端发超时信息。</p>
<p>　　此参数仅仅在"阻塞请求"的情况下起作用，因此，理解它，关键要理解什么是阻塞请求（Blocking call）？习惯上，我们将"阻塞请求"理解为"同步请求"，将"异步请求"理解为"非阻塞请求"，是源于将"&lt;发送请求－得到结果&gt;"这一过程看成为一个整体。如果是整体的同步操作，就认为是"阻塞请求"；如果是分开异步的操作，就认为是"非阻塞请求"。</p>
<p>　　其实，异步操作中，同样存在"阻塞请求"，tuxedo中，tpacall和tpgetrply这两个异步操作各自本身就是"阻塞请求"，tpacall是将请求发送到请求队列，tpgetrply是将从结果队列中取出结果，如果没有特殊的设置，这两个操作本身都是阻塞的，BLOCKTIME将起作用。</p>
<p>　　以Request/Reply方式为例，将成功发送请求的时长设置为T1，将请求处理的时长(含排队等待)设置为T2,将成功取得结果的时长设置为T3，那么在下面不同的情况下，将触发BLOCKTIME，引起超时：</p>
<p>　　（1） tpcall（）不在transaction中，flag为0，当T1+T2+T3 &gt; BLOCKTIME时，发生超时 ;</p>
<p>　　（2） tpcall（）不在transaction中，flag为TPNOBLOCK，只有当一次调用即成功完成T1阶段发送请求的任务后，T2+T3 &gt; BLOCKTIME时，发生超时 ; </p>
<p>　　（3） tpacall（）不在transaction中，flag为0，当T1 &gt; BLOCKTIME时，发生超时 ;</p>
<p>　　（4） tpgetrply（）不在transaction中，flag为0，当T2+T3 &gt; BLOCKTIME时，发生超时 ;</p>
<p>　　（5） tpcall，tpacall,tpgetrply,在其他任何情况，BLOCKTIME都将不起作用。</p>
<p>　　（6） 如果请求处于事务交易（transaction）中，此参数不起作用，取代它的是 TransactionTimeOut。</p>
<p>2.5.6 超时后果<br />
　　在上面描述的四种情况下，Server端 BBL返回Client端超时错误：tperrno ＝ 13 （TPETIME）。同时，client端和Server端已经建立的联接不受任何影响，继续保持联接。</p>
<p>2.5.7 设置考虑<br />
　　此参数涉及整个TUXEDO系统，不能够直接适应业务系统中不同场合的不同时间等待要求，而且在应用过程中，存在误差，不适合进行精确到秒的时间控制。</p>
<p>　　准确有效的使用这个参数，需要考虑好以下几个问题： </p>
<p>　　（1） 应用中是否完全依赖这个参数进行时间控制？</p>
<p>　　（2） 有哪些业务依赖这个参数进行时间控制？</p>
<p>　　（3） 平衡各种业务，此参数设置为多少？</p>
<p>　　（4） 除此参数外，是否需要利用其他的超时控制机制？</p>
<p>2.6 WSL CLOPT [-T Client_timeout]<br />
2.6.1 参数出处<br />
　　UBBCONFIG配置文件 -&gt; SERVERS -&gt; WSL -&gt; CLOPT "-T"。</p>
<p>2.*** 默认取值<br />
　　0 ，意味着无限等待时间。</p>
<p>2.6.5 用途解释 ⑻<br />
　　系统允许的Client静默时长，即Client和WSH建立联接后，如果在此指定的时间内客户端没有发送任何请求，WSH会自动回收与此Client端相关的资源。</p>
<p>2.6.6 超时后果 ⑼<br />
　　WSH会自动释放和这个Client端的联接，并将此Client在Bulletin Board中的数据项清空，RollBack它未完成的事务 。</p>
<p>2.6.7 设置考虑<br />
　　此参数的设置目的，主要是从Server的角度进行考虑，防止在Client端意外失效的情况下仍然耗费Server端的资源。</p>
<p>　　如果设置太小，那么频繁的检测同样要消耗Server端一定的资源，而且，在使用长联接的系统中，系统空闲时，容易造成已经建立的长联接频繁的释放，影响正常业务的提供。</p>
<p>　　如果设置为0，不管发生什么状况，哪怕是Client端系统真的崩溃了，也不会触发此超时，这将造成Server端资源的无谓浪费。</p>
<p>　　建议：在业务负载不均衡的长联接业务系统中，根据业务实际空闲时间，适当加大此数值。</p>
<p>2.7 WSL CLOPT [-t timeout]<br />
2.7.1 参数出处<br />
　　UBBCONFIG配置文件 -&gt; SERVERS -&gt; WSL -&gt; CLOPT "-t"。</p>
<p>2.7.2 时间单位<br />
　　SCANUNIT。</p>
<p>2.7.3 取值范围<br />
　　大于0。</p>
<p>2.7.4 默认取值<br />
　　非安全应用为3，安全应用为6 。</p>
<p>2.7.5 用途解释<br />
　　建立联接时长，指workstation Client建立与server端WSH建立联接允许的最长时间，因为非安全应用建立联接时不需要进行用户校验等步骤，因此，建立联接需要的时间较短。同理，安全应用需要的时间较长。</p>
<p>2.7.6 超时后果<br />
　　建立联接失败。</p>
<p>2.7.7 设置考虑<br />
　　设置此参数，要分析业务系统中，网络带宽因素、用户验证的复杂程度等。<br />
<br />
<br />
</p>
<p>2.8 WSL CLOPT [-I init_timeout]<br />
2.8.1 参数出处<br />
　　UBBCONFIG配置文件 -&gt; SERVERS -&gt; WSL -&gt; CLOPT "-I"。</p>
<p>2.8.2 时间单位<br />
　　秒。</p>
<p>2.8.3 取值范围<br />
　　大于0。</p>
<p>2.8.4 默认取值<br />
　　60 。</p>
<p>2.8.5 用途解释 ⑽<br />
　　WorkStation Client端和后台建立联接的超时参数值。</p>
<p>　　（注：感觉上此参数和 WSL CLOPT [-t timeout]功能类似）</p>
<p>2.8.6 超时后果<br />
　　不确定，现有的文档没有一点儿解释。按照字面解释，应该是tpinit的超时时间，但是，在tpinit所有的错误中，只有TPESYSTEM可能承担这个超时后果，而且仅仅是可能。</p>
<p>2.8.7 设置考虑<br />
　　在使用过程中，没有感觉到这个参数的存在，也从来没有专门设置过。</p>
<p>2.9 WSL CLOPT [-N network_timeout]<br />
2.9.1 参数出处<br />
　　UBBCONFIG配置文件 -&gt; SERVERS -&gt; WSL -&gt; CLOPT "-N"。</p>
<p>2.9.2 时间单位<br />
　　秒。</p>
<p>2.9.3 取值范围<br />
　　大于等于0。</p>
<p>2.9.4 默认取值<br />
　　0 。</p>
<p>2.9.5 用途解释<br />
　　网络超时，指Workstation client利用网络接收数据（receive data）的等待时间。</p>
<p>　　我们同样需要分析触发Network Timeout的不同条件。</p>
<p>　　以Request/Reply方式中的tpcall为例，将成功发送请求的时长设置为T1，将请求处理的时长(含排队等待)设置为T2,将成功取得结果的时长设置为T3，那么在如下情况下，将触发Network Timeout，引起超时：</p>
<p>　　（1） tpcall（）不在transaction中，flag为0，当 BLOCKTIME&gt; T1+T2+T3 &gt; NetworkTimeout时，触发此超时 ;</p>
<p>　　（2） tpcall（） 在transaction中，flag为0，当TranactionTimeout&gt; TI+T2+T3 &gt; NetworkTimeout时，触发此超时 ;</p>
<p>　　（3） tpcall（）不在transaction中，flag为TPNOBLOCK，只有当一次调用即成功完成T1阶段发送请求的任务后，当 BLOCKTIME&gt; T2+T3 &gt; NetworkTimeout时，触发此超时 ;</p>
<p>　　（4） tpcall（）在transaction中，flag为TPNOBLOCK，只有当一次调用即成功完成T1阶段发送请求的任务后，当 TranactionTimeout&gt; T2+T3 &gt; NetworkTimeout时，触发此超时 ;</p>
<p>　　（5） tpcall（）不在transaction中，flag为TPNOTIME，当 T1+T2+T3 &gt; NetworkTimeout时，触发此超时 ;</p>
<p>　　（6） tpcall（）在transaction中，flag为TPNOTIME，当 TranactionTimeout&gt; TI+T2+T3 &gt; NetworkTimeout时，触发此超时 ;</p>
<p>　　（7） tpcall在其他任何情况，NetworkTimeout都将不起作用。</p>
<p>　　（8） 同理，可以确定NetworkTimeout在tpacall和tpgetrply中起的作用。</p>
<p>2.9.6 超时后果<br />
　　在上面描述的六种情况下， Client端操作失败，同时，client端断开与Server端已经建立的联接。Server端已经为此Client完成的操作和分配的资源不能立刻回滚和释放，Server端需要等待前面介绍过的Client_timeout时间后，再释放相关的资源。</p>
<p>2.9.7 设置考虑<br />
　　此参数的设置目的，主要是从Client的角度进行考虑，防止在Server端意外失效的情况下，仍然消耗Client端的资源。</p>
<p>　　设置参数，必须避免小于BLOCKTIME或TransactionTimeout的情况，因为在这种情况下，会出现业务正常等待中，联接却中断的现象，这是一定要避免的。</p>
<p>　　同时，由于此超时触发的联接中断，并不能立刻释放Server端的资源，带来业务执行过程中的不确定性，因此，此参数要谨慎使用。</p>
<p>2.10 SVCTIMOUT<br />
2.10.1 参数出处<br />
　　UBBCONFIG配置文件 -&gt; SERVICES -&gt; SVCTIMEOUT 。</p>
<p>2.10.2 时间单位<br />
　　秒</p>
<p>2.10.3 取值范围<br />
　　大于等于0。</p>
<p>2.10.4 默认取值 <br />
　　0，表示无限时长 。</p>
<p>2.10.5 用途解释<br />
　　服务处理时长（Service Processing Time），表示系统允许服务处理请求的时间长度。</p>
<p>　　此参数主要是维护Server端系统安全的角度，防止由于系统异常引起的失控服务占据系统资源，阻碍正常的后续业务请求。它起到了一个清道夫的作用，将不能有效提供服务的Services清除出系统，并依靠系统的其他机制，重新产生具有活力的Services。</p>
<p>2.10.6 超时后果<br />
　　超时后，此Service所属的Server将被系统的SIGKILL信号清除；此操作不会影响与此Server相关的其他正在运行的副本Servers。</p>
<p>　　如果系统设置SERVERS-&gt;RESTART=Y，那么，被清除的Server将立刻自动重新启动。重新启动的次数受随后介绍的MAXGEN和GRACE两个参数联合限制。</p>
<p>　　如果系统设置SERVERS-&gt;RCMD为有效命令文件，那么，在重启此Server时，将同时执行此命令，如向管理员发送通知邮件等。</p>
<p>　　如果SERVER没有RESTART＝Y设置，那么Server将不会自动重新启动。</p>
<p>2.10.7 设置考虑<br />
　　此参数不是系统全局参数，而是涉及到单个Service，可以根据不同的Service的处理时间进行设置。由于其主要起到系统清道夫的角色，因此，建议设置为正常Service处理时长的3倍较合适，以避免清除正常运行的Service，使正常运行的业务受到影响。</p>
<p>　　如果系统运行环境基本稳定，一旦出现底层网络或数据库系统故障，不是在短时间内能够恢复，建议将此参数增大，至少为平均恢复时间，甚至可以利用默认值0，无限等待，避免此自动机制，而用人工介入的方法进行服务恢复。因为系统自动的、频繁的SIGKILL将影响到整个TUXEDO系统的稳定。</p>
<p>2.11 GRACE<br />
2.11.1 参数出处<br />
　　UBBCONFIG配置文件 -&gt; SERVERS -&gt; GRACE 。</p>
<p>2.11.2 时间单位<br />
　　秒。</p>
<p>2.11.3 取值范围<br />
　　0-2147,483,647。</p>
<p>2.11.4 默认取值<br />
　　86400（24小时）。</p>
<p>2.11.5 用途解释<br />
　　此时间参数与其他两个参数紧密相关RESTART, MAXGEN。</p>
<p>　　关联起来解释就是，当Server异常终止时，如果RESTART=Y，那么此SERVER可立即自动重新启动，这个重新启动的次数被限制为在GRACE指定的时间间隔内，重启不超过MAXGEN-1 次。</p>
<p>　　如果RESTART这个参数为N，其他的相关参数将都无效。</p>
<p>2.11.6 超时后果<br />
　　此参数不仅仅是时间的限制，如果在GRACE时间段内，此SERVER重启次数已经达到MAXGEN-1，后续重启将不能执行。</p>
<p>2.11.7 设置考虑<br />
　　此组参数设置的综合目的是避免运行Server无限的重启，重启次数说明了系统目前异常状况的严重程度，如果在一定时间内，Server重启次数达到了一定的数量，说明这个Server相关的资源已经出现较严重的故障，需要人工进行干预了。</p>
<p>　　另外，经过调优的生产系统是不需要自动重启的，因此，RESTART这个参数更多是应用在系统测试版本中，在正常运行的生产系统中，应该有不同参数设置，谨慎使用，因为过多的重启将有可能严重影响Server端的整体稳定性。</p>
<p>2.12 Transaction TimeOut<br />
2.12.1 参数出处<br />
　　tpbegin(timeout,0)。</p>
<p>2.12.2 时间单位<br />
　　秒,且必须为SCANUINT的倍数。</p>
<p>2.12.3 取值范围<br />
　　大于等于0。</p>
<p>2.12.4 默认取值<br />
　　无，使用时必须明确指定。</p>
<p>2.12.5 用途解释<br />
　　交易时长，确定交易（transaction）的持续时间，如果超过此时间，系统将返回超时错误。</p>
<p>　　分析其触发条件，分两种情况：</p>
<p>　　（1） Server端发起交易，</p>
<p>　　对Client而言，因为Client不在交易范围内，即使BLOCKTIME设置小于此参数，起有效作用的仍然将是BLOCKTIME，而不是此参数。</p>
<p>　　（2） Client端发起交易。</p>
<p>　　对Client而言，因为Client在交易范围内，此时，BLOCKTIME将失效，此参数取而代之。只要处理时间超过此参数，将触发此超时，交易处理将随时中断。</p>
<p>2.12.6 超时后果<br />
　　相关交易将被标识为abort only。注意，仅仅是标识为abort only，而不是系统自动执行tpabort进行交易恢复。随后必须主动执行tpabort，恢复交易，保障交易完整性。如果没有执行tpabort，系统将通过其他机制，恢复交易，保障交易完整性。</p>
<p>2.12.7 设置考虑<br />
　　此参数的主要目的是将交易处理过程限制在合理的时间范围内，如果确实是完成交易的条件不具备，系统也能够作出反应，避免系统资源的长时间占用。因此，不管交易由Client还是由Server发起，此参数都要按照业务的实际状况进行设置。如果设置为0，就基本相当于无限时长。（系统unsigned long数据类型的最大值）</p>
<p>2.13 TRANTIME<br />
2.13.1 参数出处<br />
　　UBBCONFIG配置文件 -&gt; SERVICES -&gt; TRANTIME/AUTOTRAN。</p>
<p>2.13.2 时间单位<br />
　　秒,且必须为SCANUNIT的倍数。</p>
<p>2.13.3 取值范围<br />
　　0-2147,483,647。</p>
<p>2.13.4 默认取值<br />
　　30 。</p>
<p>2.13.5 用途解释<br />
　　此参数必须与AUTOTRAN配合使用，表示不需要调用tpbegin，就可自动发起的隐含交易（AUTOTRAN implicitly transactions）处理持续时长。其实，起到与tpbegin（timeout,0）中的timeout相同的作用。</p>
<p>　　其实，AUTOTRAN这个参数更为重要，其默认值为N, 其作用描述如下：</p>
<p>　　（1） 请求发起方不在交易模式，AUTOTRAN＝Y，Service将自动发起一个交易，TRANTIME将起作用；</p>
<p>　　（2） 请求发起方在交易模式，而且，其中的标记参数（flags）不是TPNOTRAN，那么，无论AUTOTRAN如何，Service将自动加入请求方交易中，TRANTIME将不起作用；</p>
<p>　　（3） 请求发起方在交易模式，而且，其中的标记参数（flags）是TPNOTRAN，如果AUTOTRAN＝N，Service将自动加入请求方交易中，TRANTIME将不起作用；</p>
<p>　　（4） 请求发起方在交易模式，而且，其中的标记参数（flags）是TPNOTRAN，如果AUTOTRAN＝Y，Service将不加入请求方交易中，而是产生一个新的交易，TRANTIME将在新交易中起作用；</p>
<p>2.13.6 超时后果<br />
　　与tpbegin(timeout,0)中的timeout相同。</p>
<p>2.13.7 设置考虑<br />
　　与tpbegin(timeout,0)中的timeout相同。</p>
<p>2.14 ORACLE XA OPENINFO参数：SESTM<br />
2.14.1 参数出处<br />
　　UBBCONFIG配置文件 -&gt; GROUPS -&gt; OPENINFO -&gt;SESTM 。</p>
<p>2.14.2 时间单位<br />
　　秒。</p>
<p>2.14.3 取值范围<br />
　　大于等于0，0表示无限时长。</p>
<p>2.14.4 默认取值<br />
　　无，使用时必须明确设置 。</p>
<p>2.14.5 用途解释<br />
　　会话静默等待时长，即全局交易中，作为资源管理器（resource manager）已经参与交易并完成相应的数据操作的数据库，等待全局交易中的其他参与方操作完成的时间长度。</p>
<p>　　举例说明：</p>
<p>　　假设一个全局交易由以下几个部分组成：</p>
<p>　　（1）tpbegin（T,0） -&gt; </p>
<p>　　（2）tpcall(S1) -&gt; DB1 完成耗时 T1;</p>
<p>　　（3）tpcall(S2) -&gt; DB2 完成耗时 T2;</p>
<p>　　（4）其他操作 耗时 T3</p>
<p>　　（5）tpcommit()</p>
<p>　　其中，tpcall(S1)访问数据库DB1, tpcall(S2)访问数据库DB2。</p>
<p>　　对DB1而言，如果T-T1&gt; T2+T3 &gt; SESTM1,则触发超时；</p>
<p>　　对DB2而言，如果T-T1-T2 &gt; T3 &gt; SESTM2,则触发超时；</p>
<p>　　由此可以看出，此参数的主要目的是对数据库进行资源保护，避免在全局交易中，已经完成任务的数据库，为等待其他参与方耗费过多的资源，毕竟ORACLE中并发全局交易的数量是很有限的。</p>
<p>2.14.6 超时后果<br />
　　触发此超时后，数据库将自己主动回滚已经完成的任务，释放全局交易资源。TUXEDO控制的全局交易进行TPCOMMIT时，如果总时间仍没有超过Transaction TimeOut，那么TPCOMMIT将失败，XALOG会记录下ORACLE错误："ORA-24756: Transaction does not exist"。确实如此，拿上面的例子来说，等到最后TPCOMMIT的时候，DB1已经主动回滚了所有的数据，不难理解，对DB1而言，它好像根本没有参加过这个全局交易，自然就会有这样的错误提示。</p>
<p>2.14.7 设置考虑<br />
　　这个参数是从数据库自我保护的角度设计的，对数据库而言，是不得已的办法。最好的措施还是TUXEDO能够控制时间，赶在SESTM之前，进行TPABORT，主动通知数据库进行数据回滚。</p>
<p>　　用一个简单的比方：冬天，客人出门时没有关门，SESTM时间后，客人还没有回来，主人感觉到冷了，只好自己去关门，不管什么理由，这样总是不太好。如果在SESTM时间内，客人能及时回来关门，主人就不会感觉那么差。</p>
<p>　　从前面的超时触发分析，我们也能发现，只要设置SESTM 能够大于 TransactionTimeOut的话，就能够尽量的不触发此超时机制，达到较好的效果。</p>
<p>2.15 ORACLE XA OPENINFO参数：SESWT<br />
2.15.1 参数出处<br />
　　UBBCONFIG配置文件 -&gt; GROUPS -&gt; OPENINFO -&gt; SESWT。</p>
<p>2.15.2 时间单位<br />
　　秒。</p>
<p>2.15.3 取值范围<br />
　　大于0。</p>
<p>2.15.4 默认取值<br />
　　60 。</p>
<p>2.15.5 用途解释<br />
　　会话繁忙等待时长，指全局交易中的一个交易分支（transaction branch）正在一个会话（session）中处理数据过程中，如果第二个会话也要对此交易分支进行操作，那么第二个会话必须等待第一个会话完成，如果在等待SESWT后，第一个会话仍然没有完成，那么第二个分支将触发此超时。</p>
<p>　　举例说明：</p>
<p>　　假设一个全局交易由以下几个部分组成：</p>
<p>　　（1）tpbegin（T,0） -&gt; </p>
<p>　　（2）tpcall(S1) -&gt; DB1 完成数据操作耗时 T1;</p>
<p>　　（3）tpcall(S2) -&gt; DB2 完成数据操作耗时 T2;</p>
<p>　　（4）其他操作 耗时 T3</p>
<p>　　（5）tpcommit()</p>
<p>　　其中，tpcall(S1)访问数据库DB1, tpcall(S2)访问数据库DB2，任何操作失败后，将调用tpabort。</p>
<p>　　假设 T1+T2 &gt; T &gt; T1，也就是说，在成功执行第二步tpcall(S1)后，应用执行到第三步tpcall(S2)，在执行过程中，将触发TransactionTimeout，tpcall（S2）将返回错误，应用将立即调用tpabort进行交易回滚，DB1的数据将立刻回滚；而DB2并不能中断tpcall(S2)引起的数据操作，对于ORACLE DB2而言，tpcall(S2)引起的数据操作属于第一个会话，而tpabort引起的数据库回滚操作是通过XA接口，属于第二个会话，所以，tpabort作用到DB2上，只有等待第一个会话完成，如果等待SESWT后，第一个会话仍然没有完成，即SESTM </p>
<p>2.15.6 超时后果<br />
　　系统将返回XA_RETRY错误，提示TMS此操作不能完成，需再试一次。如果应用程序对此没有再次尝试的话，仍以上例说明，DB2只能利用SESTM2，触发超时，自动回滚。</p>
<p>2.15.7 设置考虑<br />
　　查阅ORACLE相关资源，发现oracal9.2与以前版本效果不同，以前的版本都是第二个会话在等待SESWT后，如果第一个会话操作仍然没有结束，第二个会话能够使第一个会话操作立即进行有效的回滚，使第一个会话返回错误：ORA-24761。</p>
<p>　　不难理解，两者的区别是，打个简单比方说，A正在用杯子喝水时，B要A放下杯子，B等待SESWT后：</p>
<p>　　oracle9.2的做法很民主，A还没有用完，通知B，A还在用杯子，不能放下，还想让A放下杯子，可以，再来一次；也就是说，只要A在用，B就不能让人家放下杯子，某种程度上，A代表了强权。</p>
<p>　　Oracle9.2之前版本的做法很粗暴，通知A不能使用这个杯子了，必须放下，因为B让你放下，而且已经等很久了，某种程度上，B代表了强权。</p>
<p>　　因此，如果是ORACLE9.2后的版本，SESWT适当放长一些，毕竟还是希望能够等到能做事的时候，把想做的事情做了。如果是以前的版本，SESWT适当放短一些，反正一定能等到，为什么不少等一会儿呢？</p>
<p>2.16 ORACLE sqlnet.expire_time<br />
2.16.1 参数出处<br />
　　$ORACLE_HOME/network/admin/sqlnet.ora -&gt; expire_time 。</p>
<p>2.16.2 时间单位<br />
　　分钟。</p>
<p>2.16.3 取值范围<br />
　　大于0。</p>
<p>2.1*** 默认取值<br />
　　无 。</p>
<p>2.16.5 用途解释 ⑾<br />
　　死联接检测DCD(Dead Connection Detection)是 SQL*NetV2.1 和此版本以后的一个新特性, 当它检测到对方 c/s 或者s/s 联接意外终止时, 释放相关占用的资源。 </p>
<p>　　DCD 起初是专为客户机没有从会话中断开联接的情况下断电的环境设计的。 </p>
<p>　　DCD由服务端开始建立联接。 这时候SQL*Net 从参数文件中读取变量, 设置一个定时器定时产生信号。 这个时间间隔是sqlnet.ora文件中的SQLNET.EXPIRE_TIME提供的。 </p>
<p>　　当定时器设定的时间到了之后, 服务器上的SQL*Net 发送一个探测包到客户端。(如果是数据库联接, 目的端的服务器发送探测包到另一端)。 探测包是由空的SQL*Net包组成, 不体现SQL*Net层任何数据, 但会在下一层的网络协议中产生数据流量。 </p>
<p>　　如果客户端的联接仍然是活动的, 探测包被丢弃,计时装置复位。 如果客户端异常断掉，服务器将收到由发送探测包的调用发出的错误。 </p>
<p>2.16.6 超时后果<br />
　　服务器将收到由发送探测包的调用发出的错误，SQL*Net 将会通知操作系统释放联接占用的资源。</p>
<p>　　需要说明的是, SQL*Net 2.1.x中 一个活动的孤儿进程（active orphan process） ，如单独的查询进程，在查询完成之前不会被杀掉。 在SQL*Net 2.2中，孤儿进程占用的资源将会被无条件释放，不管查询是否结束。 </p>
<p>2.16.7 设置考虑<br />
　　ORACLE 在NT操作系统上，此功能表现很差，检测出的无效联接（dead connection）不能被尽快释放，而必须等到数据库重新启动时才释放。SQL*Net v2.3以后版本改善了以上问题。</p>
<p>　　此功能只是服务器的特性，如果不设置此参数，此功能将不启动。按照ORACLE的建议，对大多数应用来说，设置10分钟较合适，其实关键还是分析应用系统的实际情况。</p>
<p>　　同时，不难理解，作为一个数据库自我管理的机制，也是要占用数据库资源和网络资源的，太频繁的探测同样会降低系统和网络的性能。在低速网络上，设置此参数，就需更为慎重。</p>
<p>　　客户端不需要设置此参数。</p>
<p>2.17 ORACLE distributed_lock_timeout<br />
2.17.1 参数出处<br />
　　ORACLE初始参数文件：init.ora -&gt; distributed_lock_timeout。</p>
<p>2.17.2 时间单位<br />
　　秒。</p>
<p>2.17.3 取值范围<br />
　　大于0。</p>
<p>2.17.4 默认取值<br />
　　60 。</p>
<p>2.17.5 用途解释<br />
　　分布式事务锁等待超时（distributed transaction waiting for lock），指第二个事务处理需要的数据库资源，正被第一个分布式事务占用而锁定，那么，第二个事务将等待第一个分布式事务释放此资源，等待distributed_lock_timeout时间后，如果第一分布式事务仍然没有释放此资源，第二个事务触发此超时。</p>
<p>2.17.6 超时后果<br />
　　如果资源被第一个事务正常使用锁定，ORACLE回滚第二个事务，并返回错误："ORA-02049: time-out: distributed transaction waiting for lock "。</p>
<p>　　如果第一个事务处理完成，资源释放后，再尝试第二个事务，就会成功。如果第二个事务不能等待资源自动释放，那么可以采用处理数据库死锁（deadlock）的措施，人工介入，清除第一个事务，但一般不建议采用这种方式，因为第一个事务一般会正常结束的。</p>
<p>　　如果资源被第一个事务因为处于不确定分布事务状态（in-doubt distributed transaction）而锁定，ORACLE回滚第二个事务，并返回错误："ORA-01591: lock held by in-doubt distributed transaction identifier "。</p>
<p>　　这种错误遇到的可能性较小，一般只有在分布事务的关键提交阶段出现网络、系统故障，才可能出现此故障，而且，当网络、系统故障恢复后，ORACLE一般可以自己解决此问题，不需要人工介入。如果一定要人工介入，可以查阅ORACLE专门的手册。</p>
<p>2.17.7 设置考虑<br />
　　出现这样的超时，是因为特定数据库资源的使用碰撞，要分析应用系统的业务特点，确定碰撞可能发生的条件，在此条件下，资源可能被先来者锁定多长时间（T1），后来者又能够等多长时间（T2），再来设置此参数（T）的大小。如果在大多数情况下，T1 &lt; T2, 那么就设置T1 &lt; T &lt; T2；反之，大多数情况下，T1 &gt; T2，那么，就设置T &lt; T2。</p>
<p>　　因此，不分析业务特点，一味的增大和减小是不恰当的。</p>
<p>2.18 ORACLE Max_commit_propagation_delay<br />
2.18.1 参数出处<br />
　　ORACLE初始文件:init.ora -&gt; Max_commit_propagation_delay。</p>
<p>2.18.2 时间单位<br />
　　0.01秒。</p>
<p>2.18.3 取值范围 <br />
　　0 ～ 90000。</p>
<p>2.18.4 默认取值<br />
　　700 。</p>
<p>2.18.5 用途解释<br />
　　最大提交传播时延（MAX_COMMIT_PROPAGATION_DELAY，简称MCPD），在ORACLE RAC(或OPS)环境中才使用，表示在RAC系统中，一个instance系统提交产生的最新系统改变码（SCN），能够以多快的速度反应到另一个instance中。</p>
<p>　　举例说明，RAC系统，有A,B两个实例（instance），A、B本地系统改变码为SCN1，A更新数据DATA1提交， LGWR操作完成后，A本地系统改变码为SCN2，经过不大于MAX_COMMIT_PROPAGATION_DELAY时间后，B系统本地改变码才变为SCN2。</p>
<p>2.18.6 超时后果<br />
　　Global Cache Services 将刷新RAC中的SCN。不管SCN是否及时刷新，后续的数据查询都不会因此产生数据库错误。但，在此时间内，有可能查询结果不是最新数据，产生读一致性（read consistency）问题。</p>
<p>2.18.7 设置考虑<br />
　　RAC环境中的所有实例，此参数值必须相同。</p>
<p>　　ORACLE8i后，建议常用的两个值是0和700（默认），其他数值皆不建议。其实，这两个数值就代表了RAC环境中，两种SCN 产生机制：</p>
<p>　　Lamport Scheme和 Broadcast on Commit scheme。</p>
<p>　　设置为默认值700，表示采用Lamport Scheme，SCN改变不会完全同步，同步将在 7秒钟内完成，而不是总等待7秒钟后才完成。如果系统比较空闲，同步可能在0.5秒（甚至更短时间）内完成；不管系统多繁忙，同步时间也不可能超过7秒。不难理解，采用此模式，整个RAC系统的运行效率较高。</p>
<p>　　设置为0，表示采用Broadcast on Commit scheme，SCN改变完全同步。每当commit时（即LGWR 写redo log时）：</p>
<p>　　－ LGWR发送消息更新全局SCN（global SCN），</p>
<p>　　－ LGWR 发送消息给每个活动的实例更新其本地SCN（local SCN）。</p>
<p>　　有资料说，只要MCPD &lt; 700，系统将采用Broadcast on Commit scheme。</p>
<p>　　Lamport Scheme能够适应绝大部分应用的要求，只有个别实时性特别高的业务，才需要Broadcast on Commit scheme。通过分析，不难理解，Broadcast on Commit scheme将需要更多的系统资源。</p>
<p>3 总结<br />
　　以上所有时间参数，都是tuxedo系统或ORACLE数据库系统提供的时间控制机制，更多的是从维护本系统自身安全的角度，建立起相互之间沟通的规则。在Client 端，中间件服务器，数据服务器这相互联系的三者之间，用一个简单的比方，就是先保证自己尽量不给相关人带来麻烦；一旦相关人出了麻烦，自己能够自我保障，不受别人干扰。</p>
<p>　　这些时间参数还有的一个共性的特点就是"不精确"，如果业务需要要精确到1秒，则必须依靠业务程序中更精确的时间编程。</p>
<p>　　总之，要保障整个业务系统的有效性和健壮性，必须了解都有哪些时间参数？都表示什么意思？都起哪些作用？自己的业务应用对时间要求是怎样的？这些参数该如何配置才能满足应用的要求？希望本文能够在以上方面给大家带来一点方便。</p>
<p>4 后记<br />
　　自2002年真正在项目中利用TUXEDO起，就发现已有资料在时间参数解释方面的缺憾，那时，就有写这样一个专题的打算，开始收集这方面的资料。由于后来工作内容的变化，也就没有精力再作整理，但心里一直惦记着这件事情。直到前些天知道了BEA的这个活动，再到网络上搜集资料，发现已经有了一些类似的资料，但感觉仍然不够完整，不够透彻，因此，我认为整理这样一个专题资料，还是有必要的，便下决心借此机会做完这件事情。经过近十天的查阅、整理，终于完成，算是了我多年夙愿。</p>
<p>　　文中的参数，我仅仅使用过其中的12个，其他未用参数，主要是靠查阅资料和逻辑分析，根据我自己的理解进行解释，再加上时间仓促，或遗漏、不妥之处，敬请指正，让我们一起来使此《功略》更准确、更全面，让更多的人从中受益。</p>
<p>5 参考文献<br />
<a href="http://e-docs.bea.com/">http://e-docs.bea.com</a> BEA TUXEDO RELEASE 7.1 。 <br />
<a href="http://dev2dev.bea.com.cn/techdoc/tuxedo/20030230.html">http://dev2dev.bea.com.cn/techdoc/tuxedo/20030230.html</a> 《Tuxedo 中关于时间的参数的说明》作者：不详 。 <br />
《ORACLE8i Reference》。 <br />
《ORACLE9i Reference》。 <br />
《ORACLE8i Parallel Server Concepts and Administration》。 <br />
《ORACLE8i Application Developers Guide - Fundamentals》。 <br />
<a href="http://metalink.oracle.com/">http://metalink.oracle.com</a> 相关问题解决资料。 <br />
<a href="http://www.chinaunix.net/">http://www.chinaunix.net</a> 《DCD死联接检测》作者：yukaikai <br />
　　注：文章中标注⑴～⑽涉及的内容，引自参考文献-2 ，标注⑾涉及的内容，引自参考文献-8 。</p>
<p>&nbsp;</p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/278089.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-26 21:15 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/278089.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于TUXEDO 负载均衡和MSSQ的探讨  zz </title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/278088.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 26 May 2009 13:14:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/278088.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/278088.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/278088.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/278088.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/278088.html</trackback:ping><description><![CDATA[<a href="http://kimva.blogbus.com/logs/10795676.html">http://kimva.blogbus.com/logs/10795676.html</a>&nbsp;<br />
<br />
&nbsp;在使用TUXEDO的过程中，会遇到一些并发请求量很大的情况，比如某些帐单处理服务或者是在营业下班前的日操作清单服务。这时，一些SERVICE会接收到大量客户端，甚至长时间的请求，对service，甚至整个系统是严峻的考验。遇到这种情况，单个的server往往难以应付，或者性能不好，我们就想到负载均衡或者使用TUXEDO的MSSQ（Multi Server, Single Queue）。下面笔者根据自己在TUXEDO应用开发和管理配置方面的实践，结合实际系统压力测试的结果对相关的问题进行一些探讨。
<p>在没有负载均衡的情况下，是由一个server（可能包含一个或多个service）来处理客户端对其中service的请求，所有的请求首先放入这个server的队列里面，然后server逐个取出处理。在UNIX系统上，TUXEDO较多的使用了队列，并且也用到了共享内存和信号量，相关的UNIX系统参数会影响到系统的性能，但这个不在本文讨论范围之内，这里假设已经调到了合适的范围，具体请查阅TUXEDO关于IPC的文档。</p>
<p>现以一个帐单处理的server为例，负载均衡前server的ubb配置为：<br />
billpay SRVGRP=GROUP1 SRVID=1<br />
在单个server不能满足性能要求的情况下，就考虑采用TUXEDO的负载均衡方法。<br />
方法一是直接将相关server启多份，将上面的配置改为：<br />
billpay SRVGRP=GROUP1 SRVID=1 MIN = 5 MAX = 10<br />
这样tmboot的时候，就会有MIN = 5个billpay启动，类似下面的情况：<br />
billpay 00001.00001 GROUP1 1 0 0 ( IDLE )<br />
billpay 00001.00002 GROUP1 2 0 0 ( IDLE )<br />
（依此类推，共5个）</p>
<p>其中第二列是该server的队列名，"."前面是GRPNO,后面是SRVID，每个server有自己的队列。相关的另一个参数就是在ubb的*RESOURCES段的LDBAL，表示是否启动Load Balancing，默认是"N"（不启动），你可以通过设置成"Y"来启动。这里需要注意的是，为"N"的时候并不表示多个server不能分担负载。主要的差别是为"Y"时，TUXEDO在接收到请求时会按照它的负载均衡的算法来找到合适的server来处理，而设置成"N"时，总是由第一个可用的server来处理。通过这种方法可以让多个server来处理大量并发的请求，就达到了改善性能的目的。</p>
<p>方法二是采用MSSQ（Multi Server, Single Queue），顾名思义，就是有多份server，但是只有一个队列（请求队列）。具体的配置是：<br />
billpay SRVGRP=GROUP1 SRVID=1 MIN = 5 MAX = 10<br />
RQADDR=" billpay" REPLYQ=Y</p>
<p>启动后的情况如下：<br />
billpay billpay GROUP1 1 0 0 ( IDLE )<br />
billpay billpay GROUP1 2 0 0 ( IDLE )<br />
（依此类推，共5个）</p>
<p>我们发现几个billpay server都关联相同的名为billpay的队列，这就是所谓的Single Queue。</p>
<p>与直接多server相比，多了两个参数，RQADDR是这多个server共用的队列名，是一种逻辑名，可以自己命名，不和别的冲突就可以，REPLYQ是标示是否设置返回队列，在使用MSSQ的时候是强烈建议设置，因为这样可以将请求和返回分开，避免多个server共用队列时造成混乱。相关的其它参数这里没有详细列出。</p>
<p>到底两种方式和没有负载均衡时有什么不同，后面将提供相关的测试结果。先分析一下两种方法。方法一有多个队列可以容纳请求，但是这些大量的请求怎样放入这些队列必定有一定的策略，而且根据LDBAL的设置会不同，但是这个策略本身的运算也是一种消耗，因为每个请求都面临着这个选择。因为这种情况下每个队列是和server对应的，所以队列的选择就意味着选择了相应的那个server，这样大量的请求就被分流。虽然有选择的消耗，但是额外的好处也是显而易见的，那就是有多个queue可用，有效避免了请求并发量很大时队列的溢出，这种情况在实际的压力测试中发生过。使用方法二时，放入队列时不用做选择，然后每个server的任务就是从队列取出请求去处理，考虑到多个server并发取队列，所以用MSSQ时其server的数目不宜太多，官方文档建议2－12。而且在这种情况下，建议不要设置LDBAL=Y，因为MSSQ本身就是一种基于single queue的负载均衡的方法，这时再使用系统的策略已经没有意义。这种方法也有一个问题，如果相对于请求数来说，处理得不够快，就比第一种方法更容易造成队列溢出。</p>
<p>因为我们无法知道TUXEDO一些具体的算法和策略，那就用一些具体的测试来比较吧。笔者在一个和实际生产系统配置相同的UNIX+ORACLE+TUXEDO8.0系统上做了一下试验，被测服务是一个帐单查询服务，每次根据一批ID从数据库查询出具体的帐单，由于都是实际数据和实际使用的服务，有一定的说明力，但是也不排除一些情况造成的误差。以下是测试的一些结果，每个server的实际运行份数都是10。</p>
<p>一：客户端数目为1，循环调用100次。 </p>
<p>1． 方法一，LDBAL = Y or N。结果发现所有的请求都由其中的一个server处理，其它全部空闲。在此基础上再进行两次循环调用，结果还是一样。<br />
2． 用方法二，LDBAL = Y or N。其中两次测试的情况是：<br />
&lt;1&gt;19，19，2，2，1，18，18，1，1，19<br />
&lt;2&gt;23，8，23，23，23，（其它空闲）<br />
以上数据说明了两种方式的调度策略是不一样的，在单客户端循环的情况下方法二更合适。 </p>
<p>二：客户端数目50，每个循环80次，统计总的执行时间。<br />
1． 用single server。两次测试的处理时间分别是219s和196s。<br />
2． LDBAL = Y 方法一：三次测试时间为：63s，79s，69s<br />
3． LDBAL = N 方法一：三次测试时间为：74s，79s，85s<br />
4． LDBAL = Y 方法二：三次测试时间为：74s，73s，77s<br />
5． LDBAL = N 方法二：三次测试时间为：78s，76s，75s </p>
<p>通过以上数据可以看出不管用那种方法，使用负载均衡相比单个server都可以在大量请求并发时得到更好的性能。但是同时也发现后四种情况的总时间差别不大，考虑一些实际的误差，很难断定哪种更好。</p>
<p>通过前面的分析，并查阅相关的文档，建议采用的是两种方式：多个server，不用MSSQ,设置LDBAL = Y；用MSSQ，设置LDBAL = N。当然，在使用TUXEDO的应用系统中，不能绝对的说哪一种方式更好，只能是根据具体的情况来分析，并通过实际的压力测试来进行选择，而且这个和具体server的特点也是有关的。</p>
<p>以上是一些个人分析和测试的结果，算是写出来和大家探讨，也希望大家提出自己的看法并讨论。</p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/278088.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-26 21:14 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/278088.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>中间件在电信行业中的应用 zz </title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/278087.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 26 May 2009 13:13:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/278087.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/278087.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/278087.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/278087.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/278087.html</trackback:ping><description><![CDATA[<br />
<a href="http://kimva.blogbus.com/logs/10687711.html">http://kimva.blogbus.com/logs/10687711.html</a> <br />
<br />
<p>一、中间件技术在系统设计规划方面的经验总结</p>
<p>1.1、中间件应用架构的选择 <br />
这种架构选择的焦点就是业务逻辑到底是放在前台应用中还是放在后台中间件应用中。其实，引入中间件平台的原因之一就是业务逻辑的集中管理，出现这种争论的原因主要是开发商对这种业务逻辑集中的理解不深，在应用设计的过程中仍然没有摆脱以前两层结构的影响，再加上因为业务逻辑集中会增加程序开发量，才出现了这种情况。我们认为，把业务逻辑集中在中间件服务器上不仅对于业务逻辑的集中管理有好处，而且对于系统的后期运行维护、软件的升级管理都有着明显的优势。当然,在开发的工作量方面，业务逻辑集中时工作量大很多。</p>
<p>1.2、中间件服务分布的设计和优化原则<br />
在Tuxedo中，Server可以理解成为Unix的一个进程，Service可以理解成为应用进程Server中的一个函数。用户可以任意划分Server和Service：既可以把所有的Service放在一个单一的Server中，也可以采用&#8220;一个Service一个Server&#8221;的方式。Tuxedo对此没有任何限制。<br />
中间件应用的性能直接影响到营业前台服务的稳定性和连续性，而影响中间件应用性能的因素除了硬件资源的保证、数据库响应时间、中间件应用软件本身的质量之外，中间件应用服务的分布也起着至关重要的作用。我们有着这样的一个例子，在前台进入收费界面的时候，应用程序需要调用一个SERVICE名字叫A，A分布在一个叫SA的server中，而SA中又包含着很多其他的在线统计的SERVICE B，结果造成了SA响应B调用的时候执行时间比较长，所有的SA都在执行SERVICE B ，前台请求SERVICE A得不到响应，收费的界面怎么都进不去，从而导致前台收费业务中断。这就是个典型的因为服务分布不当造成的系统故障。<br />
我们在系统规划设计过程中遵循的一个最基本原则就是尽量将&#8220;类似的Service&#8221;捆绑在一个Server之内。所谓&#8220;类似的Service&#8221;是指这些函数有相似的大小、执行时间、复杂度或者功能。我们来考虑一个极端的例子：假设一个Service A是完成非常简单工作的，它的平均执行时间是100毫秒。另一个Service B进行数据库的查询，它的执行时间可能长达20秒。如果将这两个Service捆绑在一起使用，就会出现非常严重的后果。大家知道，操作系统的基本调度单位是进程。在一个进程中的Service执行是串行的。Tuxedo把发往同一个Server的请求交易包放在同一个消息队列中。当Service B在执行的时候，Service A的请求包必须在队列里面等待20秒以上才可能被执行，虽然它的执行时间仅仅100毫秒。这显然是不能容忍和应该避免的。<br />
实际上，用户在设计和开发应用程序的时候，并不能完全估计出每个Service的执行时间和频度。所以对Server和Service的划分优化是在应用程序开发完毕后进行调整的。调整的依据就是在系统运行的时候记录每个Service的执行时间和频度，然后根据这些数据来进行优化调整。下面就是我们在实际当中获得经验的一些系统总结。<br />
1、 区分Service的不同类型：是业务操作还是简单的数据访问<br />
在中间件应用中的Service可以进一步细分成负责完成业务操作和负责数据访问的两种类型。在设计的时候，最好把这两种不同类型的Service区别出来。所谓完成业务操作的类型其实就是进行数据修改的操作，负责数据访问的类型其实就是进行数据查询的操作。但是在实际的系统运行过程中，凡是涉及到数据修改的操作过程中必然会有数据查询的操作，这种类型的操作也应该规整到第1种类型中去。<br />
2、 请求/响应类型的Service要和会话Service分开在不同的Server中<br />
在Tuxedo中，一个Server要么支持请求/响应类型类型的Service，要么支持会话方式的Service。会话方式和请求/响应方式是两种互斥的通讯方式。请求/响应方式效率比较高，是使用最频繁的通讯类型。Tpcall/tpacall/tpforward都是这种类型的函数。会话方式适合大量的数据传输，但是它的代价是效率的下降。所以这两种类型的Service要分开放在不同的Server里面。<br />
3、 执行时间相似的Service放在同一个Server里面<br />
在Tuxedo应用环境中，可能有成百上千的Service，这些Service的执行时间显然是不同的。对于单线程的Server来说，虽然它可能有多个Service，但是这些Service的执行是串行的。如果某个Server拥有两个Service，A和B。A的执行时间是100ms，B的执行时间是1000ms，也就是说执行一次B的时间可以执行十次A。那么当B在执行的时候，可能会有十个A在队列里面等待。这种情况会急剧降低该Server处理Service的吞吐率。因此很自然的一个原则是把执行时间相似的Service放在同一个Server里面。<br />
4、 具有相同执行频度的Service放在同一个Server里面<br />
在一个典型的Tuxedo应用系统中，并不是所有的Service的执行频率都是相同的。可能一些Service被频繁的执行，而另外一些Service只是偶尔才被执行几次。把这些SERVICE分开可以避免偶尔调用的SERVICE堵塞频繁调用的SERVICE。<br />
5、 避免死锁的情况发生<br />
在Tuxedo中的死锁情况有两种，第一种是一个Server中的Service A去调用同在一个Server中的Service B。这种死锁发生的原因是，对于单线程的Server来说，它其中的Service是串行执行的。Service A去调用Service B的时候，Service A还没有执行完，它等待Service B的返回结果。 但是Service B不能执行，因为该Server还处于执行Service A的状态。最终导致Service A超时而出错。<br />
第二种情况是两个Server中的Service互相调用。例如Server1和Server2，Server1中的Service A去调用Server2中的Service X。同时Server2中的Service Y去调用Server1中的Service B。这种情况发生的死锁和第一种情况非常类似，都是因为进程只能串行实行Service导致的。<br />
这两种死锁情况在应用设计时特别要注意避免。第一种情况是比较容易察觉的，但是第二种情况就不大容易察觉。<br />
1.3、中间件系统应用平台FAILOVER方式的选择在真实的生产运行环境中，运行平台的failover是一个必须考虑的问题。Tuxedo平台上的实现failover机制基本上有两种，一种是利用tuxedo自己的failover机制配合前台配两个WSNADDR地址来实现failover。一种是利用双机软件来实现。<br />
第一种方式是指使两台的中间件服务器处于MP工作模式下，在每一台都启动WSL进程，然后在中间件前台配置两个ip地址。通过自己实验发现利用tuxedo自己的failover机制只有如下好处即不需要额外的操作系统和软件的支持，但是其缺点确很多，主要缺点如下：<br />
1、两台中间件平台的MASTER切换必须要手工完成，无法自动完成。<br />
2、前台配置的两个WSNADDR地址，如果第一个地址失败，要重新连接到第二个地址需要很长的时间，并且在每次服务调用时都要等待同样的时间。<br />
通过双机软件来实现FAILOVER是指两台中间件服务器都配置在SHP模式下，然后利用双机软件的ip切换功能来实现failover。这种方法需要在各自的配置文件中加入另外一台主机的WSL的信息，在另外一台主机故障时，通过双机软件进行IP切换，再通过双机切换脚本将这台主机的备用WSL启动即可。采用这种方式有以下优点<br />
1、机器切换自动完成，不需要人工干预。<br />
2、前台业务不受影响，能保证业务开展的连续性。<br />
缺点主要有<br />
1、在正常情况下，每一台主机的启动中间件时，备用的WSL服务会启动失败，不过这不影响正常使用，备用的WSL只在另外一台主机故障后才起作用。<br />
2、需要额外的双机配置和脚本配置工作。<br />
经过这些比较，我们选定了采用双机软件的方案实现了中间件的failover机制。在以后的运行维护过程中起到了好的效果。<br />
1.4、中间件与数据库的连接方式的选择在TUXEDO中间件应用中连接数据库的方式有两种，一种为通过XA方式连接数据库，另外一种是在应用程序中自己连接数据库。<br />
通过XA方式连接数据库是指利用关系型数据库的XA接口，在tuxedo的配置文件中的OPENINFO段中加入连接信息，然后在server程序的初始化代码的中调用中tp_open（或xa_open），当SERVER启动时进行数据库连接。<br />
通过应用程序直接连接数据库，数据库的连接操作不一定是在SERVER启动时进行的。<br />
这两种方式的连接在BOSS系统中都会用到，其中的区别主要就在于如果业务操作的事务为分布式事务时，则必须要用XA方式。详细比较如下：<br />
1、通过XA连接的方式可以实现分布式事务，即能够保证事务在不同数据库中的一致性。但是正是因为如此，它对中间件系统和数据库系统都有额外的开销，有时候这种开销甚至会影响系统的性能。<br />
2、通过应用程序直连数据库的方法不能够实现分布式事务，但是同时也因此减少了对系统的压力。 <br />
综上，选择数据库的连接方式对整个系统的性能影响也是至关重要的，我们有个原则，凡是在不涉及到分布式事务的地方就不用XA连接。过多的依赖了XA接口，会给系统带来了许多额外的负担和压力，也会造成许多额外的故障。</p>
<p>二、中间件技术在系统维护方面的经验总结在中间件的日常维护中，我们了解了一些TUXEDO的基本知识，摸索了一套行之有效的故障排除方法，积累了一些常见故障的排除经验，现在与大家介绍如下：<br />
2.1、LICENSE数的问题tuxedo的license由文本文件lic.txt来控制，位于udataobj目录下，分为SDK/RTK两种，两种LICENSE不能合并使用。它控制的是并发用户数并且有10%的冗余，这里的并发用户数是指在某一时刻前台程序已经发起tpinit还没有作tpterm的连接数，所以在tuxedo中间件的使用中存在着长连接和短连接的问题。长连接是指tpinit后就开始一系列的服务调用，只在系统重启或系统非常空闲的时候才做tpterm，而短连接是指每次服务调用前作tpinit，调用结束后立即作tpterm调用。因为短连接下的服务调用需要额外的连接时间消耗，这对系统响应时间会有影响，而长连接又会占用系统的并发用户数。所以在系统设计时需要针对不同的应用情况做出不同的设计，比如针对接口类型的应用因为服务调用比较频繁，进行连接的时间就会占比较大的比重，这时就需要使用长连接，而对于前台应用类型的应用，连接过程的时间对于业务操作的时间来说是可以忽略的，这时就要用短连接来节约license资源。</p>
<p>2.2、客户端连接的问题当客户端无法连接中间件时，我们需要确认的是<br />
1、 客户端的WSNADDR是否设置或是否设置正确。<br />
2、 系统配置文件中的MAXWSCLIENTS和MAXACCESS是否正确设置。<br />
3、 WSL是否启动，WSH的个数是否足够。<br />
4、 是否有防火墙，如果有防火墙存在，则需要在WSL的配置中作相应的地址映射和端口指定。<br />
5、 操作系统是否有足够的SCOKET资源使用。<br />
解决了这些问题后，一般就可以解决客户端连接不上的问题了。<br />
在TUXEDO中，参数TMTRACE对故障的定位和排除很有作用，它是一个环境境变量。我们一般在系统监控的工作终端上将此参数设置为on，在前台报系统故障的时候，我们直接进入故障模块将故障重现，然后在终端机器的c:\或者tuxedo的安装目录下打开一个ULOG文件，在此文件中会发现tpcall服务的失败信息，根据这个信息查找对应的SERVER和SERVICE的对应表，很快就可以找到问题服务。采用这种方式，维护人员不需要了解程序的具体流程就可以定位错误解决故障。<br />
在TUXEDO的服务端，有几类文件需要关注，这些文件包括ULOG文件、XA接口的trc文件、数据库连接故障的sqlnet.log、标准输出文件stdout（也可以被应用程序自己定义）。在这些文件中包含着丰富的系统运行错误告警信息。ULOG记载关于中间件的启停记录和系统本身的一些配置运行告警信息。XA的trc文件记录的是XA接口方面的一些的错误信息，关于分布式事务的一些错误在这个文件中都有记录。sqlnet.log记录了应用程序和数据库连接故障的信息，只要有当前日期时间的这个文件存在，应用和数据库的连接就会存在问题。标准输出文件stdout记录了应用程序本身的运行信息。这些文件的跟踪检查对于及时主动的发现故障有着重要的意义。</p>
<p>2.3、事务控制的问题1、事务边界的问题：在这里我们要遵循谁发起发起事务，谁就结束的原则，这里的谁主要是指中间件的前台和后台。我们知道在TUXEDO中事务的发起既可以在前台程序中发起，也可以在后台程序中发起。无论放在前台还是放在后台都有其优缺点。事务放在前台增加了网络传输的流量，当时可以保证异常情况下前后台操作的一致性，事务放在后台可以减少网络流量，但是对于异常情况下前后台操作的一致性很难保证。我们采用的是事务放在前台程序中。但是无论放在前台还是后台，都要遵循谁发起，谁结束的原则。<br />
2、XA接口使用的注意事项：首先是XA资源文件的配置，在oracle数据库中一般用到的是libclntsh.a，我们可以先Make sample，提取其中用到的连接库加入到RM文件中即可。其次在oracle8i之前要对XA进行授权，要在oracle的DBA用户下执行grant select on dba_pending_transactions to public。<br />
3、事务挂起的问题：在使用XA的情况下，我们经常会遇到这种情况，SERVER进程还在，可就是不做事。在相关的日志文件中总是报&#8220;当前的进程已经在一个本地事务中了&#8221;的错误，这时只有重启该SERVER，才能排除故障。其实这是一个事务控制的问题，它产生的根本原因是在开启全局事务前，该SERVER执行了一个本地的事务并且没有提交或回滚。在程序代码上表现为如下三种原因：<br />
（1）、在调用该SERVER的某个带DML语句的service前没有加tpbegin。<br />
（2）、在调用tpbegin后没有判断返回值。<br />
（3）、tpbegin后的tpcall服务调用没有判断返回值，在全局事务超时后没有能够及时的退出后续的调用，导致下一个tpcall产生了一个本地事务。<br />
此外，还会有一类比较特殊的问题，那就是TMS服务挂起的问题，利用tmadmin中的pq命令发现TMS的队列中有很多请求存在，在这种情况下，一般只能等待其执行完成，情况严重时，则需要调整相应的数据库参数，在ORACLE中该参数应该设为max_commit_propagation_delay&gt;=90000。<br />
在实际的运行维护中，我们发现即使把数据库参数调整了，有时还会有TMS挂起的故障发生。针对这件事情我们专门作了研究，TMS之所以挂起是因为TMS在等待数据库中DX独占锁的释放，这种独占锁加锁的对象一般都是ORACLE的系统对象，而这种独占锁的产生一般是在全局事务中执行着DML语句，当这种DML语句执行比较慢时，就会引起TMS的锁等待现象。此外，我们还发现，一般的查询语句是不会产生锁的（OPS的lm_lock锁除外），但是如果将查询放入一个全局事务中时，它就会产生一个共享的DX锁，如果这个在全局事务中的查询的调用是通过ORACLE的工具包DBMS_SQL来进行的话，那就会产生一个DX的排他锁。基于这些结果，我们建议在程序的开发过程中要遵循能不用XA全局事务的情况下就不用，能将查询放到事务之外的就不要放到事务之内，能不用ORACLE工具包进行开发的就不要用。我们也按照这个原则要求我们的开发商，取得了比较好的效果。<br />
4、事务超时设置的问题<br />
中间件应用系统的事务超时控制很重要，不设置超时时间对系统来说简直就是一种灾难。我们曾经就有过因为超时时间的设置没设和设置不当造成了严重的系统故障，最直接的故障就是系统报全局事务数不够的错误，无法开启新的事务，从而导致前台业务的终止。TUXEDO的超时主要有三种，一个是在代码中的tpbegin超时（值为其参数）T1，他控制整个事务的完成时间，第二个为XA连接的超时（配置文件中的open_info中的SesTm ）T2，他控制同一连接中对于分布式事务锁的等待超时时间，还有一个为数据库中等待数据库对象非分布式事务锁释放的超时时间（_dirstributed_lock_timeout）T3。这三个超时共同作用并且有如下的关系 T1</p>
<p>2.4、服务不能正常启动的问题在实际的维护工作中，经常会出现服务不能正常启动的现象。明明所有的服务都已经关闭了，tmboot就是起不来。这种原因一般是因为系统的IPC资源没有释放。IPC资源是操作系统用来进行进程间通讯的系统资源，主要包括信号灯、共享内存、消息队列。在UNIX操作系统下通过IPCS命令可以清楚的看到IPC资源的使用情况。当服务无法启动的时观察IPCS就会发现tuxedo运行环境的用户下的ipc资源没有被释放，这时使用ipcrm命令清除相应的IPC资源，再启动服务就可以了。<br />
&nbsp;<br />
</p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/278087.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-26 21:13 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/278087.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java通过Jolt调用Tuxedo服务 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/277993.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 26 May 2009 06:01:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/277993.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/277993.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/277993.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/277993.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/277993.html</trackback:ping><description><![CDATA[<p>http://blog.csdn.net/liwei_cmg/archive/2006/06/02/769150.aspx<br />
<br />
http://blog.csdn.net/liwei_cmg/archive/2006/06/03/771906.aspx<br />
<br />
Java通过Jolt调用Tuxedo服务</p>
<p>草木瓜 2006-6-1</p>
<p>------------------------<br />
一、简介<br />
------------------------</p>
<p>外部应用访问Tuxedo服务是很经常的事，一般有两种方法WTC和Jolt,网上很多关于Jolt调用Tuxedo服务<br />
文章，描述的太多笼统，其实通过Jolt并不是很复杂的事情，这里使用Eclipse3.1+Jolt+WebLogic8.1<br />
+Tuxedo9.0环境描述调用服务的全过程。</p>
<p>Jolt是Bea Tuxedo自带的jar组件，在Tuxedo9.0的安装过程中可以看到安装的Jolt组件。</p>
<p>调用服务理论步骤是这样的：</p>
<p>1.先准备Tuxedo服务端代码<br />
2.在Tuxedo中配置Jolt相关文件<br />
3.启动Tuxedo服务<br />
4.配置WebLogic服务与Tuxedo Jolt相关的参数<br />
5.配置Eclipse3.1启动WebLogic服务<br />
6.编写Eclipse Servlet代码，运行调用服务。</p>
<p>本例使用了《Windows Tuxedo的安装配置-数据库补充》一文中的Tuxedo数据服务文件，所以在启动<br />
Tuxedo服务前，必须先启动数据库实例，因为在tpsvinit()里面就配置了数据连接。关于Tuxedo配置<br />
要点需参阅《Tuxedo的安装配置-...》的四篇文章。</p>
<p>------------------------<br />
二、Tuxedo服务文件全代码<br />
------------------------</p>
<p>这里把server.pc服务文件代码再次列出。其中包括三个服务DBREAD（读数据库）和TOUPPER（转换大写）。<br />
本例不使用TOUPPER，所以不用理会那段代码。</p>
<p>其中<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#108;&#105;&#119;&#101;&#105;&#47;&#108;&#105;&#119;&#101;&#105;&#64;&#119;&#105;&#110;&#100;&#98;">liwei/liwei@windb</a>连接的表tuxedo_test，结构如下：</p>
<p>CREATE TABLE TUXEDO_TEST(<br />
ID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NUMBER(2),<br />
NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VARCHAR2(10)<br />
)</p>
<p>ID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NAME<br />
------------------------<br />
1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aaaaaaa<br />
2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bbbbbbb</p>
<p>#include &lt;stdio.h&gt;<br />
#include &lt;ctype.h&gt;<br />
#include &lt;atmi.h&gt;&nbsp;/* TUXEDO Header File */<br />
#include &lt;userlog.h&gt;&nbsp;/* TUXEDO Header File */</p>
<p>EXEC SQL BEGIN DECLARE SECTION;<br />
VARCHAR ora_no[2];<br />
int ora_id;<br />
VARCHAR ora_value[10];<br />
VARCHAR ora_cn[30];<br />
EXEC SQL END DECLARE SECTION;<br />
EXEC SQL INCLUDE sqlca;</p>
<p>tpsvrinit()<br />
{<br />
&nbsp;&nbsp;strcpy(ora_cn.arr,"<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#108;&#105;&#119;&#101;&#105;&#47;&#108;&#105;&#119;&#101;&#105;&#64;&#119;&#105;&#110;&#100;&#98;">liwei/liwei@windb</a>");<br />
&nbsp;&nbsp;ora_cn.len = strlen(ora_cn.arr);<br />
&nbsp;&nbsp;ora_cn.arr[ora_cn.len]='\0';</p>
<p>&nbsp;&nbsp;//EXEC SQL WHENEVER SQLERROR GOTO cnError;<br />
&nbsp;&nbsp;EXEC SQL CONNECT :ora_cn;<br />
&nbsp;&nbsp;return(0);<br />
}<br />
tpsrvdone()<br />
{<br />
&nbsp;&nbsp;EXEC SQL COMMIT WORK RELEASE;<br />
}</p>
<p>DBREAD(TPSVCINFO *rqst)<br />
{<br />
&nbsp;<br />
&nbsp;strcpy(ora_no.arr,(char *)rqst-&gt;data);<br />
&nbsp;ora_no.len=strlen(ora_no.arr);<br />
&nbsp;ora_no.arr[ora_no.len]='\0';</p>
<p>&nbsp;userlog("ERRSRV: %s",ora_no.arr);<br />
&nbsp;EXEC SQL select name into :ora_value from tuxedo_test where id=:ora_no;<br />
&nbsp;if(sqlca.sqlcode!=0)<br />
&nbsp;{<br />
&nbsp;&nbsp;userlog("ERRSRV: select name from tuxedo_test where id=, sqlcode=%ld , sqlerr=\n",sqlca.sqlcode);<br />
&nbsp;&nbsp;strcpy(rqst-&gt;data,sqlca.sqlerrm.sqlerrmc);<br />
&nbsp;&nbsp;tpreturn(TPFAIL, 0, rqst-&gt;data, 0L, 0);<br />
&nbsp;}</p>
<p>&nbsp;/* Return the transformed buffer to the requestor. */<br />
&nbsp;strset(rqst-&gt;data,"");<br />
&nbsp;strcpy(rqst-&gt;data,ora_value.arr);<br />
&nbsp;tpreturn(TPSUCCESS, 0, rqst-&gt;data, 0L, 0);<br />
}</p>
<p>TOUPPER(TPSVCINFO *rqst)<br />
{</p>
<p>&nbsp;int i;</p>
<p>&nbsp;for(i = 0; i &lt; rqst-&gt;len-1; i++)<br />
&nbsp;&nbsp;rqst-&gt;data[i] = toupper(rqst-&gt;data[i]);</p>
<p>&nbsp;/* Return the transformed buffer to the requestor. */<br />
&nbsp;tpreturn(TPSUCCESS, 0, rqst-&gt;data, 0L, 0);<br />
}</p>
<p>------------------------<br />
三、编译服务<br />
------------------------</p>
<p>编译命令的注意事项，以前Tuxedo系列文章都提过。</p>
<p>proc server.pc include=%TUXDIR%\include<br />
buildserver -o server -f server.c -s DBREAD -s TOUPPER -v -l orasql9.lib</p>
<p>------------------------<br />
四、配置Tuxedo服务的config文件<br />
------------------------</p>
<p>完全文件如下：</p>
<p>#Liwei</p>
<p>*RESOURCES<br />
IPCKEY&nbsp;&nbsp;123456<br />
DOMAINID&nbsp;liweiapp<br />
MASTER&nbsp;&nbsp;lw<br />
MAXACCESSERS&nbsp;150<br />
MAXSERVERS&nbsp;100<br />
MAXSERVICES&nbsp;100<br />
MODEL&nbsp;&nbsp;SHM<br />
LDBAL&nbsp;&nbsp;N</p>
<p>*MACHINES<br />
LWYM<br />
&nbsp;LMID = lw <br />
&nbsp;TUXDIR = "E:\bea\tuxedo9.0"<br />
&nbsp;TUXCONFIG = "G:\Liwei\Tuxedo\dbread\tuxconfig"<br />
&nbsp;APPDIR = "G:\Liwei\Tuxedo\dbread"<br />
&nbsp;MAXWSCLIENTS=1<br />
&nbsp;TLOGDEVICE = "G:\Liwei\Tuxedo\dbread\TLOG"<br />
&nbsp;TLOGNAME=TLOG<br />
&nbsp;TLOGSIZE = 100<br />
&nbsp;<br />
*GROUPS</p>
<p>APPGRP&nbsp;LMID=lw GRPNO = 1 </p>
<p>#OPENINFO="Oracle_XA:Oracle_XA+Acc=P/liwei/liwei+SqlNet=linux+SesTm=600+MaxCur=5+LogDir=." <br />
#TMSNAME="TMS_ORA9i" TMSCOUNT=2</p>
<p>JSLGRP&nbsp; LMID=lw GRPNO = 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
JREPGRP LMID=lw GRPNO = 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>*SERVERS<br />
server SRVGRP=APPGRP SRVID=1</p>
<p>#WSL&nbsp;SRVGRP=APPGRP SRVID =300 <br />
#CLOPT="-A -- -n //192.168.0.166:8888 -d/dev/tcp -m1 -M5 -x 10"</p>
<p>JSL SRVGRP=JSLGRP SRVID=301<br />
CLOPT="-A -- -n //192.168.0.166:9878 -M 10 -x 10 -m 2"</p>
<p>JREPSVR SRVGRP=JREPGRP SRVID=302<br />
CLOPT="-A -- -W -P E:\bea\tuxedo9.0\udataobj\jolt\repository\jrepository"</p>
<p>*SERVICES<br />
DBREAD<br />
TOUPPER</p>
<p>细心一看，这个config文件比在*GROUPS和*SERVERS各加了两项对应内容：</p>
<p>JSLGRP&nbsp; LMID=lw GRPNO = 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
JREPGRP LMID=lw GRPNO = 3&nbsp;&nbsp;&nbsp;&nbsp; <br />
和<br />
JSL SRVGRP=JSLGRP SRVID=301<br />
CLOPT="-A -- -n //192.168.0.166:9878 -M 10 -x 10 -m 2"<br />
JREPSVR SRVGRP=JREPGRP SRVID=302<br />
CLOPT="-A -- -W -P E:\bea\tuxedo9.0\udataobj\jolt\repository\jrepository"</p>
<p>这两项是用于jolt的访问接口。JSL和JREPSVR必须是独自的GROUP，由于外部调用通过JSL和JREPSVR直接于<br />
Tuxedo服务通信，WSL就没用了。JSL的主机地址是Tuxedo服务器地址，端口是随意指定的，不要与其他重复<br />
即可，一般设置的大一点。在config里面加入这此内容后，config文件就Ok了。</p>
<p>------------------------<br />
五、配置jrepository文件<br />
------------------------</p>
<p>直接打开E:\bea\tuxedo9.0\udataobj\jolt\repository\jrepository文件添加以下内容：</p>
<p>add SVC/DBREAD:vs=1:ex=1:bt=STRING:\<br />
&nbsp;bp:pn=STRING:pt=string:pf=167772161:pa=rw:ep:<br />
add PKG/SIMPSERV:DBREAD:</p>
<p>这个文件如果没有DBREAD配置，调用会提示DBREAD not avaliable，DBREAD配置错误，会提示DBREAD has<br />
been modifed ...</p>
<p>对这个文件操作还有以下两种方式：</p>
<p>方式一：在IE打开E:\bea\tuxedo9.0\udataobj\jolt\RE.html，是个java applet，里面是图形界面，添加<br />
&nbsp;完服务，设置好参数即可。具体方法可以查阅网上资料，有图文解说的。<br />
方式二：在命令行键入Java bea.jolt.admin.jbld //192.168.0.166:9878 services.rep，其中//192..要与<br />
JSL的设置一致。services.rep是随意的一个文件，不过文件内容绝对不随意，如下：</p>
<p>service=DBREAD<br />
inbuf=STRING<br />
outbuf=STRING<br />
export=true<br />
param=STRING<br />
type=string<br />
access=inout</p>
<p>要执行这个命令设置jdk环境变量是必不可少的，PATH，CLASSPATH。我的CLASSPATH加入了以下内容，<br />
E:\bea\tuxedo9.0\udataobj\jolt\joltwls.jar;<br />
E:\bea\tuxedo9.0\udataobj\jolt\joltjse.jar;<br />
E:\bea\tuxedo9.0\udataobj\jolt\jolt.jar;<br />
E:\bea\tuxedo9.0\udataobj\jolt\joltadmin.jar<br />
个人认为对于这个java命令joltadmin是必须的，其他三个没用，不过没有亲自尝试。</p>
<p>方式一，二修改后，jrepository文件会显得比较乱，所以我不喜欢！最好是自已手工修改。</p>
<p><br />
------------------------<br />
六、编译启动服务<br />
------------------------</p>
<p>当然，数据库必须要有了，编译config后，启动tuxedo服务。<br />
tmloadcf -y config<br />
tmboot -y</p>
<p>提示如下：</p>
<p>exec BBL -A :<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; process id=3520 ... Started.</p>
<p>Booting server processes ...</p>
<p>exec server -A :<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; process id=3812 ... Started.<br />
exec JSL -A -- -n //192.168.0.166:9878 -M 10 -x 10 -m 2 :<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; process id=252 ... Started.<br />
exec JREPSVR -A -- -W -P E:\bea\tuxedo9.0\udataobj\jolt\repository\jrepository :</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; process id=2920 ... Started.<br />
4 processes started.</p>
<p><br />
------------------------<br />
七、配置Eclipse3.1<br />
------------------------</p>
<p>我们要在Eclipse里面启动WebLogic服务，《Eclipse3.1 Web开发配置》一文中已经说明了，如何配置<br />
WebLogic8.1的服务器，这里对WebLogic的服务本身也要做此必要的配置。</p>
<p>打开选用的WebLogic8.1服务路径，编辑config.xml文件，本机位置G:\Liwei\WebLogic\WebServer\config.xml。<br />
加入如下内容：</p>
<p>&nbsp;&nbsp;&nbsp; &lt;JoltConnectionPool FailoverAddresses="//192.168.0.166:9878"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MaximumPoolSize="2" MinimumPoolSize="1" Name="JoltPool"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PrimaryAddresses="//192.168.0.166:9878"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SecurityContextEnabled="false" Targets="webServer"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;StartupClass<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClassName="bea.jolt.pool.servlet.weblogic.PoolManagerStartUp"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LoadBeforeAppActivation="true" Name="MyStartup" Targets="webServer"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;ShutdownClass<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClassName="bea.jolt.pool.servlet.weblogic.PoolManagerShutDown"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="MyShutdown" Targets="webServer"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
这里要说明一下：PrimaryAddresses，FailoverAddresses地址是前面Tuxedo配置文件指定的JSL地址和端口<br />
SecurityContextEnabled一定要是false!所有name是随意的，StartupClass和ShutdownClass的ClassName<br />
绝对不能错！</p>
<p>除此方法，还可以通过Weblogic服务的控制台进行管理。启动服务后，在<a href="http://localhost:7001/console/">http://localhost:7001/console/</a><br />
登陆后的主页面上选择 连接性-&gt;通过 JOLT 的 Tuxedo&nbsp; -&gt;配置新的Jolt 连接缓冲池...输入参数内容就是<br />
上面config.xml的参数。另外首页中 已部署的资源 -&gt;启动和关闭 需要设置两个Class，参数也同上。</p>
<p><br />
连接性-&gt;通过 JOLT 的 Tuxedo&nbsp; -&gt;配置新的Jolt 连接缓冲池...</p>
<p>①进入选项卡的General(常规),输入参数:<br />
Name:&nbsp;&nbsp; JoltPool<br />
Minimum Pool Size:&nbsp; 1 <br />
Maximum Pool Size:&nbsp;&nbsp; 2<br />
Recv Timeout:&nbsp; 0<br />
点击Apply应用按纽<br />
②进入选项卡的Addresses(地址),输入参数:<br />
Primary Addresses: //192.168.0.166:9878<br />
Failover Addresses: //192.168.0.166:9878<br />
点击Apply应用按纽<br />
③进入Targets栏：<br />
选中myserver<br />
点击Apply应用按纽.</p>
<p>已部署的资源 -&gt;启动和关闭 分别配置StartUp,ShutDown的Class</p>
<p>①进入Configuration(设置)<br />
在ClassName:中输入: bea.jolt.pool.servlet.weblogic.PoolManagerStartUp,<br />
其它采用默认值。<br />
②进入Targets(目标)栏：<br />
选中myserver,点击Apply应用按纽.</p>
<p>①进入Configuration(设置)<br />
在ClassName:中输入: bea.jolt.pool.servlet.weblogic.PoolManagerShutDown,<br />
其它采用默认值。<br />
②进入Targets(目标)：<br />
选中myserver,点击Apply应用按纽.</p>
<p>到这里config.xml文件就设置完了，下面需要编辑startWebLogic.cmd，加入对jolt jar的引用，否则jolt<br />
是启动失败的。在echo CLASSPATH=%CLASSPATH%前加入以下代码，设置CLASSPATH，这里classpath跟外部环境<br />
变量是两码事。</p>
<p>set CLASSPATH=%CLASSPATH%;E:\bea\tuxedo9.0\udataobj\jolt\jolt.jar;E:\bea\tuxedo9.0\udataobj\jolt\joltwls.jar;E:\bea\tuxedo9.0\udataobj\jolt\joltjse.jar</p>
<p>设置完后，重启服务，在Eclipse管理WebLogic服务时如果出现启动关闭不成功，多试几次就可以了。<br />
在启动的console界面里如果出现以下内容，就说明jolt pool启动成功！</p>
<p>&lt;2006-6-1 下午09时12分55秒 CST&gt; &lt;Notice&gt; &lt;WebLogicServer&gt; &lt;BEA-000327&gt; &lt;Starting WebLogic Admin Server "webServer" for domain "WebServer"&gt; <br />
Jolt pool deployed for webServer<br />
&lt;2006-6-1 下午09时13分13秒 CST&gt; &lt;Notice&gt; &lt;Security&gt;...</p>
<p><br />
------------------------<br />
八、Eclipse3.1编写Servlet代码<br />
------------------------</p>
<p>首先需要在所建工程-&gt;右键-&gt;Properties-&gt;Java BuildPath -&gt;Library-&gt;Add External jars ..添加jolt的jar包<br />
这里需要三个jar包，如下：<br />
E:\bea\tuxedo9.0\udataobj\jolt\jolt.jar<br />
E:\bea\tuxedo9.0\udataobj\jolt\joltjse.jar<br />
E:\bea\tuxedo9.0\udataobj\jolt\joltwls.jar<br />
&nbsp;<br />
添加jar完后，建立一个主页面index.jsp和一个Servlet。关于Eclipse3.1配置Servlet在《Eclipse3.1 Web开发配置》<br />
一文也有说明。这里Servlet名为liwei.java,具体内容如下：</p>
<p>index.jsp</p>
<p>&lt;body&gt;<br />
&lt;form id=form1 name="form1" action=liwei method=post&gt;<br />
&lt;input type="text" name="STRING" value="1"&gt;<br />
&lt;input type=submit value=submit&gt;<br />
&lt;/form&gt;<br />
&lt;/body&gt;</p>
<p>说明：name要是STRING,用来传递参数，action是liwei.java即Servlet。</p>
<p>liwei.java</p>
<p><br />
&nbsp;public class liwei extends javax.servlet.http.HttpServlet&nbsp; <br />
&nbsp;{<br />
&nbsp; <br />
&nbsp;//声明管理连接池的变量<br />
&nbsp;private bea.jolt.pool.servlet.ServletSessionPoolManager bool_mgr = (bea.jolt.pool.servlet.ServletSessionPoolManager) bea.jolt.pool.SessionPoolManager.poolmgr;</p>
<p>&nbsp;public void init(javax.servlet.ServletConfig config) throws javax.servlet.ServletException <br />
&nbsp;{<br />
&nbsp;&nbsp;//初始化servlet<br />
&nbsp;&nbsp;super.init(config);<br />
&nbsp;}<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;public void destroy() {<br />
&nbsp;&nbsp;//关闭servlet<br />
&nbsp;&nbsp;bool_mgr = null;<br />
&nbsp;}&nbsp;<br />
&nbsp;<br />
&nbsp;protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) <br />
&nbsp;throws javax.servlet.ServletException, java.io.IOException <br />
&nbsp;{<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;bea.jolt.pool.servlet.ServletResult result;<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;//设置页面类型，如果没有charset，显示汉字会乱码<br />
&nbsp;&nbsp;response.setContentType("text/html;charset=UTF-8");<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;java.io.PrintWriter out =response.getWriter();<br />
&nbsp;&nbsp;out.println("&lt;html&gt;");<br />
&nbsp;&nbsp;out.println("&lt;body&gt;");<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;//System.out.println("TEST________________________1");</p>
<p>&nbsp;&nbsp; //获取在WebLogic服务定义好的连接池,JoltPool<br />
&nbsp;&nbsp; bea.jolt.pool.servlet.ServletSessionPool pool_session = (bea.jolt.pool.servlet.ServletSessionPool)<br />
&nbsp;&nbsp; bool_mgr.getSessionPool("JoltPool");</p>
<p>&nbsp;&nbsp;&nbsp; if (pool_session == null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("获取Tuxedo服务失败。"+<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "&lt;br&gt;"+<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "确认Tuxedo服务已启动且配置正确。"+<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "&lt;/font&gt;&lt;/body&gt;&lt;/html&gt;");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; System.out.println(pool_session);<br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; String sendvalue;<br />
&nbsp;&nbsp;&nbsp; sendvalue=request.getParameter("STRING");<br />
&nbsp;&nbsp;&nbsp; System.out.println(sendvalue);<br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; // 调用服务<br />
&nbsp;&nbsp;&nbsp; try <br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp; &nbsp;System.out.println("OK!");<br />
&nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp;&nbsp; &nbsp;//调用服务DBREAD<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result = pool_session.call("DBREAD", request);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("传递的参数 = " + sendvalue);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("Tuxedo服务成功调用。 ");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //跟据传递的变量STRING值，获取返回值<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("返回值："+result.getValue("STRING", ""));<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; catch (bea.jolt.pool.SessionPoolException e) <br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 连接池繁忙<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("此时请求不能被处理。\n"+<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "错误信息: "+e.getMessage()+"\n"+<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "可能的原因：1.无效的连接池 2.连接池已关闭");<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; catch (bea.jolt.pool.ServiceException e) <br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp; // 服务出错，这里主要是Tuxedo的服务配置，jrepository文件出错<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("错误信息："+<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Error message:"+e.getMessage()+<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Error number:"+e.getErrno());<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; catch (bea.jolt.pool.ApplicationException e) <br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 应用程序出错<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result = (bea.jolt.pool.servlet.ServletResult) e.getResult();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("错误代码："+result.getApplicationCode());<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; catch (Exception e) <br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("意外错误："+e);<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; out.println("&lt;/body&gt;&lt;/html&gt;\n");<br />
&nbsp;&nbsp;&nbsp; out.close();<br />
&nbsp;}&nbsp;<br />
&nbsp;<br />
&nbsp;protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) <br />
&nbsp;throws javax.servlet.ServletException, java.io.IOException <br />
&nbsp;{<br />
&nbsp;&nbsp;this.doGet(request,response);<br />
&nbsp;}&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; <br />
}</p>
<p><br />
------------------------<br />
九、RUN<br />
------------------------</p>
<p>Run on server ，点击Submit，显示 传递的参数 = 1 Tuxedo服务成功调用。 返回值：aaaaaaa !<br />
<br />
<br />
=======================<br />
<br />
</p>
<p>Java通过Jolt访问Tuxedo服务-补充说明</p>
<p>草木瓜</p>
<p>2006-6-2</p>
<p>一、页面参数文件说明以及通过WebLogic调用Tuxedo服务</p>
<p>《Java通过Jolt访问Tuxedo服务》一文是在Eclipse环境下开发调用Tuxedo服务。</p>
<p>其中在index.asp和liwei.java中有这么几句语句：</p>
<p>index.asp</p>
<p>&lt;form id=form1 name="form1" action=liwei method=post&gt;<br />
&lt;input type="text" name="STRING" value="1"&gt;<br />
&lt;input type=submit value=submit&gt;</p>
<p>liwei.java</p>
<p>//跟据传递的变量STRING值，获取返回值<br />
out.println("返回值："+result.getValue("STRING", ""));</p>
<p>这两段的STRING是不可以随便替换的，这必须与E:\bea\tuxedo9.0\udataobj\jolt\repository<br />
下jrepository文件描述的服务参数相一致！且必须为大写！即：</p>
<p>add SVC/DBREAD:vs=1:ex=1:bt=STRING:\<br />
&nbsp;bp:pn=STRING:pt=string:pf=167772161:pa=rw:ep:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //这里面定义的参数就是STRING<br />
add PKG/SIMPSERV:DBREAD:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
下面用WebLogic对Tuxedo服务调用再详细的举例说明。</p>
<p>WebLogic通过jolt调用Tuxedo服务，有了上文的前提就显得很容易，说白了只是Servlet与Jsp的一点<br />
小转换。除了最后一步在Eclipse3.1编写Servlet代码，其他的前提步骤是必须的。WebLogic要使用<br />
Jolt接口，同样需要jolt,joltjse,joltwls三个jar包，将它们Copy到WEB-INF下的lib文件夹即可。</p>
<p>以下是完整的示例，参数名用大写的TEST。</p>
<p>index.jsp<br />
---------------------------------</p>
<p>&lt;form action="_test/testTuxedo.jsp" method="post"&gt;<br />
&lt;p&gt;<br />
&lt;input type="text" value="1" name="TEST" id="text1"&gt;&lt;input type="submit" value="tuxedo"&gt; <br />
&lt;/p&gt;<br />
&lt;/form&gt;</p>
<p>testTuxedo.jsp<br />
---------------------------------</p>
<p>&lt;body&gt;<br />
&lt;p&gt;<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &lt;%<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bea.jolt.pool.servlet.ServletSessionPoolManager bool_mgr = (bea.jolt.pool.servlet.ServletSessionPoolManager) bea.jolt.pool.SessionPoolManager.poolmgr;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bea.jolt.pool.servlet.ServletResult result;<br />
&nbsp;&nbsp;&nbsp;&nbsp; //获取在WebLogic服务定义好的连接池<br />
&nbsp;&nbsp;&nbsp;&nbsp; bea.jolt.pool.servlet.ServletSessionPool pool_session = (bea.jolt.pool.servlet.ServletSessionPool)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool_mgr.getSessionPool("JoltPool");<br />
&nbsp;&nbsp;&nbsp;&nbsp; if (pool_session == null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("获取Tuxedo服务失败。"+<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "&lt;br&gt;"+<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "确认Tuxedo服务已启动且配置正确。");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp; // 调用服务<br />
&nbsp;&nbsp;&nbsp;&nbsp; try <br />
&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result = pool_session.call("DBREAD", request);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("OK!");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("传递的参数 = " + sendvalue);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("Tuxedo服务成功调用。 ");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("返回值："+result.getValue("TEST", ""));<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp; catch (bea.jolt.pool.SessionPoolException e) <br />
&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 连接池繁忙<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("此时请求不能被处理。\n"+<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "错误信息: "+e.getMessage()+"\n"+<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "可能的原因：1.无效的连接池 2.连接池已关闭");<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp; catch (bea.jolt.pool.ServiceException e) <br />
&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 服务出错<br />
&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("Error："+<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Error message:"+e.getMessage()+<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Error number:"+e.getErrno());<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp; catch (bea.jolt.pool.ApplicationException e) <br />
&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 应用程序出错<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result = (bea.jolt.pool.servlet.ServletResult) e.getResult();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("错误代码："+result.getApplicationCode());<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp; catch (Exception e) <br />
&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("意外错误："+e);<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp; %&gt;<br />
&lt;/p&gt;<br />
&lt;/body&gt;</p>
<p><br />
二、典型的服务应用</p>
<p>Oracle服务器&nbsp;&nbsp; 192.168.0.111<br />
Tuxedo服务器&nbsp;&nbsp; 192.168.0.66<br />
Web服务器&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>通过Web客户端访问Web服务器，调用Tuxedo服务访问数据库</p>
<p>1.配置Tuxedo服务器的Oracle连接字符串(TNSNAME)。注意Oracle服务器的防火墙</p>
<p>WINDB =<br />
&nbsp; (DESCRIPTION =<br />
&nbsp;&nbsp;&nbsp; (ADDRESS_LIST =<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.111)(PORT = 1521))<br />
&nbsp;&nbsp;&nbsp; )<br />
&nbsp;&nbsp;&nbsp; (CONNECT_DATA =<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (SERVICE_NAME = wincn)<br />
&nbsp;&nbsp;&nbsp; )<br />
&nbsp; )<br />
&nbsp; <br />
2.配置Tuxedo的jrepository文件<br />
3.配置Tuxedo的Config文件,JSR服务的IP与端口要与本地IP一致。</p>
<p>JSL SRVGRP=JSLGRP SRVID=301<br />
CLOPT="-A -- -n //192.168.0.66:9988 -M 10 -x 10 -m 2"</p>
<p>4.配置Web服务器的WebServer，config.xml中ip与端口即为Tuxedo服务器的JSR服务IP与端口<br />
注意Tuxedo服务器的防火墙。</p>
<p>&lt;JoltConnectionPool FailoverAddresses="//192.168.0.66:9988"<br />
&nbsp;&nbsp;&nbsp; MaximumPoolSize="2" MinimumPoolSize="1" Name="JoltPool"<br />
&nbsp;&nbsp;&nbsp; PrimaryAddresses="//192.168.0.66:9988"<br />
&nbsp;&nbsp;&nbsp; SecurityContextEnabled="false" Targets="webServer"/&gt;</p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/277993.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-26 14:01 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/277993.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows下Tuxedo的安装与配置 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/277992.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 26 May 2009 06:00:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/277992.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/277992.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/277992.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/277992.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/277992.html</trackback:ping><description><![CDATA[http://blog.csdn.net/liwei_cmg/archive/2006/05/28/759301.aspx<br />
<br />
<a href="http://blog.csdn.net/liwei_cmg/archive/2006/05/29/759803.aspx">http://blog.csdn.net/liwei_cmg/archive/2006/05/29/759803.aspx</a><br />
<br />
<br />
<p>Windows下Tuxedo的安装与配置-数据库</p>
<p>草木瓜 2005-5-18</p>
<p>一、准备数据库环境</p>
<p>&nbsp; 这里使用的是Linux+Oracle9的虚拟机数据库环境。本机配置Tuxedo服务器。在上篇无数据库配置<br />
&nbsp; 基础上，配置Tuxedo服务器连接Oracle理解起来就相对简单了。这里采用XA方式连接数据库。</p>
<p>二、准备客户端，服务端的应用程序 <br />
&nbsp; <br />
&nbsp; 这里是自已写的代码。<br />
&nbsp; <br />
&nbsp; client.c<br />
&nbsp; <br />
#include &lt;stdio.h&gt;<br />
#include &lt;atmi.h&gt;&nbsp;&nbsp;/* TUXEDO&nbsp; Header File */</p>
<p>main(int argc, char *argv[])<br />
{<br />
&nbsp;char *sendbuf;<br />
&nbsp;long sendlen=1024;<br />
&nbsp;int ret;</p>
<p>&nbsp;<br />
&nbsp;if(argc != 2) {<br />
&nbsp;&nbsp;(void) fprintf(stderr, "args not exist\n");<br />
&nbsp;&nbsp;exit(1);<br />
&nbsp;}</p>
<p>&nbsp;/* 与服务器建立连接 */<br />
&nbsp;if (tpinit((TPINIT *) NULL) == -1) <br />
&nbsp;{<br />
&nbsp;&nbsp;(void) fprintf(stderr, "Tpinit failed\n");<br />
&nbsp;&nbsp;exit(1);<br />
&nbsp;}</p>
<p>&nbsp;/* 分配发送缓冲 */<br />
&nbsp;sendbuf = (char *) tpalloc("STRING", NULL, sendlen);<br />
&nbsp;if (sendbuf== (char *)NULL) <br />
&nbsp;{<br />
&nbsp;&nbsp;(void) fprintf(stderr,"Error allocating send buffer\n");<br />
&nbsp;&nbsp;tpterm();<br />
&nbsp;&nbsp;exit(1);<br />
&nbsp;}</p>
<p>&nbsp;(void) strcpy(sendbuf, argv[1]);<br />
&nbsp;(void) fprintf(stdout, "InputValue: %s\n", argv[1]);</p>
<p>&nbsp;/* 向服务发送请求 */<br />
&nbsp;ret = tpcall("DBSERV", (char *)sendbuf, 0L, (char **)&amp;sendbuf, &amp;sendlen, 0L);<br />
&nbsp;if(ret == -1) {<br />
&nbsp;&nbsp;(void) fprintf(stderr, "Can't send request to service DBREAD\n");<br />
&nbsp;&nbsp;(void) fprintf(stderr, "Tperrno = %d\n", tperrno);<br />
&nbsp;&nbsp;tpfree(sendbuf);<br />
&nbsp;&nbsp;tpterm();<br />
&nbsp;&nbsp;exit(1);<br />
&nbsp;}</p>
<p>&nbsp;(void) fprintf(stdout, "Returned string is: %s\n", sendbuf);<br />
&nbsp;<br />
&nbsp;/* Free Buffers &amp; Detach from System/T */<br />
&nbsp;tpfree(sendbuf);<br />
&nbsp;tpterm();<br />
&nbsp;return(0);<br />
}</p>
<p>&nbsp; server.pc 这里用到了ProC的一些方法<br />
&nbsp;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;ctype.h&gt;<br />
#include &lt;atmi.h&gt;&nbsp;/* TUXEDO Header File */<br />
#include &lt;userlog.h&gt;&nbsp;/* TUXEDO Header File */</p>
<p>EXEC SQL INCLUDE sqlca;</p>
<p>EXEC SQL BEGIN DECLARE SECTION;</p>
<p>VARCHAR ora_no[2];<br />
VARCHAR ora_value[10];<br />
EXEC SQL END DECLARE SECTION;</p>
<p>DBREAD(TPSVCINFO *rqst)<br />
{</p>
<p>&nbsp;strcpy(ora_no.arr,(char *)rqst-&gt;data);<br />
&nbsp;ora_no.len=strlen(ora_no.arr);<br />
&nbsp;ora_no.arr[ora_no.len]='\0';<br />
&nbsp;<br />
&nbsp;userlog("ERRSRV: %s",ora_no.arr);<br />
&nbsp;EXEC SQL select name into :ora_value from tuxedo_test where id=:ora_no;<br />
&nbsp;if(sqlca.sqlcode!=0)<br />
&nbsp;{<br />
&nbsp;&nbsp;userlog("ERRSRV: select name from tuxedo_test where id=, sqlcode=%ld \n",sqlca.sqlcode);<br />
&nbsp;&nbsp;tpreturn(TPFAIL, 0, rqst-&gt;data, 0L, 0);<br />
&nbsp;}</p>
<p>&nbsp;strcpy(rqst-&gt;data,ora_value.arr);<br />
&nbsp;tpreturn(TPSUCCESS, 0, rqst-&gt;data, 0L, 0);<br />
}</p>
<p>三、编译客户端，服务端代码</p>
<p>&nbsp; buildclient -o client -f client.c -v<br />
&nbsp; <br />
&nbsp; 对pc文件，须首先编译为c，然后再编译为服务。在proc编译的过程，会出现link错误，这是因为tuxedo本身<br />
&nbsp; 带的sqlca.h与oracle proc冲突，将C:\bea\tuxedo9.0\include\sqlca.h改名即可。<br />
&nbsp; proc server.pc include=%TUXDIR%/include<br />
&nbsp; buildserver -o server -f server.c -r Oracle_XA -s DBREAD -v</p>
<p>&nbsp;<br />
四、配置Tuxedo的RM文件</p>
<p>&nbsp; 本地Tuxedo9.0安装路径为C:\bea\tuxedo9.0。<br />
&nbsp; 打开C:\bea\tuxedo9.0\udataobj路径下的rm文件。找到Oracle_XA:这句，可以注释掉，然后增加下面内容。<br />
&nbsp; Oracle_XA;xaosw;E:\oracle\ora92\rdbms\XA\ORAXA9.LIB E:\oracle\ora92\precomp\lib\orasql9.lib<br />
&nbsp; 注：本机如果没有装服务版的Oracle,可能找不到对应库文件，可从服务器端复制过来即可。<br />
&nbsp; <br />
&nbsp; 这两个lib是用于编译tms文件的，tuxedo通过TMS_ORA9i与Oracle数据库进行XA通讯。<br />
&nbsp; 修改完rm文件，运行命令buildtms -o C:\bea\tuxedo9.0\bin\TMS_ORA9i -r Oracle_XA编译生成TMS_ORA9i。<br />
&nbsp; <br />
&nbsp; <br />
五、修改ubbconfig文件</p>
<p>&nbsp; 在*GROUP下添加下面语句。<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#108;&#105;&#119;&#101;&#105;&#47;&#108;&#105;&#119;&#101;&#105;&#64;&#108;&#105;&#110;&#117;&#120;">liwei/liwei@linux</a>是连接数据库的tns配置。TMSCOUNT=2表示启动两个TMS服务。<br />
&nbsp; <br />
&nbsp; OPENINFO="Oracle_XA:Oracle_XA+Acc=P/liwei/liwei+SqlNet=linux+SesTm=600+MaxCur=5+LogDir=." <br />
&nbsp; TMSNAME="TMS_ORA9i" TMSCOUNT=2<br />
&nbsp; <br />
&nbsp; 另外在*MACHINES添加日志的处理，语句如下。<br />
&nbsp; TLOGDEVICE = "F:\Liwei\Tuxedo\dbread\TLOG"<br />
&nbsp;TLOGNAME = TLOG<br />
&nbsp;TLOGSIZE = 100<br />
&nbsp;<br />
&nbsp;因为编译的服务文件名是server,*SERVERS下改为server<br />
&nbsp;因为生成的服务是DBREAD，所以*SERVICES下也要改成DBREAD。<br />
&nbsp;<br />
&nbsp;修改后文件如下：<br />
&nbsp;&nbsp;<br />
#Liwei</p>
<p>*RESOURCES<br />
IPCKEY&nbsp;&nbsp;123456<br />
DOMAINID&nbsp;liweiapp<br />
MASTER&nbsp;&nbsp;lw<br />
MAXACCESSERS&nbsp;5<br />
MAXSERVERS&nbsp;5<br />
MAXSERVICES&nbsp;5<br />
MODEL&nbsp;&nbsp;SHM<br />
LDBAL&nbsp;&nbsp;N</p>
<p>*MACHINES<br />
LWYM<br />
&nbsp;LMID = lw <br />
&nbsp;TUXDIR = "C:\bea\tuxedo9.0"<br />
&nbsp;TUXCONFIG = "F:\Liwei\Tuxedo\dbread\tuxconfig"<br />
&nbsp;APPDIR = "F:\Liwei\Tuxedo\dbread"<br />
&nbsp;<br />
&nbsp;TLOGDEVICE = "F:\Liwei\Tuxedo\dbread\TLOG"<br />
&nbsp;TLOGNAME=TLOG<br />
&nbsp;TLOGSIZE = 100<br />
&nbsp;#ULOGPFX = "F:\Liwei\Tuxedo\dbread\ULOG"<br />
&nbsp;<br />
*GROUPS<br />
GROUP1&nbsp;LMID=lw GRPNO = 1 <br />
OPENINFO="Oracle_XA:Oracle_XA+Acc=P/liwei/liwei+SqlNet=linux+SesTm=600+MaxCur=5+LogDir=." <br />
TMSNAME="TMS_ORA9i" TMSCOUNT=2</p>
<p>*SERVERS<br />
DEFAULT:<br />
&nbsp;&nbsp;CLOPT="-A"<br />
server SRVGRP=GROUP1 SRVID=1<br />
*SERVICES<br />
DBREAD</p>
<p>&nbsp; 编译日志后不能立刻tmboot运行，需要先用tmadmin创建TLOG。方法如下：<br />
&nbsp; <br />
&nbsp; C:\&gt;tmadmin<br />
&nbsp; &gt;crdl -b 500 -z F:\Liwei\Tuxedo\dbread\TLOG<br />
&nbsp; &gt;crlog -m lw</p>
<p>&nbsp; 注：lw是ubbconfig里面的LMID。<br />
&nbsp; <br />
六、Oracle的设置<br />
&nbsp; <br />
&nbsp; Linux：<br />
&nbsp; <br />
&nbsp; sqlplus /nolog <br />
&nbsp; conn sys/*** as sysdba<br />
&nbsp; <br />
&nbsp; 执行xaview.sql 创建v$xatrans$和 v$pending_xatrans$两个视图<br />
&nbsp; @...../rdbms/admin/xaview.sql<br />
&nbsp; <br />
&nbsp; 并把两个视图的select权限授给连接用户如liwei。<br />
&nbsp; <br />
&nbsp; grant select on v$xatrans$ to liwei with grant option;<br />
&nbsp; grant select on v$pending_xatrans$ to liwei with grant option;<br />
&nbsp; <br />
&nbsp; 最后<br />
&nbsp; <br />
&nbsp; grant select any table to liwei;<br />
&nbsp; <br />
&nbsp; <br />
七、运行tmboot</p>
<p>&nbsp; 你会发现有个TMS_ORA9i启动不起来，没关系，将*RESOURCES max系列的设大点，tmshutdown,并且<br />
&nbsp; 结束掉tuxipc，重新编译config然后tmboot就ok了。<br />
&nbsp; <br />
&nbsp; client 1<br />
&nbsp; 执行就会返回数据库结果了。</p>
=========================<br />
<br />
<p>Windows下Tuxedo的安装配置-数据库补充</p>
<p>草木瓜 2006-5-28</p>
<p>一、序</p>
<p>《Windows下Tuxedo的安装配置-数据库》一文中介绍了通过XA方法连接数据库，步骤比较多，当然也可以采<br />
用另一种方法，在服务程序里面直接写入连接数据库的命令。</p>
<p>二、服务端程序</p>
<p>小作修改</p>
<p>EXEC SQL BEGIN DECLARE SECTION;<br />
VARCHAR ora_no[2];<br />
VARCHAR ora_value[10];<br />
VARCHAR ora_cn[30];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //新增<br />
EXEC SQL END DECLARE SECTION;</p>
<p>EXEC SQL INCLUDE sqlca;</p>
<p><br />
//新增以下内容，tpsvinit,tpsrdone是tuxedo默认构造和析构函数。<br />
tpsvrinit()<br />
{<br />
&nbsp;&nbsp;strcpy(ora_cn.arr,"<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#108;&#105;&#119;&#101;&#105;&#47;&#108;&#105;&#119;&#101;&#105;&#64;&#119;&#105;&#110;&#100;&#98;">liwei/liwei@windb</a>");<br />
&nbsp;&nbsp;ora_cn.len = strlen(ora_cn.arr);<br />
&nbsp;&nbsp;ora_cn.arr[ora_cn.len]='\0';<br />
&nbsp;&nbsp;EXEC SQL CONNECT :ora_cn;<br />
&nbsp;&nbsp;return(0);<br />
}<br />
tpsrvdone()<br />
{<br />
&nbsp;&nbsp;EXEC SQL COMMIT WORK RELEASE;<br />
}</p>
<p>三、配置文件</p>
<p>注释掉与XA相关项，由于客户端与服务器是同一台机器，WSL要不要无所谓。这里也注释掉。</p>
<p>*GROUPS</p>
<p>GROUP1&nbsp;LMID=lw GRPNO = 1 </p>
<p>#OPENINFO="Oracle_XA:Oracle_XA+Acc=P/liwei/liwei+SqlNet=linux+SesTm=600+MaxCur=5+LogDir=." <br />
#TMSNAME="TMS_ORA9i" TMSCOUNT=2</p>
<p><br />
*SERVERS<br />
server SRVGRP=GROUP1 SRVID=1</p>
<p>#WSL&nbsp;SRVGRP=GROUP1 &nbsp;SRVID =300 <br />
#CLOPT="-A -- -n //192.168.98.166:5898 -d/dev/tcp -m1 -M5 -x 10"</p>
<p>*SERVICES<br />
DBREAD</p>
<p>四、编译服务端的命令</p>
<p>修改如下：</p>
<p>proc server.pc include=%TUXDIR%\include<br />
buildserver -o server -f server.c -s DBREAD -v </p>
<p>如果在编译过程中出现error LNK2001: unresolved external symbol _sqlcxt类似的错误，那是因为<br />
找不到orasql9.lib文件。设置环境变量lib,加上e:\oracle\ora92\precomp\lib，修改命令：</p>
<p>buildserver -o server -f server.c -s DBREAD -v -l orasql9.lib</p>
<p>五、编译config文件，运行tmboot</p>
<p>六、补充说明</p>
<p>Tuxedo配置一般如下步骤：</p>
<p>1 设置环境变量。<br />
2 准备服务端客户端程序。<br />
3 服务端客户端编译（buildclient buildserver）。<br />
4 准备tuxedo的config文件。<br />
5 编译config文件（tmload）。<br />
6 如果config文件包含日志，必须通过tmadmin生成日志文件(crdl,crlog)，如出错须将原日志文件删除。<br />
7 启动tuxedo服务（tmboot）。</p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/277992.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-26 14:00 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/26/277992.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TUXEDO应用系统的配置 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/25/277918.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Mon, 25 May 2009 15:19:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/25/277918.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/277918.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/25/277918.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/277918.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/277918.html</trackback:ping><description><![CDATA[<div class="t_msgfont" id="message18926"><a onclick="javascript:tagshow(event, 'TUXEDO');" href="javascript:;" target="_self"><u><strong>TUXEDO</strong></u></a>应用系统的配置<br />
<br />
3.1 TUXEDO应用系统的常见配置<br />
<br />
配置文件UBBCONFIG介绍<br />
一个TUXEDO应用系统的所有资源都在一个文本文件中进行定义，该文件称为UBBCONFIG，在配置完成后，UBBCONFIG被编译成一个二进制的文件TUXCONFIG.在TUXEDO系统启动时,从该文件中读取系统的配置信息。 UBBCONFIG文件类似WINDOWS下的*.INI文件。它包括以下9大部分, 我们称之为节,RESOURCES，MACHIENS，GROUPS<br />
这三个节必须的，<a onclick="javascript:tagshow(event, '%C6%E4%CB%FB');" href="javascript:;" target="_self"><u><strong>其他</strong></u></a>的节是可选的。<br />
<br />
RESOURCES(必需): 与整个系统有关的配置信息<br />
MACHINES(必需): 一个TUXEDO应用系统可以跨越多台服务器,在该节中配置与每台服务<br />
器有关的信息<br />
GROUPS(必需): TUXEDO中的服务可被分为多个组,在该节中配置与组有关的信息<br />
SERVERS(可选): 与SERVER有关的信息在该节配置<br />
SERVICES(可选): 与SERVICES有关的信息在该节配置<br />
NETWORK(可选):与网络有关的信息在该节配置<br />
ROUTING(可选) :路由规则在该节配置<br />
NETGROUPS(可选):与网络分组有关的信息在该节配置<br />
<br />
名称解释：<br />
TUXEDO应用系统<br />
一个TUXEDO应用系统包括服务端，客户端，服务端安装在服务器上，客户端一般安装在PC机上，从开发角度看，一个TUXEDO应用系统包括服务端程序，客户端程序，一个配置文件。此外，一个TUXEDO应用系统可以部署在一台服务器上，也可以部署在多台服务器上。<br />
SERVER：<br />
服务端程序用C或COBAL编写，每一个程序文件编译成一个相应可执行文件，该可执行文件在运行时称为SERVER，它实际上就是一个进程。每个 SERVER都有一个名字，也就是该进程的名字。为与TUXEDO应用系统的服务端区分，我们在本书中，我们用SERVER表示该进程，用服务端表示 TUXEDO应用系统的服务端。<br />
SERVICE：<br />
在每个服务端程序中，主要是一个个的函数，在TUXEDO中称这些函数为SERVICE，一般<br />
也称之为服务。在该SERVICE中实现业务逻辑，在客户端中调用这些SERVICE来实现各<br />
种操作，如在前面的例子simpapp中， 服务端程序为simpserv.c，它编译成可执行文件<br />
simperv, simpserv就是一个SERVER，该SERVER包括SERVICE：TOUPPER。<br />
<br />
<br />
下面我们给出一个配置文件的例子：<br />
*RESOURCES<br />
IPCKEY 123456<br />
MASTER simple<br />
UID 0<br />
GID 0<br />
PERM 0666<br />
MAXACCESSERS 100<br />
DOMAINID simpapp<br />
MODEL SHM<br />
LDBAL Y<br />
SCANUNIT 10<br />
SANITYSCAN 12<br />
BLOCKTIME 6<br />
NOTIFY DIPIN<br />
MAXCONV 10<br />
<br />
*MACHINES<br />
"MYSYS" LMID="simple"<br />
TUXCONFIG="d:\tuxdemo\pbdemo\tuxconfig"<br />
TUXDIR="d:\tuxedo65"<br />
APPDIR="d:\tuxdemo\pbdemo"<br />
TLOGDEVICE="d:\tuxdemo\pbdemo\TLOG"<br />
TLOGNAME="TLOG"<br />
TLOGSIZE=100<br />
MAXWSCLIENTS=5<br />
CMPLIMIT="MAXLONG,MAXLONG"<br />
NETLOAD=0<br />
SPINCOUNT=0<br />
MAXACLCACHE=100<br />
<br />
*GROUPS<br />
"GROUP1" LMID="simple" GRPNO=1<br />
OPENINFO="Oracle_XA:Oracle_XA+Acc=P/scott/tiger+SesTm=600+MaxCur=5+LogDir=."<br />
TMSNAME="TMS_ORA8i"<br />
TMSCOUNT=2<br />
<br />
*SERVERS<br />
DEFAULT:<br />
CLOPT="-A"<br />
"simpserv" SRVGRP="GROUP1" SRVID=1<br />
CLOPT="-A"<br />
RQADDR="simpserv"<br />
RQPERM=0660 REPLYQ=Y RPPERM=0660 MIN=5 MAX=5 CONV=N<br />
MAXGEN=1 GRACE=86400 RESTART=N<br />
"WSL" SRVGRP="GROUP1" SRVID=1116<br />
CLOPT="-A -- -n //MYSERVER:8888 -m 2 -M 5 -x 6"<br />
*SERVICES<br />
"TOUPPER"<br />
LOAD=50 PRIO=50<br />
BUFTYPE="ALL"<br />
TRANTIME=30<br />
AUTOTRAN=N<br />
<br />
*ROUTING<br />
<br />
各节之间的包含关系：<br />
从上到下，是一对多的关系，即：一个TUXEDO系统可以跨越多台服务器，一台服务器上可以有多个GROUP，但一个GROUP只能在一台服务器上，一个GROUP中可以有多个SERVER，一个SERVER中可以有多个SERVICE。<br />
<br />
<br />
下面我们分别对每个节种具体的常见配置进行说明，NETWORK,NETGROUP,ROUTING三节及其他比较高级的配置我们在后面的内容中说明<br />
<br />
RESOURCES(必需):<br />
该节是必须的，在这里配置与整个TUXEDO应用系统有关的信息，主要包括：<br />
IPCKEY: TUXEDO使用它标识公告板及其他的IPC资源。它不能与该服务器上其他的IPC资源的ID号冲突范围：32,769-262,142<br />
MASTER: 指定该TUXEDO应用系统的MASTER服务器,在该服务器上对整个TUXEDO系<br />
统进行管理配置,可以为该MASTER服务器指定一台备份服务器,当该MASTER<br />
服务器当机时,可从BACKUP服务器上进行管理。在进行系统迁移时,也要指定<br />
BACKUP服务器。<br />
DOMAINID：该TUXEDO应用系统的唯一标识<br />
<br />
UID, GID, PERM:这三个参数控制对系统IPC资源的存取权限<br />
UID: 可对该TUXEDO应用系统进行管理的TUXEDO系统管理员的用户ID,在UNIX下<br />
就是UNIX系统的用户ID,默认为执行TMLOADCF的用户的ID。在NT下该设置<br />
没有用,要设为0。<br />
GID: 在UNIX下为UID中所指定的用户所在的组ID,在NT下该设置没有用,要设为0<br />
PERM: 指定对TUXEDO系统IPC资源的存取权限。默认值为0666,即任何人都可以对该IPC资源进行存取。<br />
<br />
MAXACCESSERS,MAXSERVERS,MAXSERVICES:这三个参数控制该TUXEDO应用系统对IPC资源的使用情况。<br />
<br />
MAXACCESSERS: 在本系统的一个节点(一台服务器)上,同时可以有多少个进程可以访问<br />
该TUXEDO系统的公告板,默认值为50,它包括本地客户端进<br />
程,SERVER进程,但不包括管理进程如:BBL,DBBL等<br />
MAXSERVERS: 在本系统中,总共可以有多少个SERVER存在,包括进行管理的SERVER,<br />
如:BBL,TMS等。默认值为50。<br />
MAXSERVICES: 在本系统中,总共可以有多少个SEVICE存在, 默认值为100。<br />
<br />
<br />
<br />
TUXEDO应用系统的部署方式：<br />
一个TUXEDO应用系统可能部署在一台服务器上或多台服务器上,也可能是部署在共用一块全局共享内存的几台服务器上,可在MODEL,OPTIONAS中配置该TUXEDO应用系统的部署模式。<br />
MODLE:<br />
SHM: 单机或多台服务器但共用一个全局共享内存<br />
MP: 多台服务器但没有共用一个全局共享内存<br />
<br />
OPTION: LAN: 是多机（MP）部署模式<br />
MIGRATE: 可对该系统进行迁移<br />
<br />
<br />
TUXEDO的管理进程BBL定时对它所在的服务器上的TUXEDO系统进行检查,检查超时的事务,超时的客户端连接等。<br />
SCANUNIT: 指定检查的最小时间单位,它单位为秒,必须是5的倍数。 默认值为10,即10<br />
秒。<br />
SANTIYSCAN: 每隔多少个SCANUNIT检查一次, SANITYSCAN*SCANUNIT不能大于<br />
300秒。默认值为12,即120秒<br />
BLOCKTIME: 指定一个消息可以阻塞多长时间,如果过了SANITYSCAN*SCANUNIT秒该<br />
消息还没有发送出去,将超时出错。 SANITYSCAN*SCANUNIT不能大于<br />
32767秒。默认值为6,即60秒。<br />
<br />
同步调用的超时说明<br />
对同步调用TPCALL,指从TPCALL()开始调用,到SERVER端的返回结果到到该客户端并写到输入缓冲区这一段时间。包括:<br />
客户端开始调用TPCALL()<br />
把数据通过网络发送到SERVER端<br />
SERVER端的处理时间<br />
SERVER端把处理结果通过网络返回给客户端<br />
客户端TPCALL()调用返回<br />
<br />
异步调用和会话方式的超时说明<br />
对异步调用和会话方式,当一个进程调用TPACALL(),TPCONNECT(),TPSEND()时,超时时间只包括如果接收队列满时,这些调用的等待时间,如在TPACALL()中,所调用的SERVICE的接收队列如果满了,那么TPACALL()可阻塞在那里,直到该队列不满,可以接收该 TPACALL()发送的请求。从TPACALL()开始阻塞到TPACALL()返回这段时间为它的超时时间。<br />
对TPGETRPLY(),TPRECV()的超时时间是指如果要接收的队列为空时,它们应该阻塞在那里等待有消息到来的时间。<br />
如:超时时间为60秒, 一点整调用TPGETRPLY()从接收的队列取消息,这时接收队列为空,如果到了一点零一分该接受队列还为空,那么TPGETRPLY()将超时出错。<br />
<br />
LDBAL： 要不要进行负载均衡,Y:要,N:不要,默认值为不要<br />
<br />
MAXCONV: 在一台服务器上最多可以有多少个会话同时存在。范围:0-32768,默认值为10<br />
<br />
NOTIFY : 设置消息的通知方式,有以下3种:默认为DIPIN<br />
IGNORE: 该TUXEDO系统中的CLIENT不接收任何消息<br />
SIGNAL: 用SIGUSR1,SIGUSR2信号通知CLIENT有消息到来，如果在非UNIX平台上设<br />
置采用该方式，那么会被自动转化为DIPIN方式。<br />
DIPIN: 当CLIENT调用ATMI函数时,顺便检查看是否有消息,如果有就发送给该<br />
CLIENT默认值为DIPIN<br />
<br />
<br />
*MACHINES(必需):<br />
该TUXEDO应用系统所包含的每台服务器都要在该节中进行配置<br />
LMID:　在TUXEDO，要为该应用系统中的每台服务器指定一个逻辑服务器名。如在上面的例子中，TUXEDO应用服务器MYSYS对应的逻辑服务器名为：simple，<br />
TUXEDO应用服务器名的查看方法：<br />
1. 在UNIX下用uname -n 查看SERVER的名字,<br />
2. 在NT,WIN2000下用ECHO %COMPUTERNAME$查看SERVER的名字<br />
注意：如果SERVER的名字太长(&gt;8)或含有&#8220;.&#8221;等可以" "引起来,如: "DEMOSERVER.COM"<br />
<br />
TUXCONFIG: 配置文件TUXCONFIG所在的路径<br />
TUXDIR: TUXEDO的安装目录<br />
APPDIR: TUXEDO应用系统所在的目录<br />
注意：TUXCONFIG，TUXDIR，APPDIR的值要与它们在环境变量中的设置的值一样<br />
ENVFILE: 该服务器上与TUXEDO有关的环境变量(如FIELDTBLS等)可以放到一个文件中,在ENVFILE中指定该文件名。<br />
TYPE: 指定该服务器的类型,当两台服务器的TYPE不一样时,在它们之间传送数据时要进行<br />
编码/解码<a onclick="javascript:tagshow(event, '%B9%A4%D7%F7');" href="javascript:;" target="_self"><u><strong>工作</strong></u></a><br />
ULOGPFX: 指定ULOG文件所在的目录及前缀。默认目录为$APPDIR,前缀为ULOG。<br />
<br />
UID,GID,PERM,MAXACCESSERS,MAXCONV这些在RESOURCES中的配置,在MACHINES中可以被重新配置,并且这些新的配置值会覆盖在RESOURCES中的配置值。<br />
<br />
<br />
*GROUPS(必需):<br />
在TUXEDO中,要对SERVER进行分组,配置GROUP主要有三个目的,<br />
1． 因为GROUP与<a onclick="javascript:tagshow(event, '%CA%FD%BE%DD%BF%E2');" href="javascript:;" target="_self"><u><strong>数据库</strong></u></a>之间是一对一的关系,在一TUXEDO应用系统中,如果有多个数据库,就要配置多个GROUP,在该GROUP中配置TUXEDO与数据库的连接参数等。<br />
2． 数据依赖路由是以GROUP为单位的。<br />
3． 进行系统迁移时也时以GROUP为单位的一个GROUP有一个名字,还有一个GROUP<br />
ID,GROUP的名字和GROUP ID在一个TUXEDO系统中必须唯一。<br />
每个GROUP要指定该GROUP所在的服务器，及它的ID号，如果该GROUP中的SERVER<br />
要与数据库或其他的资源管理器相连，那么要在OPENINFO中设置连接的参数。具体的配置我们在讲数据库编程时再说明。<br />
*SERVERS:<br />
该系统所包含的所有的SERVER都在该节进行配置。<br />
在DEFAULT中的设置对所有的SERVER起作用。<br />
SRVGRP: 该SERVER所在的GROUP<br />
SRVID: 该SERVER所对应的ID,在一个GROUP中必须唯一<br />
MIN: 该SERVER最少启动的进程数<br />
MAX: 该SERVER最多可启动的进程数<br />
<br />
RESTART,RCMD,MAXGEN,GRACE: 在TUXEDO系统了,当一个SERVER进程因某中原因死掉时,可设置它可以自动重起,这4个参数指定TUXEDO的重起信息。<br />
RESTART 该进程死掉时是否可以自动重起,默认为N(不可以)<br />
GRACE，MAXGEN：在GRACE秒内，该进程最多可以重启MAXGEN次。<br />
RCMD： 指定该SERVER重启时,要执行的一个可执行文件名<br />
注意：<br />
1． 为了使一个SERVER可以自动重起,光设置了RESTART=Y还不够,还要设置<br />
GRACE,MAXGEN才能在该SERVER死掉时,自动重起,<br />
2． 该SERVER能够被自动重起的一个前提条件时它还没有被从BULLITION BOARD中清<br />
除。<br />
例子:<br />
<a onclick="javascript:tagshow(event, 'test');" href="javascript:;" target="_self"><u><strong>test</strong></u></a> SRVGRP=GROUP1 SRVID=1 RESTART=Y GRACE=100000 MAXGEN=255<br />
该设置指定在100000秒的时间内,SERVER进程:test可以启动255次。<br />
在缺省情况下，TUXEDEO的每一个SERVER对应一个请求队列，该SERVER从该请求队列中取客户端发来的请求，并把处理的结果通过该请求队列返回给客户端，TUXEDO的SERVER可以配置成多个SERVER对应一个请求队列，即MSSQ方式，以提高响应的速度。<br />
与MSSQ有关的参数是：<br />
RQADDR：该请求队列的名字，一般设成与该SERVER的名字一样<br />
RAPERM：该请求队列的存取权限，默认为0666<br />
REPLYQ：该SERVER中的某个SERVICE调用其他的SERVICE，并有返回结果，则应设置<br />
REPLYQ=Y，即把其他SERVICE的应答放到该队列中<br />
<br />
CONV: 该SERVER是否采用会话(CONVERSATION)通讯方式,注意采用会话通讯方式的SERVICE要单独在一个SERVER中,不能与采用其他通讯方式的SERVICE在同一个SERVER中,并且该SERVER要设置CONV=Y<br />
CLOPT: 指定该SERVER的启动参数。默认为-A,即再该SERVER启动时，发布该SERVER<br />
包含的所有的SERVICE。通用的有:<br />
-e: 指定错误输出文件,默认为$APPDIR下的stderr<br />
-o: 指定标准输出文件,默认为$APPDIR下的stdout<br />
-r: 记录该SERVER中SERVICE每次调用的处理时间,并记录在错误输出文件中,然后可采用<br />
TUXEDO的性能分析工具txrpt对该SERVER进行性能分析。<br />
-A: 发布该SERVER包含的所有的SERVICE<br />
<br />
<br />
<br />
*SERVICES:<br />
在SERVICES中可针对每个SERVICE进行配置，主要有：<br />
AUTOTRAN: 调用该SERVICE时是否自动启一个全局事务。默认为N<br />
LOAD:如果采用负载均衡,指定该SERVICE的负载因子，默认为50<br />
PRIO: 指定该SERVICE的优先级，默认为50<br />
DDR: 指定数据依赖路由规则<br />
<br />
与远程客户端有关的配置：<br />
1． 在MACHINES中要配置MAXWSCLIENTS，即最多可以有多少个远程客户端同时连接<br />
到该服务器上，在上面的例子中为5个<br />
2． 在SERVERS中要配置SERVER：WSL<br />
WSL SRVGRP="GROUP1" SRVID=1116 CLOPT="-A -- -n //192.168.120.113:8888 -m 2 -M 5 -x 6"<br />
说明：<br />
-n //192.168.120.113:8888: 远程客户端通过该端口与服务器建立连接<br />
-m 2 :最少启动多少个WSH进程<br />
-M 5：:最多启动多少个WSH进程，默认值为MAXWSCLIENTS/m<br />
-x 6：每个WSH进程可同时处理多少个远程客户端<br />
3． 程客户端所在的服务器上要配置WSNADDR环境变量，它的值为-n参数的值，如在上面的配置中为：<br />
SET WSNADDR= //192.168.120.113:8888<br />
<br />
配置文件UBBCONFIG的编译:<br />
用下面的命令进行编译:<br />
tmloadcf &#8211;y UBBCONFIG文件名<br />
如:下面的命令编译名为ubbsimple的TUXEDO配置文件,生成二进制的配置文件<br />
TUXCONFIG<br />
tmloadcf &#8211;y ubbsimple<br />
<br />
有时候在改变了TUXCONFIG的目录后，执行tmloadcf，会出现类似下面的错误，可把原来的TUXCONFIG文件删除，然后再编译。<br />
D:\simpdb&gt;tmloadcf -y ubb<br />
CMDTUX_CAT:1601: ERROR: TUXCONFIG,TUXOFFSET d:\simpdb\tuxconfig 0 doesn't match first device entry on configuration d:\tuxdemo\simpdb\tuxconfig 0<br />
D:\simpdb&gt;del tuxconfig<br />
D:\simpdb&gt;tmloadcf -y ubb<br />
D:\simpdb&gt;<br />
用tmunloadcf命令可对二进制的配置文件TUXCONFIG的进行反编译<br />
如下面的命令反编译TUXCONFIG,并把结果输出到文件myubb中。通过反编译TUXCONFIG可查看该TUXEDO应用系统的一些属性默认值。<br />
Tmunloadcf &gt;myubb<br />
<br />
<br />
<br />
<br />
3.2 多机（MP）模式的配置<br />
<br />
一个TUXEDO应用系统可以部署在多台服务器上，这些服务器通过网络连接，这种部署方式称为MP方式，在这些服务器中要选择一台服务器做MASTER服务器,在该服务器上有一个DBBL进程,负责整个TUXEDO应用系统的管理工作。在每台服务器上都有一个BBL进程，它与DBBL进程进行通信，管理各自服务器上的配置。每台服务器上还有一个名为BRIDGE的进程和一个名为TLISTEN的进程，他们负责服务器之间的通讯。MP模式的结构如图：<br />
<br />
<br />
<br />
<br />
如果配置了MP方式，那么在这些服务器之间可以做负载均衡和容错，客户端可以和其中的任何一台服务器建立连接，如果该服务器上没有该客户端所要调用的服务（SERVICE），TUXEDO可以自动把请求发送到别的有该服务的机器取处理，并把结果返回个客户端，如图，SITE1上的客户端调用了 TRANSFER，当在SITE1上没有该SERVICE，在SITE3上有，<br />
那么SITE1通过BRIDGE把请求发送到SITE3上，SITE3把处理结果也通过BRIDGE发送到SITE1上，通过SITE1把结果返回给该客户端。这些对客户端时透明的。<br />
如果SITE1，STIE2，SITE3上都有TRANSFER，并且设置了负载均衡（LDBAL =Y），那么TUXEDO将根据他们的负载情况把请求发送到负载较小的服务器上。<br />
如果SITE1，SITE2，SITE3中的某一台或两台服务器出故障了，那么正常的机器仍然能够继续运行，如果时他们之间的网络连接断了，BRIDGE进程会自动进行重试，并且在这些服务器之间可以配置多个网络连接通路，当其中的一个出现故障时，会自动采用别的连接通路。<br />
所以MP方式提供了一个高可靠性的分布式应用系统。<br />
<br />
配置成MP方式的步骤：<br />
1. 这些服务器之间可以通过网络互相访问<br />
2. 在每台服务器上都正确安装了TUXEDO SERVER<br />
3. 应用已发布到这些服务器上<br />
4. 选择一台服务器做MASTER机,<br />
6. 在RESOURCES,MACHINES,NETWORK,NETGROUPS中做相应的配置<br />
<br />
RESOURCE:<br />
MODEL要设为MP<br />
OPTIONS中要有LAN<br />
<br />
MASTER服务器上的DBBL负责与其他服务器上的服务器BBL进行通讯,<br />
BBLQUERY: 每台服务器上的BBL每隔SCANUNIT*BBLQUERY秒,向MASTER机上的<br />
DBBL发送一次状态信息。默认为300秒<br />
DBBLWAIT: 如果MASTER机没有收到某台服务器发送的状态信息,它将等待<br />
SCANUNIT*DBBLWAIT秒,如果还没有收到该服务器发送的状态信息,这台服务<br />
器将被分离出去(PARTITIONED)，默认为20秒<br />
<br />
MACHIENS：<br />
在MACHINES中要配置该TUXEDO应用系统包括的每台服务器，如果该服务器时UNIX服务器，那么要配置UID，GID，UID，GID为TUXEDO用户所对应的UID，GID，在UNIX下可以用ID命令查看。<br />
<br />
<br />
NETWORK:<br />
在该节中配置服务器之间通讯的IP地址及BRIDGE进程,<br />
在一个MP方式的TUXEDO应用系统中，不同的服务器之间通过BRIDGE进程进行通讯，该BRIDGE的侦听IP地址及端口<br />
在NADDR中指定。如果是在UNIX下要指定该BRIDGE所用的网络设备，如果是在NT下则不要。<br />
<br />
在TUXEDO系统启动前，不同服务器之间的通讯是通过tlisten进程来进行的，因为这时还没有BRIDGE进程。<br />
NLSADDR为tlisten进程侦听的IP地址及端口。<br />
<br />
一个MP方式的配置例子。<br />
<br />
环境：WIN2000服务器一台，安装TUXEDO6.5, 服务器名WIN,IP地址:10.13.1.124<br />
SUN5.8服务器一台，安装TUXEDO6.5, 服务器名SUN,IP地址:12.22.32.35<br />
MASTER机为WIN2000服务器,BACKUP机为SUN。<br />
<br />
在WIN2000服务器上的UBBCONFIG的内容如下,在SUN服务器上不用编写UBBCONFIG文件。当启动时DBBL会自动<br />
把WIN2000服务器上的TUXCONFIG文件发送到SUN服务器上。<br />
<br />
*RESOURCES<br />
IPCKEY 87656<br />
MASTER site1,site2<br />
MAXACCESSERS 40<br />
MAXSERVERS 40<br />
MAXSERVICES 40<br />
MODEL MP<br />
OPTIONS LAN<br />
<br />
*MACHINES<br />
WIN LMID=site1<br />
APPDIR="d:\tuxdemo\mp"<br />
TUXCONFIG="d:\tuxdemo\mp\tuxconfig"<br />
TYPE="win2000"<br />
TUXDIR="d:\tuxedo65"<br />
<br />
SUN LMID=site2<br />
UID=1006<br />
GID=30<br />
APPDIR="/export/home/simpapp"<br />
TUXCONFIG="/export/home/simpapp/tuxconfig"<br />
TYPE="sun"<br />
TUXDIR="/export/home/tuxedo"<br />
<br />
*GROUPS<br />
GROUP1<br />
LMID=site1 GRPNO=1 ōPENINFO=NONE<br />
GROUP2<br />
LMID=site2 GRPNO=2 ōPENINFO=NONE<br />
<br />
<br />
*NETWORK<br />
site1 NADDR="//10.13.1.124:6000"<br />
NLSADDR="//10.13.1.124:6001"<br />
<br />
<br />
site2 NADDR="//12.22.32.35:6000"<br />
NLSADDR="//12.22.32.35:6001"<br />
BRIDGE="/dev/tcp"<br />
<br />
<br />
<br />
*SERVERS<br />
DEFAULT:<br />
CLOPT="-A"<br />
<br />
simpserv SRVGRP=GROUP2 SRVID=3<br />
<br />
*SERVICES<br />
TOUPPER<br />
<br />
<br />
1. 在WIN2000服务器上把该UBBCONFIG编译成TUXCONFIG<br />
tmloadcf -y ubbmp<br />
2. 在WIN2000服务器上启动tlisten进程<br />
tlisten -l //10.13.1.124:6001<br />
3. 在SUN服务器上启动tlisten进程<br />
tlisten -d /dev/tcp -l /12.22.32.35:6001<br />
4. 在WIN2000服务器上启动该TUXEDO应用系统<br />
tmboot -y<br />
TUXEDO会先启动WIN2000服务器上的进程,然后再启动SUN服务器上的进程。<br />
5. 在WIN2000服务器上执行<br />
simpcl mp test<br />
因为WIN2000服务器上没有TOUPPER服务,TUXEDO会自动调用SUN服务器上的TOUPPER服务,并把结果返回给客户端,这一切对客户端来说是透明的。</div>
 <img src ="http://www.blogjava.net/jinfeng_wang/aggbug/277918.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-25 23:19 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/25/277918.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TUXEDO监视系统运行tmadmin命令</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/25/277916.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Mon, 25 May 2009 15:17:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/25/277916.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/277916.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/25/277916.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/277916.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/277916.html</trackback:ping><description><![CDATA[<p>TUXEDO监视系统运行</p>
<p>进入linux系统，在linux系统dos输入页面中输入tmadmin</p>
<p>进入tmadmin的监管环境，输入下面的命令即可</p>
<p>启动tmadmin<br />
tmboot&nbsp; -y</p>
<p>1查看服务信息psr<br />
(1) 命令: printserver 简写 psr<br />
(2)&nbsp; psr [-m machine] [-g groupname] [-i srvid] [-q qaddress]<br />
-m machine LMID为 machine的所有服务进程<br />
-g groupname 组名为groupname的所有服务进程<br />
-I srvid SRVID为srvid的服务进程<br />
-q qaddress 消息队列为qaddress的所有SERVERS查看<a onclick="javascript:tagshow(event, 'server');" href="javascript:;" target="_self"><u><strong>server</strong></u></a>的信息<br />
(3) 结果示例:<br />
Prog Name&nbsp;&nbsp;&nbsp;&nbsp; Queue Name&nbsp;&nbsp; Grp Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ID&nbsp;&nbsp; RqDone&nbsp; Load Done&nbsp; Current Service<br />
---------&nbsp;&nbsp;&nbsp; ----------&nbsp;&nbsp;&nbsp; --------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&nbsp;&nbsp;&nbsp; ------&nbsp; ---------&nbsp; ---------------<br />
rz_Ecsb&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00004.04000&nbsp;&nbsp; APGP2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4000&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (&nbsp; IDLE )<br />
BBL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 70020&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; simple&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (&nbsp; IDLE )<br />
IFMTMS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; APGP2_TMS&nbsp;&nbsp;&nbsp; APGP2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 30001&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (&nbsp; IDLE )<br />
ftpserv32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00002.00001&nbsp; FTPGP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; 60&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3000&nbsp;&nbsp;&nbsp;&nbsp; (&nbsp; IDLE )<br />
结果说明:<br />
列号&nbsp; 描述<br />
1. 服务的可执行文件名<br />
2. 服务连接的队列名<br />
3. 组名<br />
4. 服务的数字id<br />
5. 服务已经处理的请求数(该SERVER的所有service的负载因子总和)<br />
6. 服务处理的全部请求的参数和，如果当前没有service被调用，则为IDLE<br />
2查看交易信息psc<br />
(1)&nbsp; 命令: printservice&nbsp; 简写: psc<br />
psc [-m machine] [-g groupname] [-I srvid] [-q qaddress]<br />
[-s service] [-a {0|1|2}]<br />
-s service 显示名为sevice的service信息<br />
-a {0|1|2} 显示系统的隐含的service<br />
<a onclick="javascript:tagshow(event, '%C6%E4%CB%FB');" href="javascript:;" target="_self"><u><strong>其他</strong></u></a>参数与psr命令相同<br />
(2)&nbsp; 结果示例:<br />
Service Name&nbsp;&nbsp; Routine Name&nbsp;&nbsp; Prog Name&nbsp; Grp Name&nbsp;&nbsp; ID&nbsp;&nbsp; Machine&nbsp;&nbsp; # Done&nbsp;&nbsp; Status<br />
------------&nbsp;&nbsp; ------------&nbsp;&nbsp;&nbsp;&nbsp; -------&nbsp;&nbsp;&nbsp; --------&nbsp;&nbsp; --&nbsp;&nbsp; -------&nbsp;&nbsp; ------&nbsp;&nbsp; ------<br />
416701&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rz_Ecsb&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rz_Ecsb&nbsp;&nbsp;&nbsp;&nbsp; APGP2&nbsp;&nbsp;&nbsp;&nbsp; 4000&nbsp;&nbsp;&nbsp; simple&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AVAIL<br />
416601&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rz_Ecsb&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rz_Ecsb&nbsp;&nbsp;&nbsp;&nbsp; APGP2&nbsp;&nbsp;&nbsp;&nbsp; 4000&nbsp;&nbsp;&nbsp; simple&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AVAIL<br />
416501&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rz_Ecsb&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rz_Ecsb&nbsp;&nbsp;&nbsp;&nbsp; APGP2&nbsp;&nbsp;&nbsp;&nbsp; 4000&nbsp;&nbsp;&nbsp; simple&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AVAIL<br />
(3)&nbsp; 结果说明:<br />
列号&nbsp;&nbsp;&nbsp; 描述<br />
1. Service Name :服务名<br />
2. Routine Name :函数名(采用TUXEDO服务的别名机制,一个函数可以对应多个服务名)<br />
3. Prog Name :service 所在的SERVER名<br />
4. Grp Name :组名<br />
5. ID ：server的ID号<br />
6. Machine :server所在的LMID<br />
7. # Done ：service被调用的次数<br />
8. Status :service的状态。AVAIL表示可用<br />
3查看队列信息pq<br />
(1) 命令: printqueue 简写:pq [PADRESS]<br />
(2) 结果示例:<br />
pq 00004.05062<br />
Prog Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Queue Name&nbsp; # Serve&nbsp; Wk Queued&nbsp; # Queued&nbsp; Ave. Len&nbsp;&nbsp;&nbsp; Machine<br />
---------&nbsp;&nbsp;&nbsp; ------------&nbsp; ------&nbsp;&nbsp; ---------&nbsp; --------&nbsp; --------&nbsp;&nbsp;&nbsp; -------<br />
CCS_GEDAIPC_50 00004.05062&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; simple<br />
(3) 结果说明:<br />
列号&nbsp;&nbsp;&nbsp; 描述<br />
1. Prog Name :队列连接的服务的可执行文件名<br />
2. Queue Name :字符队列名，是RQADDR参数或一个随机值<br />
3. #Serve :连接的服务数<br />
4. Wk Queued :当前队列的所有请求的参数和<br />
5. #Queued :实际请求数<br />
6. Ave.Len :平均队列长度<br />
7. Machine :队列所在机器的LMID<br />
4查看客户端信息pclt<br />
(1) 命令: printclient 简写:pclt<br />
-m machine 显示LMID号为machine上的客户端连接<br />
-u username 显示用户名为username 的客户端连接<br />
-c ctlname 显示用户进程为ctlname的客户端连接<br />
(2) 结果示例:<br />
&nbsp; LMID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; User Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Client Name&nbsp;&nbsp;&nbsp; Time&nbsp;&nbsp;&nbsp; Status&nbsp; Bgn/Cmmt/Abrt<br />
---------- --------------- --------------- -------- ------- -------------<br />
simple&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ccsmis&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WSH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 17:42:47&nbsp;&nbsp; IDLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0/0/0<br />
simple&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ccsmis&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tmadmin&nbsp;&nbsp;&nbsp; 0:44:28&nbsp;&nbsp;&nbsp; IDLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0/0/0<br />
(3) 结果说明:<br />
列号&nbsp; 描述<br />
1. 已经登录的客户端机器的LMID<br />
2. 用户名，由tpinit()提供的<br />
3. 客户端名，由tpinit()提供的<br />
4. 客户端连接后经过的时间<br />
5. 客户端状态<br />
6. IDLE——表示客户端目前没有任何交易在<a onclick="javascript:tagshow(event, '%B9%A4%D7%F7');" href="javascript:;" target="_self"><u><strong>工作</strong></u></a><br />
7. IDLET——表示客户端启动了一个交易<br />
8. BUSY——表示客户端在工作中<br />
9. BUSYT——表示客户端正在交易控制下工作<br />
10. 启动/提交/中断的交易数</p>
<p>5查看部分统计信息bbs<br />
(4) 命令: bbstats&nbsp; 简写：bbs<br />
&gt; bbs<br />
Current Bulletin Board Status:<br />
Current number of servers: 335<br />
Current number of services: 2324<br />
Current number of request queues: 27<br />
Current number of server groups: 11<br />
Current number of interfaces: 0</p>
<p>6观察某个节点的进程信息default<br />
(5) 命令：default &#8211;m<br />
&gt; default -m SITE13</p>
<p>SITE13&gt; psr<br />
Prog Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Queue Name&nbsp; Grp Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ID RqDone Load Done Current Service<br />
---------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------&nbsp; --------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -- ------ --------- ---------------<br />
BBL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 30004.00000 SITE13&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; 22827&nbsp;&nbsp; 1141350 ..ADJUNCTBB<br />
BRIDGE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 836437&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SITE13&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp; IDLE )<br />
GWADM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00021.00019 BGWGRP1+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 19&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp; IDLE )<br />
GWTDOMAIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00021.00020 BGWGRP1+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20 123826&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br />
GWADM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00022.00021 BGWGRP2+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 21&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp; IDLE )<br />
GWTDOMAIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00022.00022 BGWGRP2+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 (&nbsp; IDLE )<br />
GWADM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00025.00027 GWGRP1_+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 27&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 200 (&nbsp; IDLE )</p>
<p>7查看消息发送状态pnw<br />
(6) 命令：printnetwork 简写 pnw<br />
&gt; pnw SITE12</p>
<p>SITE12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Connected To:&nbsp; msgs sent&nbsp;&nbsp;&nbsp; msgs received<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SITE14&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 61904&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 62319<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SITE13&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 61890&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 62288<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SITE11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 15972&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 13564<br />
8退出管理模式q<br />
(7) 命令: quit&nbsp; 简写：q<br />
</p>
 <img src ="http://www.blogjava.net/jinfeng_wang/aggbug/277916.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-25 23:17 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/25/277916.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>tuxedo bankapp</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/23/277621.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Sat, 23 May 2009 15:33:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/23/277621.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/277621.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/23/277621.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/277621.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/277621.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Normal0falsefalsefalseMicrosoftInternetExplorer4st1\:*{behavior:url(#ieooui) }/* Style Definitions */table.MsoNormalTable{mso-style-name:標準の...&nbsp;&nbsp;<a href='http://www.blogjava.net/jinfeng_wang/archive/2009/05/23/277621.html'>阅读全文</a><img src ="http://www.blogjava.net/jinfeng_wang/aggbug/277621.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-23 23:33 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/23/277621.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>rhel and ubuntu 三网卡设置</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/23/277458.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Sat, 23 May 2009 02:48:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/23/277458.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/277458.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/23/277458.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/277458.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/277458.html</trackback:ping><description><![CDATA[<h1 class="title">如何在 Windows XP 中配置 Internet 连接共享</h1>
<h1 class="title"><a href="http://support.microsoft.com/kb/306126/zh-cn">http://support.microsoft.com/kb/306126/zh-cn</a> </h1>
<p>通过上面的方法， 在家里就可以让自己的虚拟机使用DHCP的方式上网。<br />
当然，这是针对VMWARE里面使用Bridge的网卡，分配IP。<br />
<br />
为了让自己的机器到任何地方，1）虚拟机都可以使用网络，2）主机和虚拟机可以互相访问，<br />
就给各个虚拟机配置了三个网卡，分别是Bridge，NAT，HostOnly的方式。<br />
<br />
配置完之后，RHEL重启， 就可以立即找到新的网卡，并且DHCP配置完成，主机可以通过NAT<br />
网卡Telnet虚拟机。<br />
<br />
可Ubuntu就没这么幸运了。<br />
ifconfig命令，只看到eth0可以用。<br />
ifconfig -a命令，可以看到三块网卡都在。<br />
看来得手工在/etc/network/interface中，手工添加DHCP的相关配置。参考如下：<br />
auto eth0<br />
iface eth0 inet dhcp</p>
<p>iface eth1 inet dhcp<br />
auto eth1<br />
iface eth2 inet dhcp<br />
auto eth2<br />
<br />
重启网络，好不容易三块网卡都爬上来了。</p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/277458.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-23 10:48 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/23/277458.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Tuxedo : Using C++'s  Constructors and Destructors</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/22/277360.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 22 May 2009 08:20:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/22/277360.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/277360.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/22/277360.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/277360.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/277360.html</trackback:ping><description><![CDATA[<a name="1390181">&nbsp;
<p>C++ constructors are called to initialize class objects when those objects are created, and destructors are invoked when class objects are destroyed. For automatic (that is, local, non-static) variables that contain constructors and destructors, the constructor is called when the variable comes into scope and the destructor is called when the variable goes out of scope. However,<span style="color: red">&nbsp;when you call the <a href="file:///C:/beadocs/beatux81doc/pgc/pgserv.htm#1167965">tpreturn()</a><a name="1390181"> or <a href="file:///C:/beadocs/beatux81doc/pgc/pgserv.htm#1168061">tpforward()</a><a name="1390181"> function, the compiler performs a non-local goto (using <font class="code">longjmp</font>(3)) such that destructors for automatic variables are not called. </span></a>To avoid this problem, write the application so that you call <font class="code">tpreturn()</font> or <font class="code">tpforward()</font> from the service routine directly (instead of from any functions that are called from the service routine). In addition, one of the following should be true:</p>
</a>
<ul type="square"><a name="1390184">
    <p>
    <li type="square">The <span style="color: red">service routine should not have any automatic variables with destructors </span>(they should be declared and used in a function called by the service routine).</a> <a name="1390185">
    <p>&nbsp;</p>
    <li type="square"><span style="color: red">Automatic variables should be declared and used in a nested scope (contained within curly brackets {}) </span>in such a way that the scope ends before calling the <a href="file:///C:/beadocs/beatux81doc/pgc/pgserv.htm#1167965">tpreturn()</a> or <a href="file:///C:/beadocs/beatux81doc/pgc/pgserv.htm#1168061">tpforward()</a> function. </a></li>
</ul>
 <img src ="http://www.blogjava.net/jinfeng_wang/aggbug/277360.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-22 16:20 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/22/277360.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>tuxedo  config the Tuxedo Administration Console</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/22/277251.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 22 May 2009 03:02:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/22/277251.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/277251.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/22/277251.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/277251.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/277251.html</trackback:ping><description><![CDATA[1) start&nbsp; tuxwsvr
<p class="table"><font class="code">tuxwsvr -l //</font><em><font class="code">machine</font></em><font class="code">:</font><em><font class="code"><span style="color: red"><em><font class="code">port1</font></em><font class="code"> </font></span></font></em><font class="code">-i $TUXDIR/udataobj/tuxwsvr.ini<br />
Example : tuxwsvr -l //192.168.122.128:4003 -i $TUXDIR/udataobj/tuxwsvr.ini<br />
<br />
2)Starting wlisten<br />
&nbsp;&nbsp;&nbsp; a) edit the file &nbsp;<font class="code">$TUXDIR/udataobj/webgui/webgui.ini</font> , and config the <font class="code">NADDR</font> parameter as follows:<br />
&nbsp;&nbsp;&nbsp;&nbsp;Example&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font class="code">NADDR=//192.168.122.128:<span style="color: red">6060</span></font><br />
&nbsp;&nbsp;&nbsp; b) run cmd : <font class="code">wlisten</font></font></p>
<br />
<br />
3) open your client browser and enter into the administrator console page:<br />
&nbsp;&nbsp; Example: http://192.168.122.128:4003/webguitop.html <br />
<br />
<br />
Reference :&nbsp; http://forums.oracle.com/forums/thread.jspa?threadID=883934&amp;tstart=30<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; http://peoplesofttipster.com/2007/04/02/get-up-and-running-with-the-tuxedo-administration-console/
 <img src ="http://www.blogjava.net/jinfeng_wang/aggbug/277251.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-22 11:02 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/22/277251.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ubuntu: network can not work</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/22/277236.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 22 May 2009 01:50:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/22/277236.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/277236.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/22/277236.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/277236.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/277236.html</trackback:ping><description><![CDATA[<p>虚拟机拷贝移动之后，&nbsp;ifup <span class="hilite1">eth0</span>出现如下错误：<br />
<span class="hilite1">eth0</span>: <span class="hilite2">ERROR</span> while getting interface flags: <span class="hilite3">No</span> <span class="hilite4">such</span> <span class="hilite5">device</span> <br />
SIOCSIFADDR: <span class="hilite3">No</span> <span class="hilite4">such</span> <span class="hilite5">device</span> <br />
<span class="hilite1">eth0</span>: <span class="hilite2">ERROR</span> while getting interface flags: <span class="hilite3">No</span> <span class="hilite4">such</span> <span class="hilite5">device</span> <br />
SIOCSIFNETMASK: <span class="hilite3">No</span> <span class="hilite4">such</span> <span class="hilite5">device</span> <br />
SIOCSIFBRDADDR: <span class="hilite3">No</span> <span class="hilite4">such</span> <span class="hilite5">device</span> <br />
<span class="hilite1">eth0</span>: <span class="hilite2">ERROR</span> while getting interface flags: <span class="hilite3">No</span> <span class="hilite4">such</span> <span class="hilite5">device</span> <br />
<span class="hilite1">eth0</span>: <span class="hilite2">ERROR</span> while getting interface flags: <span class="hilite3">No</span> <span class="hilite4">such</span> <span class="hilite5">device</span> <br />
Failed to bring up <span class="hilite1">eth0</span>.<br />
</p>
<strong>原因</strong><br />
&nbsp;&nbsp;新克隆的虚拟机镜像的网卡mac地址已经变更。打开虚拟机的.vmx文件，ethernet0.generatedAddress项记录了该虚拟机的mac地址。 <br />
<br />
<br />
<p>解决办法：</p>
<p>rm /etc/udev/rules.d/70-persistent-net.rules</p>
<p>通过 vi <span>/etc/network/interfaces 修改ip地址</span><br />
然后 /etc/init.d/networking restart</p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/277236.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-22 09:50 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/22/277236.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>rhel install svn server</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/19/271622.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 19 May 2009 15:49:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/19/271622.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/271622.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/19/271622.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/271622.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/271622.html</trackback:ping><description><![CDATA[1) install svn server:&nbsp; yum install subversion<br />
<br />
2) install&nbsp; dav mod :&nbsp; <code>yum install mod_dav_svn</code><br />
<br />
3) create the svn dir<br />
$ mkdir /home/svn<br />
$ cd /home/svn<br />
$ mkdir myproject<br />
$ chown -R root:subversion myproject<br />
$ chmod -R g+rws myproject<br />
<br />
4) create the respository<br />
<span class="mw-headline">$ <span style="color: red">svnadmin create /home/svn/myproject</span><br />
</span><br />
5) config the WebDAV protocals for web access the svn server:<br />
&nbsp;&nbsp;&nbsp; a)edit the&nbsp;file of /etc/httpd/conf.d/subversion.conf", and add the following code:
<pre>&lt;Location <span style="color: red">/svn</span>&gt;DAV svn
SVNPath <span style="color: red">/home/svn <br />
</span>AuthType Basic
AuthName "myproject subversion repository"
AuthUserFile /etc/subversion/passwd
&lt;LimitExcept GET PROPFIND OPTIONS REPORT&gt;
Require valid-user
&lt;/LimitExcept&gt;
&lt;/Location&gt;
</pre>
<pre>   b)restart apache :   <span class="mw-headline">/etc/init.d/apache2 restart</span></pre>
<pre>   c)create the pass file, and add the user:</pre>
<pre>      touch  /etc/subversion/passwd <br />
htpasswd -c /etc/subversion/passwd user_name</pre>
<pre>6) now you can use your client browser/tool to access your svn server ： <span style="color: red">http://localhost/svn/myproject<br />
</span>
</pre>
<pre>&nbsp;</pre>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/271622.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-19 23:49 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/19/271622.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>locale的设定及其LANG、LC_ALL、LANGUAGE环境变量的区别   zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/18/271410.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Mon, 18 May 2009 14:48:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/18/271410.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/271410.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/18/271410.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/271410.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/271410.html</trackback:ping><description><![CDATA[<a href="http://www.baidu.com/s?wd=etc+sysconfig+i18n&amp;lm=0&amp;si=&amp;rn=10&amp;ie=gb2312&amp;ct=0&amp;cl=3&amp;f=1&amp;rsp=1">/etc/sysconfig/i18n</a> <br />
<font face="宋体"><br />
locale 是国际化与本土化过程中的一个非常重要的概念，个人认为，对于中文用户来说，通常会涉及到的国际化或者本土化，大致包含三个方面：看中文，写中文，与 window中文系统的兼容和通信。从实际经验上看来，locale的设定与看中文关系不大，但是与写中文，及window分区的挂载方式有很密切的关系。本人认为就像一个纯英文的Windows能够浏览中文，日文或者意大利文网页一样，你不需要设定locale就可以看中文。那么，为什么要设定 locale呢？什么时候会用到locale呢？ <br />
<br />
Tags: locale 设定 原因 解释 <br />
<br />
一、为什么要设定locale 正如前面我所讲的，设定locale与你能否浏览中文的网页没有直接的关系，即便你把locale设置成 en_US.ISO-8859-1这样一个标准的英文locale你照样可以浏览中文的网页，只要你的系统里面有相应的字符集（这个都不一定需要）和合适的字体（如simsun），浏览器就可以把网页翻译成中文给你看。具体的过程是网络把网页传送到你的机器上之后，浏览器会判断相应的编码的字符集，根据网页采用的字符集，去字体库里面找合适的字体，然后由文字渲染工具把相应的文字在屏幕上显示出来。 <br />
<br />
在下文本人会偶尔把字符集比喻成密码本，个人觉得对于一些东西比较容易理解，假如你不习惯的话，把全文copy到任何文本编辑器，用字符集替换密码本即可。 <br />
<br />
那有时候网页显示乱码或者都是方框是怎么回事呢？个人认为，显示乱码是因为设定的字符集不对(或者没有相应的字符集)，例如网页是用UTF-8编码的，你非要用GB2312去看，而系统根据GB2312去找字体，然后在屏幕上显示，当然是一堆的乱码，也就是说你用一个错误的密码本去翻译发给你的电报，当然内容那叫一个乱；至于有些时候浏览的网页能显示一部分汉字，但有很多的地方是方框，能够显示汉字说明浏览器已经正确的判断出了网页的编码，并在字体库里面找到了相应的文字，但是并不是每个字体库都包含某个字符集全部的字体的缘故，有些时候会显示不完全，找一个比较全的支持较多字符集的字体就可以了。 <br />
<br />
既然我能够浏览中文网页，那为什么我还要设定locale呢？ <br />
<br />
其实你有没有想过这么一个问题，为什么gentoo官方论坛上中文论坛的网页是用UTF-8编码的（虽然大家一直强烈建议用GB2312编码），但是新浪网就是用GB2312编码的呢？而Xorg的官方网页竟然是ISO-8859-15编码的，我没有设定这个locale怎么一样的能浏览呢？这个问题就像是你有所有的密码本，不论某个网站是用什么字符集编码的，你都可以用你手里的密码本把他们翻译过来，但问题是虽然你能浏览中文网页，但是在整个操作系统里面流动的还是英文字符。所以，就像你能听懂英语，也能听懂中文。 最根本的问题是：你不可以写中文。 <br />
<br />
当你决定要写什么东西的时候，首先要决定的一件事情是用那种语言，对于计算机来说就是你要是用哪一种字符集，你就必须告诉你的linux系统，你想用那一本密码本去写你想要写的东西。知道为什么需要用GB2312字符集去浏览新浪了吧，因为新浪的网页是用GB2312写的。 <br />
<br />
为了让你的Linux能够输入中文，就需要把系统的locale设定成中文的(严格说来是locale中的语言类别LC_CTYPE )，例如 zh_CN.GB2312、zh_CN.GB18030或者zh_CN.UTF-8。很多人都不明白这些古里古怪的表达方式。这个外星表达式规定了什么东西呢？这个问题稍后详述，现在只需要知道，这是locale的表达方式就可以了。 <br />
<br />
<font face="宋体">locale这个单词中文翻译成地区或者地域，其实这个单词包含的意义要宽泛很多。Locale是根据计算机用户所使用的语言，所在国家或者地区，以及当地的文化传统所定义的一个软件运行时的语言环境。<br />
<br />
[oracle@game ~]$ <font color="#0000ff">locale</font><br />
<font color="#ff0000">LANG=en_US.UTF-8</font><br />
<font color="#ff0000">LC_CTYPE="en_US.UTF-8"</font><br />
LC_NUMERIC="en_US.UTF-8"<br />
LC_TIME="en_US.UTF-8"<br />
LC_COLLATE="en_US.UTF-8"<br />
LC_MONETARY="en_US.UTF-8"<br />
LC_MESSAGES="en_US.UTF-8"<br />
LC_PAPER="en_US.UTF-8"<br />
LC_NAME="en_US.UTF-8"<br />
LC_ADDRESS="en_US.UTF-8"<br />
LC_TELEPHONE="en_US.UTF-8"<br />
LC_MEASUREMENT="en_US.UTF-8"<br />
LC_IDENTIFICATION="en_US.UTF-8"<br />
<font color="#ff0000">LC_ALL=en_US.UTF-8</font><br />
[oracle@game ~]$ <br />
<br />
</font><font face="宋体">locale把按照所涉及到的文化传统的各个方面分成12个大类，这12个大类分别是： <br />
<br />
<font color="#ff0000">1、语言符号及其分类(LC_CTYPE) </font><br />
2、数字(LC_NUMERIC) <br />
3、比较和排序习惯(LC_COLLATE) <br />
4、时间显示格式(LC_TIME) <br />
5、货币单位(LC_MONETARY) <br />
6、信息主要是提示信息,错误信息,状态信息,标题,标签,按钮和菜单等(LC_MESSAGES) <br />
7、姓名书写方式(LC_NAME) <br />
8、地址书写方式(LC_ADDRESS) <br />
9、电话号码书写方式(LC_TELEPHONE) <br />
10、度量衡表达方式 (LC_MEASUREMENT) <br />
11、默认纸张尺寸大小(LC_PAPER) <br />
12、对locale自身包含信息的概述(LC_IDENTIFICATION)。</font><br />
<font face="宋体"><br />
所以说，locale就是某一个地域内的人们的语言习惯和文化传统和生活习惯。一个地区的locale就是根据这几大类的习惯定义的，这些locale定义文件放在/usr/share/i18n/locales目录下面，例如en_US, zh_CN and de_DE@euro都是locale的定义文件，这些文件都是用文本格式书写的，你可以用写字板打开，看看里边的内容，当然出了有限的注释以外，大部分东西可能你都看不懂，因为是用的Unicode的字符索引方式。<br />
<br />
[oracle@game ~]$ <font color="#0000ff">cd /usr/share/i18n/locales</font><br />
[oracle@game locales]$ <font color="#0000ff">ls</font><br />
aa_DJ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ar_YE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; el_GR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_ES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fr_CH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iso14651_t1 ne_NP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; so_ET&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translit_hangul<br />
aa_ER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; az_AZ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; el_GR@euro es_ES@euro fr_FR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; it_CH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nl_BE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; so_KE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translit_narrow<br />
aa_ER@saaho be_BY&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; en_AU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_GT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fr_FR@euro&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; it_IT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nl_BE@euro so_SO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translit_neutral<br />
aa_ET&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bg_BG&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; en_BW&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_HN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fr_LU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; it_IT@euro&nbsp;&nbsp; nl_NL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sq_AL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translit_small<br />
af_ZA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bn_BD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; en_CA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_MX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fr_LU@euro&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iw_IL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nl_NL@euro sr_CS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translit_wide<br />
am_ET&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bn_IN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; en_DK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_NI&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ga_IE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ja_JP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nn_NO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; st_ZA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tr_TR<br />
an_ES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; br_FR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; en_GB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_PA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ga_IE@euro&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ka_GE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; no_NO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sv_FI&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tt_RU<br />
ar_AE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; br_FR@euro en_HK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_PE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gd_GB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kk_KZ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oc_FR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sv_FI@euro&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; uk_UA<br />
ar_BH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bs_BA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; en_IE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_PR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gez_ER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kl_GL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; om_ET&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sv_SE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ur_PK<br />
ar_DZ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byn_ER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; en_IE@euro es_PY&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gez_ER@abegede kn_IN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; om_KE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ta_IN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; uz_UZ<br />
ar_EG&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ca_ES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; en_IN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_SV&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gez_ET&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ko_KR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pa_IN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; te_IN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; uz_UZ@cyrillic<br />
ar_IN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ca_ES@euro en_NZ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_US&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gez_ET@abegede kw_GB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pl_PL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tg_TJ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vi_VN<br />
ar_IQ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cs_CZ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; en_PH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_UY&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gl_ES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lg_UG&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; POSIX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; th_TH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wa_BE<br />
ar_JO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cy_GB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; en_SG&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_VE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gl_ES@euro&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lo_LA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pt_BR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ti_ER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wa_BE@euro<br />
ar_KW&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; da_DK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; en_US&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; et_EE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gu_IN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lt_LT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pt_PT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ti_ET&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wal_ET<br />
ar_LB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de_AT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; en_ZA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eu_ES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gv_GB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lv_LV&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pt_PT@euro tig_ER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xh_ZA<br />
ar_LY&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de_AT@euro en_ZW&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eu_ES@euro he_IL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mi_NZ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ro_RO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tl_PH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yi_US<br />
ar_MA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de_BE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_AR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fa_IR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hi_IN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mk_MK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ru_RU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translit_circle&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; zh_CN<br />
ar_OM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de_BE@euro es_BO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fi_FI&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hr_HR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ml_IN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ru_UA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translit_cjk_compat&nbsp;&nbsp;&nbsp; zh_HK<br />
ar_QA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de_CH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_CL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fi_FI@euro hu_HU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mn_MN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; se_NO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translit_cjk_variants zh_SG<br />
ar_SA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de_DE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_CO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fo_FO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hy_AM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mr_IN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sid_ET&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translit_combining&nbsp;&nbsp;&nbsp;&nbsp; zh_TW<br />
ar_SD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de_DE@euro es_CR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fr_BE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i18n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ms_MY&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sk_SK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translit_compat&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; zu_ZA<br />
ar_SY&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de_LU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; es_DO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fr_BE@euro id_ID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mt_MT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sl_SI&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translit_font<br />
ar_TN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de_LU@euro es_EC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fr_CA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; is_IS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nb_NO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; so_DJ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translit_fraction<br />
[oracle@game locales]$ <br />
<br />
对于de_DE@euro的一点说明，@后边是修正项，也就是说你可以看到两个德国的locale：/usr/share/i18n/locales /de_DE@euro和/usr/share/i18n/locales/de_DE。打开这两个locale定义，你就会知道它们的差别在于 de_DE@euro使用的是欧洲的排序、比较和缩进习惯，而de_DE用的是德国的标准习惯。 <br />
<br />
上面我们说到了zh_CN.GB18030的前半部分，后半部分是什么呢？大部分Linux用户都知道是系统采用的字符集。 </font><br />
<br />
<font face="宋体">zh_CN.GB2312到底是在说什么？ <font color="#ff0000">Locale是软件在运行时的语言环境, 它包括语言(Language), 地域 (Territory) 和字符集(Codeset)。一个locale的书写格式为: 语言[_地域[.字符集]]。</font>所以说呢，locale总是和一定的字符集相联系的。下面举几个例子： <br />
<br />
1、我说中文，身处中华人民共和国，使用国标2312字符集来表达字符。zh_CN.GB2312＝中文_中华人民共和国＋国标2312字符集。 <br />
<br />
2、我说中文，身处中华人民共和国，使用国标18030字符集来表达字符。zh_CN.GB18030＝中文_中华人民共和国＋国标18030字符集。 <br />
<br />
3、我说中文，身处中华人民共和国台湾省，使用国标Big5字符集来表达字符。zh_TW.BIG5=中文_台湾.大五码字符集 <br />
<br />
4、我说英文，身处大不列颠，使用ISO-8859-1字符集来表达字符。 en_GB.ISO-8859-1=英文_大不列颠.ISO-8859-1字符集 <br />
<br />
5、我说德语，身处德国，使用UTF-8字符集，习惯了欧洲风格。</font><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#100;&#101;&#95;&#68;&#69;&#46;&#85;&#84;&#70;&#45;&#56;&#64;&#101;&#117;&#114;&#111;&#61;&#37;&#69;&#53;&#37;&#66;&#69;&#37;&#66;&#55;&#37;&#69;&#56;&#37;&#65;&#70;&#37;&#65;&#68;&#95;&#37;&#69;&#53;&#37;&#66;&#69;&#37;&#66;&#55;&#37;&#69;&#53;&#37;&#57;&#66;&#37;&#66;&#68;&#46;&#85;&#84;&#70;"><font face="宋体" color="#000000">de_DE.UTF-8@euro＝德语_德国.UTF</font></a><font face="宋体">-8字符集@按照欧洲习惯加以修正，注意不是</font><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#100;&#101;&#95;&#68;&#69;&#64;&#101;&#117;&#114;&#111;&#46;&#85;&#84;&#70;"><font face="宋体" color="#000000">de_DE@euro.UTF</font></a><font face="宋体">-8，<font color="#ff0000">所以完全的locale表达方式是 [语言[_地域][.字符集] [@修正值]。 </font><br />
<br />
</font><font face="宋体"><font color="#ff0000">其中，与中文输入关系最密切的就是LC_CTYPE，LC_CTYPE规定了系统内有效的字符以及这些字符的分类</font>，诸如什么是大写字母，小写字母，大小写转换，标点符号、可打印字符和其他的字符属性等方面。而locale定义zh_CN中最最重要的一项就是定义了汉字(Class&#8220;hanzi&#8221;)这一个大类，当然也是用Unicode描述的，这就让中文字符在Linux系统中成为合法的有效字符，而且不论它们是用什么字符集编码的。 <br />
<br />
</font><font face="宋体"><strong>怎样设定locale呢？ </strong><br />
<br />
设定locale就是设定12大类的locale分类属性，即12个LC_*。除了这12个变量可以设定以外，<font color="#ff0000">为了简便起见，还有两个变量：LC_ALL和LANG。<strong>它们之间有一个优先级的关系：LC_ALL &gt; LC_* &gt;LANG</strong></font><strong>。</strong>可以这么说，LC_ALL是最上级设定或者强制设定，而LANG是默认设定值。 <br />
<br />
1、如果你设定了LC_ALL＝zh_CN.UTF-8，那么不管LC_*和LANG设定成什么值，它们都会被强制服从LC_ALL的设定，成为 zh_CN.UTF-8。 <br />
<br />
2、假如你设定了LANG＝zh_CN.UTF-8，而其他的LC_*=en_US.UTF-8，并且没有设定LC_ALL的话，那么系统的locale设定以LC_*=en_US.UTF-8。 <br />
<br />
3、假如你设定了LANG＝zh_CN.UTF-8，而其他的LC_*，和LC_ALL均未设定的话，系统会将LC_*设定成默认值，也就是LANG的值zh_CN.UTF-8。 <br />
<br />
4、假如你设定了LANG＝zh_CN.UTF-8，而其他的LC_CTYPE=en_US.UTF-8，其他的LC_*，和LC_ALL均未设定的话，那么系统的locale设定将是：LC_CTYPE=en_US.UTF-8，其余的 LC_COLLATE，LC_MESSAGES等等均会采用默认值，也就是 LANG的值，也就是LC_COLLATE＝LC_MESSAGES＝&#8230;&#8230;＝ LC_PAPER＝LANG＝zh_CN.UTF-8。 <br />
<br />
<strong>所以，locale是这样设定的： </strong><br />
<br />
<font color="#ff0000">1、如果你需要一个纯中文的系统的话，设定LC_ALL= zh_CN.XXXX，或者LANG=zh_CN.XXXX都可以</font>，当然你可以两个都设定，但正如上面所讲，LC_ALL的值将覆盖所有其他的locale设定，不要作无用功。 <br />
<br />
<strong><font color="#ff0000">2、如果你只想要一个可以输入中文的环境，而保持菜单、标题，系统信息等等为英文界面，那么只需要设定 LC_CTYPE＝zh_CN.XXXX，LANG=en_US.XXXX就可以了。</font></strong>这样LC_CTYPE＝zh_CN.XXXX，而LC_COLLATE＝LC_MESSAGES＝&#8230;&#8230;＝ LC_PAPER＝LANG＝en_US.XXXX。 <br />
<br />
3、假如你高兴的话，可以把12个LC_*一一设定成你需要的值，打造一个古灵精怪的系统： LC_CTYPE＝zh_CN.GBK/GBK(使用中文编码内码GBK字符集)； LC_NUMERIC=en_GB.ISO-8859-1(使用大不列颠的数字系统) LC_MEASUREMEN=de_DE@euro.ISO-8859-15(德国的度量衡使用ISO-8859-15字符集) 罗马的地址书写方式，美国的纸张设定&#8230;&#8230;。估计没人这么干吧。 <br />
<br />
<font color="#ff0000">4、假如你什么也不做的话，也就是LC_ALL，LANG和LC_*均不指定特定值的话，系统将采用POSIX作为lcoale，也就是C locale。</font></font><br />
<br />
<strong>另外LANG和LANGUAGE有什么区别呢？</strong><br />
<br />
LANG - Specifies the default locale for all unset locale variables<br />
LANGUAGE - Most programs use this for the language of its interface<br />
LANGUAGE是设置应用程序的界面语言。而LANG是优先级很低的一个变量，它指定所有与locale有关的变量的默认值，<br />
<br />
</font>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/271410.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-18 22:48 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/18/271410.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用 lsof 查找打开的文件</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/17/271159.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Sun, 17 May 2009 14:01:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/17/271159.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/271159.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/17/271159.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/271159.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/271159.html</trackback:ping><description><![CDATA[http://www.ibm.com/developerworks/cn/aix/library/au-lsof.html<br />
<br />
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr valign="top">
            <td width="100%">
            <h1>使用 lsof 查找打开的文件</h1>
            <img class="display-img" alt="" src="//www.ibm.com/i/c.gif" width="1" height="6" /></td>
            <td class="no-print" width="192"><img alt="developerWorks" src="/developerworks/i/dw.gif" width="192" height="18" /></td>
        </tr>
    </tbody>
</table>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr valign="top">
            <td width="10"><img alt="" src="//www.ibm.com/i/c.gif" width="10" height="1" /></td>
            <td width="100%">
            <table class="no-print" border="0" cellspacing="0" cellpadding="0" width="160" align="right">
                <tbody>
                    <tr>
                        <td width="10"><img alt="" src="//www.ibm.com/i/c.gif" width="10" height="1" /></td>
                        <td>
                        <table border="0" cellspacing="0" cellpadding="0" width="150">
                            <tbody>
                                <tr>
                                    <td class="v14-header-1-small">文档选项</td>
                                </tr>
                            </tbody>
                        </table>
                        <table class="v14-gray-table-border" border="0" cellspacing="0" cellpadding="0">
                            <tbody>
                                <tr>
                                    <td class="no-padding" width="150">
                                    <table border="0" cellspacing="0" cellpadding="0" width="143">
                                        <script language="JavaScript" type="text/javascript">
<!-- document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt="" /></td><td width="16"><img alt="将打印机的版面设置成横向打印模式" height="16" src="//www.ibm.com/i/v14/icons/printer.gif" width="16" vspace="3"  /></td><td width="122"><p><strong><a class="smallplainlink" href="javascript:print()">打印本页</a></strong></p></td></tr>');
//-->
</script>
                                        <tbody>
                                            <tr valign="top">
                                                <td width="8"><img alt="" src="//www.ibm.com/i/c.gif" width="8" height="1" /></td>
                                                <td width="16"><img alt="将打印机的版面设置成横向打印模式" vspace="3" src="//www.ibm.com/i/v14/icons/printer.gif" width="16" height="16" /></td>
                                                <td width="122">
                                                <p><strong><a class="smallplainlink" href="javascript:print()" cmimpressionsent="1">打印本页</a></strong></p>
                                                </td>
                                            </tr>
                                            <noscript></noscript>
                                            <form name="email" action="https://www.ibm.com/developerworks/secure/email-it.jsp" cm1="1">
                                                <input value="通过查看打开的文件，能够了解更多关于系统的信息。了解应用程序打开了哪些文件或者哪个应用程序打开了特定的文件，作为系统管理员，这将使得您能够作出更好的决策。例如，您不应该卸载具有打开文件的文件系统。使用 lsof，您可以检查打开的文件，并根据需要在卸载之前中止相应的进程。同样地，如果您发现了一个未知的文件，那么可以找出到底是哪个应用程序打开了这个文件。" type="hidden" name="body" cM3 cm1="1" cm2="0" /><input value="使用 lsof 查找打开的文件" type="hidden" name="subject" cM3 cm1="1" cm2="1" /><input value="cn" type="hidden" name="lang" cM3 cm1="1" cm2="2" /> <script language="JavaScript" type="text/javascript">
<!-- document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt="" /></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送"  /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><strong>将此页作为电子邮件发送</strong></a></p></td></tr>');
//-->
</script>
                                                <tr valign="top">
                                                    <td width="8"><img alt="" src="//www.ibm.com/i/c.gif" width="8" height="1" /></td>
                                                    <td width="16"><img alt="将此页作为电子邮件发送" vspace="3" src="//www.ibm.com/i/v14/icons/em.gif" width="16" height="16" /></td>
                                                    <td width="122">
                                                    <p><a class="smallplainlink" href="javascript:document.email.submit();" cmimpressionsent="1"><strong>将此页作为电子邮件发送</strong></a></p>
                                                    </td>
                                                </tr>
                                                <noscript></noscript>
                                            </form>
                                        </tbody>
                                    </table>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        <!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- this content will be automatically generated across all content areas --><!--end RESERVED FOR FUTURE USE INCLUDE FILES--><br />
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>级别： 中级</p>
            <p><a href="#author" cmimpressionsent="1">Sean A. Walberg</a> (<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#101;&#97;&#110;&#64;&#101;&#114;&#116;&#119;&#46;&#99;&#111;&#109;&#63;&#115;&#117;&#98;&#106;&#101;&#99;&#116;&#61;&#20351;&#29992;&#32;&#108;&#115;&#111;&#102;&#32;&#26597;&#25214;&#25171;&#24320;&#30340;&#25991;&#20214;" cmimpressionsent="1">sean@ertw.com</a>), 高级网络工程师<br />
            </p>
            <p>2006 年 8 月 21 日</p>
            <blockquote>通过查看打开的文件，了解更多关于系统的信息。了解应用程序打开了哪些文件或者哪个应用程序打开了特定的文件，作为系统管理员，这将使得您能够作出更好的决策。例如，您不应该卸载具有打开文件的文件系统。使用 <code>lsof</code>，您可以检查打开的文件，并根据需要在卸载之前中止相应的进程。同样地，如果您发现了一个未知的文件，那么可以找出到底是哪个应用程序打开了这个文件。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
            <p>在 UNIX&#174; 环境中，文件无处不在，这便产生了一句格言：&#8220;任何事物都是文件&#8221;。通过文件不仅仅可以访问常规数据，通常还可以访问网络连接和硬件。在有些情况下，当您使用 <code>ls</code> 请求目录清单时，将出现相应的条目。在其他情况下，如传输控制协议 (TCP) 和用户数据报协议 (UDP) 套接字，不存在相应的目录清单。但是在后台为该应用程序分配了一个<em>文件描述符</em>，无论这个文件的本质如何，该文件描述符为应用程序与基础操作系统之间的交互提供了通用接口。</p>
            <p>因为应用程序打开文件的描述符列表提供了大量关于这个应用程序本身的信息，所以能够查看这个列表将是很有帮助的。完成这项任务的实用程序称为 <code>lsof</code>，它对应于&#8220;list open files&#8221;（列出打开的文件）。几乎在每个 UNIX 版本中都有这个实用程序，但奇怪的是，大多数供应商并没有将其包含在操作系统的初始安装中。要获取更多关于 <code>lsof</code> 的信息，请参见<a href="#resources" cmimpressionsent="1">参考资料</a>部分。</p>
            <p><a name="N10065"><span class="atitle">lsof 简介</span></a></p>
            <p>只需输入 <code>lsof</code> 就可以生成大量的信息，如<a href="#listing1" cmimpressionsent="1">清单 1</a> 所示。因为 <code>lsof</code> 需要访问核心内存和各种文件，所以必须以 root 用户的身份运行它才能够充分地发挥其功能。</p>
            <br />
            <a name="listing1"><strong>清单 1. lsof 的示例输出</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="572">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">bash-3.00# lsof
                        COMMAND    PID   USER   FD   TYPE        DEVICE SIZE/OFF      NODE NAME
                        sched        0   root  cwd   VDIR         136,8     1024         2 /
                        init         1   root  cwd   VDIR         136,8     1024         2 /
                        init         1   root  txt   VREG         136,8    49016      1655 /sbin/init
                        init         1   root  txt   VREG         136,8    51084      3185 /lib/libuutil.so.1
                        vi        2013   root    3u  VREG         136,8        0      8501 /var/tmp/ExXDaO7d
                        ...
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>每行显示一个打开的文件，除非另外指定，否则将显示所有进程打开的所有文件。<code>Command</code>、<code>PID</code> 和 <code>User</code> 列分别表示进程的名称、进程标识符 (PID) 和所有者名称。<code>Device</code>、<code>SIZE/OFF</code>、<code>Node</code> 和 <code>Name</code> 列涉及到文件本身的信息，分别表示指定磁盘的名称、文件的大小、索引节点（文件在磁盘上的标识）和该文件的确切名称。根据 UNIX 版本的不同，可能将文件的大小报告为应用程序在文件中进行读取的当前位置（偏移量）。<a href="#listing1" cmimpressionsent="1">清单 1</a> 来自一台可以报告该信息的 Sun Solaris 10 计算机，而 Linux&#174; 没有这个功能。</p>
            <p><code>FD</code> 和 <code>Type</code> 列的含义最为模糊，它们提供了关于文件如何使用的更多信息。<code>FD</code> 列表示文件描述符，应用程序通过文件描述符识别该文件。<code>Type</code> 列提供了关于文件格式的更多描述。我们来具体研究一下文件描述符列，<a href="#listing1" cmimpressionsent="1">清单 1</a> 中出现了三种不同的值。<code>cwd</code> 值表示应用程序的当前工作目录，这是该应用程序启动的目录，除非它本身对这个目录进行更改。<code>txt</code> 类型的文件是程序代码，如应用程序二进制文件本身或共享库，再比如本示例的列表中显示的 <code>init</code> 程序。最后，数值表示应用程序的文件描述符，这是打开该文件时返回的一个整数。在<a href="#listing1" cmimpressionsent="1">清单 1</a> 输出的最后一行中，您可以看到用户正在使用 <code>vi</code> 编辑 /var/tmp/ExXDaO7d，其文件描述符为 3。<code>u</code> 表示该文件被打开并处于读取/写入模式，而不是只读 (<code>r</code>) 或只写 (<code>w</code>) 模式。有一点不是很重要但却很有帮助，初始打开每个应用程序时，都具有三个文件描述符，从 0 到 2，分别表示标准输入、输出和错误流。正因为如此，大多数应用程序所打开的文件的 FD 都是从 3 开始。 </p>
            <p>与 <code>FD</code> 列相比，<code>Type</code> 列则比较直观。根据具体操作系统的不同，您会发现将文件和目录称为 <code>REG</code> 和 <code>DIR</code>（在 Solaris 中，称为 <code>VREG</code> 和 <code>VDIR</code>）。其他可能的取值为 <code>CHR</code> 和 <code>BLK</code>，分别表示字符和块设备；或者 <code>UNIX</code>、<code>FIFO</code> 和 <code>IPv4</code>，分别表示 UNIX 域套接字、先进先出 (FIFO) 队列和网际协议 (IP) 套接字。</p>
            <p><a name="diversion"><span class="atitle">转到 /proc 目录</span></a></p>
            <p>尽管与使用 <code>lsof</code> 没有什么直接的关系，但对 /proc 目录进行简要的介绍是有必要的。/proc 是一个目录，其中包含了反映内核和进程树的各种文件。这些文件和目录并不存在于磁盘中，因此当您对这些文件进行读取和写入时，实际上是在从操作系统本身获取相关信息。大多数与 <code>lsof</code> 相关的信息都存储于以进程的 PID 命名的目录中，所以 /proc/1234 中包含的是 PID 为 1234 的进程的信息。</p>
            <p>在 /proc 目录的每个进程目录中存在着各种文件，它们可以使得应用程序简单地了解进程的内存空间、文件描述符列表、指向磁盘上的文件的符号链接和其他系统信息。<code>lsof</code> 实用程序使用该信息和其他关于内核内部状态的信息来产生其输出。稍后我将把 <code>lsof</code> 的输出与 /proc 目录中的信息联系起来。</p>
            <p><a name="N1012C"><span class="atitle">常见用法</span></a></p>
            <p>前面，我向您介绍了如何简单地运行不带任何参数的 <code>lsof</code>，以便显示关于每个进程所打开的文件的信息。本文余下的部分将重点关注如何使用 <code>lsof</code> 来显示所需的信息以及如何正确地对其进行解释。</p>
            <p><a name="N1013E"><span class="smalltitle">查找应用程序打开的文件</span></a></p>
            <p><code>lsof</code> 常见的用法是查找应用程序打开的文件的名称和数目。您可能想尝试找出某个特定应用程序将日志数据记录到何处，或者正在跟踪某个问题。例如，UNIX 限制了进程能够打开文件的数目。通常这个数值很大，所以不会产生问题，并且在需要时，应用程序可以请求更大的值（直到某个上限）。如果您怀疑应用程序耗尽了文件描述符，那么可以使用 <code>lsof</code> 统计打开的文件数目，以进行验证。</p>
            <p>要指定单个进程，可以使用 <code>-p</code> 参数，后面加上该进程的 PID。因为这样做不仅会返回该应用程序所打开的文件，还会返回共享库和代码，所以通常需要对输出进行筛选。要完成此任务，可以使用 <code>-d</code> 标志根据 <code>FD</code> 列进行筛选，使用 <code>-a</code> 标志表示两个参数都必须满足 (AND)。如果没有 <code>-a</code> 标志，缺省的情况是显示匹配任何一个参数 (OR) 的文件。<a href="#listing2" cmimpressionsent="1">清单 2</a> 显示了 <code>sendmail</code> 进程打开的文件，并使用 txt 对这些文件进行筛选。</p>
            <br />
            <a name="listing2"><strong>清单 2. 带有 PID 筛选器并进行 txt 文件描述符筛选的 lsof 输出</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="572">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">sh-3.00# lsof -a -p 605 -d ^txt
                        COMMAND  PID USER   FD   TYPE  DEVICE SIZE/OFF     NODE NAME
                        sendmail 605 root  cwd   VDIR  136,8     1024    23554 /var/spool/mqueue
                        sendmail 605 root    0r  VCHR  13,2            6815752 /devices/pseudo/mm@0:null
                        sendmail 605 root    1w  VCHR  13,2            6815752 /devices/pseudo/mm@0:null
                        sendmail 605 root    2w  VCHR  13,2            6815752 /devices/pseudo/mm@0:null
                        sendmail 605 root    3r  DOOR             0t0       58
                        /var/run/name_service_door(door to nscd[81]) (FA:-&gt;0x30002b156c0)
                        sendmail 605 root    4w  VCHR  21,0           11010052
                        /devices/pseudo/log@0:conslog-&gt;LOG
                        sendmail 605 root    5u  IPv4 0x300010ea640      0t0      TCP *:smtp (LISTEN)
                        sendmail 605 root    6u  IPv6 0x3000431c180      0t0      TCP *:smtp (LISTEN)
                        sendmail 605 root    7u  IPv4 0x300046d39c0      0t0      TCP *:submission (LISTEN)
                        sendmail 605 root    8wW VREG         281,3       32  8778600 /var/run/sendmail.pid
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p><a href="#listing2" cmimpressionsent="1">清单 2</a> 为 <code>lsof</code> 指定了三个参数。第一个是 <code>-a</code>，它表示当所有的参数都为真时，才显示这个文件。第二个参数是 <code>-p 605</code>，它限制仅输出 PID 为 605 的进程，可以通过 <code>ps</code> 命令获取这个信息。最后一个参数 <code>-d ^txt</code>，它表示筛选出其中 txt 类型的记录（脱字符号 [^] 表示排除）。</p>
            <p><a href="#listing2" cmimpressionsent="1">清单 2</a> 的输出提供了关于进程行为的信息。如 <code>cwd</code> 行所示，该应用程序的工作目录为 /var/spool/mqueue。文件描述符 0、1 和 2 分配给了 /dev/null（Solaris 大量使用符号链接，所以这里显示了相应的伪设备）。FD 3 是一个 Solaris 门（高速远程过程调用 (RPC) 接口），以只读模式打开。FD 4 中的内容比较有趣，因为它是一个字符设备的只读句柄，实质上是 /dev/log。从这个文件中，您可以收集该应用程序向 UNIX syslog 守护进程进行的记录，所以 /etc/syslog.conf 规定了日志文件的位置。</p>
            <p>作为一个网络应用程序，<code>sendmail</code> 对网络端口进行监听。文件描述符 5、6 和 7 可以告诉您，该应用程序正以 IPv4 和 IPv6 模式监听简单邮件传输协议 (SMTP) 端口，并以 IPv4 模式监听提交端口。最后一个文件描述符是只写的，并且指向 /var/run/sendmail.pid。<code>FD</code> 列中的大写 <code>W</code> 表示该应用程序具有对整个文件的写锁。该文件用于确保每次只能打开一个应用程序实例。</p>
            <p><a name="N101AA"><span class="smalltitle">查找打开某个文件的应用程序</span></a></p>
            <p>在其他情况下，您有一个文件或目录，并且需要知道哪个应用程序控制了该文件（打开了该文件）。<a href="#listing2" cmimpressionsent="1">清单 2</a> 显示了由 <code>sendmail</code> 进程打开了 /var/run/sendmail.pid。如果您不知道这个信息，那么在给定文件名的情况下，<code>lsof</code> 可以提供该信息。<a href="#listing3" cmimpressionsent="1">清单 3</a> 显示了相应的输出。</p>
            <br />
            <a name="listing3"><strong>清单 3. 要求 lsof 显示关于某个文件的信息</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="572">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">bash-3.00# lsof /var/run/sendmail.pid
                        COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF    NODE NAME
                        sendmail 605 root    8wW VREG  281,3       32 8778600 /var/run/sendmail.pid
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>正如输出所示，进程 <code>sendmail</code>（PID 为 605）控制了文件 /var/run/sendmail.pid，并且通过排它锁打开该文件以便进行写入。如果出于某种原因，您需要删除这个文件，那么正确的做法是中止该进程，而不是直接删除这个文件。否则，这个守护进程下次可能无法正常启动，或者可能稍后会启动另一个实例，从而导致争用。</p>
            <p>有时您只知道在文件系统的某处打开了文件。在卸载文件系统时，如果该文件系统中有任何打开的文件，那么操作将会失败。通过指定装入点的名称，您可以使用 <code>lsof</code> 显示一个文件系统中所有打开的文件。<a href="#listing4" cmimpressionsent="1">清单 4</a> 显示了如何尝试卸载 /export/home，然后使用 <code>lsof</code> 找出谁在使用该文件系统。</p>
            <br />
            <a name="listing4"><strong>清单 4. 使用 lsof 找出谁在使用文件系统</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="572">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">bash-3.00# umount /export/home
                        umount: /export/home busy
                        bash-3.00# lsof /export/home
                        COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
                        bash    1943 root  cwd   VDIR  136,7     1024    4 /export/home/sean
                        bash    2970 sean  cwd   VDIR  136,7     1024    4 /export/home/sean
                        ct      3030 sean  cwd   VDIR  136,7     1024    4 /export/home/sean
                        ct      3030 sean    1w  VREG  136,7        0   25 /export/home/sean/output
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>在这个示例中，用户 sean 正在其 home 目录中进行一些操作。有两个 <code>bash</code>（一种 Shell）实例正在运行，并且当前目录设置为 sean 的 home 目录。还有一个名为 <code>ct</code> 的应用程序正运行于相同的目录，并且其标准输出（文件描述符 1）重定向到一个名为 output 的文件。要成功地卸载 /export/home，应该在通知用户以确保情况正常之后，中止这些进程。</p>
            <p>这个示例说明了应用程序的当前工作目录非常重要，因为它仍保持着文件资源，并且可以防止文件系统被卸载。这就是为什么大部分守护进程（后台进程）将它们的目录更改为根目录、或服务特定的目录（如 <code>sendmail</code> 示例中的 /var/spool/mqueue）的原因，以避免该守护进程阻止卸载不相关的文件系统。如果 <code>sendmail</code> 从 /export/home/sean 目录启动，并且没有将其目录更改为 /var/spool/mqueue，那么在卸载 /export/home 前必须中止它。</p>
            <p>如果您对非装入点目录中打开的文件感兴趣，那么必须通过 <code>+d</code> 或 <code>+D</code> 指定该目录的名称，具体使用其中的哪一个标志取决于您需要递归到子目录（<code>+D</code>）或者不需要递归到子目录（<code>+d</code>）。例如，要查看 /export/home/sean 中所有打开的文件，可以使用 <code>lsof +D /export/home/sean</code>。在前面的示例中，相关的目录是一个装入点，而这里与前面的示例存在细微的差别，并且限制了 <code>lsof</code> 和内核之间的交互。这还会引起潜在的问题，即 <code>lsof /export/home</code> 与 <code>lsof /export/home/</code>（请注意尾部的斜杠）有所区别。第一种方式可以正常工作，因为它指向了装入点。第二种方式不会生成任何输出，因为它指向了目录。如果您在 Shell 中使用 Tab 键自动完成命令，那么可能碰到这个问题，其中会帮助您添加结尾的斜杠。在这种情况下，您可以删除这个斜杠或者使用 <code>+D</code> 指定目录。前者是首选的方法，因为与指定任意的目录相比，其执行速度更快。</p>
            <p><a name="N10229"><span class="atitle">不常见的用法</span></a></p>
            <p>在前面的部分中，我们研究了 <code>lsof</code> 的基本用法，即显示打开的文件和控制它们的进程之间的关系。当您想对系统进行一些烦琐的操作，而又不希望破坏别人重要的文档时，这种方法很有帮助。您还可以使用相同的方法执行一些高难度的 UNIX 操作。</p>
            <p><a name="N10237"><span class="smalltitle">恢复删除的文件</span></a></p>
            <p>当 UNIX 计算机受到入侵时，常见的情况是日志文件被删除，以掩盖攻击者的踪迹。管理错误也可能导致意外删除重要的文件，比如在清理旧日志时，意外地删除了数据库的活动事务日志。有时可以恢复这些文件，并且 <code>lsof</code> 可以为您提供帮助。</p>
            <p>当进程打开了某个文件时，只要该进程保持打开该文件，即使将其删除，它依然存在于磁盘中。这意味着，进程并不知道文件已经被删除，它仍然可以向打开该文件时提供给它的文件描述符进行读取和写入。除了该进程之外，这个文件是不可见的，因为已经删除了其相应的目录条目。</p>
            <p>前面曾在<a href="#diversion" cmimpressionsent="1">转到 /proc 目录</a>部分中说过，通过在适当的目录中进行查找，您可以访问进程的文件描述符。在随后的内容中，您看到了 <code>lsof</code> 可以显示进程的文件描述符和相关的文件名。您能明白我的意思吗？</p>
            <p>但愿它真的这么简单！当您向 <code>lsof</code> 传递文件名时，比如在 <code>lsof /file/I/deleted</code> 中，它首先使用 <code>stat()</code> 系统调用获得有关该文件的信息，不幸的是，这个文件已经被删除。在不同的操作系统中，<code>lsof</code> 可能可以从核心内存中捕获该文件的名称。<a href="#listing5" cmimpressionsent="1">清单 5</a> 显示了一个 Linux 系统，其中意外地删除了 Apache 日志，我正使用 <code>grep</code> 工具查找是否有人打开了该文件。</p>
            <br />
            <a name="listing5"><strong>清单 5. 在 Linux 中使用 lsof 查找删除的文件</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="572">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode"># lsof | grep error_log
                        httpd      2452     root    2w      REG       33,2      499    3090660
                        /var/log/httpd/error_log (deleted)
                        httpd      2452     root    7w      REG       33,2      499    3090660
                        /var/log/httpd/error_log (deleted)
                        ... more httpd processes ...
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>在这个示例中，您可以看到 PID 2452 打开文件的文件描述符为 2（标准错误）和 7。因此，可以在 /proc/2452/fd/7 中查看相应的信息，如<a href="#listing6" cmimpressionsent="1">清单 6</a> 所示。</p>
            <br />
            <a name="listing6"><strong>清单 6. 通过 /proc 查找删除的文件</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="572">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode"># cat /proc/2452/fd/7
                        [Sun Apr 30 04:02:48 2006] [notice] Digest: generating secret for digest authentication
                        [Sun Apr 30 04:02:48 2006] [notice] Digest: done
                        [Sun Apr 30 04:02:48 2006] [notice] LDAP: Built with OpenLDAP LDAP SDK
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>Linux 的优点在于，它保存了文件的名称，甚至可以告诉我们它已经被删除。在遭到破坏的系统中查找相关内容时，这是非常有用的内容，因为攻击者通常会删除日志以隐藏他们的踪迹。Solaris 并不提供这些信息。然而，我们知道 <code>httpd</code> 守护进程使用了 error_log 文件，所以可以使用 <code>ps</code> 命令找到这个 PID，然后可以查看这个守护进程打开的所有文件。</p>
            <br />
            <a name="listing7"><strong>清单 7. 在 Solaris 中查找删除的文件</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="572">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode"># lsof -a -p 8663 -d ^txt
                        COMMAND  PID   USER   FD   TYPE        DEVICE SIZE/OFF    NODE NAME
                        httpd   8663 nobody  cwd   VDIR         136,8     1024       2 /
                        httpd   8663 nobody    0r  VCHR          13,2          6815752 /devices/pseudo/mm@0:null
                        httpd   8663 nobody    1w  VCHR          13,2          6815752 /devices/pseudo/mm@0:null
                        httpd   8663 nobody    2w  VREG         136,8      185  145465 / (/dev/dsk/c0t0d0s0)
                        httpd   8663 nobody    4r  DOOR                    0t0      58 /var/run/name_service_door
                        (door to nscd[81]) (FA:-&gt;0x30002b156c0)
                        httpd   8663 nobody   15w  VREG         136,8      185  145465 / (/dev/dsk/c0t0d0s0)
                        httpd   8663 nobody   16u  IPv4 0x300046d27c0      0t0     TCP *:80 (LISTEN)
                        httpd   8663 nobody   17w  VREG         136,8        0  145466
                        /var/apache/logs/access_log
                        httpd   8663 nobody   18w  VREG         281,3        0 9518013 /var/run (swap)
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>我使用 <code>-a</code> 和 <code>-d</code> 参数对输出进行筛选，以排除代码程序段，因为我知道需要查找的是哪些文件。<code>Name</code> 列显示出，其中的两个文件（FD 2 和 15）使用磁盘名代替了文件名，并且它们的类型为 <code>VREG</code>（常规文件）。在 Solaris 中，删除的文件将显示文件所在的磁盘的名称。通过这个线索，就可以知道该 FD 指向一个删除的文件。实际上，查看 <code>/proc/8663/fd/15</code> 就可以得到所要查找的数据。</p>
            <p>如果可以通过文件描述符查看相应的数据，那么您就可以使用 I/O 重定向将其复制到文件中，如 <code>cat /proc/8663/fd/15 &gt; /tmp/error_log</code> 。此时，您可以中止该守护进程（这将删除 FD，从而删除相应的文件），将这个临时文件复制到所需的位置，然后重新启动该守护进程。</p>
            <p>对于许多应用程序，尤其是日志文件和数据库，这种恢复删除文件的方法非常有用。正如您所看到的，有些操作系统（以及不同版本的 <code>lsof</code>）比其他的系统更容易查找相应的数据。</p>
            <p><a name="N102C0"><span class="smalltitle">查找网络连接</span></a></p>
            <p>网络连接也是文件，这意味着可以使用 <code>lsof</code> 获得关于它们的信息。您曾在<a href="#listing2" cmimpressionsent="1">清单 2</a> 中看到过这样的示例。该示例假设您已经知道 PID，但是有时候并非如此。如果您只知道相应的端口，那么可以使用 <code>-i</code> 参数利用套接字信息进行搜索。<a href="#listing8" cmimpressionsent="1">清单 8</a> 显示了对 TCP 端口 25 的搜索。</p>
            <br />
            <a name="listing8"><strong>清单 8. 查找监听端口 25 的进程</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="572">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode"># lsof -i :25
                        COMMAND  PID USER   FD   TYPE        DEVICE SIZE/OFF NODE NAME
                        sendmail 605 root    5u  IPv4 0x300010ea640      0t0  TCP *:smtp (LISTEN)
                        sendmail 605 root    6u  IPv6 0x3000431c180      0t0  TCP *:smtp (LISTEN)
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>需要以 <code>protocol:@ip:port</code> 的形式向 <code>lsof</code> 实用程序传递相关信息，其中的 protocol 为 TCP 或 UDP（可以使用 4 或 6 作为前缀，表示 IP 的版本），IP 为可解析的名称或 IP 地址，而 port 为数字或表示该服务的名称（来自 /etc/services）。需要一个或多个元素（端口、IP、协议）。在<a href="#listing8" cmimpressionsent="1">清单 8</a> 中，<code>:25</code> 表示端口 25。输出显示，进程 605 正在使用 IPv6 和 IPv4 监听端口 25。如果您对 IPv4 不感兴趣，那么可以将筛选器改为 <code>6:25</code>，以表示监听端口 25 的 IPv6 套接字，或者直接使用 <code>6</code> 表示所有的 IPv6 连接。</p>
            <p>除了显示出这些守护进程正在监听的对象，<code>lsof</code> 还可以发现发生的连接，同样是使用 <code>-i</code> 参数。<a href="#listing9" cmimpressionsent="1">清单 9</a> 显示了搜索与 192.168.1.10 之间的所有连接。</p>
            <br />
            <a name="listing9"><strong>清单 9. 搜索活动的连接</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="572">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode"># lsof -i @192.168.1.10
                        COMMAND  PID USER   FD   TYPE        DEVICE  SIZE/OFF NODE NAME
                        sshd    1934 root    6u  IPv6 0x300046d21c0 0t1303608  TCP sun:ssh-&gt;linux:40379
                        (ESTABLISHED)
                        sshd    1937 root    4u  IPv6 0x300046d21c0 0t1303608  TCP sun:ssh-&gt;linux:40379
                        (ESTABLISHED)
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>在这个示例中，<code>sun</code> 和 <code>linux</code> 之间有两个 IPv6 连接。对其进行更仔细的研究可以看出，这些连接来自于两个不同的进程，但它们却是相同的，这是因为两台主机是相同的，并且端口也是相同的（ssh 和 40379）。这是由于进入主进程的连接分叉出一个处理程序，并将该套接字传递给它。您还可以看到，名为 <code>sun</code> 的计算机正在使用端口 22 (ssh)，而 <code>linux</code> 具有端口 40379。这表示，<code>sun</code> 是该连接的接收者，因为它关联于该服务的已知端口。40379 是源或临时端口，并且仅对这个连接有意义。</p>
            <p>因为，至少在 UNIX 中，套接字是另一类文件，所以 <code>lsof</code> 可以获得关于这些连接的详细信息，并找出谁对它们负责。</p>
            <p><a name="N10334"><span class="atitle">结束语</span></a></p>
            <p>UNIX 大量使用了文件。作为系统管理员，<code>lsof</code> 允许您对核心内存进行查看，以找出系统当前如何使用这些文件。<code>lsof</code> 最简单的用法可以告诉您哪些进程打开了哪些文件，以及哪些文件由哪些进程打开。在收集关于应用程序工作情况的信息时，或在进行某些可能损坏数据的操作前确保文件未被使用时，这一点特别重要<code>lsof</code> 更高级的用法可以帮助您查找删除的文件，并获得关于网络连接的信息。这是一个功能强大的工具，它几乎可以用于任何地方。</p>
            <br />
            <br />
            <p><a name="resources"><span class="atitle">参考资料 </span></a></p>
            <strong>学习</strong><br />
            <ul>
                <li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www.ibm.com/developerworks/aix/library/au-lsof.html?S_TACT=105AGX52&amp;S_CMP=cn-a-aix" target="_blank" cmimpressionsent="1">英文原文</a> 。<br />
                <br />
                <li><a href="http://www.ouah.org/RevEng/x47.htm#AEN70" cmimpressionsent="1">Introduction to reverse engineering software</a>：这个站点使用 lsof 和其他的 UNIX 工具，以找出关于给定二进制文件的所有信息。<br />
                <br />
                <li><a href="http://www.netadmintools.com/html/lsof.man.html" cmimpressionsent="1">lsof 的 man 页面</a>：如果您发现控制台版本的内容不大容易读懂，那么这个页面将提供非常有用的内容。<br />
                <br />
                <li><a href="http://www.ibm.com/developerworks/cn/aix/" cmimpressionsent="1">AIX and UNIX</a>：想了解更多内容吗？developerWorks 的 AIX and UNIX 专区提供数百篇关于 AIX 和 UNIX 的文章以及入门级、中级和高级教程，将让您打开眼界。<br />
                <br />
                <li><a href="http://www.ibm.com/developerworks/cn/offers/techbriefings/" cmimpressionsent="1">developerWorks 技术事件与网络广播</a>：跟踪最新的 developerWorks 技术事件与网络广播。<br />
                <br />
                <li><a href="http://www.ibm.com/developerworks/podcast/?S_TACT=105AGX52&amp;S_CMP=cn-a-aix" cmimpressionsent="1">技术讲座</a>：收听技术讲座并保持与 IBM 技术专家同步。</li>
            </ul>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/271159.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-17 22:01 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/17/271159.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入理解软件包的配置、编译与安装</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/17/271157.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Sun, 17 May 2009 13:59:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/17/271157.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/271157.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/17/271157.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/271157.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/271157.html</trackback:ping><description><![CDATA[http://lamp.linux.gov.cn/Linux/inside_config_compile_install.html<br />
<br />
<br />
<h1>深入理解软件包的配置、编译与安装</h1>
<h2 id="auth_name">作者：<strong><a href="http://lamp.linux.gov.cn/jinbuguo_florilegium.html">金步国</a></strong></h2>
<hr />
<h3>版权声明</h3>
<p>本文作者是一位自由软件爱好者，所以本文虽然不是软件，但是本着 GPL 的精神发布。任何人都可以自由使用、转载、复制和再分发，但必须保留作者署名，亦不得对声明中的任何条款作任何形式的修改，也不得附加任何其它条件。您可以自由链接、下载、传播此文档，但前提是必须保证全文完整转载，包括完整的版权信息和作译者声明。</p>
<h3>其他作品</h3>
<p>本文作者十分愿意与他人共享劳动成果，如果你对我的其他翻译作品或者技术文章有兴趣，可以在如下位置查看现有作品的列表：</p>
<ul>
    <li><a href="http://lamp.linux.gov.cn/jinbuguo_florilegium.html">金步国作品列表</a></li>
</ul>
<h3>BUG报告，切磋与探讨</h3>
<p>由于作者水平有限，因此不能保证作品内容准确无误，请在阅读中自行鉴别。如果你发现了作品中的错误，请您来信指出，哪怕是错别字也好，任何提高作品质量的建议我都将虚心接纳。如果你愿意就作品中的相关内容与我进行进一步切磋与探讨，也欢迎你与我联系。联系方式：MSN: csfrank122@hotmail.com</p>
<hr />
<h2>前言</h2>
<p>从源代码安装过软件的朋友一定对 ./configure &amp;&amp; make &amp;&amp; make install 安装三步曲非常熟悉了。然而究竟这个过程中的每一步幕后都发生了些什么呢？本文将带领你一探究竟。深入理解这个过程将有助于你在LFS的基础上玩出自己的花样来。不过需要说明的是本文对 Makefile 和 make 的讲解是相当近视和粗浅的，但是对于理解安装过程来说足够了。</p>
<h2>概述</h2>
<p>用一句话来解释这个过程就是：</p>
<p>根据源码包中 Makefile.in 文件的指示，configure 脚本检查当前的系统环境和配置选项，在当前目录中生成 Makefile 文件(还有其它本文无需关心的文件)，然后 make 程序就按照当前目录中的 Makefile 文件的指示将源代码编译为二进制文件，最后将这些二进制文件移动(即安装)到指定的地方(仍然按照 Makefile 文件的指示)。</p>
<p>由此可见 Makefile 文件是幕后的核心。要深入理解安装过程，必须首先对 Makefile 文件有充分的了解。本文将首先讲述 Makefile 与 make ，然后再讲述 configure 脚本。并且在讲述这两部分内容时，提供了尽可能详细的、可以运用于实践的参考资料。</p>
<h2>Makefile 与 make</h2>
<p>用一句话来概括Makefile 与 make 的关系就是：<br />
Makefile 包含了所有的规则和目标，而 make 则是为了完成目标而去解释 Makefile 规则的工具。</p>
<h3>make 语法</h3>
<p>首先看看 make 的命令行语法：</p>
<p><code>make [options] [targets] [VAR=VALUE]...</code></p>
<p>[options]是命令行选项，可以用 make --help 命令查看全部，[VAR=VALUE]是在命令行上指定环境变量，这两个大家都很熟悉，将在稍后详细讲解。而[targets]是什么呢？字面的意思是"目标"，也就是希望本次 make 命令所完成的任务。凭经验猜测，这个[targets]大概可以用"ckeck","install"之类(也就是常见的测试和安装命令)。但是它到底是个啥玩意儿？不带任何"目标"的 make 命令是什么意思？为什么在安装 LFS 工具链中的 Perl-5.8.8 软件包时会出现"make perl utilities"这样怪异的命令？要回答这些问题必须首先理解 Makefile 文件中的"规则"。 </p>
<h3>Makefile 规则</h3>
<p>Makefile 规则包含了文件之间的依赖关系和更新此规则目标所需要的命令。</p>
<p>一个简单的 Makefile 规则是这样写的：</p>
<pre>TARGET : PREREQUISITES
COMMAND
</pre>
<dl>
<dt>TARGET</dt>
<dd>规则的目标。也就是可以被 make 使用的"目标"。有些目标可以没有依赖而只有动作(命令行)，比如"clean"，通常仅仅定义一系列删除中间文件的命令。同样，有些目标可以没有动作而只有依赖，比如"all"，通常仅仅用作"终极目标"。</dd>
<dt>PREREQUISITES</dt>
<dd>规则的依赖。通常一个目标依赖于一个或者多个文件。</dd>
<dt>COMMAND</dt>
<dd>规则的命令行。一个规则可以有零个或多个命令行。</dd></dl>
<p>OK! 现在你明白[targets]是什么了，原来它们来自于 Makefile 文件中一条条规则的目标(TARGET)。另外，Makefile文件中第一条规则的目标被称为"终极目标"，也就是你省略[targets]参数时的目标(通常为"all")。</p>
<p>当你查看一个实际的 Makefile 文件时，你会发现有些规则非常复杂，但是它都符合规则的基本格式。此外，Makefile 文件中通常还包含了除规则以外的其它很多东西，不过本文只关心其中的变量。</p>
<h3>Makefile 变量</h3>
<p>Makefile 中的"变量"更像是 C 语言中的宏，代表一个文本字符串(变量的值)，可以用于规则的任何部分。变量的定义很简单：VAR=VALUE；变量的引用也很简单：$(VAR) 或者 ${VAR}。变量引用的展开过程是严格的文本替换过程，就是说变量值的字符串被精确的展开在变量被引用的地方。比如，若定义：VAR=c，那么，"$(VAR) $(VAR)-$(VAR) VAR.$(VAR)"将被展开为"c c-c VAR.c"。</p>
<p>虽然在 Makefile 中可以直接使用系统的环境变量，但是也可以通过在 Makefile 中定义同名变量来"遮盖"系统的环境变量。另一方面，我们可以在调用 make 时使用 -e 参数强制使系统中的环境变量覆盖 Makefile 中的同名变量，除此之外，在调用 make 的命令行上使用 VAR=VALUE 格式指定的环境变量也可以覆盖 Makefile 中的同名变量。</p>
<h3>Makefile 实例</h3>
<p>下面看一个简单的、实际的Makefile文件：</p>
<code>
<pre>CC=gcc
CPPFLAGS=
CFLAGS=-O2 -pipe
LDFLAGS=-s
PREFIX=/usr
all : prog1 prog2
prog1 : prog1.o
$(CC) $(LDFLAGS) -o prog1 prog1.o
prog1.o : prog1.c
$(CC) -c $(CFLAGS) prog1.c
prog2 : prog2.o
$(CC) $(CFLAGS) $(LDFLAGS) -o prog2 prog2.o
prog2.o : prog2.c
$(CC) -c $(CPPFLAGS) $(CFLAGS) prog2.c
clean :
rm -f *.{o,a} prog{1,2}
install : prog1 prog2
if ( test ! -d $(PREFIX)/bin ) ; then mkdir -p $(PREFIX)/bin ; fi
cp -f prog1 $(PREFIX)/bin/prog1
cp -f prog2 $(PREFIX)/bin/prog2
check test : prog1 prog2
prog1 &lt; sample1.ref &gt; sample1.rz
prog1 &lt; sample2.ref &gt; sample3.rz
cmp sample1.ok sample1.rz
cmp sample2.ok sample2.rz
</pre>
</code>
<p>从中可以看出，make 与 make all 以及 make prog1 prog2 三条命令其实是等价的。而常用的 make check 和 make install 也找到了归属。同时我们也看到了 Makefile 中的各种变量是如何影响编译的。针对这个特定的 Makefile ，你甚至可以省略安装三步曲中的 make 命令而直接使用 make install 进行安装。</p>
<p>同样，为了使用自定义的编译参数编译 prog2 ，我们可以使用 make prog2 CFLAGS="-O3 -march=athlon64" 或 CFLAGS="-O3 -march=athlon64" &amp;&amp; make -e prog2 命令达到此目的。</p>
<h3>Makefile 惯例</h3>
<p>下面是Makefile中一些约定俗成的目标名称及其含义：</p>
<dl>
<dt>all</dt>
<dd>编译整个软件包，但不重建任何文档。一般此目标作为默认的终极目标。此目标一般对所有源程序的编译和连接使用"-g"选项，以使最终的可执行程序中包含调试信息。可使用 strip 程序去掉这些调试符号。</dd>
<dt>clean</dt>
<dd>清除当前目录下在 make 过程中产生的文件。它不能删除软件包的配置文件，也不能删除 build 时创建的那些文件。</dd>
<dt>distclean</dt>
<dd>类似于"clean"，但增加删除当前目录下的的配置文件、build 过程产生的文件。</dd>
<dt>info</dt>
<dd>产生必要的 Info 文档。</dd>
<dt>check 或 test</dt>
<dd>完成所有的自检功能。在执行检查之前，应确保所有程序已经被创建(但可以尚未安装)。为了进行测试，需要实现在程序没有安装的情况下被执行的测试命令。</dd>
<dt>install</dt>
<dd>完成程序的编译并将最终的可执行程序、库文件等拷贝到指定的目录。此种安装一般不对可执行程序进行 strip 操作。</dd>
<dt>install-strip</dt>
<dd>和"install"类似，但是会对复制到安装目录下的可执行文件进行 strip 操作。</dd>
<dt>uninstall</dt>
<dd>删除所有由"install"安装的文件。</dd>
<dt>installcheck</dt>
<dd>执行安装检查。在执行安装检查之前，需要确保所有程序已经被创建并且被安装。</dd>
<dt>installdirs</dt>
<dd>创建安装目录及其子目录。它不能更改软件的编译目录，而仅仅是创建程序的安装目录。</dd></dl>
<p>下面是 Makefile 中一些约定俗成的变量名称及其含义：</p>
<p>这些约定俗成的变量分为三类。第一类代表可执行程序的名字，例如 CC 代表编译器这个可执行程序；第二类代表程序使用的参数(多个参数使用空格分开)，例如 CFLAGS 代表编译器执行时使用的参数(一种怪异的做法是直接在 CC 中包含参数)；第三类代表安装目录，例如 prefix 等等，含义简单，下面只列出它们的默认值。</p>
<pre>AR   函数库打包程序，可创建静态库.a文档。默认是"ar"。
AS   汇编程序。默认是"as"。
CC   C编译程序。默认是"cc"。
CXX  C++编译程序。默认是"g++"。
CPP  C/C++预处理器。默认是"$(CC) -E"。
FC   Fortran编译器。默认是"f77"。
PC   Pascal语言编译器。默认是"pc"。
YACC Yacc文法分析器。默认是"yacc"。
ARFLAGS     函数库打包程序的命令行参数。默认值是"rv"。
ASFLAGS     汇编程序的命令行参数。
CFLAGS      C编译程序的命令行参数。
CXXFLAGS    C++编译程序的命令行参数。
CPPFLAGS    C/C++预处理器的命令行参数。
FFLAGS      Fortran编译器的命令行参数。
PFLAGS      Pascal编译器的命令行参数。
YFLAGS      Yacc文法分析器的命令行参数。
LDFLAGS     链接器的命令行参数。
prefix      /usr/local
exec_prefix $(prefix)
bindir      $(exec_prefix)/bin
sbindir     $(exec_prefix)/sbin
libexecdir  $(exec_prefix)/libexec
datadir     $(prefix)/share
sysconfdir  $(prefix)/etc
sharedstatedir $(prefix)/com
localstatedir  $(prefix)/var
libdir      $(exec_prefix)/lib
infodir     $(prefix)/info
includedir  $(prefix)/include
oldincludedir  $(prefix)/include
mandir      $(prefix)/man
srcdir      需要编译的源文件所在的目录，无默认值
</pre>
<h3>make 选项</h3>
<p>最后说说 make 的命令行选项(以Make-3.81版本为准)：</p>
<dl>
<dt>-B, --always-make</dt>
<dd>无条件的重建所有规则的目标，而不是根据规则的依赖关系决定是否重建某些目标文件。</dd>
<dt>-C DIR, --directory=DIR</dt>
<dd>在做任何动作之前先切换工作目录到 DIR ，然后再执行 make 程序。</dd>
<dt>-d</dt>
<dd>在 make 执行过程中打印出所有的调试信息。包括：make 认为那些文件需要重建；那些文件需要比较它们的最后修改时间、比较的结果；重建目标所要执行的命令；使用的隐含规则等。使用该选项我们可以看到 make 构造依赖关系链、重建目标过程的所有信息，它等效于"-debug=a"。</dd>
<dt>--debug=FLAGS</dt>
<dd>在 make 执行过程中打印出调试信息。FLAGS 用于控制调试信息级别：
<dl>
<dt>a</dt>
<dd>输出所有类型的调试信息</dd>
<dt>b</dt>
<dd>输出基本调试信息。包括：那些目标过期、是否重建成功过期目标文件。</dd>
<dt>v</dt>
<dd>除 b 级别以外还包括：解析的 makefile 文件名，不需要重建文件等。</dd>
<dt>i</dt>
<dd>除 b 级别以外还包括：所有使用到的隐含规则描述。</dd>
<dt>j</dt>
<dd>输出所有执行命令的子进程，包括命令执行的 PID 等。</dd>
<dt>m</dt>
<dd>输出 make 读取、更新、执行 makefile 的信息。</dd></dl></dd>
<dt>-e, --environment-overrides</dt>
<dd>使用系统环境变量的定义覆盖 Makefile 中的同名变量定义。</dd>
<dt>-f FILE, --file=FILE, --makefile=FILE</dt>
<dd>将 FILE 指定为 Makefile 文件。</dd>
<dt>-h, --help</dt>
<dd>打印帮助信息。</dd>
<dt>-i, --ignore-errors</dt>
<dd>忽略规则命令执行过程中的错误。</dd>
<dt>-I DIR, --include-dir=DIR</dt>
<dd>指定包含 Makefile 文件的搜索目录。使用多个"-I"指定目录时，搜索目录按照指定顺序进行。</dd>
<dt>-j [N], --jobs[=N]</dt>
<dd>指定并行执行的命令数目。在没有指定"-j"参数的情况下，执行的命令数目将是系统允许的最大可能数目。</dd>
<dt>-k, --keep-going</dt>
<dd>遇见命令执行错误时不终止 make 的执行，也就是尽可能执行所有的命令，直到出现致命错误才终止。</dd>
<dt>-l [N], --load-average[=N], --max-load[=N]</dt>
<dd>如果系统负荷超过 LOAD(浮点数)，不再启动新任务。</dd>
<dt>-L, --check-symlink-times</dt>
<dd>同时考察符号连接的时间戳和它所指向的目标文件的时间戳，以两者中较晚的时间戳为准。</dd>
<dt>-n, --just-print, --dry-run, --recon</dt>
<dd>只打印出所要执行的命令，但并不实际执行命令。</dd>
<dt>-o FILE, --old-file=FILE, --assume-old=FILE</dt>
<dd>即使相对于它的依赖已经过期也不重建 FILE 文件；同时也不重建依赖于此文件任何文件。</dd>
<dt>-p, --print-data-base</dt>
<dd>命令执行之前，打印出 make 读取的 Makefile 的所有数据（包括规则和变量的值），同时打印出 make 的版本信息。如果只需要打印这些数据信息，可以使用 make -qp 命令。查看 make 执行前的预设规则和变量，可使用命令 make -p -f /dev/null 。</dd>
<dt>-q, --question</dt>
<dd>"询问模式"。不运行任何命令，并且无输出，只是返回一个查询状态。返回状态为 0 表示没有目标需要重建，1 表示存在需要重建的目标，2 表示有错误发生。</dd>
<dt>-r, --no-builtin-rules</dt>
<dd>取消所有内嵌的隐含规则，不过你可以在 Makefile 中使用模式规则来定义规则。同时还会取消所有支持后追规则的隐含后缀列表，同样我们也可以在 Makefile 中使用".SUFFIXES"定义我们自己的后缀规则。此选项不会取消 make 内嵌的隐含变量。</dd>
<dt>-R, --no-builtin-variables</dt>
<dd>取消 make 内嵌的隐含变量，不过我们可以在 Makefile 中明确定义某些变量。注意，此选项同时打开了"-r"选项。因为隐含规则是以内嵌的隐含变量为基础的。</dd>
<dt>-s, --silent, --quiet</dt>
<dd>不显示所执行的命令。</dd>
<dt>-S, --no-keep-going, --stop</dt>
<dd>取消"-k"选项。在递归的 make 过程中子 make 通过 MAKEFLAGS 变量继承了上层的命令行选项。我们可以在子 make 中使用"-S"选项取消上层传递的"-k"选项，或者取消系统环境变量 MAKEFLAGS 中的"-k"选项。</dd>
<dt>-t, --touch</dt>
<dd>更新所有目标文件的时间戳到当前系统时间。防止 make 对所有过时目标文件的重建。</dd>
<dt>-v, --version</dt>
<dd>打印版本信息。</dd>
<dt>-w, --print-directory</dt>
<dd>在 make 进入一个目录之前打印工作目录。使用"-C"选项时默认打开这个选项。</dd>
<dt>--no-print-directory</dt>
<dd>取消"-w"选项。可以是用在递归的 make 调用过程中，取消"-C"参数将默认打开"-w"。</dd>
<dt>-W FILE, --what-if=FILE, --new-file=FILE, --assume-new=FILE</dt>
<dd>设定 FILE 文件的时间戳为当前时间，但不改变文件实际的最后修改时间。此选项主要是为实现了对所有依赖于 FILE 文件的目标的强制重建。</dd>
<dt>--warn-undefined-variables</dt>
<dd>在发现 Makefile 中存在对未定义的变量进行引用时给出告警信息。此功能可以帮助我们调试一个存在多级套嵌变量引用的复杂 Makefile 。但是：我们建议在书写 Makefile 时尽量避免超过三级以上的变量套嵌引用。</dd></dl>
<h2>configure</h2>
<p>此阶段的主要目的是生成 Makefile 文件，是最关键的运筹帷幄阶段，基本上所有可以对安装过程进行的个性化调整都集中在这一步。</p>
<p>configure 脚本能够对 Makefile 中的哪些内容产生影响呢？基本上可以这么说：所有内容，包括本文最关心的 Makefile 规则与 Makefile 变量。那么又是哪些因素影响着最终生成的 Makefile 文件呢？答曰：系统环境和配置选项。</p>
<p>配置选项的影响是显而易见的。但是"系统环境"的概念却很宽泛，包含很多方面内容，不过我们这里只关心环境变量，具体说来就是将来会在 Makefile 中使用到的环境变量以及与 Makefile 中的变量同名的环境变量。</p>
<h3>通用 configure 语法</h3>
<p>在进一步讲述之前，先看看 configure 脚本的语法，一般有两种：</p>
<p>configure [OPTIONS] [VAR=VALUE]...</p>
<p>configure [OPTIONS] [HOST]</p>
<p>不管是哪种语法，我们都可以用 configure --help 查看所有可用的[OPTIONS]，并且通常在结尾部分还能看到这个脚本所关心的环境变量有哪些。在本文中将对这两种语法进行合并，使用下面这种简化的语法：</p>
<p>configure [OPTIONS]</p>
<p>这种语法能够被所有的 configure 脚本所识别，同时也能通过设置环境变量和使用特定的[OPTIONS]完成上述两种语法的一切功能。</p>
<h3>通用 configure 选项</h3>
<p>虽然每个软件包的 configure 脚本千差万别，但是它们却都有一些共同的选项，也基本上都遵守相同的选项语法。</p>
<h4>脚本自身选项</h4>
<dl>
<dt>--help</dt>
<dd>显示帮助信息。</dd>
<dt>--version</dt>
<dd>显示版本信息。</dd>
<dt>--cache-file=FILE</dt>
<dd>在FILE文件中缓存测试结果(默认禁用)。</dd>
<dt>--no-create</dt>
<dd>configure脚本运行结束后不输出结果文件，常用于正式编译前的测试。</dd>
<dt>--quiet, --silent</dt>
<dd>不显示脚本工作期间输出的"checking ..."消息。</dd></dl>
<h4>目录选项</h4>
<dl>
<dt>--srcdir=DIR</dt>
<dd>源代码文件所在目录，默认为configure脚本所在目录或其父目录。</dd>
<dt>--prefix=PREFIX</dt>
<dd>体系无关文件的顶级安装目录PREFIX ，默认值一般是 /usr/local 或 /usr/local/pkgName</dd>
<dt>--exec-prefix=EPREFIX</dt>
<dd>体系相关文件的顶级安装目录EPREFIX ，默认值一般是 PREFIX</dd>
<dt>--bindir=DIR</dt>
<dd>用户可执行文件的存放目录DIR ，默认值一般是 EPREFIX/bin</dd>
<dt>--sbindir=DIR</dt>
<dd>系统管理员可执行目录DIR ，默认值一般是 EPREFIX/sbin</dd>
<dt>--libexecdir=DIR</dt>
<dd>程序可执行目录DIR ，默认值一般是 EPREFIX/libexec</dd>
<dt>--datadir=DIR</dt>
<dd>通用数据文件的安装目录DIR ，默认值一般是 PREFIX/share</dd>
<dt>--sysconfdir=DIR</dt>
<dd>只读的单一机器数据目录DIR ，默认值一般是 PREFIX/etc</dd>
<dt>--sharedstatedir=DIR</dt>
<dd>可写的体系无关数据目录DIR ，默认值一般是 PREFIX/com</dd>
<dt>--localstatedir=DIR</dt>
<dd>可写的单一机器数据目录DIR ，默认值一般是 PREFIX/var</dd>
<dt>--libdir=DIR</dt>
<dd>库文件的安装目录DIR ，默认值一般是 EPREFIX/lib</dd>
<dt>--includedir=DIR</dt>
<dd>C头文件目录DIR ，默认值一般是 PREFIX/include</dd>
<dt>--oldincludedir=DIR</dt>
<dd>非gcc的C头文件目录DIR ，默认值一般是 /usr/include</dd>
<dt>--infodir=DIR</dt>
<dd>Info文档的安装目录DIR ，默认值一般是 PREFIX/info</dd>
<dt>--mandir=DIR</dt>
<dd>Man文档的安装目录DIR ，默认值一般是 PREFIX/man</dd></dl>
<h4>体系结构选项</h4>
<p>玩交叉编译的朋友对这些选项已经很熟悉了，对于不使用交叉编译的朋友也不必担心，不要理它们就可以了。</p>
<dl>
<dt>--build=BUILD</dt>
<dd>工具链当前的运行环境，默认是 config.guess 脚本的输出结果。</dd>
<dt>--host=HOST</dt>
<dd>编译出的二进制代码将要运行在HOST上，默认值是BUILD。</dd>
<dt>--target=TARGET</dt>
<dd>编译出的工具链所将来生成的二进制代码要在TARGET上运行，这个选项仅对工具链(也就是GCC和Binutils两者)有意义。</dd></dl>
<h4>特性选项</h4>
<dl>
<dt>--enable-FEATURE</dt>
<dd>启用FEATURE特性</dd>
<dt>--disable-FEATURE</dt>
<dd>禁用FEATURE特性</dd>
<dt>--with-PACKAGE[=DIR]</dt>
<dd>启用附加软件包PACKAGE，亦可同时指定PACKAGE所在目录DIR</dd>
<dt>--without-PACKAGE</dt>
<dd>禁用附加软件包PACKAGE</dd></dl>
<h3>通用环境变量</h3>
<p>除了上述通用的选项外，下列环境变量影响着最终生成的 Makefile 文件：</p>
<dl>
<dt>CPP</dt>
<dd>C预处理器命令</dd>
<dt>CXXCPP</dt>
<dd>C++预处理器命令</dd>
<dt>CPPFLAGS</dt>
<dd>C/C++预处理器命令行参数</dd>
<dt>CC</dt>
<dd>C编译器命令</dd>
<dt>CFLAGS</dt>
<dd>C编译器命令行参数</dd>
<dt>CXX</dt>
<dd>C++编译器命令</dd>
<dt>CXXFLAGS</dt>
<dd>C++编译器命令行参数</dd>
<dt>LDFLAGS</dt>
<dd>连接器命令行参数</dd></dl>
<p>至于设置这些环境变量的方法，你可以将它们 export 为全局变量在全局范围内使用，也可以在命令行上使用 [VAR=VALUE]... configure [OPTIONS] 的语法局部使用。此处就不详细描述了。</p>
<hr />
<p>看完上述内容以后，不用多说你应当自然而然的明白该进行如何对自己的软件包进行定制安装了。祝你好运！</p>
<hr />
<h3>补充读物</h3>
<p>根据d00m3d的推荐，LinuxSir.Org上的另外两篇帖子：<a href="http://www.linuxsir.org/bbs/showthread.php?t=184419">《编译的一点体会》</a>和<a href="http://www.linuxsir.org/bbs/showthread.php?t=269631">《关于库的深入思考》</a>，可以作为本文的进一步读物，更加有助于深入理解本文的主题。另外建立在本文基础上的<a href="http://www.linuxsir.org/bbs/thread282237.html">《编译优化指南》</a>专门针对与优化相关的问题进行了探讨。推荐阅读。</p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/271157.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-17 21:59 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/17/271157.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>rhel, using the centos's yum zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/17/271151.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Sun, 17 May 2009 13:18:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/17/271151.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/271151.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/17/271151.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/271151.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/271151.html</trackback:ping><description><![CDATA[<p>http://xuev.blogbus.com/logs/37302025.html<br />
<br />
<br />
用RHEL5做虚拟机，好多软件需要装，可是RHEL5的yum一直没搞明白，既然CentOS号称和RH同步更新，那我们就来尝试一下是否可以用CentOS的源来更新RHEL5或者安装软件。&nbsp;</p>
<p>CentOS的源有好多，网上大多数人都推荐中科大的源，经测试在青岛网通连接中科大（<a href="http://centos.ustc.edu.cn/">http://centos.ustc.edu.cn/</a>）的速度也非常好，那就用它吧。</p>
<p>先将RHEL5自带的yum删除，执行
<div style="background-color: #202020" id="codee_html">
<div style="background-color: #202020; font-family: 'monospace', 'Lucida Console', 'Courier New'" class="source"><span style="color: #d0d0d0">rpm</span> <span style="color: #d0d0d0">-qa|grep</span> <span style="color: #d0d0d0">yum</span><br />
</div>
</div>
<p>&nbsp;</p>
<p>将所有找到的包都删除（有些需要加上--nodeps），然后到中科大的网站下载如下两个包：&nbsp;
<div style="background-color: #202020" id="codee_html">
<div style="background-color: #202020; font-family: 'monospace', 'Lucida Console', 'Courier New'" class="source"><span style="color: #d0d0d0">wget</span> <span style="color: #d0d0d0">http://centos.ustc.edu.cn/centos/5.3/os/i386/CentOS/yum-3.2.19-18.el5.centos.noarch.rpm</span><br />
<span style="color: #d0d0d0">wget</span> <span style="color: #d0d0d0">http://centos.ustc.edu.cn/centos/5.3/os/i386/CentOS/yum-metadata-parser-1.1.2-2.el5.i386.rpm</span><br />
</div>
</div>
<p>&nbsp;</p>
<p>下载后安装：
<div style="background-color: #202020" id="codee_html">
<div style="background-color: #202020; font-family: 'monospace', 'Lucida Console', 'Courier New'" class="source"><span style="color: #d0d0d0">rpm</span> <span style="color: #d0d0d0">-ivh</span> <span style="color: #d0d0d0">yum-metadata-parser-1.1.2-2.el5.i386.rpm</span><br />
<span style="color: #d0d0d0">rpm</span> <span style="color: #d0d0d0">-ivh</span> <span style="color: #d0d0d0">yum-3.2.8-9.el5.centos.1.noarch.rpm</span><br />
</div>
</div>
<p>&nbsp;</p>
<p>然后下载中科大专用的配置文件：
<div style="background-color: #202020; font-family: 'monospace', 'Lucida Console', 'Courier New'" class="source">
<div style="background-color: #202020" id="codee_html">
<div style="background-color: #202020; font-family: 'monospace', 'Lucida Console', 'Courier New'" class="source"><span style="color: #d0d0d0">cd</span> <span style="color: #d0d0d0">/etc/yum.repos.d/</span><br />
<span style="color: #d0d0d0">wget</span> <span style="color: #d0d0d0">http://centos.ustc.edu.cn/CentOS-Base.repo.5</span><br />
<span style="color: #d0d0d0">mv</span> <span style="color: #d0d0d0">CentOS-Base.repo.5</span> <span style="color: #d0d0d0">CentOS-Base.repo</span></div>
</div>
</div>
<p>&nbsp;</p>
<p>因为默认的配置文件中服务器地址用的版本号是变量$releasever，所以需要将其替换为实际的版本号，否则是无法连接到服务器的，当前CentOS最新版是5.3，所以我们修改CentOS-Base.repo
<div style="background-color: #202020" id="codee_html">
<div style="background-color: #202020; font-family: 'monospace', 'Lucida Console', 'Courier New'" class="source"><span style="color: #d0d0d0">vi</span> <span style="color: #d0d0d0">CentOS-Base.repo</span><br />
</div>
</div>
<p>&nbsp;</p>
<p>在vi编辑器中进行全文件替换
<div style="background-color: #202020" id="codee_html">
<div style="background-color: #202020; font-family: 'monospace', 'Lucida Console', 'Courier New'" class="source"><span style="color: #d0d0d0">:%s/$releasever/5.3/</span><br />
</div>
</div>
<p>&nbsp;</p>
<p>最后保存退出即可。然后执行
<div style="background-color: #202020; font-family: 'monospace', 'Lucida Console', 'Courier New'" class="source"><span style="color: #d0d0d0">yum update</span><br />
</div>
<p>&nbsp;</p>
<p>即可进行更新，当然，你也可以选择不更新，只安装你需要的软件，如：
<div style="background-color: #202020; font-family: 'monospace', 'Lucida Console', 'Courier New'" class="source"><span style="color: #d0d0d0">yum install httpd</span></div>
<p>&nbsp;</p>
<p>大功告成！</p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/271151.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-17 21:18 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/17/271151.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Command compare of all the unix/linux.</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/16/270961.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 15 May 2009 17:24:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/16/270961.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/270961.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/16/270961.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/270961.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/270961.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: http://www.unixguide.net/unixguide.shtml                                    Directory Mappings&nbsp;                        AIX                        FreeBSD                ...&nbsp;&nbsp;<a href='http://www.blogjava.net/jinfeng_wang/archive/2009/05/16/270961.html'>阅读全文</a><img src ="http://www.blogjava.net/jinfeng_wang/aggbug/270961.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-16 01:24 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/16/270961.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ubuntu apt-get cmd</title><link>http://www.blogjava.net/jinfeng_wang/archive/2009/05/15/270955.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 15 May 2009 15:52:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2009/05/15/270955.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/270955.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2009/05/15/270955.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/270955.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/270955.html</trackback:ping><description><![CDATA[<p style="text-indent: 2em">常用的APT命令参数：
<p style="text-indent: 2em">apt-cache search package 搜索包
<p style="text-indent: 2em">
<p style="text-indent: 2em">apt-cache show package 获取包的相关信息，如说明、大小、版本等
<p style="text-indent: 2em">
<p style="text-indent: 2em">sudo apt-get install package 安装包
<p style="text-indent: 2em">
<p style="text-indent: 2em">sudo apt-get install package - - reinstall 重新安装包
<p style="text-indent: 2em">
<p style="text-indent: 2em">sudo apt-get -f install 修复安装"-f = ——fix-missing"
<p style="text-indent: 2em">
<p style="text-indent: 2em">sudo apt-get remove package 删除包
<p style="text-indent: 2em">
<p style="text-indent: 2em">sudo apt-get remove package - - purge 删除包，包括删除配置文件等
<p style="text-indent: 2em">
<p style="text-indent: 2em">sudo apt-get update 更新源
<p style="text-indent: 2em">
<p style="text-indent: 2em">sudo apt-get upgrade 更新已安装的包
<p style="text-indent: 2em">
<p style="text-indent: 2em">sudo apt-get dist-upgrade 升级系统
<p style="text-indent: 2em">
<p style="text-indent: 2em">sudo apt-get dselect-upgrade 使用 dselect 升级
<p style="text-indent: 2em">
<p style="text-indent: 2em">apt-cache depends package 了解使用依赖
<p style="text-indent: 2em">
<p style="text-indent: 2em">apt-cache rdepends package 是查看该包被哪些包依赖
<p style="text-indent: 2em">
<p style="text-indent: 2em">sudo apt-get build-dep package 安装相关的编译环境
<p style="text-indent: 2em">
<p style="text-indent: 2em">apt-get source package 下载该包的源代码
<p style="text-indent: 2em">
<p style="text-indent: 2em">sudo apt-get clean &amp;&amp; sudo apt-get autoclean 清理无用的包
<p style="text-indent: 2em">
<p style="text-indent: 2em">sudo apt-get check 检查是否有损坏的依赖 </p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/270955.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2009-05-15 23:52 <a href="http://www.blogjava.net/jinfeng_wang/archive/2009/05/15/270955.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>