<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>JavaEye论坛精彩帖子</title>
    <description>JavaEye论坛精彩帖子 - Java编程，Ruby编程，微软.net，AJAX，敏捷软件开发，综合软件技术</description>
    <link>http://www.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>我们不是在做技术决策，我们在玩</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liuqiang.javaeye.com">liuqiang</a>&nbsp;
          链接：<a href="http://www.javaeye.com/topic/222159" style="color:red;">http://www.javaeye.com/topic/222159</a>&nbsp;
          发表时间: 2008年07月31日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p class="0" style="margin-top: 5pt; text-align: justify;"><span style="font-size: 11pt; font-family: 'Times New Roman'; mso-spacerun: 'yes';"><span style="font-family: 宋体;">&nbsp;&nbsp;&nbsp;&nbsp;在这里我不想一味地去抱怨对公司管理的不满，只想和大家一起分享下我们在做技术决策时遇到的问题。</span></span></p>
<p class="0" style="margin-top: 5pt; margin-bottom: 5pt;"><span style="font-size: 11pt; font-family: 'Times New Roman'; mso-spacerun: 'yes';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">遇到的很多情况是，公司领导是以前在大公司做过什么CTO之类的人，有了自己的一些资本和人脉积累后，自己跳出来开个小公司，这类人在技术上有着自己独特的见解和十分强的自信心。然后开始招兵买马，那么是什么兵什么马呢？据我了解招的大多数是应届生或不足一年工作经验的。之后项目来了，于是开始带领大家做项目，那么这里存在一个采用什么技术进行开发的问题，如果我是老板我一定会选最NiuBility的技术，你想啊，既然我的人拼不过别人，那么我在技术的先进性一定要比别人NiuBility，再加上自己伟大的创意，这样才有和别人竞争的余地。</span></span></p>
<p class="0" style="margin-top: 5pt; margin-bottom: 5pt;"><span style="font-size: 11pt; font-family: 'Times New Roman'; mso-spacerun: 'yes';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">事实上我们目前也是这么干的，昨天j2ee，今天SOA，明天restful，后天&hellip;&hellip;，为了超越别人。于是一帮本来就没有多少的经验的人在各种技术之间疲于奔命。就举个实例吧，最近就有这么一个项目，为了做的比别的同类产品NiuBility，打算用flex做前端，丫的，多酷啊，加上javaeyer一起看好，难道有错？在项目启动会上老板首先把这个东西鼓吹一番，什么跨平台、未来趋势、AIR&hellip;&hellip;，之后大家开始讨论这个东西，当时只有我保留意见，其他人一致通过。现在不想去讨论这个东西有多么NiuBility，关键是目前我们有几个人会这个东西，nobody！于是边学边干，据说pureMVC好，于是就基于pureMVC做，做着做着，我越发的感觉这个这个世界太疯狂了，连AS都还没弄清楚怎么回事的人就开始玩转pureMVC了，尽管我没有参与这个项目。结果是一片混乱，工期开始肆意的延长，老板急了，那加工加点吧，赶制出来的东西粗糙的不得了，一堆问题也不知道怎么办，界面不好看为什么不去修改flex的皮肤？由于不熟练怕耽误时间。性能跟不上为什么不去找瓶颈去优化？因为不会。出错了为什么不能很快解决？因为没有调试经验。和后台交互为什么那么费劲？还是因为不熟。&nbsp;&nbsp;&nbsp;</span></span></p>
<p class="0" style="margin-top: 5pt; margin-bottom: 5pt;"><span style="font-size: 11pt; font-family: 'Times New Roman'; mso-spacerun: 'yes';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">那么你可能奇怪，为什么不早点发现这个技术瓶颈问题从而解决呢？那就要话说在项目决策的时候，经过老板这么一鼓吹，大家都被吹晕了，因为这个东西大家都没接触过，也不好反对，再说确实也拿不出一手的经验去证明这个东西不适宜我们，谁能反驳他？他说好那就好喽。而且还有一点，你要是反对使用这个东西你就会被贴上保守、不自信的标签，老板嘴上不说，心里多少会这么想的，我就由于就没投赞成票，老板几次和我说(做不满意状)，叫偶多了解点这方面的东西。</span></span></p>
<p class="0" style="margin-top: 5pt; margin-bottom: 5pt; text-indent: 22pt;"><span style="font-size: 11pt; font-family: 'Times New Roman'; mso-spacerun: 'yes';"><span style="font-family: 宋体;">那么技术决策该怎么做呢？我觉得很简单，根据员工的特长去选，扬长避短，其实这个道理是蛮简单的，但是这里有个矛盾的地方，就是有些公司为了节省人力成本还是愿意接收大量的应届生或者刚入门者，但又想提高公司的技术水准，所以在做项目的时候大家基本是边学边做，这无疑把企业培训的风险和成本加到项目开发过程中了，不可否认的是技术的提高是个积累的过程&nbsp;，拔苗助长只会让项目死的更快，所以也就让老板感觉，他在工作，我们却在玩。折腾了那么多的NiuBility的技术，结果我们</span></span><span style="font-size: 11pt; font-family: '宋体'; mso-spacerun: 'yes';">j2ee<span style="font-family: 宋体;">了吗？我们soa了吗？我们restful了吗？我只感觉我们在玩！</span></span></p>
<p class="0" style="margin-top: 5pt; margin-bottom: 5pt; text-indent: 22pt;"><span style="font-size: 11pt; font-family: '宋体'; mso-spacerun: 'yes';"><span style="font-family: 宋体;">那么为什么不去聘请O6Z这样的大佬来带领大家一边拍脑袋，一边干活，多帅啊，很遗憾，我们供不起这样的大佛，你还别不信，在用人上，有些老板有时就会把一块钱看的和月亮一样大，谁要价低，包装看得过得去，就买谁。这里人力资源可能就是T1说的服务期货，没有服务器、路由器等这样的现货来的实在，来的让人心里感到踏实。</span></span></p>
<p class="0" style="margin-top: 5pt; margin-bottom: 5pt; text-indent: 22pt;"><span style="font-size: 11pt; font-family: '宋体'; mso-spacerun: 'yes';"><span style="font-family: 宋体;">总之我认为，老板有什么样的枪，就去打什么样的鸟，否则结局很可能就是，老板来收我们的烂摊子，而我们只得另投明主了。</span></span><span style="font-size: 11pt; font-family: 'Times New Roman'; mso-spacerun: 'yes';">&nbsp;&nbsp;&nbsp;&nbsp;</span></p>
<p class="0"><span style="font-size: 11pt; font-family: 'Times New Roman'; mso-spacerun: 'yes';"><span style="font-family: 宋体;">&nbsp;&nbsp;&nbsp; 欢迎拍砖，如有雷同，纯属巧合</span></span></p>
<!--EndFragment-->
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/topic/222159#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 31 Jul 2008 12:58:32 +0800</pubDate>
        <link>http://www.javaeye.com/topic/222159</link>
        <guid>http://www.javaeye.com/topic/222159</guid>
      </item>
      <item>
        <title>OGNL &amp; ValueStack 入门</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jxb8901.javaeye.com">jxb8901</a>&nbsp;
          链接：<a href="http://www.javaeye.com/topic/223612" style="color:red;">http://www.javaeye.com/topic/223612</a>&nbsp;
          发表时间: 2008年08月03日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          h1. 一个例子<br />请看下面的需求，假设有如下用户对象模型：<br /><pre name="code" class="java">
public interface User {
    public String getName();
    public Date getRegisterDate();
    public Customer getCustomer();
}
public interface Customer {
    public String getId();
    public String getName();
    public boolean isVip();
}
public interface EntCustomer extends Customer {
    public String getTrustId(); // 组织机构代码证号
}
</pre><br />对于给定的用户jack，且该用户所属客户是企业客户，那么我们如何获取该用户的姓名？如何获取用户所属客户的名称？如何判断该用户所属客户是否是VIP客户？如何取jack所属企业的组织机构代码证号？<br /><br />* 采用java代码的方式，我们可以用如下的API调用得到所需信息：<br /><pre name="code" class="java">
jack.getName();
jack.getCustomer().getName();
jack.getCustomer().isVip();
((EntCustomer)jack.getCustomer()).getTrustId();
</pre><br /><br />* 但我们现在在讲述OGNL，因此通过采用OGNL，我们可以用如下方式取得我们所需要的信息：<br /><pre name="code" class="java">
jack.name
jack.customer.name
jack.vip
jack.customer.trustId
</pre><br />由此我们可以看到OGNL的表达方式与java表达方式有以下几点不同：<br />** 不需关注对象类型，不需进行类型转换<br />** 表达方式更简短和直观<br /><br />OGNL表达式最大的优点就是：*简单* 和 *直观*，你不这样认为吗？ 如果你觉得上面的表达式还不够简单和直观，那我们再来看：<br /><pre name="code" class="java">name</pre><br />这也是一个OGNL表达式，也就是取姓名！简单吗？至少足够直观了吧:)<br /><br />h1. 基本概念<br />我们前面看到了OGNL的一个最简单的例子，事实上OGNL确实很简单，如果能理解上面那个例子的用法，那么我们就掌握了OGNL的80%的用法了。<br />上面的例子虽然简单，但其中却含有OGNL的两个最基本的概念：*表达式(expression)* 和 *上下文(context)*，我们先看*表达式*。<br /><br />h3. 表达式<br />OGNL就是表达式！它能让我们用简洁直观的语法表达我们的想法，如同上面的例子一般。简洁直观就是表达式的最大优点！我们知道表达式总是有一个结果，也就是说表达式总是会求值出一个结果，这个结果可能是一个字符串（如名称、组织机构代码证号等），或者是一个布尔值（如是否是VIP客户等），至于这个结果要怎么使用，那就是我们自己来决定的了。<br /><br />h3. 上下文（context）<br />表达式的概念，我相信很好理解，但什么是上下文（context）？简单来说上下文就是环境，表达式求值的环境！还是不理解吗？我们来看一个例子：<br />还是上面最后那个例子：<pre name="code" class="java">name</pre> 细心的你是否会问，这个表达式要取谁的姓名呢？OK，很好！这就是环境，"谁"就存在于环境之中，也就是存在上下文之中。对于不同的环境/上下文，相同的表达式会有不同的结果！而环境/上下文的实质是什么呢？就是一组带名称的对象集合。<br /><div class="quote_title">引用</div><div class="quote_div">思考：表达环境或上下文这个概念的最好的数据结构是什么？</div><br /><br />h3. OGNL上下文概念详解<br />我们前面说上下文就是一组名称-对象对的集合，如下图所示就是一个简单的上下文：<br /><pre name="code" class="java">
user ---> User(name:"jack", ...)
request ---> HttpServletRequest(header: ...)
</pre><br />那么在上面的环境中，我们可以有如下的OGNL表达式：<br /><pre name="code" class="java">
#user.name // 取用户的姓名
#user.age // 取用户年龄
#user.birthday // 取用户生日
#user.customer.name // 取用户所属客户的名称
#request.parameters // 取请求参数
</pre><br />请注意上面表达式中的"#user"和"#request"的用法，"#"表示访问环境/上下文中的对象。<br /><br />现在可以很方便地访问环境中的对象了，那么如果你比较懒惰的话（记住：在程序员群体，懒惰是褒义词！），你是否觉得访问用户的姓名，年龄，生日，等等其它属性如果全部要使用"#user"来访问会不会太麻烦了呢？OK，ONGL的设计者早就考虑了这个问题，我们可以指定user为环境中的特权对象，访问该对象可以不需要使用#user的方式，如下所示代码与上面的完全等价，当然，前提是要预先指定user为特权对象：<br /><pre name="code" class="java">
name // 取用户的姓名
age // 取用户年龄
birthday // 取用户生日
customer.name // 取用户所属客户的名称
#request.parameters // 取请求参数
</pre><br /><br />我们上面所说的"特权对象"在OGNL中称为"根对象"(root)<br /><br />h3. 小结<br />综上所述，理解OGNL表达式的关键是理解其上下文的概念，因为OGNL的上下文概念中引入了"根对象"的概念，所以初学者往往会在这里迷失方向。<br /><br /><div class="quote_title">引用</div><div class="quote_div"><br />OGNL的中文全称是对象图导航语言，也就是说OGNL是一门语言，如同java是一门语言一样。你是否会认为OGNL的作者太夸张了，竟敢把表达式谎称为语言？不，OGNL的语法确实非常简洁，OGNL的代码（我没有说表达式，因为代码是和语法相匹配的词语）通常不会换行，这意味着我们不可能把OGNL的代码写得很长，但是，这并不意味着OGNL的表达能力很弱。事实上，OGNL的语法设计非常简洁，但其功能却相当强大，如果你有兴趣，可以深入阅读OGNL参考手册的集合与lambda章节。<br /></div><br /><br />慢着，事情还未至此结束！struts2对OGNL中的上下文的概念又定义了新的含义，且听我慢慢道来！<br /><br />h3. struts2中的OGNL上下文<br />struts2对OGNL上下文的概念又做了进一步扩充，在struts2中，OGNL上下文通常如下所示：<br /><pre name="code" class="java">
|
                     |--request
                     |
                     |--application
                     |
       context map---|--OgnlValueStack(root) [ user, action, OgnlUtil, ... ]
                     |
                     |--session
                     |
                     |--attr
                     |
                     |--parameters
</pre><br />我们可以使用"#requet"访问HttpServletRequest对象, "#session"访问HttpSession对象，但请注意"根对象"是什么？是ValueStack!<br />那么ValueStack是什么？值栈。也就是一组对象的堆栈。也就是说，在struts2中，根对象不是我们通常的一个对象，而是一组对象。我们可以push新的对象到值栈中，也可以弹出值栈的栈顶对象。如上图所示，假设我们将user对象push到值栈中，那么如下的表达式将与之前我们见过的表达式一样，具有相同的结果：<br /><pre name="code" class="java">
name // 取用户的姓名
age // 取用户年龄
birthday // 取用户生日
customer.name // 取用户所属客户的名称
#request.parameters // 取请求参数
</pre><br />也就是说，我们使用name这个表达式的时候，ONGL会取"根对象"的name属性，但现在根对象是ValueStack！那么访问ValueStack的name属性意味着什么呢？这意味着: ValueStack会先查看栈顶元素是否有name属性，如果有就返回该属性值，否则取出栈顶下的元素，继续查看，直到栈底为止。<br /><br />以上就是OGNL表达式的核心概念，你理解了吗？下一步，你需要了进一步了解OGNL的语法，以发掘其更强大的功能！
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/topic/223612#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 03 Aug 2008 12:53:45 +0800</pubDate>
        <link>http://www.javaeye.com/topic/223612</link>
        <guid>http://www.javaeye.com/topic/223612</guid>
      </item>
      <item>
        <title>让google来为rails画图表</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liuqiang.javaeye.com">liuqiang</a>&nbsp;
          链接：<a href="http://www.javaeye.com/topic/219853" style="color:red;">http://www.javaeye.com/topic/219853</a>&nbsp;
          发表时间: 2008年07月26日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 去年年底的时候，所做的一个rails项目涉及到图表功能，主要有显示投票结果（柱状图）、网上办事统计结果（饼状图）、已办事件按月统计结果（线状图）&hellip;&hellip;，当时可真是一件很麻烦的事情，开始准备搬flex来做，结果考虑到开发成本等等原因没采用，后来是自己写js，做了好一段时间，结果在跨平台上效果却不是很理想。当时真是苦煞我也，心想要是请几个专职的google专家来帮我做成和google一样的效果多好啊，巧合的是Google于去年晚些时候悄然推出了新图表API。Google图表最初是作为视频和财经服务的一项中间项目，后来Google决定将其公诸于世。Google始终如一地向大家提供如此优雅和高效的解决方案来处理通用问题，当然Google图表也不例外。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那么是Google图表是如何为我们服务的呢？主要通过简单地发送一条URL来生成图表，调用者的主要工作是构建这些URL，该URL最主要有以下三个参数：图表的类型、图表的大小和图表的数据。图表的类型由&ldquo;cht&rdquo;参数指定。图表大小用chs指定，包括图表的长和宽，用整数来表示。图表数据用chd表示，Google提供了四种不同的数据编码方式，最简单的就是文本编码。通过给数据添加&ldquo;t:&rdquo;前缀。比如 <a href="http://chart.apis.google.com/chart?cht=lc&amp;chs=100x50&amp;chd=t:25,75,50"><span style="font-family: Courier New;">http://chart.apis.google.com/chart?cht=lc&amp;chs=100x50&amp;chd=t:25,75,50</span></a> 就是一条完整的图表服务路径，更完整的图表API可以参考 <a href="http://code.google.com/apis/chart/">http://code.google.com/apis/chart/</a>。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那么接下来我们就是去构建这些URL，这里仍存在2个问题：</p>
<p>1 构建这样的URL需要大量的字符串拼接操作，较为繁琐，对于比较数据量比较大的图表，构建这样的URL就很麻烦。</p>
<p>2&nbsp;构建这样的URL其实很多地方是重复的，只是少数的参数不一样，对于有大量图表显示的系统来说要做很多重复性的工作。</p>
<p>幸运的是，我们不必重复发明轮子了，DEEpak Jois已经封装了该API，他的gem叫做gchartrb，提供一个整洁简明的方式来生成图表URL。使用该gem的第一步是安装它：gem install gchartrb。&nbsp;使用起来超乎想象的简单，效果也非常的炫，不信？看看我做的一些demo吧：）</p>
<p>&nbsp;</p>
<p>场景一 venn图<strong> </strong>例如：A有500个元素，B有400个元素，C有300个元素，AB交集为200，AC交集为100， BC交集为50通过以上的数据得到代表变量A,B,C的三个圆圈，圆圈的面积代表变量所含元素个数，圆圈的交集代表变量之间的交集。</p>
<p>&nbsp;</p>
<pre name="code" class="java">require 'rubygems'
require 'google_chart'
def venn_diagram
    GoogleChart::VennDiagram.new("400x400", 'Venn Diagram') do |vd|
      vd.data "Blue", 500,'0000ff'
      vd.data "Green", 400, '00ff00'
      vd.data "Red", 300, 'ff0000'
      vd.intersections 200, 100, 50
      @chart = vd.to_url
    end
end</pre>
<pre name="code" class="java">&lt;%= image_tag @chart %&gt; </pre>
<p>&nbsp;</p>
<p><img src=" http://liuqiang.javaeye.com/upload/picture/pic/18582/47d44714-be77-36d8-9899-b0f50dc410b8.bmp " height="417" alt="" width="427" /></p>
<p>&nbsp;</p>
<p>场景二 柱状图 例如JE会员分布图，beijing 20000人， shanghai 18000人，tianjin 10000人，nanjing 8000 ，guangzhou 14000，shenzhen 16000</p>
<p>&nbsp;</p>
<pre name="code" class="ruby"> def bar_chart
    GoogleChart::BarChart.new('800x200', "Bar Chart", :vertical, false) do |bc|
      bc.data "beijing", [20000], '0000ff'
      bc.data "shanghai", [18000], 'ff0000'
      bc.data "tianjin", [10000], '00ff00'
      bc.data "nanjing", [8000], '00aaff'
      bc.data "guangzhou", [16000], '0effee'
      bc.data "shenzhen", [14000], 'eeff00'
      @chart = bc.to_url
    end
  end</pre>
<pre name="code" class="ruby">&lt;%= image_tag @chart %&gt; </pre>
<p>&nbsp;</p>
<p><img src=" http://liuqiang.javaeye.com/upload/picture/pic/18584/91e6df58-d84e-3bea-91b5-a41c62eb8490.bmp " height="259" alt="" width="339" /></p>
<p>场景三 饼状图 例如JE文章投票人数统计，very good 200票， good 150票，just so so100票，bad 180票</p>
<p>&nbsp;</p>
<pre name="code" class="ruby">def pie_chart
    GoogleChart::PieChart.new('320x200', "Pie Chart",false) do |pc|
      pc.data "very good", 300
      pc.data "good", 200
      pc.data "just so so", 100
      pc.data "bad", 180
      pc.show_labels = true
      @chart = pc.to_url
    end
end</pre>
<pre name="code" class="ruby">&lt;%= image_tag @chart %&gt; </pre>
<p>&nbsp;&nbsp;</p>
<p><img src="http://liuqiang.javaeye.com/upload/picture/pic/18586/ec091b1d-7705-3a77-a3fa-dd7457bdf61d.bmp " height="248" alt="" width="354" /></p>
<p>&nbsp;场景四 折线图 例如统计每周JE会员增加数量，一周的数量分别是 56&nbsp;48 68 59 66 67 59</p>
<p>&nbsp;</p>
<pre name="code" class="ruby">GoogleChart::LineChart.new('320x200', "Line XY Chart", true) do |lcxy|
      lcxy.data "amount", [[1,56], [2,48], [3,68], [4,59], [5,66], [6,67], [7,59]], '0000ff'
      @chart = lcxy.to_url
end</pre>
<pre name="code" class="ruby">&lt;%= image_tag @chart %&gt; </pre>
<p>&nbsp;<img src="http://liuqiang.javaeye.com/upload/picture/pic/18588/1648c1da-21d3-32f6-bff2-27dbaa131e7e.bmp " height="252" alt="" width="346" /></p>
<p>&nbsp;</p>
<p>当然图表中的数据源在实际应用是应该来自于数据库的！</p>
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/topic/219853#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 26 Jul 2008 17:05:30 +0800</pubDate>
        <link>http://www.javaeye.com/topic/219853</link>
        <guid>http://www.javaeye.com/topic/219853</guid>
      </item>
      <item>
        <title>Ruby/Rails: 不一样的'Web'应用（续）</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://rubynroll.javaeye.com">rubynroll</a>&nbsp;
          链接：<a href="http://www.javaeye.com/topic/220771" style="color:red;">http://www.javaeye.com/topic/220771</a>&nbsp;
          发表时间: 2008年07月28日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          上一篇文章(<a href="http://www.javaeye.com/topic/219826" target="_blank">http://www.javaeye.com/topic/219826</a>)发出之后，很多人表示对这个案例很感兴趣，要求我再深入地谈谈。应大家之邀, 我对上一篇内容进行一些补充,谈谈如何在一个传统的嵌入式领域项目中为了拥抱变化而引入web技术,以及用定制的rails框架解决非web应用问题，最后简要地谈谈一般性应用的思考。<br /><br />在上一篇中,我轻描淡写地描述了由于客户对W设备赋予更多角色而导致W设备功能需求暴涨,最终选择web技术来解决问题,其实这里并非一蹴而就。<br /><br /><br />首先我们来分析一下要迎接的挑战:<br />(1) 增加很多复杂的操作界面(超出了W设备的现有资源能力)<br />(2) 功能变化快<br />(3) 需要日后可定制功能(二次开发)<br />(4) 维持低成本(意味着维持现有硬件架构不变)<br /><br />和大多数人一样,我们首先想到的是客户的要求是不是不太合理呢?又要马儿跑又要马儿不吃草?但是很快发现这里有一个契机,那就是W设备是保持在线的(通过ZigBee网络),那么我们就有机会透过网络转移计算,于是一个方案马上跃上来: unix终端。<br /><br />是的,古老的终端。<br /><br />在许多年前,我刚迈出大学校门时参加第一项开发工作就是字符终端设备的开发,所以对终端还算熟悉。不幸的是,我也开发过服务端的程序,知道在ncurses库下开发应用并不轻松,拥抱变化?难！客户的第(2)和第(3)项需求也无法很好地得到满足。<br /><br />我突然想起DHH在RailsConf 2007上的那个keynote(就是他大谈Cargo Cult的那次), 他把浏览器和IBM 3270做了有趣的对比。是的,那是个绝妙的对比,它给我留下的印象远大于Cargo Cult调侃。<strong>浏览器和终端本质上要解决的是同一个问题</strong>。由于web技术的发展取得长足的进步,服务器端进行应用开发资源也异常丰富。rails正是其中一颗冉冉升起的新星,更重要的是,服务器已经在跑rails了,就理所当然继续用rails。<br />在rails下开发应用那是太轻松了, 那么对付"功能快速变化"和"二次开发"就好办了。<br /><br />那么,焦点又回到了W设备: 浏览器?<br /><br />显然,在现有的硬件平台上上web浏览器是不可能的,如果升级硬件平台上WinCE或Embedded Linux,那么W设备的成本势必上升。况且,还有另外一些问题: 耗电问题，传输问题（ZigBee网络带宽极其有限,肥大的HTTP/HTML并不适合）; 而且W设备显示屏很小,所有功能操作都是只需要字符即可,无需fancy界面。因此,定义一套适合在ZigBee网络传输的协议和适合W设备的简易标记语言(MML)显然更为合理。<br /><br />客户端问题解决了，现在焦点回到服务器端，rails可以作为非web应用么？<br /><br />我对rails的内部细节不太了解，但是从外部来看，rails提供了以下主要服务：<br />1) MVC编程框架<br />2) 透过ActiveRecord与数据库打交道<br />3) 为HTML渲染提供服务<br />4) 其他如测试，数据迁移，插件，与web server的接口等等<br /><br />其中份量最重的ActiveRecord部分，与web完全无关。很多便利工具例如测试，数据迁移，插件机制等等，其实与web也无多大关系。<br /><br />既然rails的MVC中，M与web无关，C部分主要留给业务逻辑，而V部分对于非web领域价值不大，倘若要基于rails再建一个领域特定的MVC，工作量也就是集中在V部分了。而这个V部分，既然是领域相关，则无论采用什么方案都是一个不可避免的工作。当我们把rails的思想与习惯用法再应用到这个新的MVC上时，我们就得到了一个基于rails并且与rails神似的框架。我也来创一个buzzworld: DSF(Domain Specific Framework)，或者谦虚一点：RBDSF(Rails Based Domain Specific Framework)。<br /><br />就我的案例来看，这个DSF采用的通讯不是HTTP/TCP/IP而是基于ZigBee无线网络的自定义协议，展现数据的不是HTML而是自定义的MML，然而编程模式却和rails的web应用类似。举个简单的"Hello world"程序作为例子:<br /><br />contollers/main.rb:<br /><br /><pre name="code" class="ruby">class MainController &lt; WSController
  controller_map_to :m
  action_maps :index => "i"
  
  def index
    @text = "Hello world"
  end
end
</pre><br /><br />views/main/main_index.erb:<br /><pre name="code" class="ruby">&lt;%= "&lt;P3.20CrS2E3.5>#{@text}" %></pre><br /><br />在MainController中的controller_map_to起的作用是把自己（main）映射到一个较短的名字"m"，而action_maps则对action取短的别名，这样做主要是为了减少请求串的长度。剩下的，就和rails差不多了。<br />再看看view，其中&lt;P3.20CrS2E3.5>表示在第3行20列处(P3.20)，以红色(Cr)2号字(S2)显示@text，经过3.5妙后清楚屏幕(E3.5)。当然，如果想以更好的方式描述这个MML属性，则可以定义一系列helper函数,或者来点重的，弄个DSL。<br />这个例子没有演示Models，因为它是直接使用rails的Models，因此使用起来没有丝毫差别。<br /><br /><strong>[</strong>补充：在我的上一篇文章中，有一个功能我没有介绍，那就是模拟器。由于W设备需要在ZigBee网络中工作，对应用开发人员来说，为了开发应用而去安装整套设备比较麻烦;另外，客户想把整个系统作为产品推广，而不仅仅是自家用。这样一来，就需要有一个可以模拟W设备的环境,怎么实现？<br /><br />由于W Server是用ruby写的并基于rails，因此产生了一个绝妙的解决方案：在rails应用程序的一个控制器中直接调用W Server，把W Server的输出(MML)转换成HTML;同样的，把浏览器传来的@params内容转换成W Server需要的格式，然后我们就可以用浏览器模拟W设备了。当我们把用浏览器模拟W设备这个解决方案告诉客户时，客户诺以重金，而其实我们才用了数百行Ruby代码而已:-)<strong>]</strong><br /><br />总结：<br />遇到需求变化时，运用恰当的技术手段有时候可以柳暗花明,特别是跨领域交叉应用，能收到意想不到的效果。<strong>web技术的长足发展，也能给其他领域带来福音</strong>。当我们把MVC的概念推广到web之外，那么这个V就可以是任意的领域特定的数据展示格式。它既可以是基于文本的，也可以是基于二进制的；既可以自定义，也可以去兼容已有的格式；如果我们仔细去分析，其实很多基于主机计算模型的应用都可以用定制的MVC框架来实现，好处是MVC能够使应用程序结构更加清晰。而基于rails来实现DSF的优势是:rails已经提供了很好的基础，加上Ruby语言的强大语法，可以<strong>以很小的代价来实现适合你的应用的DSF</strong>。
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/topic/220771#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 28 Jul 2008 21:23:30 +0800</pubDate>
        <link>http://www.javaeye.com/topic/220771</link>
        <guid>http://www.javaeye.com/topic/220771</guid>
      </item>
      <item>
        <title>Ruby/Rails: 不一样的'Web'应用</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://rubynroll.javaeye.com">rubynroll</a>&nbsp;
          链接：<a href="http://www.javaeye.com/topic/219826" style="color:red;">http://www.javaeye.com/topic/219826</a>&nbsp;
          发表时间: 2008年07月26日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          我不是Web程序员，也从来未开发过用户超过10个人的'web'项目:-)，但这并不意味着web技术对我无用，正相反，我开发过许多和web技术有关的程序和库,web架构和思想对我的设计和开发有着很大的帮助。<br /><br />例如我经常碰到要设计通讯协议以适应不可靠的传输，web的无状态特性正是克服不可靠传输的法宝，屡试屡爽:-)<br /><br />最近一段时间用ruby作了不少东西，也有用rails，但是从来没有对rails内部进行深究.直到最近碰到一个项目，促使我不得不到rails里面去挖掘，以借鉴rails一些优秀的东西。<br /><br />这个项目非常有趣，是一套用于农场的自动化系统（所以使用者大多是奶牛，哈哈～），包含用于工人们佩戴的移动W设备，到处安装的RFID数据收集设备，闸门控制设备，电子称，显示屏....还有许多名目繁多的设备，这些设备的数据有的通过电缆，有的通过ZigBee无线网络汇集到一台服务器上，这台服务器跑Linux，运行Rails。服务器负责收集所有设备的数据，和发送指令给相关设备，例如控制闸门。<br /><br />给我不断带来麻烦的是这个W设备。<br /><br />W设备是这个系统里面除服务器外唯一和人打交道的设备，配有RFID数据收集器，一个小液晶显示屏，数个按钮，通过ZigBee无线网络和服务器通讯---典型的嵌入式移动设备。<br /><br />客户起初对W设备的操作功能要求不高而对成本敏感，因此W设备配置了很低的硬件资源，基本上就是靠一个集成了无线功能的MCU操作，仅数十k的内存，但对于简单的数据收集和传输，绰绰有余。<br /><br />然而好景不长，随着项目进行，客户对W设备赋予了更多更重要的角色，功能需求暴涨，且快速变化，更重要的是，要求日后能够定制功能（二次开发）。<br /><br />Mission Impossible ? 不，这正是一个典型B/S架构的系统最适合做的事情了。当然普通web浏览器无法在W的数十k内存上跑，HTTP/HTML也无法有效的在ZigBee网络上传输，因此我们就新设计了一套通讯协议和标记语言,姑且称之为MML(Mini Markup Language)。<br /><br />那么服务器端呢？已经有rails在跑用于收集/展示数据，输出报表之类的任务，可以利用rails来作为W的服务端么?<br /><br />通讯问题，改改Webric，从串口(连接到ZigBee网络控制器)上获取数据并伪装成web请求,或许还可以...路由问题，由于ZigBee带宽极其有限，需要高度精简传输内容，因此传回来的请求串中包含的是非常简短的内容，需要转换到对应的Controller/Action，这个通过添加router映射似乎也可行。MML Render问题就比较头痛了，需要修改的地方不少。<br /><br />考虑到修改rails可能工作量和分险比较高，于是采用另外一个方案：设计新的W Server，在W Server里面直接利用rails的资源。实际上，最终这个W Server直接放在rails应用程序的scripte目录下（也许vendor目录更合适），在W server里面只要加入：<br /><pre name="code" class="ruby">require File.dirname(__FILE__) + '/../config/boot'</pre><br />然后就可以使用rails提供的任何资源了，当然对于W Server来说，最有用的是Models。这样W Server就和rails应用程序浑然一体了。<br /><br />W Server有了rails这样强大的后盾之后，还需要有：Parser, Router,Server,Controller,和MML render.<br /><br />Parser: 负责解析自定义的通讯协议，获取从W设备传来的请求，并解析参数等。<br />Router: 扫描并装载Controllers，缓存Controller对象，根据Parser的结果取得相应Controller的对象。Router还负责监控Controller是否已被修改和重新装载Controller.<br />Server: 总控制，从串口中读取数据，调用Parser解析,把结果传给Router，从Router中获取Controler对象并根据Parser结果调用Controller.Action，以及缓存Controller的输出等等。<br />Controller: 业务逻辑都在这里实现，调用rails的Models访问数据库。<br />MML Render: 使用erb作为模板文件，再加上一些辅助的功能，例如render link，menu之类的，和自定义的MML特性密切相关。MML Render作为module最终mixin到Controller里面。<br /><br />可以看出，W Server模型几乎和rails的一样，在设计W Server碰到问题时经常探究在rails中是怎么解决的，因此倒是对rails的了解增进了不少。当然由于W Server的工作方式，目标和rails不同，因此复杂度就不可同日而语.<br /><br />这个项目做完之后，我有几个感受：<br />＊ Ruby的开发效率真的很高<br />  除去注释，full stack的W Server的全部代码少于千行,what a surprise！当然，这里沾了rails的光，省却了Models，还有erb拿来就用.然而相比之下，在W上光MML解析和Render部分C代码就超过2000行.<br /><br />＊ 复用Rails真的很容易<br />  在这个案例中，就是增加一行代码而已.<br /><br />＊ Web技术，可以无处不在<br />Ruby/Rails = Make it real!<br /><br /><br />～文以共勉～
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/topic/219826#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 26 Jul 2008 15:45:24 +0800</pubDate>
        <link>http://www.javaeye.com/topic/219826</link>
        <guid>http://www.javaeye.com/topic/219826</guid>
      </item>
      <item>
        <title>潜入memcached server</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ahuaxuan.javaeye.com">ahuaxuan</a>&nbsp;
          链接：<a href="http://www.javaeye.com/topic/225692" style="color:red;">http://www.javaeye.com/topic/225692</a>&nbsp;
          发表时间: 2008年08月08日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          /**<br /><br />*作者：张荣华<br /><br />*日期：2008-08-08<br /><br />**/ <br /><br />Memcached，人所皆知的remote distribute cache（不知道的可以javaeye一下下，或者google一下下，或者baidu一下下，但是鉴于baidu的排名商业味道太浓（从最近得某某事件可以看出），所以还是建议javaeye一下下），使用起来也非常的简单，它被用在了很多网站上面，几乎很少有大型的网站不会使用memcached。<br /><br />	曾经我也看过很多剖析memcached内部机制的文章，有一点收获，但是看过之后又忘记了，而且没有什么深刻的概念，但是最近我遇到一个问题，这个问题迫使我重新来认识memcache，下面我阐述一下我遇到的问题<br /><br />	问题：我有几千万的数据，这些数据会经常被用到，目前来看，它必须要放到memcached中，以保证访问速度，但是我的memcached中数据经常会有丢失，而业务需求是memcached中的数据是不能丢失的。我的数据丢失的时候，memcached server的内存才使用到60%，也就是还有40%内存被严重的浪费掉了。但不是所有的应用都是这样，其他应用内存浪费的就比较少。为什么内存才使用到60%的时候LRU就执行了呢（之所以确定是LRU执行是因为我发现我的数据丢失的总是前面放进去的，而且这个过程中，这些数据都没有被访问，比如第一次访问的时候，只能访问第1000w条，而第300w条或者之前的数据都已经丢失了，从日志里看，第300w条肯定是放进去了）。<br /><br />	带着这些疑问，我开始重新审视memcached这个产品，首先从它的内存模型开始：我们知道c++里分配内存有两种方式，预先分配和动态分配，显然，预先分配内存会使程序比较快，但是它的缺点是不能有效利用内存，而动态分配可以有效利用内存，但是会使程序运行效率下降，memcached的内存分配就是基于以上原理，显然为了获得更快的速度，有时候我们不得不以空间换时间。<br /><br />	也就是说memcached会预先分配内存，对了，memcached分配内存方式称之为allocator，首先，这里有3个概念：<br />1 slab<br />2 page<br />3 chunk<br />解释一下，一般来说一个memcahced进程会预先将自己划分为若干个slab，每个slab下又有若干个page，每个page下又有多个chunk，如果我们把这3个咚咚看作是object得话，这是两个一对多得关系。再一般来说，slab得数量是有限得，几个，十几个，或者几十个，这个跟进程配置得内存有关。而每个slab下得page默认情况是1m，也就是说如果一个slab占用100m得内存得话，那么默认情况下这个slab所拥有得page得个数就是100，而chunk就是我们得数据存放得最终地方。<br /><br />举一个例子，我启动一个memcached进程，占用内存100m，再打开telnet，telnet localhost 11211，连接上memcache之后，输入stats  slabs，回车，出现如下数据：<br /><pre name="code" class="java">STAT 1:chunk_size 80
STAT 1:chunks_per_page 13107
STAT 1:total_pages 1
STAT 1:total_chunks 13107
STAT 1:used_chunks 13107
STAT 1:free_chunks 0
STAT 1:free_chunks_end 13107
STAT 2:chunk_size 100
STAT 2:chunks_per_page 10485
STAT 2:total_pages 1
STAT 2:total_chunks 10485
STAT 2:used_chunks 10485
STAT 2:free_chunks 0
STAT 2:free_chunks_end 10485
STAT 3:chunk_size 128
STAT 3:chunks_per_page 8192
STAT 3:total_pages 1
STAT 3:total_chunks 8192
STAT 3:used_chunks 8192
STAT 3:free_chunks 0
STAT 3:free_chunks_end 8192</pre><br /><br />以上就是前3个slab得详细信息<br />chunk_size表示数据存放块得大小，chunks_per_page表示一个内存页page中拥有得chunk得数量，total_pages表示每个slab下page得个数。total_chunks表示这个slab下chunk得总数（＝total_pages * chunks_per_page），used_chunks表示该slab下已经使用得chunk得数量，free_chunks表示该slab下还可以使用得chunks数量。<br /><br />从上面得示例slab 1一共有1m得内存空间，而且现在已经被用完了，slab2也有1m得内存空间，也被用完了，slab3得情况依然如此。 而且从这3个slab中chunk得size可以看出来，第一个chunk为80b，第二个是100b，第3个是128b，基本上后一个是前一个得1.25倍，但是这个增长情况我们是可以控制得，我们可以通过在启动时得进程参数 –f来修改这个值，比如说 –f 1.1表示这个增长因子为1.1，那么第一个slab中得chunk为80b得话，第二个slab中得chunk应该是80*1.1左右。<br /><br />解释了这么多也该可以看出来我遇到得问题得原因了，如果还看不出来，那我再补充关键的一句：memcached中新的value过来存放的地址是该value的大小决定的，value总是会被选择存放到chunk与其最接近的一个slab中，比如上面的例子，如果我的value是80b，那么我这所有的value总是会被存放到1号slab中，而1号slab中的free_chunks已经是0了，怎么办呢，如果你在启动memcached的时候没有追加-M（禁止LRU，这种情况下内存不够时会out of memory），那么memcached会把这个slab中最近最少被使用的chunk中的数据清掉，然后放上最新的数据。这就解释了为什么我的内存还有40%的时候LRU就执行了，因为我的其他slab中的chunk_size都远大于我的value，所以我的value根本不会放到那几个slab中，而只会放到和我的value最接近的chunk所在的slab中(而这些slab早就满了，郁闷了)。这就导致了我的数据被不停的覆盖，后者覆盖前者。<br /><br />问题找到了，解决方案还是没有找到，因为我的数据必须要求命中率时100%，我只能通过调整slab的增长因子和page的大小来尽量来使命中率接近100%，但是并不能100%保证命中率是100%（这话怎么读起来这么别扭呢，自我检讨一下自己的语文水平），如果您说，这种方案不行啊，因为我的memcached server不能停啊，不要紧还有另外一个方法，就是memcached-tool，执行move命令，如：move 3 1，代表把3号slab中的一个内存页移动到1号slab中，有人问了，这有什么用呢，比如说我的20号slab的利用率非常低，但是page却又很多，比如200，那么就是200m，而2好slab经常发生LRU，明显page不够，我就可以move 20 2，把20号slab的一个内存页移动到2号slab上，这样就能更加有效的利用内存了（有人说了，一次只移动一个page，多麻烦啊？ahuaxuan说，还是写个脚本，循环一下吧）。<br /><br />	有人说不行啊，我的memcache中的数据不能丢失啊，ok，试试新浪的memcachedb吧，虽然我没有用过，但是建议大家可以试试，它也使利用memcache协议和berkeleyDB做的（写到这里，我不得不佩服danga了，我觉得它最大的贡献不是memcache server本身，而是memcache协议），据说它被用在新浪的不少应用上，包括新浪的博客。<br /><br />	补充，stats slab命令可以查看memcached中slab的情况，而stats命令可以查看你的memcached的一些健康情况，比如说命中率之类的，示例如下：<br /><pre name="code" class="java">STAT pid 2232
STAT uptime 1348
STAT time 1218120955
STAT version 1.2.1
STAT pointer_size 32
STAT curr_items 0
STAT total_items 0
STAT bytes 0
STAT curr_connections 1
STAT total_connections 3
STAT connection_structures 2
STAT cmd_get 0
STAT cmd_set 0
STAT get_hits 0
STAT get_misses 0
STAT bytes_read 26
STAT bytes_written 16655
STAT limit_maxbytes 104857600</pre><br />从上面的数据可以看到这个memcached进程的命中率很好，get_misses低达0个，怎么回事啊，因为这个进程使我刚启动的，我只用telnet连了一下，所以curr_connections为1，而total_items为0，因为我没有放数据进去，get_hits为0，因为我没有调用get方法，最后的结果就是misses当然为0，哇哦，换句话说命中率就是100%，又yy了。<br /><br />该到总结的时候了，从这篇文章里我们可以得到以下几个结论：<br />结论一，memcached得LRU不是全局的，而是针对slab的，可以说是区域性的。<br />结论二，要提高memcached的命中率，预估我们的value大小并且适当的调整内存页大小和增长因子是必须的。<br />结论三，带着问题找答案理解的要比随便看看的效果好得多。<br /><br />Ok,晚了，睡了。<br /><br /><br />注明：由于ahuaxuan水平有限，文中不妥之处还望不吝指正，谢谢。
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/topic/225692#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 08 Aug 2008 09:57:40 +0800</pubDate>
        <link>http://www.javaeye.com/topic/225692</link>
        <guid>http://www.javaeye.com/topic/225692</guid>
      </item>
      <item>
        <title>Why OO sucks</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://www.javaeye.com/topic/230760" style="color:red;">http://www.javaeye.com/topic/230760</a>&nbsp;
          发表时间: 2008年08月20日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          为什么OO很恶心<br /><br />原文： <a href="http://www.sics.se/~joe/bluetail/vol1/v1_oo.html" target="_blank">http://www.sics.se/~joe/bluetail/vol1/v1_oo.html</a><br />作者：Joe Armstrong<br /><br />当我第一次知道OOP的概念时，我非常疑惑，但是不知道为啥——它仅仅在感觉上“不对”。<br />在OOP问世之后变得粉流行（稍后解释为什么），而批评OOP就像“在教堂里咒骂”。<br />OO成为了每个受尊敬的语言必须具备的一部分。<br /><br />而当Erlang变得越来越流行时，我们经常问一个问题“Erlang是OO的吗？”<br />当然正确的答案是“当然不是”——但是我们没有大肆宣扬——我们只是换了种精心设计的说法，Erlang是某种OO但不是真的是。<br /><br />这时我想起在法国巴黎时IBM的老板在7th IEEE逻辑编程大会上的演讲。<br />IBM prolog添加了许多OO扩展，当人们问起时他说：“我们的客户想要OO的prolog，所以我们构建了OO的prolog”<br /><br />我想到了“多么简单，没有良心的疑虑，没有灵魂的搜索，没有‘这是正确的事情’的问题。。。”<br /><br /><strong>为什么OO很恶心</strong><br /><br />我对OOP的反对原则源自一些基本的概念，我将概述其中一些反对意见。<br /><br /><strong>反对之一——数据类型和方法应该绑定在一起</strong><br />对象将方法和数据结构绑定在一起成为不可分割的单元。我认为这是基本的错误，因为方法和数据结构属于完全不同的世界。为啥哩？<br /><br />1，方法做事情。它们是输入和输出。输入和输出的是方法所改变的数据结构。<br />在大部分编程语言里，方法由命令式语句顺序构建：“做这件事然后那件事。。。”<br />理解方法首先得理解做事情的顺序（在懒惰函数编程语言和逻辑语言中这个限制被放宽了）<br />2，数据结构是结构。它们不做任何事情。它们本质上是声明。“理解”数据结构比“理解”方法简单多了。<br /><br />方法作为黑盒子来转换输入和输出。如果我理解输入和输出，这样我就理解了方法。这并不意味着我可以写这个方法。<br /><br />方法通常理解为在一个计算系统里用来将数据结构T1转换为数据结构T2的东西。<br /><br /><strong>既然方法和数据结构是完全不同类型的动物，那么将它们锁在一个笼子里就是完全错误的。</strong><br /><br /><strong>2，反对之二——任何东西都必须为对象</strong><br />考虑“时间”。在OO语言里“时间”也必须是对象。但是在非OO语言里一个“时间”是一个数据结构的实例。<br />例如，在Erlang里有许多不同类型的时间，它们可以使用类型声明来明确指定：<br /><pre name="code" class="java">
-deftype day() = 1..31.
-deftype month() = 1..12.
-deftype year() = int().
-deftype hour() = 1..24.
-deftype minute() = 1..60.
-deftype second() = 1..60.
-deftype abstime() = {abstime, year(), month(), day(), hour(), min(), sec()}.
-deftype hms() = {hms, hour(), min(), sec()}.
...
</pre><br />注意这些定义不属于任何特殊的对象。它们很普遍，并且数据结构表示的时间可以被系统中的任何方法处理。<br /><br />没有相关联的方法。<br /><br /><strong>反对之三——在一个OOP语言里数据类型定义散布到任意位置</strong><br />在OOP语言里数据类型定义属于对象。<br />这样我就不能在一个地方找到所有的数据类型定义。<br />在Erlang或者C里我可以在一个单独的include文件或数据字典里定义我所有的数据类型。<br />在一个OOP语言里我不能——数据类型定义散布到任意位置。<br /><br />让我举一个例子。假设我想定义一个通用的数据结构。通用数据类型是一个数据类型，它在系统中的任意位置出现。<br /><br />lisp程序员知道，拥有一个较小数量的通用数据类型和在它上面的大量的小方法会更好。<br /><br />通用数据类型就比如linked list，或者一个array或者一个hash table或者更高级的对象如time或者date或者filename。<br /><br />在一个OOP语言里我不得不选择一些base对象来在里面定义通用的数据结构，所有其他想使用这些数据结构的对象必须继承该对象。<br />假设现在我想创建一些“time”对象，那么它应该属于哪个对象呢。。。<br /><br /><strong>反对之四——对象拥有私有状态</strong><br />状态是所有罪恶的根源。特别是有副作用的方法应该避免。<br /><br />在编程语言里状态是令人讨厌的，而真实世界里状态却千奇百怪的存在着。<br />我对我的银行账户的状态很感兴趣，当我从我的账户存钱或取钱时我希望我的银行账户状态成功更新。<br /><br />既然状态在真实世界里存在，那么编程语言应该提供什么能力来处理状态呢？<br /><br />1，OOP语言说“将状态隐藏”。状态仅仅通过访问方法来隐藏和可见。<br />2，传统编程语言（C，Pascal）说状态的可见度由语言的scope规则来决定。<br />3，纯声明式语言说没有状态。系统的全局状态转移到方法里然后从方法里出来。<br />类似于monad(函数式编程语言)和DCG(逻辑语言)等机制被用来隐藏状态，这样它们可以像“有没有状态无所谓”一样来编程，但是对系统状态的完全访问是必需的。<br /><br />OOP语言所选择的“隐藏状态”可能是最坏的选择。<br />它们不是将状态显示出来并寻找减少状态的坏处的方式，而是将状态隐藏起来。<br /><br /><strong>为什么OO粉流行？</strong><br /><br />1，原因1——它被认为很容易学<br />2，原因2——它被认为让代码更易重用<br />3，原因3——它被大肆宣传<br />4，原因4——它创建了一个新的软件工业<br /><br />我看不到原因1和原因2的证据。原因看起来像是技术背后的驱动力。<br />如果一个编程语言技术如此之差，然后它创建了一个新的工业来解决它自己本身的问题，则它会成为想从中牟利的人的好工具。<br /><br />这就是OOP背后真正的驱动力。<br /><br />以及一篇回复：<a href="http://konryd.blogspot.com/2007/08/why-why-oo-sucks-sucks.html" target="_blank">Why "Why OO Sucks" Sucks </a>
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/topic/230760#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 20 Aug 2008 16:53:35 +0800</pubDate>
        <link>http://www.javaeye.com/topic/230760</link>
        <guid>http://www.javaeye.com/topic/230760</guid>
      </item>
      <item>
        <title>HDFS用户指南(翻译）</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://dennis-zane.javaeye.com">dennis_zane</a>&nbsp;
          链接：<a href="http://www.javaeye.com/topic/228132" style="color:red;">http://www.javaeye.com/topic/228132</a>&nbsp;
          发表时间: 2008年08月14日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>
    
    
HDFS用户指南<br id="o3qg" />
原文地址：http://hadoop.apache.org/core/docs/current/hdfs_user_guide.html<br id="jh7z" />
译者：dennis zhuang(killme2008@gmail.com),有错误请指正，多谢。<br id="o3qg0" />
</p>
<h2 class="h3" id="o3qg1">目的<br id="o3qg2" />
</h2>
<div class="section" id="o3qg3">
<p id="o3qg4">本文档可以作为使用Hadoop分布式文件系统用户的起点，无论是将HDFS应用在一个Hadoop集群中还是作为一个单独的分布式文件系统使用。HDFS被设计成可以马上在许多环境中工作起来，那么一些HDFS的运行知识肯定能大大地帮助你对一个集群做配置改进和诊断。<br id="bz6u" />
 </p>
</div>
<p>
    
<a name="N1001B" id="o3qg6"></a>
<a name="Overview" id="o3qg7"></a>
</p>
<h2 class="h3" id="o3qg8">概览<br id="l_0z" />
  </h2>
<div class="section" id="o3qg9">
<p id="o3qg10">HDFS是Hadoop应用的主要分布式存储。一个HDFS集群由一个管理文件系统元数据的NameNode，和存储实际
数据的一些Datanode组成。HDFS的架构在这里有详细描述。这个用户指南主要提供给需要跟HDFS集群打交道的用户或者管理员。HDFS架构文章
中的图描绘了Namenode、Datanode和客户端们之间的基本交互。本质上，客户端与Namenode通讯获取或者修改文件的元数据，与
Datanode进行实际的IO操作。<br id="l_0z0" />
 </p>
<p id="l_0z1">下面的列表应该是大多数用户关心的HDFS突出特点。斜体字的术语将在后面详细描述。</p>
<p id="fvr_">1）Hadoop，包括HDFS，非常适合廉价机器上的分布式存储和分布式处理。它是容错的、可伸缩的，并且非常易于扩展。并且，以简单性和适用性著称的Map-Reduce是Hadoop不可或缺的组成部分。</p>
<p id="ag1q">2）HDFS的默认配置适合于大多数安装的应用。通常情况下，只有在一个非常大规模的集群上才需要修改默认配置。</p>
<p id="d26i">3）HDFS是用java编写的，支持大多数平台。</p>
<p id="queu">4）支持shell命令行风格的HDFS目录交互。</p>
<p id="ufzm">5）Namenode和Datanode都内建了web服务器，可以方便地查看集群的状态</p>
<p id="jafv">6）HDFS经常性地实现新的特性和改进，下面是HDFS中的一些有用特性的子集：</p>
<p id="c.lr">&nbsp;&nbsp; <em id="v3zz">文件许可和授权</em>
</p>
<p id="c.lr0">&nbsp;&nbsp; <em id="v3zz0">Rack awareness</em>
:当调度任务和分配存储的时候将节点的物理位置考虑进去。</p>
<p id="cbpn">&nbsp;&nbsp; <em id="v3zz1">Safemode(安全模式）</em>
：用于维护的一个管理状态</p>
<p id="tej-">&nbsp;&nbsp; <em id="v3zz2">fsck</em>
： 诊断文件系统的一个工具，用来查找丢失的文件或者block</p>
<p id="z_lu">&nbsp;&nbsp; <em id="v3zz3">Rebalancer</em>
:当数据在Datanode间没有均匀分布的时候，用于重新平衡集群的工具</p>
<p id="p1gp">&nbsp;&nbsp; <em id="v3zz4">升级和回滚</em>
：当Hadoop软件升级，在升级遇到不可预期的问题的时候，可以回滚到HDFS升级前的状态</p>
<p id="fzza">&nbsp;&nbsp; <em id="v3zz5">二级Namenode</em>
：帮助Namenode维持包含了HDFS修改的日志的文件（edits日志文件,下文谈到）大小在限制范围内。<br id="ubzs" />
</p>
</div>
<p> 
<a name="N10083" id="o3qg41"></a>
<a name="Pre-requisites" id="o3qg42"></a>
</p>
<h2 class="h3" id="o3qg43">前提条件<br id="px_d" />
  </h2>
<div class="section" id="o3qg44">
<p id="o3qg45">下面的文档描述了一个Hadoop集群的安装和设置：<br id="px_d0" />
</p>
<ul id="o3qg46">
<li id="o3qg47">
 		
<a href="http://hadoop.apache.org/core/docs/current/quickstart.html" id="o3qg48">Hadoop Quickstart</a>
，给初次使用用户 <br id="bb4w" />
</li>
<li id="o3qg49">
 		
<a href="http://hadoop.apache.org/core/docs/current/cluster_setup.html" id="o3qg50">Hadoop Cluster Setup</a>
 大规模、分布式集群<br id="p8d9" />
</li>
</ul>
<p id="o3qg51"><br id="tegj" />
</p>
<p id="tegj0">本文档的剩余部分假设你已经搭设并运行了一个至少拥有一个Datanode的HDFS。基于本文档的目的，Namenode和Datanode可以运行在同一台机器上。<br id="tegj1" />
</p>
</div>
<p> 
<a name="N100A1" id="o3qg52"></a>
<a name="Web+Interface" id="o3qg53"></a>
</p>
<h2 class="h3" id="o3qg54"> Web接口 </h2>
<div class="section" id="o3qg55">
<p id="o3qg56">Namenode和Datanode分别跑了一个内置的web服务器，来展现集群当前状态的一些基本信息。在默认配置
下，Namenode的首页地址是http://namenode:50070（namenode就是Namenode节点所在机器IP或者名称）。这个
页面列出了集群中的所有datanode以及集群的基本统计。web接口同样可以用于浏览文件系统（点击Namenode首页上的&ldquo;Browse
the file system&quot;链接）。<br id="lb5-" />
 	</p>
</div>
<p> 
<a name="N100AE" id="o3qg58"></a>
<a name="Shell+Commands" id="o3qg59"></a>
</p>
<h2 class="h3" id="o3qg60">Shell命令<br id="wep4" />
</h2>
<div class="section" id="o3qg61">
<p id="o3qg62">Hadoop包括了多种shell风格的命令，用于跟HDFS或者Hadoop支持的其他文件系统交互。命令
bin/hadoop fs -help 可以列出Hadoop shell支持的命令。更进一步，bin/hadoop fs -help
command
可以展现特定命令command的帮助细节。这些命令支持一般文件系统的操作，例如拷贝文件、修改文件权限等。同时也支持了部分HDFS特有的命令，例如
修改文件的replication因子。<br id="wep40" />
      </p>
<a name="N100BD" id="o3qg65"></a>
<a name="DFSAdmin+Command" id="o3qg66"></a>
<h3 class="h4" id="o3qg67"> DFSAdmin命令 </h3>
<p id="o3qg68">
   	
<span class="codefrag" id="o3qg69">'bin/hadoop dfsadmin'</span>
命令支持一些HDFS管理功能的操作。'bin/hadoop dfsadmin -help'可以列出所有当前支持的命令。例如：</p>
<ul id="o3qg71">
<li id="o3qg72">
   	    
<span class="codefrag" id="o3qg73">-report</span>
   	    : 报告HDFS的基本统计信息。部分信息同时展现在Namenode的web首页上。&nbsp;
   	</li>
<li id="o3qg74">
   		
<span class="codefrag" id="o3qg75">-safemode</span>
   		: 尽管通常并不需要，管理员还是可以通过手工操作进入或者离开safemode状态
   	</li>
<li id="o3qg77">
   		
<span class="codefrag" id="o3qg78">-finalizeUpgrade</span>
   		: 移除上一次升级时集群所做的备份。
   	</li>
</ul>
</div>
<p> 
<a name="N100E6" id="o3qg79"></a>
<a name="Secondary+Namenode" id="o3qg80"></a>
</p>
<h2 class="h3" id="o3qg81"> 二级Namenode </h2>
<div class="section" id="o3qg82">
<p id="o3qg83">Namenode将对文件系统的修改存储在一个原生文件系统文件中（名为edits的文件）。当Namenode启动的时
候，它从映像文件（fsimage)读取HDFS的状态，然后将edits日志文件中的修改作用在此内存状态上，接着将得到的新的HDFS状态写回
fsimage，后续的正常操作开始于一个空的edits日志文件。由于Namenode仅仅在启动的时候将fsimage和edits合并，因此在一个
大的集群上经过一定时间操作后，edits文件将会非常大。由此带来的一个副作用就是下次Namenode的重新启动将花费很长时间。二级
Namenode就是为了解决这个问题，它会周期性地合并fsimage和edits日志文件，并且将edits日志文件的大小保持在限制范围内。通常它
会跑在另一个机器上，因为它的内存要求跟主namenode一样。二级Namenode可以通过'bin/start-dfs.sh'启动在conf
/masters配置文件里配置的节点上。<br id="s4h2" />
     </p>
</div>
<p> 
<a name="N1010B" id="o3qg93"></a>
<a name="Rebalancer" id="o3qg94"></a>
</p>
<h2 class="h3" id="o3qg95"> Rebalancer </h2>
<div class="section" id="o3qg96">
<p id="o3qg97">HDFS的数据可能不会总是在Datanode之间分布得很一致。一个常见的原因是往现有的集群中加入了新的Datanode。当分配block的时候，Namenode依据几个参数来决定哪个datanode来接受这些block。一些需要考虑的因素如下：</p>
<p id="i-5q">1）一个block的副本存放在正在写该block的节点上</p>
<p id="fj63">2）需要将一个block的副本扩展到其他机架上，防止因为整个机架故障导致的数据丢失。</p>
<p id="ik23">3）副本之一通常放在同一个机架的另一个节点上，减少跨机架的网络IO</p>
<p id="dswx">4）将HDFS数据均匀一致地分布在集群中的datanode上。</p>
<p id="dswx0">&nbsp;&nbsp;&nbsp; 基于这些相互竞争的因素，数据可能不会在Datanode之间扩展得一致。HDFS给管理员提供了一个工具，用来分析block的分配情况和在datanode之间重新平衡数据。这个功能暂未实现，它的描述可以在这个&nbsp;<a href="http://issues.apache.org/jira/secure/attachment/12368261/RebalanceDesign6.pdf" id="o3qg105">PDF</a>
文档中看到，记录编号<a href="http://issues.apache.org/jira/browse/HADOOP-1652" id="o3qg106">HADOOP-1652</a>
.</p>
</div>
<p> 
<a name="N10132" id="o3qg107"></a>
<a name="Rack+Awareness" id="o3qg108"></a>
</p>
<h2 class="h3" id="o3qg109"> Rack Awareness </h2>
<div class="section" id="o3qg110">
<p id="o3qg111">典型的大规模Hadoop集群是部署在数个机架上的，那么显然同一个机架内的节点间的网络通讯比之不同机架间节点间的网
络通讯更可取。另外，Namenode会尝试将block的副本分布在数个机架中以提高容错性。Hadoop让集群管理员来决定某个节点从属于哪个机架，
通过配置变量dfs.network.script来实现。当这个脚本有配置的时候，每个节点都运行该脚本来决定它的rackid。默认安装假设所有的节
点从属于同一个机架。这个特性和配置进一步的阐述在这个<a href="http://issues.apache.org/jira/secure/attachment/12345251/Rack_aware_HDFS_proposal.pdf" id="o3qg116">PDF</a>
文档，编号为 
      <a href="http://issues.apache.org/jira/browse/HADOOP-692" id="o3qg117">HADOOP-692</a>
。
      </p>
</div>
<p> 
<a name="N10150" id="o3qg118"></a>
<a name="Safemode" id="o3qg119"></a>
</p>
<h2 class="h3" id="o3qg120"> Safemod(安全模式） </h2>
<div class="section" id="o3qg121">
<p id="o3qg122">当Namenode启动的时候，它从fsimage和edits日志两个文件中加载文件系统的状态。然后等待
datanode报告他们的block信息，以便防止Namenode在确认block副本是否足够前过早地开始复制block。这段时间的
Namenode就是处于所谓safemode状态。处于safemode的Namenode也是HDFS集群的只读模型，此时不允许任何对文件系统或者
block的修改。正常情况下，Namenode会在开始后自动退出safemode。如果有需要，HDFS可以通过'bin/hadoop
dfsadmin
-safemode'命令显式地进入safemode状态。Namenode的web首页显示当前的safemode是否打开。更详细的描述和配置可以参
考<a href="http://hadoop.apache.org/core/docs/current/api/org/apache/hadoop/dfs/NameNode.html#setSafeMode%28org.apache.hadoop.dfs.FSConstants.SafeModeAction%29" id="o3qg128"><span class="codefrag" id="o3qg129">setSafeMode()</span>
</a>
方法的JavaDoc。</p>
<p id="y.-h">译
注：详细介绍下safemode的配置参数，在safemode状态，Namenode会等待所有的datanode报告他们自己的block信息，看看
所有的block的副本是否达到最低要求的数目，这个数目可以通过dfs.replication.min参数配置，默认是1,也就是至少要求有一个副
本。当报告合格的Datanode的数目达到一定百分比，Namenode才会离开safemode状态。这个百分比也是可配置的，通过
dfs.safemode.<tt id="v23v">threshold.pct参数，默认是0.999f(也就是要求99.9%的Datanode
合格）。Namenode在合格的datanode数目达到要求的时候，并不是马上离开safemode状态，会有一个扩展时间，让剩余的
datanode来报告block信息，这个扩展时间默认是30秒，可以通过</tt>
<tt id="zwjf">dfs.safemode.extension参数配置，单位是毫秒。</tt>
      </p>
</div>
<p> 
<a name="N1016E" id="o3qg130"></a>
<a name="Fsck" id="o3qg131"></a>
</p>
<h2 class="h3" id="o3qg132"> Fsck </h2>
<div class="section" id="o3qg133">
<p id="o3qg134">HDFS提供了fsck命令用来检测各种各样的不一致性。fsck被设计用来报告各种文件的问题，例如某个文件丢失的
block，block的副本数目是否低于设置等。不同于传统的一般原生文件系统的fsck命令，hdfs的fsck命令并不修正所检测到的错误。通常情
况下，Namenode会自动修正大多数可以被修复的错误，HDFS的fsck不是Hadoop shel的命令，可以通过'bin/hadoop
fsck'执行，可以运行在整个文件系统上或者一个文件子集上。<br id="as3m" />
    
      </p>
</div>
<p> 
<a name="N1017E" id="o3qg137"></a>
<a name="Upgrade+and+Rollback" id="o3qg138"></a>
</p>
<h2 class="h3" id="o3qg139"> 升级和回滚 </h2>
<div class="section" id="o3qg140">
<p id="o3qg141">当升级某个集群的Hadoop的时候，正如任何软件的升级一样，可能会引入新的bug或者不兼容的修改导致现有的应用出
现过去没有发现的问题。在所有重要的HDFS安装应用中，是不允许出现因丢失任何数据需要从零开始重启HDFS的情况。HDFS允许管理员恢复到
Hadoop的早期版本，并且将集群的状态回滚到升级前。HDFS的升级细节请参考 <a href="http://wiki.apache.org/hadoop/Hadoop%20Upgrade" id="o3qg143">upgrade wiki</a>
。HDFS在任何时间只能有一个备份，因此在升级前，管理员需要通过'bin/hadoop dfsadmin -finalizeUpgrade'命令移除现有的备份。下面简要描述了典型的升级过程：</p>
<p id="ctbn">1）在升级Hadoop前，如果已经存在备份，需要先结束（finalize)它。可以通过'dfsadmin -upgradeProgress status'命令查询集群是否需要执行finalize</p>
<p id="kpd3">2)停止集群，分发部署新版本的Hadoop</p>
<p id="ur8l">3）执行新版本的hadoop，通过添加 -upgrade 选项，例如/bin/start-dfs.sh -upgrade</p>
<p id="gmxl">4)大多数情况下，集群在升级后可以正常运行。一旦新的HDFS在运行若干天的操作后没有出现问题，那么就可以结束(finalize)这次升级。请注意，在升级前删除的文件并不释放在datanode上的实际磁盘空间,直到集群被结束（finalize)升级前。</p>
<p id="xsll">5）如果有需要回到老版本的Hadoop，那么可以：</p>
<p id="xsll0">&nbsp;&nbsp; a)停止集群，分发部署老版本的Hadoop</p>
<p id="xsll1">&nbsp;&nbsp; b)通过rollback选项启动集群，例如bin/start-dfs.sh -rollback<br id="ctbn0" />
      </p>
<a name="N101BF" id="o3qg160"></a>
<a name="File+Permissions+and+Security" id="o3qg161"></a>
</div>
<h2 class="h3" id="o3qg162"> 文件许可和安全</h2>
<div class="section" id="o3qg163">
<p id="o3qg164">文件许可的设计与其他平台(如linux)
的文件系统类似。在当前实现，安全被限制在简单的文件许可上。启动Namenode的用户被作为HDFS的超级用户。HDFS的未来版本将支持网络验证，
例如Kerberos方案（译注：MIT开发的一个验证系统）的用户验证以及数据传输的加密。更详细的讨论参考<a href="http://hadoop.apache.org/core/docs/current/hdfs_permissions_guide.html" id="o3qg166"><em id="o3qg167">Permissions User and Administrator Guide</em>
</a>
。           
      </p>
</div>
<p> 
<a name="N101D1" id="o3qg168"></a>
<a name="Scalability" id="o3qg169"></a>
</p>
<h2 class="h3" id="o3qg170">伸缩性<br id="m-bp" />
  </h2>
<div class="section" id="o3qg171">
<p id="o3qg172">Hadoop正运行在成千上万个节点的集群上。      <a href="http://wiki.apache.org/hadoop/PoweredBy" id="o3qg173">PoweredBy Hadoop</a>
列
出了一些部署Hadoop在大规模集群上的组织和机构。HDFS在每个集群上只有一个Namenode节点，Namenode节点上可用内存是当前伸缩性
的主要限制。在非常大规模的集群上，增加HDFS中存储的文件的平均大小，将可以帮助提高集群的大小而不用增加Namenode的内存需求。默认的配置可
能不适合非常大规模的集群应用。<a href="http://wiki.apache.org/hadoop/FAQ" id="o3qg174">Hadoop FAQ</a>
页列出了对于大规模Hadoop集群的配置改进建议。
      </p>
</div>
<p> 
<a name="N101E3" id="o3qg175"></a>
<a name="Related+Documentation" id="o3qg176"></a>
</p>
<h2 class="h3" id="o3qg177">关联文档<br id="n8q_" />
 </h2>
<div class="section" id="o3qg178">
<p id="o3qg179">&nbsp;本用户指南可作为使用HDFS很好的一个起点，在本文档持续改进的同时，有一些非常有价值的关于Hadoop和HDFS的文档资料可供参考。下列资料可作为进一步探索的起点：<br id="ugay" />
</p>
<ul id="o3qg180">
<li id="o3qg181">
        
<a href="http://hadoop.apache.org/" id="o3qg182">Hadoop Home Page</a>
        : Hadoop一切的起始页。
      </li>
<li id="o3qg183">
        
<a href="http://wiki.apache.org/hadoop/FrontPage" id="o3qg184">Hadoop Wiki</a>
        :由社区维护的wiki文档。</li>
<li id="o3qg185"> 
<a href="http://wiki.apache.org/hadoop/FAQ" id="o3qg186">FAQ</a>
 from Hadoop Wiki.
      </li>
<li id="o3qg187">
        Hadoop <a href="http://hadoop.apache.org/core/docs/current/api/" id="o3qg188">
          JavaDoc API</a>
.
      </li>
<li id="o3qg189">
        Hadoop User Mailing List : 
        <a href="mailto:core-user@hadoop.apache.org" id="o3qg190">core-user[at]hadoop.apache.org</a>
.
      </li>
<li id="o3qg191">
         浏览<span class="codefrag" id="o3qg192">conf/hadoop-default.xml</span>
文件，它包括了当前可用配置变量的概括介绍。
      </li>
</ul>
</div>
<p><br id="o3qg193" />
</p>
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/topic/228132#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 14 Aug 2008 20:27:56 +0800</pubDate>
        <link>http://www.javaeye.com/topic/228132</link>
        <guid>http://www.javaeye.com/topic/228132</guid>
      </item>
      <item>
        <title>ActionView 的魔术：ERB &amp; Binding</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hozaka.javaeye.com">hozaka</a>&nbsp;
          链接：<a href="http://www.javaeye.com/topic/227618" style="color:red;">http://www.javaeye.com/topic/227618</a>&nbsp;
          发表时间: 2008年08月13日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>Rails 作为一个 MVC 框架，其核心包括三个模块：ActiveRecord，ActionController 和 ActionView。今天这篇博文的主角是 ActionView，解开模板系统的魔术。<br />

<br />

通常情況下，通过 scaffold 已经能够建立简单的、包含CRUD基本功能的页面，完全不需要手动修改 view 的代码。即使不使用 scaffold ，Rails 也提供了众多的辅助方法，创造一个功能丰富的动态页面简直是易如反掌。但是，会用不代表深入理解，最近有朋友问我这些问题：</p>
<ol>
<li>为什么编辑一个对象需要在 Controller 创造一个实例变量</li>
<li>View 通过什么方式访问这些实例变量的</li>
<li>那么多表单辅助方法，都是需要提供 object_name, method 两个参数，怎么就变成实例变量的值了</li>
</ol>
<p>相信大部分的 Rails 程序员手边的书都是《Agile Web Development with Rails》，书中提到这一点的时候一笔带过，只是说 Rails 在这里用了一个小魔术。这里，我们就来揭开这个魔术吧！</p>
<p>&nbsp;</p>
<h4>Part 1. Template Files - 模板文件<br />
</h4>
<p>当一个 action 需要返回一段 html 片段的时候，我们需要建立一个模板文件。根据不同的版本、请求类型，模板文件的文件名也各不相同，从早期版本的 action_name.rhtml ，到现在的 action_name.text.html.erb ，以及扩展的 rjs，在扩展名中都包含了一个关键字：r / erb。它就是 Rails 模板系统的关键：ERb。</p>
<p>&nbsp;</p>
<h4>Part 2. ERb - Ruby Templating<br />

</h4>
<p>ERb - 嵌入式 Ruby (<a href="http://ruby-doc.org/stdlib/libdoc/erb/rdoc/index.html">http://ruby-doc.org/stdlib/libdoc/erb/rdoc/index.html</a>

)，是 Ruby 语言提供的一个基本扩展。它支持在字符串中嵌入 ruby 代码片段。看上去似乎很神秘，其实我们每天都用到，下面这种形式一定不陌生吧？</p>
<pre name="code" class="ruby">&lt;p&gt;
  &lt;%= Time.now %&gt;
&lt;/p&gt;</pre>
<p>没错，正是因为 ERb 的存在，使得模板中可以动态地引用对象的属性。</p>
<p>&nbsp;</p>
<h4>Part 3. Instance Variables of Ruby - Ruby 的实例变量</h4>
<p>让我们回顾一下 Ruby 语言的基本要素之一：实例变量。通常我们通过</p>
<pre name="code" class="ruby">@time = Time.now</pre>
<p>的形式创造一个实例变量。这里我们不重新解释对于&ldquo;实例变量&rdquo;的定义，但是必须牢记一点，正如字面所见，实例变量的作用域是当前实例内，也就是说，只有在实例的内部，才可以直接对实例变量进行读写操作（扩展的访问子方法等等不在讨论范围之内）。那么，为什么 Rails Controller 里创建的实例变量能够在 View 里面访问呢，不是自相矛盾吗？</p>
<p>&nbsp;</p>
<p>Ruby 作为一种动态语言，因为其&ldquo;开放&rdquo;的特点，使很多原本不可能的编程模式变为可能。比如，通过 Open Class 的特性，你可以动态的为对象注入新的方法定义，或者改写方法的逻辑，或者，通过不同的方法可以在对象的外部访问对象的内部的实例变量。经常使用 console 的朋友可能会了解其中的一种方式：Object#instance_variable_get / Object#instance_variable_set 方法。举个简单的例子：</p>
<pre name="code" class="ruby">class User
  def initialize( name )
    @name = name
  end
end

user = User.new( &quot;Jack&quot; )
user.name #=&gt; raise NoMethodError
user.instance_variable_get('@name') #=&gt; &quot;Jack&quot;
user.instance_variable_set('@name', 'Tom')
user.instance_variable_get('@name') #=&gt; &quot;Tom&quot;</pre>
<p>可以看到，在没有任何访问子的情况下，我们用这种方式对一个实例变量进行读写操作。</p>
<p>&nbsp;</p>
<p>除了这种简单的方式，还有另外一种进阶的方式，也是 ERb 常用的一种方式：Binding</p>
<p>&nbsp;</p>
<h4>Part 4. Binding</h4>
<p>Binding (<a href="http://ruby-doc.org/core/classes/Binding.html">http://ruby-doc.org/core/classes/Binding.html</a>
) 是 Ruby 语言的自身的一个特性，在任何对象内，self.binding 方法都会返回一个当前对象关联的 binding 实例。不精确的说，binding 对象可以理解成为当前对象的完整的上下文环境。文档中已经包含了一些示例代码，帮助大家理解 Binding 对象的作用。最重要的一点是：既然是当前对象的完整上下文环境，自然就包括了对象的实例变量。</p>
<p>&nbsp;</p>
<p>那么 Binding 在 ERb 中扮演一个什么样的角色？是一个运行环境的提供者。</p>
<p>&nbsp;</p>
<p>回到我们最初的问题，Rails 在 ActionView 中使用了什么样的魔法？答案就是 ERb 和 Binding。首先，获得当前实例的 binding，自然，binding 内也包括了实例变量；紧接着，ERb 允许将模板的内容动态地，绑定到另一个环境中运行。我们依然用刚才的 User 的例子来说明这一点：</p>
<pre name="code" class="ruby"># A simple template string
template = &quot;Hello, &lt;%= @user.name %&gt;&quot;

@user = User.new( &quot;Jack&quot; )

# Get binding
binder = self.send( :binding ) # calling a private method

# Rendering template
puts ERB.new( template ).result( binder )
#=&gt; &quot;Helo, Jack&quot;</pre>
<p>虽然例子不是非常恰当，但是足以展示 Binding 和 ERB 的用法。我们可以看到，ERB#result 方法将模板字符串绑定在另外一个环境中运行，而这个环境包含了我们创建的 @user 实例变量，因此，模板中的 @user.name 得到了正确的值。</p>
<p>&nbsp;</p>
<p>这就是 ERB 的真面目，也是为什么在 ActionView 中能够访问到 Controller 里实例变量的原因。感叹一下动态语言的强大吧！以上只是非常粗糙的讲述ERB的使用，Rails 所做的魔法远不止如此，如果有兴趣，可以查看 Rails 源代码，对于深入学习 Rails 框架也有很大的好处。</p>
<p>&nbsp;</p>
<p>最后，补充一下 ERB 的应用场景。虽然在普通的需求中，ActionView 所做的已经足够，但是某些情况还是需要创造独立的模板系统。比如某个场景，客户要求提供一个完全自定义的模板系统，这个时候 ERB 就大显身手了。只需要将数据源载入实例变量中，并且在使用手册里列出可以访问的方法，即使完全不懂 Ruby 语言也可以写出使用这套简单的模板系统了。</p>
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/topic/227618#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 13 Aug 2008 18:53:49 +0800</pubDate>
        <link>http://www.javaeye.com/topic/227618</link>
        <guid>http://www.javaeye.com/topic/227618</guid>
      </item>
      <item>
        <title>结合Maven2进行J2EE项目构建</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://orpheus.javaeye.com">orpheus</a>&nbsp;
          链接：<a href="http://www.javaeye.com/topic/230265" style="color:red;">http://www.javaeye.com/topic/230265</a>&nbsp;
          发表时间: 2008年08月19日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>一.背景</strong><br />Maven2 的基本原理很简单，采用远程仓库和本地仓库以及  pom（project object model）.xml  ，将  pom.xml  中定义的  jar  文件从远程仓库下载到本地仓库，各个应用使用同一个本地仓库的  jar  ，同一个版本的  jar  只需下载一次，而且避免每个应用都去拷贝  jar  。如图  1  。同时它采用了现在流行的插件体系架构，只保留最小的核心，其余功能都通过插件的形式提供，所以在执行  maven  任务时，才会自动下载需要的插件。这个特性也为客户系统的升级带来的很大的方便，客户每次升级的时候可以使用maven的远程部署功能自动下载最新的系统组件（jar），并重新打包部署，很大程度的减少的系统升级的工作量。<br />理解Maven的原理，可以参考 Pear ――ＰＨＰ扩展与应用库（ the PHP Extension and Application Repository ），其原理非常类似，都有一个官方库，都是微内核，通过网络将需要的文件下载到本地，通过官方仓库将相应的类库进行统一管理。<br />     Maven2的基本安装方法网上很多，就到<a href="http://maven.apache.org" target="_blank">http://maven.apache.org</a>下载一个最新版，解压后即可，如果需要在命令行运行，还需要设置一些环境变量，网上的资料很多，这里就不多说了。总之，安装成功后当你在命令行下执行maven -version后正确显示当前maven的版本即可。<br />     我们在项目中结合maven的进行开发的主要思路：<br />   1.建立支持Maven2的开发框架，框架中结合了一些项目功能和工具类，并且此框架本身是一个eclipse工程，支持使用eclipse IDE的开发，并通过CVS可进行团队协作。<br />   2.在Maven2的pom.xml中制定开发框架的依赖包，并建立依赖包的团队管理本地服务器，使团队中的包依赖得到统一管理。<br />   3.每日下班后，在构建服务器上每日从cvs上下载各个团队开发人员的代码，统一进行集成构建和测试。由于是每日构建，所以发现的bug可及时反馈给开发人员进行修正，避免了一般开发过程中的bug长时间遗留的情况。<br /><br /><strong>二.实施过程</strong><br /><br />为了实现上述思路，我们分几步实施：<br /><br /><strong>1.首先需要构建一个系统的开发框架</strong>，<br />    我们有两种方式构建，<br />    其一是从零开始构建全新的框架，进入commond line，cd 到一个目录 ，执行<br /> <br /> <br /><pre name="code" class="java">mvn archetype:create -DgroupId=com.mycompany.app -DartifactId=my-webapp -DarchetypeArtifactId=maven-archetype-webapp</pre><br /><br />执行完毕后接下来cd 到项目目录my-webapp下，执行<br /><pre name="code" class="java">mvn package
mvn eclipse:eclipse
</pre><br /><br />之后，打开eclipse，到其目录下导入项目，并手动编辑pom.xml文件，设定指定的jar包，比如加入一个jwebunit的jar包，我们需要在pom中添加一段：<br /><pre name="code" class="java">&lt;dependency>
            &lt;groupId>jwebunit&lt;/groupId>
            &lt;artifactId>jwebunit&lt;/artifactId>
            &lt;version>1.2&lt;/version>
            &lt;scope>test&lt;/scope>
            &lt;exclusions>
                &lt;exclusion>
                    &lt;groupId>rhino&lt;/groupId>
                    &lt;artifactId>js&lt;/artifactId>
                &lt;/exclusion>
            &lt;/exclusions>
        &lt;/dependency></pre><br /><br /><br />其中指定了包的名称，版本，使用的范围域等，pom.xml设置方式网上也是一堆一堆的，具体的可以自己搜搜。同时我们也可以使用maven2在 eclipse中的插件进行编辑，很方便，就不用记住那些该死的标签了。插件下载地址 <a href="http://m2eclipse.codehaus.org" target="_blank">http://m2eclipse.codehaus.org</a> /update，将这个url填入到eclipse的Help-》Software Updates->find&install中新建一个插件下载地址的对话框中即可下载。<br />这种方式是完全自定义一个全新的工程后再进行框架搭建，比较累，尤其是添加依赖包的时候，需要根据自己的项目需要一个一个添加，很烦人，所以我们使用的第二个方法就直接找了一个现成的，到 Appfus 的网站<a href="http://appfuse.org/" target="_blank">http://appfuse.org/</a> 根据项目需要下载了一个项目框架作为原型，我们使用的是appfuse-light-webwork-ibatis- 1.8.2（webwork2.26,spring2.0,ibatis2.0），如果你使用的是其他的的技术，如 struts2，hibernate....直接到网站上下载一个相应的框架即可。appfuse框架使用maven2作为基本构建工具，其中自带的 pom.xml也替开发人员写好了，中所定义的依赖包可满足一般的开发需要，如需要自己指定的包，那么直接在其pom.xml中添加即可。要将这个框架作为eclipse工程使用，需要在解压后的框架目录下执行：<br /><br /><pre name="code" class="java">mvn eclipse:eclipse -DdownloadSources=true</pre><br /><br /><br />这个命令会将工程将框架转换为eclipse工程，并从远程下载jar包到本地仓库（window下是(C:\Documents and Settings\${username}\.m2\repository），之后执行：<br /><br /><pre name="code" class="java">mvn -Declipse.workspace=&lt;path-to-eclipse-workspace> eclipse:add-maven-repo</pre><br /><br /><br />其中path-to-eclipse-workspace是本机的eclipse的worksapce的路径。执行后maven会在eclipse中建立一个M2_REPO环境变量，并将其中所有的jar包引入到工程中，完全自动化，十分方便。<br />     打开eclipse修改开发中的环境变量（我们项目中使用了Myeclipse插件），找到相应的工程，发现框架中已有一些代码，这是appfuse提供给开发人员的示例代码，我们可以按照自己以前项目的积累进行对框架进行完善，形成一套自己的开发框架，之后设置工程环境变量，在该项目中右键 ->Myeclipse->add web capabilities->指定该工作空间下的Src/main/webapps作为WEB工程的根路径，并指定修改JAVA Build Path中<br /><pre name="code" class="java">src/main/java
src/main/resource
src/test/java
</pre><br />的三个soucrefolder的outputpath 为scr/main/webapp/WEB-Inf/class，这样设置的目的是便于开发人员在本地进行部署测试，否则按照appfuse原有的工程设置是不能进行顺利部署的。<br />至此，我们已经将Maven2结合到项目中，一开始可能对目录结构有些不适应，毕竟这是maven提供的项目框架格式，可以修改为自己习惯的，但是不建议这样做。设置完成后，cd到项目路径下，运行<br /><pre name="code" class="java">mvn test
mvn package
mvn install</pre><br /><br /><br />三个命令，均成功后，可上传到cvs/svn上面去，共享给项目组人员，各开发人员可直接使用，但有可能M2_REPO环境设置路径不一样（C:\Documents and Settings\${username}\.m2\repository，毕竟不是所有人都把系统装在C盘），需要手动修改一下。<br /><br /><strong>2.建立开发团队内部仓库</strong><br />          为了便于团队的依赖包管理，我们不能全部使用官网的仓库，毕竟上面不具备我们项目开发所需要的所有的依赖包，所以我们需要为自己的团队建立一个内部仓库，可以自己管理所需的依赖包，建立一个内部仓库也十分简单（附录中我们会使用artifactory进行开发内部库建立）：<br /><br />首先需要一个 http server ，找台服务器装上 apache 就行。放一个空的 maven 目录到 htdocs 下，假设服务器 ip 为 192.168.0.1 ，确认能用 <a href="http://192.168.0.1/maven" target="_blank">http://192.168.0.1/maven</a> 访问到。<br /><br />copy 本地仓库的jar包到服务器：对于 windows xp 来说一般在 C:\Documents and Settings\ ％ username%\.m2 下，其中％ username ％为操作系统登录用户名。这时你可以看到 ${user.home}/.m2/ 下有个 repository 目录，里面有很多的项目相关 jar ，目录按 groupId/ artifactId/version 排好。把 repository 目录整个拷贝到 apache 服务器的 maven 目录下，如果需要官方缺少的 jar 或公司内部 jar ，仿照这个目录结构，做好 jar 放到 maven 目录下。或者把包copy到本地，运行：<br /><pre name="code" class="java">mvn install:install-file -Dfile=X:/path/mail-1.3.jar -DartifactId=javamail -Dversion=1.3.1 -Dpackaging=jar -DgroupId=javamail</pre><br /><br /><br /><br />开发人员要使用内部仓库，只需修改本地工程pom.xml ，在 repository 配置后加上：<br /><br /><pre name="code" class="java">&lt;repository>
      &lt;id>companyName&lt;/id>
      &lt;url>http:// ${ip}/maven&lt;/url>
&lt;/repository></pre><br /><strong><br />3.每日构建</strong><br />    为了保证项目质量，尽早的发现项目中的bug，我们需要每日对系统进行构建，这也是我们使用maven的初衷之一，maven的几个命令就可帮助我们完成这项任务，当然我们可以使用持续构建工具与maven结合实现定时自动构建。构建方式：<br /><pre name="code" class="java">mvn test
mvn package
mvn install</pre><br /><br />maven 会自动编译，测试，运行所有的testcase，这也要求我们的开发人员一定要按照规则编写单元测试代码，否则每日构建的意义就不大了。appfuse框架中提供了很好的单元测试代码，包括针对数据库层，业务逻辑层，web展示层等等，如果我们能很好的编写这些单元测试，那么对于系统后续的缺陷管理和控制是大有裨益的。<br /><br />构建完成后或构建时需要对最新版本的项目进行部署，便于次日安排测试人员进行测试，maven提供多多种部署方式，在pom.xml进行项目的部署配置，不同的部署方式根据协议的不同，配置方式也有所差异：<br />以文件方式部署<br />  <br /><pre name="code" class="java">&lt;project>
        [...]
        &lt;distributionManagement>
            &lt;repository>
                &lt;id>proficio-repository&lt;/id>
                &lt;name>Proficio Repository&lt;/name>
                &lt;url>file://${basedir}/target/deploy&lt;/url>
            &lt;/repository>
        &lt;/distributionManagement>
        [...]
    &lt;/project></pre><br /><br />以SSH2方式部署<br />  <br /><pre name="code" class="java">&lt;project>
        [...]
        &lt;distributionManagement>
            &lt;repository>
                &lt;id>proficio-repository&lt;/id>
                &lt;name>Proficio Repository&lt;/name>
                &lt;url>scp://sshserver.yourcompany.com/deploy&lt;/url>
            &lt;/repository>
            &lt;/distributionManagement>
        [...]
    &lt;/project></pre><br /><br /> 以SFTP方式部署<br /> <br /><pre name="code" class="java">   &lt;project>
    [...]
    &lt;distributionManagement>
        &lt;repository>
            &lt;id>proficio-repository&lt;/id>
            &lt;name>Proficio Repository&lt;/name>
            &lt;url>sftp://ftpserver.yourcompany.com/deploy&lt;/url>
        &lt;/repository>
    &lt;/distributionManagement>
    [...]
    &lt;/project></pre><br />以扩展SSH方式部署<br />   <br />目前为止上述3中方式已经被Maven包含，所以只要distributionManagement就可以了，但是使用扩展SSH命令部署的话你不仅需要配置distributionManagement还需要一个build extension，如下<br />   <pre name="code" class="java"> &lt;project>
        [...]
        &lt;distributionManagement>
            &lt;repository>
                &lt;id>proficio-repository&lt;/id>
                &lt;name>Proficio Repository&lt;/name>
                &lt;url>scpexe://sshserver.yourcompany.com/deploy&lt;/url>
            &lt;/repository>
        &lt;/distributionManagement>
        &lt;build>
            &lt;extensions>
                &lt;extension>
                    &lt;groupId>org.apache.maven.wagon&lt;/groupId>
                    &lt;artifactId>wagon-ssh-external&lt;/artifactId>
                    &lt;version>1.0-alpha-6&lt;/version>
                &lt;/extension>
            &lt;/extensions>
        &lt;/build>
        [...]
    &lt;/project></pre><br />    The build extension specifies the use of the Wagon external SSH provider, which does the work of moving your files to the remote server. Wagon is the general purpose transport mechanism used throughout Maven.<br /><br />以FTP方式部署<br />  <br /><pre name="code" class="java">&lt;project>
        [...]
        &lt;distributionManagement>
        &lt;repository>
            &lt;id>proficio-repository&lt;/id>
            &lt;name>Proficio Repository&lt;/name>
            &lt;url><a href="ftp://ftpserver.yourcompany.com/deploy&lt;/url>" target="_blank">ftp://ftpserver.yourcompany.com/deploy&lt;/url></a>
        &lt;/repository>
        &lt;/distributionManagement>
        &lt;build>
            &lt;extensions>
                &lt;extension>
                &lt;groupId>org.apache.maven.wagon&lt;/groupId>
                &lt;artifactId>wagon-ftp&lt;/artifactId>
                &lt;version>1.0-alpha-6&lt;/version>
                &lt;/extension>
            &lt;/extensions>
        &lt;/build>
        [...]
    &lt;/project></pre><br /><br />一旦你配置好了相应的POM你可以执行下列命令来开始部署：<br />mvn deploy<br /><br />同时也可通过执行一下命令生成此项目的站点报告，供项目参与人员使用。<br />mvn site<br /><br /><br /><strong>三. 结论<br /></strong><br />    maven的强大显而易见，有很多其他的特性本文没有提及，如对各类插件的支持，以及对项目模块划分和继承关系的管理，这些都是maven的特性，也是 maven对项目生命周期的详尽诠释，有兴趣深入的TX可以下载我在附件中提供的教程《Better Builds With Maven2》.同时我也提供我根据appfuse建立的一套项目框架，可在myeclipse环境下使用，大家可以共同探讨完善。<br /><br /><strong>附1：使用artifactory为Maven2团队开发建立内部开发仓库详解</strong><br />在真正使用Maven后是为团队进行定制，所以我们不应使用官网的开发库，应在本地建立一个内部开发库对团队的jar包进行管理，所以我们首先搭建一个内部库环境，除文章上面所述的搭建Apache服务器方法外，我们还可以使用artifactory(下载地址：<a href="http://www.jfrog.org/sites/artifactory /latest/" target="_blank">http://www.jfrog.org/sites/artifactory /latest/</a>)，一个很好的maven内部库的应用系统，下载后执行bin目录下的artifactory.bat命令即可。启动后可访问控制台http://内部库ip:8081/artifactory/验证服务是否成功启动。默认的用户名为admin，密码为password。artifactory最重要的是可配置第三方jar包，在deploy artifacts中加入并制定其groupId和artifactId即可<br />（不要忘记更改本地的pom.xml文件引入新加的jar包）。<br />在开发端我们需要更改全局配置文件setting.xml文件，将工程中setting.xml放入本地maven2->conf目录下，配置内部仓库的地址，只需要在setting.xml的mirrors元素中加入以下配置：<br /><pre name="code" class="java">&lt;mirror>
      &lt;id>emay local&lt;/id>
      &lt;mirrorOf>central&lt;/mirrorOf>
      &lt;name>emay local artifactory&lt;/name>
      &lt;url>http://内部库ip:8081/artifactory/repo&lt;/url>
    &lt;/mirror>
</pre><br />这里要注意的是，在加入这段代码后我使用的appfuse框架中自带的应用服务器tomcat6进行构建，不能正常运行，报tomcat出错，把这段去掉或者在pom.xml中将应用服务器改为tomcat5.5后运行正常。看来maven还是有不少bug需要改进。<br /><br />配置完成后再运行mvn install即可正常进行构建，maven会从本地内部库中寻找项目所依赖的jar包。运行mvn clean清除maven生成文件。<br /><br /><br /><br /><strong>附2：maven2命令大全</strong><br /><br />    validate，验证工程是否正确，所有需要的资源是否可用。<br />    compile，编译项目的源代码。<br />    test-compile，编译项目测试代码。<br />    test，使用已编译的测试代码，测试已编译的源代码。<br />    package，已发布的格式，如jar，将已编译的源代码打包。<br />    integration-test，在集成测试可以运行的环境中处理和发布包。<br />    verify，运行任何检查，验证包是否有效且达到质量标准。<br />    install，把包安装在本地的repository中，可以被其他工程作为依赖来使用<br />    deploy，在整合或者发布环境下执行，将最终版本的包拷贝到远程的repository，使得其他的开发者或者工程可以共享。 <br />    generate-sources，产生应用需要的任何额外的源代码，如xdoclet。
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/topic/230265#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 19 Aug 2008 22:14:44 +0800</pubDate>
        <link>http://www.javaeye.com/topic/230265</link>
        <guid>http://www.javaeye.com/topic/230265</guid>
      </item>
      <item>
        <title>项目管理工具-streber中文资料-实践使用笔记 </title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://orpheus.javaeye.com">orpheus</a>&nbsp;
          链接：<a href="http://www.javaeye.com/topic/230399" style="color:red;">http://www.javaeye.com/topic/230399</a>&nbsp;
          发表时间: 2008年08月20日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><span style="font-size: small;"><strong>1.Streber背景介绍：</strong>
</span>
<br />
    Streber是一个基于WEB的在线项目协调工具，它融合了wiki的思想和项目协作管理机制，成为了一个适用于小型团队的可以贯穿真个项目生命周期的项目协作和管理工具。<br />
    Streber的出现的历史并不长，作者为德国人，网名pixtur，其产品原型为05年一个作者的在线office系统，在进行这个在线office系统开发工作当中，作者发现其思路可以很好的成为一个在项目开发的协调组织的工作平台。于是作者对原有产品的不断的修改和完善其在线协作理念，乃至到最后从原有产品中完全剥离出来成为独立的开源项目。<br />
    &ldquo;Streber&rdquo;在德语中意为是一个具有高度热情和经理旺盛的人，按我们白话说就是一得瑟的人....它是基于PHP开发的项目，目前的最高版本是 0.803，基于php5。同时Sreber是基于GPL开源协议，这点一定要注意，这意味着你如果使用其进行修改和发布也要遵循GPL协议，把你的修改的代码进行开源发布。</p>
<p><img src="http://zhangmeng.blog.51cto.com/attachment/200807/200807191216457710137.jpg" height="571" alt="Strber" width="993" />
<br />
                                                          图1-1<br />
Streber截图<br />
-----------------------------------------------------------------------------------------------------------------------<br />
<span style="font-size: small;"><strong>2.Streber特点概述：</strong>
</span>
<br />
1.基于wiki的方式<br />
    Streber中采用基于wiki的管理方式和语法，采用多人协作的方式进行项目的管理和文档的编写，项目中的任务和文档资源无论创建者是谁，其他人都可以方便的修改和完善，在这种方式下项目人员自由度很高，极大的提高了项目协作的效率，但需要求人员在遵守一定的项目规则下进行项目协作。基于wiki的思想更可以使知识管理与项目紧密的联合起来，不用再为项目搭建一套知识管理系统。<br />
2.简单而灵活项目管理协作系统<br />
    Streber类似于jira，也是面向issue的项目协作系统，使用方便，操作简单，系统中常用操作基本元素就是task（也称为issue）和 comment，并且可灵活进行运用，可以作为贯穿项目生命周期中的支持系统，也可以单独作为缺陷跟踪系统，甚至可以单独作为项目知识管理系统使用。<br />
3.label标签分类功能：<br />
    Streber中的任务类型可以是一般Task类型，DOC类型，bug类型，idel类型，<br />
feature类型，research类型，refactor类型等等，我们可以通过这些类型对任务进行表示和搜索。<br />
4.全面的角色及其权限分类<br />
    Steber中的默认角色按照一般项目类的角色分为项目成员，系统管理员，项目经理，开发人员，方案人员，测试人员，客户，受信客户，以及Guest集中角色，各个角色的默认权限不同，如有特殊情况，管理员可以为每个人定制权限。这些角色是系统自带的，如果要添加自己项目的权限，可以通过直接修改数据库数据实现。<br />
5.邮件通知和RSS支持<br />
    在项目中的每个task的辨变更和更新记录都会被详细的记录下来，从更改者的角度，这些在进行变更和任务更新的时候可以选择是否将更新邮件通知此此任务的相关干系人，保证信息的及时同步。 并且任务的相关联系人可以使用RSS的方式对项目的变更和更新记录进行订阅，实现主动获取变更信息的功能。<br />
6.替代sharepoint等项目门户网站功能<br />
    我们可以使用Streber代替项目门户的功能，可以将在各个项目中中Contact Info、Baselined Schedule、News元素放在项目主页上，使项目信息沟通全面通畅。Streber支持，英，德，法，西班牙，意大利等12种语言的系统界面，可惜的是目前还不支持中文的系统界面。<br />
-----------------------------------------------------------------------------------------------------------------------<br />
<br />
<strong><span style="font-size: small;">3.Streber应用介绍</span>
</strong>
<br />
3.1安装：<br />
   Streber的安装十分简单，简单分为以下几个步骤：<br />
1.环境准备：准备一个Mysql，一个Apache服务器，并从http://www.streber-pm.org/index.php?go=fileDownload&amp;file=6434下载一个Streber的最新版，然后直接copy到htdocs目录下。<br />
2.启动Apache，运行index.php。根据提示填写管理员用户名，密码，数据库地址，用户名，密码。<br />
3.删除安装目录下的install文件夹<br />
<br />
3.2应用：<br />
    使用过其他项目协作工具的人都会发现Streber的应用操作十分简单，它主模块分为Home，Project，People，Company几个部分，一般在进行项目开发的时候我们只会经常用到项目模块中的功能。<br />
<br />
3.2.1 Home：<br />
    在Home模块我们可以管理与自己相关的项目，任务，为添加评论，查看最近的任务更新列表，书签，和effort（人工管理）<br />
3.2.2 Project:<br />
    Projec是我们在进行项目协作的时候最为经常使用的模块，也是Streber系统的核心模块。在Project中我们可以进行项目的定义，项目中任务的分配，项目相关文档的撰写，项目后期缺陷的跟踪调试，项目进度控制，项目任务的变更和进度更新等等，所有项目的行为活动都是在这里进行定义。每个Project中针对项目的管理又细分为 Task，Topic，Milestone，Version，Files，Effors，Chages几个元素进行管理，为了便于理解，我们可以把所有这些元素都看为不同类型的task。当我们建立Project的时候，我们可以根据自己的需要设定是否保留这些元素。<br />
    Task：Streber中所有项目活动Streber中所有项目活动都是基于Task，不论这个Task可能是个开发任务，或是个Bug，又或者是一个说明文档，设计文档，可以说，它是类似JIRA中的 issue驱动，在Streber中的Task驱动。每个Task都有一个唯一的Streber中所有项目活动都是基于Task，不论这个Task可能是个开发任务，或是个Bug，又或者是一个说明文档，设计文档，可以说，它是类似JIRA中的issue驱动，在Streber中的Task驱动。每个 Task都有一个唯一的TaskID，通过这个TaskID会对应一个唯一的项目URL，这样，我们可以使用这个TaskID作为每次代码check-in的说明，说明此次check-in的目的，就不用写太多的commit日志了。Task的属性分为一下几种：<br />
      1.Task属性： Task中可以指定任务的Milestone（指定的为了实现某个Milestone所做的任务），任务的优先级，任务分配人员，目前的状态(new,open,block,done,approved,closed)，任务完成节点（如果本次无法完成，设定为下个版完成，如果本次可以完成，需要设定在哪个milestone完成的，或者可以不做设定），任务完成类型（如果是task则类型为done，如果为bug则类型为fixed，其他的根据任务类型依此类推）。<br />
      2.Task时间管理：预计正常完成时长，最坏情况下的完成时长（正常时间+buffertime），任务起始时间，任务结束时间。<br />
      3.Task描述:使用wiki格式。<br />
      4.Task显示：任务的缩写（显示在导航栏的名称），任务id（可根据任务所需自由设定，这个是由项目组设定的，比如ex-01）,任务的标签，供以后任务分类和搜索使用，其中包括Bug，Feature，Enhancement，Refactor，Research，Idea，Orgnaize，Wiki，Docu。<br />
    Topic：主题信息管理,Topic在项目中一般起到发布主题类的信息，可以是项目说明文档，项目会议记录，项目需求变更计划，项目内部新闻等等文档类信息，同样可作为项目知识管理的功能使用，比如代码规范，项目开发规范。<br />
     Milestone：里程碑计划管理，项目的里程碑管理，同Task设置类似，Milestone中可以设置负责人，时间管理，任务描述，显示描述，Milestone设置后可以与每个task进行关联，上面已经提到，每个task的目标都是要针对于某个具体的Milestone的，所以把Milestone看做是一个大的 Task，由无数小的task的集合形成。Streber的思想还是比较严谨的开发模式，具体的使用还是看各个项目了。<br />
     Version：版本计划管理,里面元素设定与Milestone和Task类似，我们在项目中可以把Version看做Milestone的父类，把Milestone看做Task的父类。在项目周期前期，按照这种方式，先设定Version，再设定这个Version中的Milestone，在设定每一个具体的 Task，给其指定所属的Milestone。<br />
     File：项目文件管理，与此项目有关的资源文件可以同一放在这里进行管理，与一般的在线系统类似，Streber的理念就是使平台达到能将项目周期活动都集中在此的目的，所以此功能虽然简单，但是还是相当有用的，我们可以把项目工具，框架，各类前期说明书等等文件都在此进行资源共享，统一管理。具体好处就不多说了。<br />
     Efforts：字面上的意思是人工管理，感觉其实就是在在项目任务中计算人员工作量的工具，在项目开发和人工绩效考核的时候应该有一定作用，同时Streber提供统计Efforts功能，但似乎这个功能还未完全完善，建议可以先不用使用。<br />
     Changes：变更记录，所有任务和文档的内容更新和状态更新以及评论添加都会在这里可以进行查看详细的信息记录<br />
<br />
 <br />
3.2.3 People: 人员管理 ，在这里设置项目人员信息，人员类型，默认的人员类型系统管理员，项目经理，开发人员，方案人员，测试人员，客户等项目基本干系人。我们在这里对项目中所有干系人进行管理，并设定相关人员所属的公司。个人认为在项目中，无论大小，上述的这些角色一定要尽量全员参与到系统的使用中来。不同的干系人具有不同的人员权限的设置，系统在初次安装之后只有管理员有用最高权限。权限设置比较简单，只有针对项目，人员，登陆，等相关权限，但对于一般的小型项目足够使用了。<br />
<br />
3.2.4 Companies:公司管理 在这里可设置公司信息，公司类型，公司类型可为<br />
一般客户，高活跃度客户，供应商，各做伙伴。在建立项目和建立人员的时候都可以设定所属公司。<br />
3.2.5 Search:搜索功能，在所搜的关键字前加入&lsquo;！&rsquo;可直接跳到最佳结果页面中。<br />
----------------------------------------------------------------------------------------------<br />
<strong><span style="font-size: small;">4.Streber在项目中的实践：</span>
</strong>
<br />
    我们基本上了解了Streber的功能，可以看出系统使用相对大型商业软件要简单的多，相对缺少了很多纷繁复杂的工作流程，细化流程和统计功能。但是对于中小型项目来说，Streber已经抓住了项目中的关键要素，只要使用方法得当，将项目管理思想很好的融合到工具中，Streber可以使一般项目的质量和开发过程得到一个很大层次的以高。下面就我在项目管理过程中的经验与结合Streber的一些实践方法分享出来，希望大家能提出宝贵意见并且能将自己在管理过程中的经验或使用工具的经验分享出来。<br />
<br />
4.1 建立新项目<br />
        建立新项目的时候，注意项目描述的重要性，项目描述是显示在项目首页最醒目的地方，所有的项目干系人每次进入项目的时候都会看到，在项目描述中将项目的目标和意义写好，稍微夸大也是允许的，要让让开发人员认识到他们所做的事情的重要程度，做到信息对称，我们的项目团队对项目的成功有共同的认识，使我们项目顺利完成的第一步。在项目关闭后，我们将项目总结再补充到项目描述之中，整个项目周期完成。<br />
<br />
4.2  建立干系人管理和沟通机制<br />
        包括客户，公司领导，开发人员，市场人员等和项目相关的一切人员，都在系统中设立相应的账号使各方人员均能参与其中。我们在其中一个项目中为客户开放阅读（RSS订阅）与编辑权限（编辑权限看情况而定），可以让其参与其中，增加客户的团队归属感，使其了解团队的各个方面，包括项目进度汇报，各类文档资料，潜在困难，资源需求，使其主动帮助项目向更好的方式发展。<br />
4.3   建立项目知识库<br />
        建立知识库的好处众所周知，知识库已经越来越成为现在软件项目过程中的一个重要组成部分。我们使用Project中的<br />
Topic功能实现项目中的知识库的功能。我们在知识库中记录项目的代码规范，测试用户编写，项目工具经验，页面设计规范，文档专业规范等一般基础性知识点，同时在一些项目中，可以直接在知识库中进行项目说明书，开发设计文档，项目风险列表等开发类文档。使用 Project中的Folder功能可以进行文档的分级显示和管理。<br />
<br />
4.4沟通管理<br />
     Streber本身就是一个很好的沟通管理工具，我们在项目中主要使用其作为一个被动信息共享平台。开发人员使用Topic功能进行针对项目周报和月报的撰写，具体方法可以让汇报与上面的知识管理使用不同的Folder，在汇报的folder中为每一个开发人员（使用姓名或员工编号）建立一个周汇报的 topic和一个月报的topic。客户，公司领导和项目其他相关人员可以通过邮件或者Streber的RSS功能定期或手动收取这些报告，以了解项目的进度。同时项目经理和高层领导可以在这些汇报中批示自己的意见建议，或鼓励或表扬，对于开发人员的开发热情是一种激励。<br />
     在Streber中，每个任务都有一个TaskID的唯一标识，我们利用这个ID与其他的项目协作工具关联起来，例如我们在每日编译后在Check in到代码库的时候可以将每人负责的的Taskid作为comment提交到代码库。这样每次的提交都会有一个具体的taskid任务与之对应，以后有问题可以根据taskid对代码版本进行针对性的复查，将版本与代码关联统一管理起来。<br />
 <br />
4.5任务流程和任务设置<br />
     任务分配和协作无疑是使用Streber最大的目的之一，在我们的项目实施周期，我们无论采用何种软件开发方式，最终都离不开以下几步： 计划任务（全体项目组人员）-》形成项目阶段界定（milestone，项目组全体成员）-》分析安排任务（全员讨论，项目助理在Project中登记任务）-》任务实施（项目组员打开任务，实施更新进度）-》完成任务，项目经理审核-》审核通过，项目助理关闭此任务。注意在进行任务分配和登记的时候注意任务一定是具体的，可以验证的。 Strber中的任务具有new，open，done，apprived，closed几个状态，我们对应任务在上述不同阶段使用不同的状态进行标示，同时任务在分配登记的信息只是任务的一些基本信息，描述，优先级，状态，类型（Label，新的开发任务可以为task或feather，如果是），至于任务的时间就需要和每个被分配人员商议讨论，最好由本人进行估算，之后再进行登记。<br />
     在项目实施过程中，注意要让团队成员养成每日对任务情况进行汇报的习惯，这个习惯如同每日编译和每日check in一样重要，目的是事project中的任务进度一定要反映最近的情况。具体的汇报方式可以使用评论的方式，每个成员对本人每日的工作在相应任务下面以 add comment(评论)方式加入简短的总结，并根据自己的任务完成情况更新项目情况，包括项目完成进度百分比，项目状态，如果遇到任务变更或突发实践也可以直接更改任务的周期，并最好在comment中说明一下。在任务完成并审核后，登记人员对任务进行关闭，关闭的时候需要选择此任务的关系原因，如果是新功能（feather）的task，直接选择done即可，如果是bug类型的任务，则需要选择fixed，其他类型可根据任务的状态和类型不同选择相应的关系状态。<br />
     <br />
     在缺陷跟踪方面，Streber中对缺陷的跟踪方式与任务一样，事实上这里的缺陷就是一种类型为BUG的任务，其发布方式和流程与其他任务没有太大区别，只需要注意task的类型，label的类型等设置即可。Tester（测试）在进行缺陷登记的时候，注意是否写清了Bug的重现步骤，并且保证所有的缺陷都是有登记的人进行验证后进行关闭。我们在登记缺陷的时候，也要写清楚这个缺陷的优先级，开发人员一定要保证在开发新功能前把所有严重的缺陷解决掉再开始新任务。<br />
<br />
-----------------------------------------------------------------------------------------------<br />
5.不适合使用Streber的情况：<br />
1.Streber官方网站上表明，本系统只适合1-40人的小型团队，的确，Streber没有商业软件的自动化流程，也没有其软件的功能细化程度高，但这个描述也有点儿太绝对了，还是要根据项目实际情况和管理体系来确定。<br />
2.Streber不是一个缺陷管理工具，它关注与项目的整个生命周期，同样也没有相应的二次开发的API和与CVS，SunVersion之间的插件。<br />
3.Streber不是一个纯wiki系统，不要把它当作WIKI系统使用，它的性能对于项目内部管理来说足够使用了，但是并没有加入太多的性能优化和缓存机制，所以把其当作内容管理系统来做是相当不明智的。<br />
-----------------------------------------------------------------------------------------------<br />
6.使用总结：<br />
在软件过程改进技术不断发展和进步的情况下，很多国内中小型企业的开发规范化和项目管理机制确一直滞留在4，5年前的样子，这样的情况对于企业和雇员的发展都具有很大的弊端。近年来市面上也出了很多项目协作和管理工具，例如有名的JIRA，Xplanner，版本控制工具svn，cvs等等。这些工具各有个的特点，关键使用工具不是目的，目的是要在工具辅助基础使用项目管理思想上最大限度的对项目进行控制，对软件构造过程进行不断持续的优化和改造，这样才能使软件企业和项目得到良性发展。本文在Streber上也是个大体的说明，strber中包括wiki的使用，任务的转接分派，项目元素的移动，权限的具体设置本文都没详细的进行阐述，我会在以后的相关文章中逐渐进行说明，由于streber的中文文档几乎没有，如果大家有相关的经验技术可以一同进行交流。<br />
-----------------------------------------------------------------------------------------------<br />
<strong><span style="font-size: small;">附录：WIKI语法初窥</span>
</strong>
<br />
WIKI语法适合进行多人协作文档和版本控制，由于streber基于WIKI的思想和语法进行设计，文档的编写使用都是WIKI的语法，所以本文简单介绍一下WIKI语法核项目中常用的语法，如在使用过程中需要wiki语法的进一步支持，可以到这里http://www.allwiki.com/wiki/Wiki查询。<br />
 <br />
标题（heading）<br />
== Top Level ==<br />
<br />
=== Second Level ===<br />
<br />
或者<br />
<br />
Top Level<br />
=========  &lt;-  3 or more '=' characters<br />
<br />
Second Level<br />
------------   &lt;- 3 or more '-' charaters<br />
列表（List）<br />
 <br />
# Numbered<br />
# Numbered<br />
代码框<br />
[ code from=&quot;index.php&quot;]<br />
some more code<br />
[ /code]<br />
 <br />
Email链接<br />
Send to mailto:zm@streber<br />
 <br />
表格（Tables）<br />
<br />
|Header |Header |<br />
|Cell |Cell |</p>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://fuliang.javaeye.com/topic/230399#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 20 Aug 2008 10:33:24 +0800</pubDate>
        <link>http://www.javaeye.com/topic/230399</link>
        <guid>http://www.javaeye.com/topic/230399</guid>
      </item>
      <item>
        <title>关于两个世界体系的对话</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://trustno1.javaeye.com">Trustno1</a>&nbsp;
          链接：<a href="http://www.javaeye.com/topic/231515" style="color:red;">http://www.javaeye.com/topic/231515</a>&nbsp;
          发表时间: 2008年08月22日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          这一段是我还在写的文章中的一部分.<br />=========================================<br />.....<br />习惯经验的强大惯性，源自于背景的长期稳定性。软件体系的快速变革，让我们忽视了硬件体系的长期稳定。这种稳定性使得很多习惯经验变成了不言自明的信条。大多数的软件设计方法的革新只不过是用旧石斧打造出来新石斧。在C中我们使用getc,putc来进行IO,在Java中无非是变成了System.in.read(),System.out.print ()。为什么IO必定是这种形式呢?这是因为我们长期使用着同一种计算机。我们知道PC/Mac这样的计算机中CPU与IO设备进行通信，需要通过各种总线。下面这张图演示了CPU与IO设备之间通信的基本过程.<br /><br /><img src=" http://www.javaeye.com/upload/attachment/35779/78c89cc6-93be-386c-8086-a48b5a8510ec.jpg" />以C语言为代表的传统的IO,实际上是单CPU上单任务工作模式的投影。在单台计算机上, 传统计算机体系结构决定了CPU处于控制者和决策者的地位。换而言之,我们历来习惯于以CPU的视角来考虑程序的IO逻辑.程序员是将自己假设为CPU. 程序员关心的IO设施只是一个黑盒.我们只需要往IO发送一个请求，然后等待请求回来进行运算,完全不关心这一来一回之间到底发生了什么过程.<br />但是当我们打开黑盒,观察CPU与IO的通信过程的时候, IO Monad就从幕后走向了台前。以总线的角度看,CPU和外设是等同的，都只是一个具有运算能力和输入输出端口的黑盒.总线正如Bind/>>=函数一样不关心这些黑盒子里如何运算的，它只关心从这个黑盒拿数据出来放入那个黑盒. 从整个计算机的体系结构看，传统的IO观念只不过是IO Monad的一个局部化形态。<br />  IO Monad实则上在一些接近操作系统底层的软件中，经常扮演者数据总线这种核心角色。比如说Unix/linux shell的管道命令就是彻头彻尾的IO Monad. cat,命令是r