<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet href='http://feed.feedsky.com/styles/feedsky0.xsl' type='text/xsl' ?><!--这是一个由Feedsy提供技术支持的Feed，为了提高读者阅读的体验，以及满足用户美化自己Feed的需要，我们设计了多种精美的Feed模板，提供给大家选择，所有最终呈现出来的样式，皆由用户自愿选择使用，未经许可，任何团体和个人，请不要擅自修改样式或者盗用，这是对于用户选择权的尊重。--><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:fs="http://www.feedsky.com/namespace/feed" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><atom:link href="http://feed.feedsky.com/realdodo" type="application/rss+xml" rel="self"></atom:link><fs:self_link href="http://feed.feedsky.com/realdodo" type="application/rss+xml"></fs:self_link><lastBuildDate>Tue, 08 Feb 2011 16:31:48 GMT</lastBuildDate><title>随心所记</title><description>享受生活的乐趣与烦恼</description><link>http://huandu.me</link><sy:updatePeriod>hourly</sy:updatePeriod><sy:updateFrequency>1</sy:updateFrequency><language>en</language><pubDate>Sat, 21 May 2011 16:56:16 GMT</pubDate><item><title>开源轻量级HTML5游戏引擎gin简介</title><link>http://item.feedsky.com/~feedsky/realdodo/~8631699/513995827/6015014/1/item.html</link><content:encoded>&lt;p&gt;&lt;a href=&quot;http://www.w3schools.com/html5/html5_canvas.asp&quot; target=&quot;_blank&quot;&gt;HTML5 canvas&lt;/a&gt;让程序员自由的绘制想要的图形和动画，使得纯粹基于HTML/Javascript/CSS的游戏铺平了道路。只不过canvas最初并非为游戏而设计，而且除了绘图以外，做游戏还有不少其他事情要考虑，例如鼠标键盘事件、图层、动画等。为了方便程序员开发游戏，游戏框架/引擎陆续被开发出来，&lt;a href=&quot;https://github.com/huandu/gin&quot; target=&quot;_blank&quot;&gt;gin&lt;/a&gt;就是其中之一。&lt;/p&gt;
&lt;p&gt;Project home: &lt;a href=&quot;https://github.com/huandu/gin&quot; target=&quot;_blank&quot;&gt;https://github.com/huandu/gin&lt;br /&gt;
&lt;/a&gt;&lt;br /&gt;
Author: Huan Du (&lt;a href=&quot;http://www.realdodo.com/&quot;&gt;blog&lt;/a&gt;, &lt;a href=&quot;http://twitter.com/huandu&quot;&gt;twitter&lt;/a&gt;)&lt;br /&gt;
Samples: &lt;a href=&quot;https://github.com/huandu/gin-samples&quot; target=&quot;_blank&quot;&gt;https://github.com/huandu/gin-samples&lt;/a&gt;&lt;br /&gt;
Live demo: &lt;a href=&quot;/gin-samples/simple-sample/&quot; target=&quot;_blank&quot;&gt;simple sample&lt;/a&gt; &lt;a href=&quot;/gin-samples/mouse-tracer/&quot; target=&quot;_blank&quot;&gt;mouse tracer&lt;/a&gt; &lt;a href=&quot;/gin-samples/shape-breaker/&quot; target=&quot;_blank&quot;&gt;shape breaker&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;gin是什么&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;gin是一个开源轻量级的HTML5游戏引擎，只专注于搭建一个简单可靠的游戏基础设施，让游戏能在网页中高效流畅的运转起来。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;gin能做什么&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;gin提供了游戏开发中各种必须的基础设施，例如固定帧率渲染、鼠标键盘事件捕捉、图层、用户数据等，这些工具都是以最简单直接的方式提供出来，不要求OOP，没有预编译。&lt;/p&gt;
&lt;p&gt;gin在设计之初就定位为一个“引擎”，而不是框架。就像汽车引擎不负责提供动力以外的事情一样，gin只专注于驱动游戏运转，不提供表现层的任何工具。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;gin的特点&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;gin的特点是：&lt;strong&gt;简单、高效&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;使用gin只需要写如下的代码，十分简单：&lt;/p&gt;
&lt;pre name=&quot;code&quot; class=&quot;javascript&quot;&gt;
$G('your-game-container-id', {}, {
    render: function(e) {
        // draw canvas with e.context
    }
});
&lt;/pre&gt;
&lt;p&gt;点击&lt;a href=&quot;../gin-samples/simple-sample/&quot; target=&quot;_blank&quot;&gt;这里&lt;/a&gt;可以看到一个完整而简单的使用gin的例子，源码看&lt;a href=&quot;https://github.com/huandu/gin-samples/blob/master/simple-sample/index.html&quot; target=&quot;_blank&quot;&gt;这里&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;相比其他现有的js游戏/绘图框架的设计思路，gin摈弃了传统的事件驱动模型，只提供固定帧率的回调接口，所有的鼠标键盘事件都由gin负责接收和缓存。gin的使用者可以在beforerender或render回调中集中处理所有缓存的事件，这样能最大化游戏性能，并提高整体游戏响应速度。&lt;/p&gt;
&lt;p&gt;根据google chrome 8.0的profiling结果，在canvas绘图函数中数清除画布数据的clearRect()消耗CPU时间最多，画布越大性能消耗越明显，约是stroke()一个相同大小的圆或长方形耗时的100倍甚至更多。如果采用传统的事件驱动模型，游戏会立即处理接收到的事件，执行绘图、逻辑判断等等，这样会不断的清除画布，浪费大量的CPU，而实际上只要达到30帧/s就能有流畅体验，在真正需要绘图的时候再绘图才更合理。&lt;/p&gt;
&lt;p&gt;而且由于现在所有javascript引擎都是单线程的，脚本执行时无法响应任何DOM事件，浏览器也不会缓存这些事件，如果脚本较长时间占用CPU，还会造成事件丢失，最终影响用户体验。&lt;/p&gt;
&lt;p&gt;由gin来缓存事件还有一个好处，这可以让键盘鼠标状态检测变得更简单。gin在beforerender和render回调中传入的事件对象带有keyStates和buttonStates数组，分别对应键盘和鼠标的按键状态，可以支持多个键盘/鼠标按键同时按下的状态检测。&lt;/p&gt;
&lt;pre name=&quot;code&quot; class=&quot;javascript&quot;&gt;
$G('your-game-container-id', {}, {
    render: function(e) {
        // check if 'blank' key pressed
        if (e.keyStates[0x20]) {
            // do something
        }

        // check if mouse L button pressed
        if (e.buttonStates[0]) {
            // do something
        }
    }
});
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;beforerender和render&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;gin将帧的回调函数分为两个，beforerender和render。这两个回调的唯一区别是beforerender参数e里面没有canvas context，不能用于画图。这样做的好处是鼓励使用者将与绘图无关的逻辑放入beforerender，让每次脚本运行的时间更短，降低丢失消息的可能性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;遍历鼠标事件&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;gin缓存的鼠标事件并不能直接暴露出来，这是因为gin支持图层，在不同图层里面，鼠标事件的clientX和clientY都会因为图层的偏移量不同而不一样。如果为每一个图层事先计算好正确的clientX和clientY并且缓存起来，那会造成很大的性能损耗。gin的做法是提供遍历鼠标事件的接口，并在遍历中计算正确的坐标。&lt;/p&gt;
&lt;pre name=&quot;code&quot; class=&quot;javascript&quot;&gt;
$G('your-game-container-id', {}, {
    render: function(e) {
        if (e.buttonStates[0]) {
            // draw mouse move path
            var ctx = e.context;
            ctx.strokeStyle = 'rgb(0,0,0)';
            ctx.beginPath();

            e.traverseHistory(function(cur, prev) {
                // the first point
                if (!prev) {
                    ctx.moveTo(cur.clientX, cur.clientY);
                } else {
                    ctx.lineTo(cur.clientX, cur.clientY);
                }
            });

            ctx.stroke();
        }

        // cached mouse history must be cleared explicitly
        // history will not be really cleared until next beforerender/render is ready to call
        // so it's safe to clear history many times in one function or in other layer
        e.clearHistory();
    }
});
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;图层&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;gin虽然并没有把图层的内部类（GinLayer）暴露出来，但它无处不在，例如，所有的回调函数的this都是当前layer的实例。&lt;/p&gt;
&lt;p&gt;gin默认会创建一个图层作为所有图层的基础，使用者可以在图层上创建无数的子图层，子图层可以继续嵌套更多子图层。每个子图层有自己的beforerender和render，可以独立的渲染。&lt;/p&gt;
&lt;p&gt;图层可以用stop()方法来停止渲染，stop的图层和它的子图层的beforerender/render都不会被调用，直到调用play()让它们继续渲染。&lt;/p&gt;
&lt;pre name=&quot;code&quot; class=&quot;javascript&quot;&gt;
$G('your-game-container-id', {}, {
    start: function() {
        this.layers('sample', {
            left: 0,
            top: 20,
            width: 100,
            height: 200
        }, {
            render: function(e) {
                // your code
            }
        })
        .layers('sample_again', {}, {
            play: function() {
                // this function will be called once the layer starts to play
                this.layers('sub_sample', {});
            }
        });
    },
    render: function(e) {
        // it's how to find layer, or even sub-layer
        var sample = this.layers('sample'),
            sub = this.layers(['sample_again', 'sub_sample']);

        // do something
    }
});
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;gin的未来&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;gin刚刚发布了&lt;a href=&quot;https://github.com/huandu/gin/tree/1.0.0&quot; target=&quot;_blank&quot;&gt;1.0.0版&lt;/a&gt;，是一个just work的版本，并且发布了三个例子，放在&lt;a href=&quot;https://github.com/huandu/gin-samples&quot; target=&quot;_blank&quot;&gt;gin-samples&lt;/a&gt;项目中。&lt;/p&gt;
&lt;p&gt;gin未来将首先考虑支持更多的浏览器和平台，特别是移动设备上的支持。对于简单高效的引擎而言，最适合应用的环境就是性能相对较差的移动设备。&lt;/p&gt;
&lt;p&gt;其次需要考虑的是实现更强大的图层（layers），现在的图层已经实现了类似于flash MovieClip的各种基本功能，还差key frame没有实现，未来会增加支持。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;FAQ&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Q: gin的api文档呢？&lt;br /&gt;
A: 暂时还没有，有问题请直接联系我（&lt;a href=&quot;https://github.com/huandu&quot;&gt;github&lt;/a&gt;，&lt;a href=&quot;http://twitter.com/huandu&quot;&gt;twitter&lt;/a&gt;）&lt;/p&gt;
&lt;p&gt;Q: gin支持哪些浏览器？&lt;br /&gt;
A: 测试过的是Firefox 3.6、Chrome 8.0/10.0，理论上Opera、Safari也会支持。IE就别想了，等IE9支持了canvas再说吧。&lt;/p&gt;
&lt;p&gt;Q: gin-samples里面的例子不能正常运行是什么原因？&lt;br /&gt;
A: 如果是下载源码运行，需要手动将gin.js拷贝到sample目录里面才能运行。此外，需要保证电脑可以访问网络，因为有些例子使用了jQuery，用的是google dns。如果还不行，请检查一下浏览器是否是gin所支持的浏览器，gin自己没有做任何浏览器检测。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;附录&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我在开发过程中了解过的游戏/绘图框架包括&lt;a href=&quot;http://www.renderengine.com/&quot; target=&quot;_blank&quot;&gt;The Render Engine&lt;/a&gt;、&lt;a href=&quot;http://gamejs.org/&quot; target=&quot;_blank&quot;&gt;GameJS&lt;/a&gt;、&lt;a href=&quot;http://code.google.com/p/cakejs/&quot; target=&quot;_blank&quot;&gt;cakejs&lt;/a&gt;、&lt;a href=&quot;http://cocos2d-javascript.org/&quot; target=&quot;_blank&quot;&gt;cocos2d&lt;/a&gt;，它们都是非常好的框架，gin从某种程度上说是针对它们的不足而设计的。 :P&lt;/p&gt;
&lt;p&gt;gin最开始是我心血来潮写的一个小框架，还未完成就在&lt;a href=&quot;http://www.chromi.org/archives/9986&quot; target=&quot;_blank&quot;&gt;“给力HTML5 —— 2011 Google HTML5训练营” 第一期活动&lt;/a&gt;中使用，非常意外且幸运的用它实现了第一个可玩的HTML5游戏&lt;a href=&quot;http://www.enjoyhtml5.com/hackathons/20110108/02/plane.html&quot; target=&quot;_blank&quot;&gt;Raiden 5 (Chrome only)&lt;/a&gt;，在&lt;a href=&quot;http://www.enjoyhtml5.com/hackathons/20110108/02/gin.js&quot; target=&quot;_blank&quot;&gt;这里&lt;/a&gt;可以围观最原始的gin。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/513995827/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995827/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://huandu.me/2011/02/09/628/feed/</wfw:commentRss><slash:comments>14</slash:comments><description>HTML5 canvas让程序员自由的绘制想要的图形和动画，使得纯粹基于HTML/Javascript/CSS的游戏铺平了道路。只不过canvas最初并非为游戏而设计，而且除了绘图以外，做游戏还有不少其他事情要考虑，例如鼠标键盘事件、图层、动画等。为了方便程序员开发游戏，游戏框架/引擎陆续被开发出来，gin就是其中之一。 Project home: https://github.com/huandu/gin Author: Huan Du (blog, twitter) Samples: https://github.com/huandu/gin-samples Live demo: simple sample mouse tracer shape breaker gin是什么 gin是一个开源轻量级的HTML5游戏引擎，只专注于搭建一个简单可靠的游戏基础设施，让游戏能在网页中高效流畅的运转起来。 gin能做什么 gin提供了游戏开发中各种必须的基础设施，例如固定帧率渲染、鼠标键盘事件捕捉、图层、用户数据等，这些工具都是以最简单直接的方式提供出来，不要求OOP，没有预编译。 gin在设计之初就定位为一个“引擎”，而不是框架。就像汽车引擎不负责提供动力以外的事情一样，gin只专注于驱动游戏运转，不提供表现层的任何工具。 gin的特点 gin的特点是：简单、高效。 使用gin只需要写如下的代码，十分简单： $G('your-game-container-id', {}, { render: function(e) { // draw canvas with e.context } }); 点击这里可以看到一个完整而简单的使用gin的例子，源码看这里。 相比其他现有的js游戏/绘图框架的设计思路，gin摈弃了传统的事件驱动模型，只提供固定帧率的回调接口，所有的鼠标键盘事件都由gin负责接收和缓存。gin的使用者可以在beforerender或render回调中集中处理所有缓存的事件，这样能最大化游戏性能，并提高整体游戏响应速度。 根据google chrome 8.0的profiling结果，在canvas绘图函数中数清除画布数据的clearRect()消耗CPU时间最多，画布越大性能消耗越明显，约是stroke()一个相同大小的圆或长方形耗时的100倍甚至更多。如果采用传统的事件驱动模型，游戏会立即处理接收到的事件，执行绘图、逻辑判断等等，这样会不断的清除画布，浪费大量的CPU，而实际上只要达到30帧/s就能有流畅体验，在真正需要绘图的时候再绘图才更合理。 而且由于现在所有javascript引擎都是单线程的，脚本执行时无法响应任何DOM事件，浏览器也不会缓存这些事件，如果脚本较长时间占用CPU，还会造成事件丢失，最终影响用户体验。 由gin来缓存事件还有一个好处，这可以让键盘鼠标状态检测变得更简单。gin在beforerender和render回调中传入的事件对象带有keyStates和buttonStates数组，分别对应键盘和鼠标的按键状态，可以支持多个键盘/鼠标按键同时按下的状态检测。 $G('your-game-container-id', {}, { render: function(e) { // [...]&lt;img src=&quot;http://www1.feedsky.com/t1/513995827/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995827/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>工作</category><category>HTML5</category><category>互联网</category><category>gin</category><category>程序</category><category>技术文章</category><category>游戏</category><category>canvas</category><category>浏览器</category><category>javascript</category><category>框架</category><pubDate>Wed, 09 Feb 2011 00:31:48 +0800</pubDate><author>Huan Du</author><comments>http://huandu.me/2011/02/09/628/#comments</comments><guid isPermaLink="false">http://www.realdodo.com/?p=628</guid><dc:creator>Huan Du</dc:creator><fs:srclink>http://huandu.me/2011/02/09/628/</fs:srclink><fs:srcfeed>http://huandu.me/feed</fs:srcfeed><fs:itemid>feedsky/realdodo/~8631699/513995827/6015014</fs:itemid></item><item><title>游戏中的物品交换逻辑抽象</title><link>http://item.feedsky.com/~feedsky/realdodo/~8631699/513995828/6015014/1/item.html</link><content:encoded>&lt;p&gt;如果总结一下游戏的内部逻辑，或许会让游戏变得很无趣，不过程序员大概会很高兴，比如说我。&lt;/p&gt;
&lt;p&gt;如果把用户的各种信息都抽象成“物品”、“个数”，那么所有的游戏逻辑都是用户之间的物品交换。&lt;/p&gt;
&lt;p&gt;为了方便描述，我来自定义一套语法表示这种交换。这里用的是类BNF语法，如果没耐心可跳过。&lt;/p&gt;
&lt;p style=&quot;padding-left: 30px;&quot;&gt;物品交换 := 物品描述 &amp;#8220;=&amp;gt;&amp;#8221; 物品描述&lt;/p&gt;
&lt;p style=&quot;padding-left: 30px;&quot;&gt;物品描述 := 物品描述 &amp;#8220;+&amp;#8221; 单个物品描述 | 单个物品描述&lt;/p&gt;
&lt;p style=&quot;padding-left: 30px;&quot;&gt;单个物品描述 := 用户 &amp;#8220;(&amp;#8221; 物品 &amp;#8220;,&amp;#8221; 数量 附加属性 &amp;#8220;)&amp;#8221;&lt;/p&gt;
&lt;p style=&quot;padding-left: 30px;&quot;&gt;物品 := 物品名称 | 物品函数&lt;/p&gt;
&lt;p style=&quot;padding-left: 30px;&quot;&gt;物品函数 := rand &amp;#8220;(&amp;#8221; 物品列表 &amp;#8220;)&amp;#8221;&lt;/p&gt;
&lt;p style=&quot;padding-left: 30px;&quot;&gt;物品列表 := 物品列表 &amp;#8220;,&amp;#8221; 物品 | 物品&lt;/p&gt;
&lt;p style=&quot;padding-left: 30px;&quot;&gt;附加属性 :=&amp;#8221;,&amp;#8221; &amp;#8220;{&amp;#8221; 附件属性列表 &amp;#8220;}&amp;#8221; | &amp;#8220;&amp;#8221;&lt;/p&gt;
&lt;p style=&quot;padding-left: 30px;&quot;&gt;附加属性列表 := 附加属性列表 &amp;#8220;,&amp;#8221; 属性 | 属性&lt;/p&gt;
&lt;p style=&quot;padding-left: 30px;&quot;&gt;属性 := 属性名 &amp;#8220;:&amp;#8221; 属性值&lt;/p&gt;
&lt;p style=&quot;padding-left: 30px;&quot;&gt;数量 := 数字 | 数字函数&lt;/p&gt;
&lt;p style=&quot;padding-left: 30px;&quot;&gt;数字 := 非负整数 | MAX&lt;/p&gt;
&lt;p style=&quot;padding-left: 30px;&quot;&gt;数字函数 := auto_change &amp;#8220;(&amp;#8221; 初始值 &amp;#8220;,&amp;#8221; 每秒变化步长 &amp;#8220;,&amp;#8221; 边界值 &amp;#8220;)&amp;#8221;&lt;br /&gt;
| max &amp;#8220;(&amp;#8221; 数字列表 &amp;#8220;)&amp;#8221;&lt;/p&gt;
&lt;p style=&quot;padding-left: 30px;&quot;&gt;数字列表 := 数字列表 &amp;#8220;,&amp;#8221; 数字 | 数字&lt;/p&gt;
&lt;p&gt;函数说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rand代表在一系列物品名称中返回任意选一个物品名称。&lt;/li&gt;
&lt;li&gt;MAX代表该物品最大数量值&lt;/li&gt;
&lt;li&gt;max代表从一系列数字中返回最大值&lt;/li&gt;
&lt;li&gt;auto_change代表从初始值开始，每隔1秒时间就加上步长，直到达到边界值为止不再变化。auto_change并非如其名一般会自动变化，而是在下次请求该物品数量的时候才计算当前值。步长可以为负。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;希望不要看这些BNF晕了头……直接看例子其实也可以。&lt;/p&gt;
&lt;p&gt;列举一下常见的游戏场景背后的数字逻辑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户A用100游戏币买一个桌子获得10点经验：
&lt;ul&gt;
&lt;li&gt;A (游戏币, 100) =&amp;gt; A (桌子, 1) + A (经验, 10)&lt;/li&gt;
&lt;li&gt;注：这里把游戏币、经验都当做物品来处理，从程序角度来说这没有问题，虽然直观上有点怪。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用户A使用10点幸运点打开一个宝箱得到一个物品，这个物品是玩具1、玩具2、玩具3中随机的一个：
&lt;ul&gt;
&lt;li&gt;A (宝箱, 1) + A (幸运点, 10) =&amp;gt; A (rand(玩具1, 玩具2, 玩具3), 1)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用户A送用户B一个玩具，留言“my present”，获得10点经验：
&lt;ul&gt;
&lt;li&gt;A (玩具, 1) =&amp;gt; B (礼物, 1, {留言: my present, 关联物品: 玩具, 关联数量: 1, 时间: YYYY-MM-DD}) + A (经验, 10)&lt;/li&gt;
&lt;li&gt;B (礼物, 1, {留言: my present, 关联物品: 玩具, 关联数量: 1, 时间: YYYY-MM-DD}) =&amp;gt; B (玩具, 1)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用户A在一号树坑种下一棵树，将在3000秒后长成桃子共4颗，其中最多2个可以被偷走，而B偷走了1个获得10点经验，A则收走了剩下3个，获得20点经验：
&lt;ul&gt;
&lt;li&gt;A (树种子, 1) =&amp;gt; A (成长中的桃子, auto_change(0, 1, 3000), {土地号: 1})&lt;/li&gt;
&lt;li&gt;A (成长中的桃子, 3000, {土地号: 1}) =&amp;gt; A (私有桃子, 2, {土地号: 1}) + A (公有桃子, 2, {土地号: 1})&lt;/li&gt;
&lt;li&gt;A (公有桃子, 1, {土地号: 1}) =&amp;gt; B (桃子, 1) + B (经验, 10)&lt;/li&gt;
&lt;li&gt;A (公有桃子, 1, {土地号: 1}) + A (私有桃子, 2, {土地号: 1}) =&amp;gt; A (桃子, 3)&lt;/li&gt;
&lt;li&gt;注：当任何一个可以看到桃子树状态的用户刷新状态且第二步中输入的数值满足要求时，第二步物品交换就会发生。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用户A接了一个任务X，用玩具1和玩具2，可以换得100游戏币和10点经验，任务有效时间为30,000秒，且A最多能接5个任务：
&lt;ul&gt;
&lt;li&gt;A (可接任务, 1) =&amp;gt; A (任务X, auto_change(30000, -1, 0))&lt;/li&gt;
&lt;li&gt;A (任务X, max(1, MAX)) + A (玩具1, 1) + A (玩具2, 2) =&amp;gt; A (游戏币, 100) + A (经验, 10) + A (可接任务, 1) + A (已完成的任务X, 1)&lt;/li&gt;
&lt;li&gt;A (任务X, MAX) =&amp;gt; A (可接任务, 1)&lt;/li&gt;
&lt;li&gt;注1：A的初始可接任务设置为5就能限制A最多接5个任务&lt;/li&gt;
&lt;li&gt;注2：第三步为取消任务逻辑&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用户A将一个桌子从储物箱里面挪到1号房间，又从1号房挪到了2号房：
&lt;ul&gt;
&lt;li&gt;A (桌子, 1) =&amp;gt; A (桌子, 1, {x: 10, y: 0, room: 1})&lt;/li&gt;
&lt;li&gt;A (桌子, {x: 10, y: 0, room: 1}) =&amp;gt; A (桌子, {x: 20, y: 10, room: 2})&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;仅仅通过这些很基本的物品交换逻辑，（理论上）就可以实现一个比较复杂的游戏数值逻辑了。只要实现一个服务，把所有这些可能的物品交换逻辑都放在某个配置里面，游戏核心就出来了。&lt;/p&gt;
&lt;p&gt;这种做法也有不足，有些时候客户端需要从右边推导出左边的可选值就比较麻烦。比如物品1可以由游戏币或充值币其一换得，客户端需要根据物品1反查出游戏币和充值币各自需要的数量。这在技术上其实可行，只是需要花费更多精力去想明白里面的需求。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/513995828/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995828/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://huandu.me/2010/06/26/624/feed/</wfw:commentRss><slash:comments>3</slash:comments><description>如果总结一下游戏的内部逻辑，或许会让游戏变得很无趣，不过程序员大概会很高兴，比如说我。 如果把用户的各种信息都抽象成“物品”、“个数”，那么所有的游戏逻辑都是用户之间的物品交换。 为了方便描述，我来自定义一套语法表示这种交换。这里用的是类BNF语法，如果没耐心可跳过。 物品交换 := 物品描述 &amp;#8220;=&amp;#62;&amp;#8221; 物品描述 物品描述 := 物品描述 &amp;#8220;+&amp;#8221; 单个物品描述 &amp;#124; 单个物品描述 单个物品描述 := 用户 &amp;#8220;(&amp;#8221; 物品 &amp;#8220;,&amp;#8221; 数量 附加属性 &amp;#8220;)&amp;#8221; 物品 := 物品名称 &amp;#124; 物品函数 物品函数 := rand &amp;#8220;(&amp;#8221; 物品列表 &amp;#8220;)&amp;#8221; 物品列表 := 物品列表 &amp;#8220;,&amp;#8221; 物品 &amp;#124; 物品 附加属性 :=&amp;#8221;,&amp;#8221; &amp;#8220;{&amp;#8221; 附件属性列表 &amp;#8220;}&amp;#8221; &amp;#124; &amp;#8220;&amp;#8221; 附加属性列表 := 附加属性列表 &amp;#8220;,&amp;#8221; 属性 &amp;#124; [...]&lt;img src=&quot;http://www1.feedsky.com/t1/513995828/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995828/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>工作</category><category>互联网</category><category>程序</category><category>技术文章</category><category>游戏</category><category>物品交换</category><pubDate>Sat, 26 Jun 2010 02:49:41 +0800</pubDate><author>Huan Du</author><comments>http://huandu.me/2010/06/26/624/#comments</comments><guid isPermaLink="false">http://www.realdodo.com/?p=624</guid><dc:creator>Huan Du</dc:creator><fs:srclink>http://huandu.me/2010/06/26/624/</fs:srclink><fs:srcfeed>http://huandu.me/feed</fs:srcfeed><fs:itemid>feedsky/realdodo/~8631699/513995828/6015014</fs:itemid></item><item><title>Amazon AWS使用体验</title><link>http://item.feedsky.com/~feedsky/realdodo/~8631699/513995829/6015014/1/item.html</link><content:encoded>&lt;p&gt;近几个月来简单体验了Amazon AWS大部分服务，现在分享一下使用感受。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Amazon EC2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;从各方面来看，EC2应该算是Amazon的王牌产品。EC2功能全面，它可以在数分钟内launch一个实例，可以（理论上）自动提升/降低机器性能配置，可以绑定EBS成为具有高可用性特质的计算核心，可以摇身一变成为ELB成为一个load balancer。这些功能非常实用，特别适合针对国外市场且快速增长的小网站，可以在前期完全不受机器约束。&lt;/p&gt;
&lt;p&gt;EC2机器的内核在申请时指定，有很多选择，如果有闲心，还可以&lt;a href=&quot;http://www.udfi.biz/2009/05/creating-a-centos-53-amazon-ami/&quot;&gt;做一个自己专用的&lt;/a&gt;，我们没用这个功能，实在太繁琐了。EC2提供了完全的root权限，可以随意安装软件，由于EC2一般带有足够的硬盘空间（Large Instance就自带850GB磁盘空间），装个mysql当数据库机器也完全没有问题。&lt;/p&gt;
&lt;p&gt;EC2最大优势是灵活，最大劣势则是性能。EC2的IO性能欠佳，当EC2机器作为mysql数据库时，系统吞吐量只能到3~10 MB/s，相比而言，在同样数据同样程序条件下，使用真实机器则能轻松达到30MB/s。EC2的CPU是多个实例共享的，一般stolen的CPU都在10%左右，对于nginx+php-cgi这样的组合，EC2高端配置也不太能撑住压力，总是CPU吃紧。&lt;/p&gt;
&lt;p&gt;虽说EC2劣势明显，但由于互联网应用一般都很容易做到水平扩展，它的灵活性可以很大程度缓解这个劣势，不会有太大问题。不过最终我们还是放弃了EC2，主要因为今年5月Amazon云计算服务出了大量的事故，仅5月头两周，在同一个机房就出了5起大事故，我们在3起事故中受影响，每次都很不幸的都是单点EC2出问题，最长的一次在高峰期中断了6个小时服务，损失巨大。最郁闷的是，到了5月下旬，有一台跑数据库的EC2机器莫名奇妙磁盘损坏，还有一台莫名其妙的无法ssh，让我们感觉越来越不靠谱，于是决定逃离。云计算的可用性始终值得担心。&lt;/p&gt;
&lt;p&gt;EC2的价格相对传统IDC要贵不少，按高端机器High-Memory Double Extra Large每小时1.2US$来算，每月就要花864US$，再加上流量费，很容易达到每台服务器1000US$的水平。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Amazon RDS&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;RDS是一种数据库托管服务，通过RDS toolkit可以很方便的创建RDS实例并立即使用。据Amazon帮助文档说，RDS的数据库软件维护、升级、优化的工作全部由Amazon负责，用户只需要使用功能即可。&lt;/p&gt;
&lt;p&gt;可惜，RDS纯粹是一个看起来很美的东西。首先是RDS为了同时支持异构的数据库，牺牲了很多灵活性，例如，因为无法访问RDS的物理机器或存储空间，使得跑着mysql的RDS实例连记录slow log都成了难事，更别说什么err log，出了问题只能一抹瞎。如果要获取bi/bo这样的数据需要使用专用的RDS toolkit完成，很难集成到现成的监控工具里去。其次，RDS底层使用的是类似于EBS的网络存储，其IO性能差到一个令人发指的地步。一个很感性的数据是：使用RDS Double Extra Large DB Instance跑mysql时慢查询占1%，同样数据转到EC2 High-Memory Double Extra Large实例后慢查询降为0.01%。最让人无语的是RDS价格高于同等配置的EC2+EBS价格，每小时都贵超过0.3US$，相当于贵25%，省下来的钱用来雇专业运维人员绰绰有余，而且RDS功能和灵活性还远弱于后者，不怕折腾但怕花钱的人不建议选择RDS。&lt;/p&gt;
&lt;p&gt;如果执意要使用RDS，那么一定要注意RDS没有固定IP，每次重启实例（或过一段时间）都会变，程序连接的时候最好写域名，当然，这会损失性能。此外，RDS可用性也没宣传的那么高，我们5月最先挂的就是RDS实例，一挂就是好几个小时。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Amazon S3&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;S3是少有的看上去物美价廉的东西，只需要花几美元就可以拿到几十GB空间，对一般的互联网应用来说绰绰有余。&lt;/p&gt;
&lt;p&gt;S3提供丰富的API和工具上传文件。对firefox用户来说，有一个专门的S3扩展可用，可直接上传本地文件。还有一个s3curl.pl工具，可以用命令行传任意文件，可以方便的做到自动化。&lt;/p&gt;
&lt;p&gt;S3存储的文件可以直接通过域名从外网访问，不过比较郁闷的是文件尾部不能带“?“，一切基于文件名后面加随机串的避免缓存的做法都会失效。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Amazon CloudFront&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;CloudFront是一种CDN，它的访问速度据称不错，我们并没有实际使用过。&lt;/p&gt;
&lt;p&gt;我们不使用CloudFront主要是因为很难控制文件在CloudFront中失效。根据我们的程序结构，我们需要在静态文件后面加version信息强制用户在必要时更新到最新版，现在由于CloudFront既不能手动invalidate某个文件，也不能保证CloudFront上缓存的文件及时更新（可能更新会花24小时），我们需要修改现在的策略才能使用到它。&lt;/p&gt;
&lt;p&gt;CloudFront是根据用户所在网络来收费的，对于国内，CloudFront使用的是香港价格，贵于美洲和欧洲，不太划算。对于欧美市场，我也不清楚欧美CDN的价格究竟如何，无法比较。CloudFront在欧美的流量费用与EC2相同，不过由于没有按小时计的租费，总体价格会比EC2提供静态文件服务便宜很多。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Amazon CloudWatch&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;CloudWatch是一个性能数据监视器，对于这个东西我只能说太小儿科了，没看出来有什么实际的用处。一旦开启CloudWatch，Amazon就会帮忙收集开启了这个服务的instance数据，包括CPU和IO信息等。可惜，数据精度不高（大概是几分钟一次统计），数字也只是个大概值，能看的数据也很少，无论对入门者还是高手来说，都不能带来太多价值。&lt;/p&gt;
&lt;p&gt;CloudWatch看上去比较好的是可以配合EC2/EBS/Load Balancer toolkit做一些auto scale的事情，自动提升/降低EC2/EBS参数，还可以（理论上）自动launch新实例。这些东西我们都没尝试过，有兴趣的同学可以试试。&lt;/p&gt;
&lt;p&gt;CloudWatch价格不贵，一台机器就只用0.1US$而已，不过一般还是不建议开，装一个其他监控软件更靠谱。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Amazon Premiun Support&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Premiun Support包括即时的电话支持和一个专有的提案系统。从服务质量上来说，Amazon的客服总是很热心，响应速度也很快，专业素质也不错，很不错。&lt;/p&gt;
&lt;p&gt;Premium Support非常贵，简直是抢钱。它是按照每月总花费来算价格的，最多增加总花费的16%，最少为无限接近于10%，代价十分明显。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;结语&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Amazon AWS提供丰富功能，还有不少toolkit和API，适合小应用使用。对于要求较高性能或较低成本的应用来说则不太适合，折腾一下IDC应该达到更好效果。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/513995829/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995829/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://huandu.me/2010/06/19/619/feed/</wfw:commentRss><slash:comments>6</slash:comments><description>近几个月来简单体验了Amazon AWS大部分服务，现在分享一下使用感受。 Amazon EC2 从各方面来看，EC2应该算是Amazon的王牌产品。EC2功能全面，它可以在数分钟内launch一个实例，可以（理论上）自动提升/降低机器性能配置，可以绑定EBS成为具有高可用性特质的计算核心，可以摇身一变成为ELB成为一个load balancer。这些功能非常实用，特别适合针对国外市场且快速增长的小网站，可以在前期完全不受机器约束。 EC2机器的内核在申请时指定，有很多选择，如果有闲心，还可以做一个自己专用的，我们没用这个功能，实在太繁琐了。EC2提供了完全的root权限，可以随意安装软件，由于EC2一般带有足够的硬盘空间（Large Instance就自带850GB磁盘空间），装个mysql当数据库机器也完全没有问题。 EC2最大优势是灵活，最大劣势则是性能。EC2的IO性能欠佳，当EC2机器作为mysql数据库时，系统吞吐量只能到3~10 MB/s，相比而言，在同样数据同样程序条件下，使用真实机器则能轻松达到30MB/s。EC2的CPU是多个实例共享的，一般stolen的CPU都在10%左右，对于nginx+php-cgi这样的组合，EC2高端配置也不太能撑住压力，总是CPU吃紧。 虽说EC2劣势明显，但由于互联网应用一般都很容易做到水平扩展，它的灵活性可以很大程度缓解这个劣势，不会有太大问题。不过最终我们还是放弃了EC2，主要因为今年5月Amazon云计算服务出了大量的事故，仅5月头两周，在同一个机房就出了5起大事故，我们在3起事故中受影响，每次都很不幸的都是单点EC2出问题，最长的一次在高峰期中断了6个小时服务，损失巨大。最郁闷的是，到了5月下旬，有一台跑数据库的EC2机器莫名奇妙磁盘损坏，还有一台莫名其妙的无法ssh，让我们感觉越来越不靠谱，于是决定逃离。云计算的可用性始终值得担心。 EC2的价格相对传统IDC要贵不少，按高端机器High-Memory Double Extra Large每小时1.2US$来算，每月就要花864US$，再加上流量费，很容易达到每台服务器1000US$的水平。 Amazon RDS RDS是一种数据库托管服务，通过RDS toolkit可以很方便的创建RDS实例并立即使用。据Amazon帮助文档说，RDS的数据库软件维护、升级、优化的工作全部由Amazon负责，用户只需要使用功能即可。 可惜，RDS纯粹是一个看起来很美的东西。首先是RDS为了同时支持异构的数据库，牺牲了很多灵活性，例如，因为无法访问RDS的物理机器或存储空间，使得跑着mysql的RDS实例连记录slow log都成了难事，更别说什么err log，出了问题只能一抹瞎。如果要获取bi/bo这样的数据需要使用专用的RDS toolkit完成，很难集成到现成的监控工具里去。其次，RDS底层使用的是类似于EBS的网络存储，其IO性能差到一个令人发指的地步。一个很感性的数据是：使用RDS Double Extra Large DB Instance跑mysql时慢查询占1%，同样数据转到EC2 High-Memory Double Extra Large实例后慢查询降为0.01%。最让人无语的是RDS价格高于同等配置的EC2+EBS价格，每小时都贵超过0.3US$，相当于贵25%，省下来的钱用来雇专业运维人员绰绰有余，而且RDS功能和灵活性还远弱于后者，不怕折腾但怕花钱的人不建议选择RDS。 如果执意要使用RDS，那么一定要注意RDS没有固定IP，每次重启实例（或过一段时间）都会变，程序连接的时候最好写域名，当然，这会损失性能。此外，RDS可用性也没宣传的那么高，我们5月最先挂的就是RDS实例，一挂就是好几个小时。 Amazon S3 S3是少有的看上去物美价廉的东西，只需要花几美元就可以拿到几十GB空间，对一般的互联网应用来说绰绰有余。 S3提供丰富的API和工具上传文件。对firefox用户来说，有一个专门的S3扩展可用，可直接上传本地文件。还有一个s3curl.pl工具，可以用命令行传任意文件，可以方便的做到自动化。 S3存储的文件可以直接通过域名从外网访问，不过比较郁闷的是文件尾部不能带“?“，一切基于文件名后面加随机串的避免缓存的做法都会失效。 Amazon CloudFront CloudFront是一种CDN，它的访问速度据称不错，我们并没有实际使用过。 我们不使用CloudFront主要是因为很难控制文件在CloudFront中失效。根据我们的程序结构，我们需要在静态文件后面加version信息强制用户在必要时更新到最新版，现在由于CloudFront既不能手动invalidate某个文件，也不能保证CloudFront上缓存的文件及时更新（可能更新会花24小时），我们需要修改现在的策略才能使用到它。 CloudFront是根据用户所在网络来收费的，对于国内，CloudFront使用的是香港价格，贵于美洲和欧洲，不太划算。对于欧美市场，我也不清楚欧美CDN的价格究竟如何，无法比较。CloudFront在欧美的流量费用与EC2相同，不过由于没有按小时计的租费，总体价格会比EC2提供静态文件服务便宜很多。 Amazon CloudWatch CloudWatch是一个性能数据监视器，对于这个东西我只能说太小儿科了，没看出来有什么实际的用处。一旦开启CloudWatch，Amazon就会帮忙收集开启了这个服务的instance数据，包括CPU和IO信息等。可惜，数据精度不高（大概是几分钟一次统计），数字也只是个大概值，能看的数据也很少，无论对入门者还是高手来说，都不能带来太多价值。 CloudWatch看上去比较好的是可以配合EC2/EBS/Load Balancer toolkit做一些auto scale的事情，自动提升/降低EC2/EBS参数，还可以（理论上）自动launch新实例。这些东西我们都没尝试过，有兴趣的同学可以试试。 CloudWatch价格不贵，一台机器就只用0.1US$而已，不过一般还是不建议开，装一个其他监控软件更靠谱。 Amazon Premiun Support Premiun [...]&lt;img src=&quot;http://www1.feedsky.com/t1/513995829/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995829/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>工作</category><category>EC2</category><category>CloudFront</category><category>互联网</category><category>电子商务&amp;互联网</category><category>云计算</category><category>amazon</category><category>EBS</category><category>AWS</category><category>S3</category><category>RDS</category><category>CloudWatch</category><pubDate>Sat, 19 Jun 2010 01:52:49 +0800</pubDate><author>Huan Du</author><comments>http://huandu.me/2010/06/19/619/#comments</comments><guid isPermaLink="false">http://www.realdodo.com/?p=619</guid><dc:creator>Huan Du</dc:creator><fs:srclink>http://huandu.me/2010/06/19/619/</fs:srclink><fs:srcfeed>http://huandu.me/feed</fs:srcfeed><fs:itemid>feedsky/realdodo/~8631699/513995829/6015014</fs:itemid></item><item><title>amazon云计算服务初体验</title><link>http://item.feedsky.com/~feedsky/realdodo/~8631699/513995830/6015014/1/item.html</link><content:encoded>&lt;p&gt;本周第一次实际使用amazon的云计算服务，主要包括&lt;a href=&quot;http://aws.amazon.com/ec2/&quot;&gt;Amazon Elastic Compute Cloud (EC2) &lt;/a&gt;和&lt;a href=&quot;http://aws.amazon.com/rds/&quot;&gt;Amazon Relational Database Service (RDS) &lt;/a&gt;，有一些感受先记在这里。&lt;/p&gt;
&lt;p&gt;先说说EC2。它很类似网上的虚拟主机服务，拥有root权限，可以完全控制上面的服务，默认拥有一个对外的dns地址，并可以开放80和22端口。&lt;/p&gt;
&lt;p&gt;从性能上来说，EC2的表现很不错。从功能上来说，EC2最大的限制就是端口和ip。对于端口，80和22只能满足最基本的web应用，如果要使用自定义端口/协议的socket方式提供服务，恐怕就比较麻烦了。对于ip，默认每个EC2服务器都是动态ip，最安全的方法是用自己的域名做CNAME转发来对外提供服务。有一点需要注意，EC2服务器的外部dns和ip是绑定的，一旦给它换ip，例如为它花钱绑定一个固定ip，它的外部dns也会变，这就需要修改自己域名的CNAME记录。可麻烦就麻烦在EC2实例换了ip后立即就换外部dns，而普通dns缓存则可能会很长，这将造成在换ip的这段时间内服务可能不稳定。为了长治久安，还是一开始就申请一个固定ip会比较稳妥呢。&lt;/p&gt;
&lt;p&gt;EC2对外部流量收费，内部不收费，或者在跨区的内部服务器通信时收非常少的费用。EC2服务器单机流量上数十MB/s不成问题，不过随着提供服务的EC2服务多了起来，我们自己搭的load balancer服务器已经有点力不从心，看到EC2有专门的load balancer服务，并且还挺便宜，下周一定试一试。&lt;/p&gt;
&lt;p&gt;相比EC2，RDS则非常的不自由，它不能用ssh登录，数据库程序也不能控制版本，还有不少数据库配置不能改。如果在RDS上使用mysql+innodb，那么有不少不爽的地方：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;innodb_data_file_path配置不能自己控制。RDS会将这个设置为ibdata:10M:autoextend，且不能改，这对高并发的读写会造成问题。&lt;/li&gt;
&lt;li&gt;没法在本地以文件方式保存slow log。&lt;/li&gt;
&lt;li&gt;无法直观的监控机器性能，只有一些RDS专用的命令行工具能得到一些实时的数据。&lt;/li&gt;
&lt;li&gt;无法对操作系统进行进一步调优，甚至都不知道底层跑的是什么操作系统。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不过RDS也有一些好处：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;存储空间可以无限增长。就像flickr的相册一样，每月都有一定的增长额度，总存储空间可以无限增加。&lt;/li&gt;
&lt;li&gt;数据安全性可以保证，可以方便的制作snapshot和完整备份，备份所花的存储空间只要不大于每月增长额度就完全免费。&lt;/li&gt;
&lt;li&gt;amazon会负责数据库软件的安全性，给它及时打补丁。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;综合来说，RDS提供的控制手段有限，不太适合对性能有较高要求的应用。从实际是用来看，同样的程序和网络环境之下，Double Extra Large DB Instance上的mysql+innodb明显慢于Double Extra Large EC2上自己安装并优化的mysql+innodb，一个典型的数据是：使用前者，慢查询比例是1%，使用后者，这个比例立即降为0.01%。&lt;/p&gt;
&lt;p&gt;放弃RDS也就意味着放弃RDS的各种好处，为了弥补这个不足，可以为EC2申请&lt;a href=&quot;http://aws.amazon.com/ebs/&quot;&gt;Amazon Elastic Block Store (EBS) &lt;/a&gt;。EC2+EBS就和RDS没什么两样了，只是管理起来还是有点费神，综合的价格应该和RDS差不多。我这里还没有真正尝试过EC2+EBS的方案，以后等存储出现瓶颈了再说了，现在EC2自带的850G存储绰绰有余。&lt;/p&gt;
&lt;p&gt;amazon的服务还是挺让我满意，特别是phone support反应很快速，服务人员也挺专业，挺好。不过我并没有其他云计算服务的使用经验，没有比较，也不清楚amazon云计算在业界究竟出于何种地位，这个以后再慢慢去了解吧。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/513995830/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995830/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://huandu.me/2010/04/25/616/feed/</wfw:commentRss><slash:comments>5</slash:comments><description>本周第一次实际使用amazon的云计算服务，主要包括Amazon Elastic Compute Cloud (EC2) 和Amazon Relational Database Service (RDS) ，有一些感受先记在这里。 先说说EC2。它很类似网上的虚拟主机服务，拥有root权限，可以完全控制上面的服务，默认拥有一个对外的dns地址，并可以开放80和22端口。 从性能上来说，EC2的表现很不错。从功能上来说，EC2最大的限制就是端口和ip。对于端口，80和22只能满足最基本的web应用，如果要使用自定义端口/协议的socket方式提供服务，恐怕就比较麻烦了。对于ip，默认每个EC2服务器都是动态ip，最安全的方法是用自己的域名做CNAME转发来对外提供服务。有一点需要注意，EC2服务器的外部dns和ip是绑定的，一旦给它换ip，例如为它花钱绑定一个固定ip，它的外部dns也会变，这就需要修改自己域名的CNAME记录。可麻烦就麻烦在EC2实例换了ip后立即就换外部dns，而普通dns缓存则可能会很长，这将造成在换ip的这段时间内服务可能不稳定。为了长治久安，还是一开始就申请一个固定ip会比较稳妥呢。 EC2对外部流量收费，内部不收费，或者在跨区的内部服务器通信时收非常少的费用。EC2服务器单机流量上数十MB/s不成问题，不过随着提供服务的EC2服务多了起来，我们自己搭的load balancer服务器已经有点力不从心，看到EC2有专门的load balancer服务，并且还挺便宜，下周一定试一试。 相比EC2，RDS则非常的不自由，它不能用ssh登录，数据库程序也不能控制版本，还有不少数据库配置不能改。如果在RDS上使用mysql+innodb，那么有不少不爽的地方： innodb_data_file_path配置不能自己控制。RDS会将这个设置为ibdata:10M:autoextend，且不能改，这对高并发的读写会造成问题。 没法在本地以文件方式保存slow log。 无法直观的监控机器性能，只有一些RDS专用的命令行工具能得到一些实时的数据。 无法对操作系统进行进一步调优，甚至都不知道底层跑的是什么操作系统。 不过RDS也有一些好处： 存储空间可以无限增长。就像flickr的相册一样，每月都有一定的增长额度，总存储空间可以无限增加。 数据安全性可以保证，可以方便的制作snapshot和完整备份，备份所花的存储空间只要不大于每月增长额度就完全免费。 amazon会负责数据库软件的安全性，给它及时打补丁。 综合来说，RDS提供的控制手段有限，不太适合对性能有较高要求的应用。从实际是用来看，同样的程序和网络环境之下，Double Extra Large DB Instance上的mysql+innodb明显慢于Double Extra Large EC2上自己安装并优化的mysql+innodb，一个典型的数据是：使用前者，慢查询比例是1%，使用后者，这个比例立即降为0.01%。 放弃RDS也就意味着放弃RDS的各种好处，为了弥补这个不足，可以为EC2申请Amazon Elastic Block Store (EBS) 。EC2+EBS就和RDS没什么两样了，只是管理起来还是有点费神，综合的价格应该和RDS差不多。我这里还没有真正尝试过EC2+EBS的方案，以后等存储出现瓶颈了再说了，现在EC2自带的850G存储绰绰有余。 amazon的服务还是挺让我满意，特别是phone support反应很快速，服务人员也挺专业，挺好。不过我并没有其他云计算服务的使用经验，没有比较，也不清楚amazon云计算在业界究竟出于何种地位，这个以后再慢慢去了解吧。&lt;img src=&quot;http://www1.feedsky.com/t1/513995830/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995830/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>工作</category><category>EC2</category><category>互联网</category><category>电子商务&amp;互联网</category><category>云计算</category><category>amazon</category><category>mysql</category><category>EBS</category><category>RDS</category><pubDate>Sun, 25 Apr 2010 19:19:55 +0800</pubDate><author>Huan Du</author><comments>http://huandu.me/2010/04/25/616/#comments</comments><guid isPermaLink="false">http://www.realdodo.com/?p=616</guid><dc:creator>Huan Du</dc:creator><fs:srclink>http://huandu.me/2010/04/25/616/</fs:srclink><fs:srcfeed>http://huandu.me/feed</fs:srcfeed><fs:itemid>feedsky/realdodo/~8631699/513995830/6015014</fs:itemid></item><item><title>如何衡量每一次决策的价值</title><link>http://item.feedsky.com/~feedsky/realdodo/~8631699/513995831/6015014/1/item.html</link><content:encoded>&lt;p&gt;最近和朋友聊到这个话题，顺便发散一下，说说完整想法。&lt;/p&gt;
&lt;p&gt;我首先觉得这是一个伪命题，不太可能有种方法能衡量决策的价值。决策有时效性，过了这个时间，决策的价值就发生变化，无法衡量。此外，决策也没有绝对的好或不好，往往都是各方权衡的结果。相对来说，决策的过程比较容易衡量，如果每次能按照最合理的过程来进行决策，决策本身就应该具有最大的价值。就能从侧面衡量决策本身的价值。&lt;/p&gt;
&lt;p&gt;我认为合理的决策过程是：找到所有决策相关的人，推动大家在合适的时间达成一致。Find out stake-holders, and push them to reach a consensus at right time.&lt;/p&gt;
&lt;p&gt;这句话非常的tricky，里面暗含很多意思。比如谁去负责push、stake-holders如何定义、right time究竟是什么时候等。&lt;/p&gt;
&lt;p&gt;如果我是决策的最终负责人，那么负责push的人就应该是我，这个最容易回答。&lt;/p&gt;
&lt;p&gt;要界定stake-holders就要看这个决策一旦制定该如何实施，所有在实施过程中会涉及到的关键人物都应该算作stake-holders，决策的过程应该是所有stake-holders buy in整个决策的过程。比如要决定一个feature是否应该做，首先可以假设要做，然后就可以知道需要有PM来决定feature细节、要dev决定如何实现、要team lead决定如何将这个插入项目计划之中，等等，这样就要引入相关的负责人来进行评估。此外，这个例子里面暗含一个角色，这个feature的提出者，他/她有可能是一个dev、客服甚至公司外的用户，他/她应该也以某种形式参与进来，直接参与或者由某个人（PM）代理。集齐所有stake-holders，每个人buy in这个决策的后果（该干活的干活、该承担风险的承担风险等），就可以让这个决策通过，否则，修改决策本身，继续讨论。&lt;/p&gt;
&lt;p&gt;要确定right time非常困难，但可以有几个简单的衡量标准，比如要做出决策的各方面条件是否满足、决策本身是否依赖于某些时间相关的因素等，总的来说是一个仁者见仁的过程，需要决策的最终负责人来给出right time的预期。需要注意避免把right time看做是deadline，任何schedule都会遇到意外，而right time则不能出意外，deadline一定要早于right time，这样才能使得这个觉得真的right。此外，right time也不是ASAP（as soon as possible），过于草率的决策往往结果不会太好。&lt;/p&gt;
&lt;p&gt;“如何做决策”是一个需要长期积累的技术活，我现在经验尚少，只有这些肤浅的认识，欢迎所有读者过来批评指导。 :)&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/513995831/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995831/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://huandu.me/2010/04/12/612/feed/</wfw:commentRss><slash:comments>3</slash:comments><description>最近和朋友聊到这个话题，顺便发散一下，说说完整想法。 我首先觉得这是一个伪命题，不太可能有种方法能衡量决策的价值。决策有时效性，过了这个时间，决策的价值就发生变化，无法衡量。此外，决策也没有绝对的好或不好，往往都是各方权衡的结果。相对来说，决策的过程比较容易衡量，如果每次能按照最合理的过程来进行决策，决策本身就应该具有最大的价值。就能从侧面衡量决策本身的价值。 我认为合理的决策过程是：找到所有决策相关的人，推动大家在合适的时间达成一致。Find out stake-holders, and push them to reach a consensus at right time. 这句话非常的tricky，里面暗含很多意思。比如谁去负责push、stake-holders如何定义、right time究竟是什么时候等。 如果我是决策的最终负责人，那么负责push的人就应该是我，这个最容易回答。 要界定stake-holders就要看这个决策一旦制定该如何实施，所有在实施过程中会涉及到的关键人物都应该算作stake-holders，决策的过程应该是所有stake-holders buy in整个决策的过程。比如要决定一个feature是否应该做，首先可以假设要做，然后就可以知道需要有PM来决定feature细节、要dev决定如何实现、要team lead决定如何将这个插入项目计划之中，等等，这样就要引入相关的负责人来进行评估。此外，这个例子里面暗含一个角色，这个feature的提出者，他/她有可能是一个dev、客服甚至公司外的用户，他/她应该也以某种形式参与进来，直接参与或者由某个人（PM）代理。集齐所有stake-holders，每个人buy in这个决策的后果（该干活的干活、该承担风险的承担风险等），就可以让这个决策通过，否则，修改决策本身，继续讨论。 要确定right time非常困难，但可以有几个简单的衡量标准，比如要做出决策的各方面条件是否满足、决策本身是否依赖于某些时间相关的因素等，总的来说是一个仁者见仁的过程，需要决策的最终负责人来给出right time的预期。需要注意避免把right time看做是deadline，任何schedule都会遇到意外，而right time则不能出意外，deadline一定要早于right time，这样才能使得这个觉得真的right。此外，right time也不是ASAP（as soon as possible），过于草率的决策往往结果不会太好。 “如何做决策”是一个需要长期积累的技术活，我现在经验尚少，只有这些肤浅的认识，欢迎所有读者过来批评指导。 :)&lt;img src=&quot;http://www1.feedsky.com/t1/513995831/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995831/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>工作&amp;生活</category><category>工作</category><category>团队管理</category><category>项目流程</category><category>决策</category><pubDate>Mon, 12 Apr 2010 18:12:48 +0800</pubDate><author>Huan Du</author><comments>http://huandu.me/2010/04/12/612/#comments</comments><guid isPermaLink="false">http://www.realdodo.com/?p=612</guid><dc:creator>Huan Du</dc:creator><fs:srclink>http://huandu.me/2010/04/12/612/</fs:srclink><fs:srcfeed>http://huandu.me/feed</fs:srcfeed><fs:itemid>feedsky/realdodo/~8631699/513995831/6015014</fs:itemid></item><item><title>Scrum的基础知识、常见问题和解决方法</title><link>http://item.feedsky.com/~feedsky/realdodo/~8631699/513995832/6015014/1/item.html</link><content:encoded>&lt;p&gt;Scrum是一种敏捷开发过程，最基础的介绍推荐看这个：&lt;a href=&quot;http://www.mountaingoatsoftware.com/presentations/30--an-overview-of-scrum&quot; target=&quot;_blank&quot;&gt;An Overview of Scrum&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;关于Scrum运行当中常见问题及解决方法，我曾写过一个slides，在公司内部分享，现在重新整理了以后共享在这里：&lt;a href=&quot;http://www.slideshare.net/huandu/scrum-3667487&quot; target=&quot;_blank&quot;&gt;近距离接触Scrum&lt;/a&gt;&lt;/p&gt;
&lt;div style=&quot;width:425px&quot; id=&quot;__ss_3667487&quot;&gt;&lt;strong style=&quot;display:block;margin:12px 0 4px&quot;&gt;&lt;a href=&quot;http://www.slideshare.net/huandu/scrum-3667487&quot; title=&quot;近距离接触Scrum&quot;&gt;近距离接触Scrum&lt;/a&gt;&lt;/strong&gt;&lt;object width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=scrum-100408115541-phpapp02&amp;#038;stripped_title=scrum-3667487&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed src=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=scrum-100408115541-phpapp02&amp;#038;stripped_title=scrum-3667487&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;
&lt;div style=&quot;padding:5px 0 12px&quot;&gt;View more &lt;a href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a href=&quot;http://www.slideshare.net/huandu&quot;&gt;huandu&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;关于Scrum，最常见的误解是Scrum master == team lead，这是不对的。Scrum master可以是任何人，不需要担当任何管理职责，在Scrum中只是一个推动者。&lt;/p&gt;
&lt;p&gt;除了Scrum过程本身，还需要整个team共同遵守一些游戏规则。这些规则可以根据实际情况来制定，不过尽量不要超过3项，毕竟人能同时记住的事情&lt;a href=&quot;http://www.dailygalaxy.com/my_weblog/2008/04/the-limits-of-m.html&quot; target=&quot;_blank&quot;&gt;一 般不超过4项&lt;/a&gt;，还得留1项给当前的工作。&lt;/p&gt;
&lt;p&gt;例如，可以制定以下规则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;No suprise：大家都有义务保持信息通畅，及时有效的将stakeholders关心的消息传递给他们，尽量不要让任何人对结果产生suprise。对于不紧急的沟通，可以选择在daily scrum的时候将信息传递出来，尽量不打断其他人的日常工作。&lt;/li&gt;
&lt;li&gt;Focus on one thing：很少有人能够快速的在多个任务之间切换还不影响效率，集中精力干好一件事情是提高效率的最简单的做法。要做到这一点，必须懂得如何对手头工作进行排序，最简单的就是focus on high priority work，这也就是Scrum过程中安排work item并对它们排序的意义。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;无论如何，Scrum过程只是一个工具，而非目的，任何事情还是以delivery为重，流程本身执行是否合乎规范完全是次要的。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/513995832/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995832/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://huandu.me/2010/04/09/608/feed/</wfw:commentRss><slash:comments>1</slash:comments><description>Scrum是一种敏捷开发过程，最基础的介绍推荐看这个：An Overview of Scrum 关于Scrum运行当中常见问题及解决方法，我曾写过一个slides，在公司内部分享，现在重新整理了以后共享在这里：近距离接触Scrum 近距离接触Scrum View more presentations from huandu. 关于Scrum，最常见的误解是Scrum master == team lead，这是不对的。Scrum master可以是任何人，不需要担当任何管理职责，在Scrum中只是一个推动者。 除了Scrum过程本身，还需要整个team共同遵守一些游戏规则。这些规则可以根据实际情况来制定，不过尽量不要超过3项，毕竟人能同时记住的事情一 般不超过4项，还得留1项给当前的工作。 例如，可以制定以下规则： No suprise：大家都有义务保持信息通畅，及时有效的将stakeholders关心的消息传递给他们，尽量不要让任何人对结果产生suprise。对于不紧急的沟通，可以选择在daily scrum的时候将信息传递出来，尽量不打断其他人的日常工作。 Focus on one thing：很少有人能够快速的在多个任务之间切换还不影响效率，集中精力干好一件事情是提高效率的最简单的做法。要做到这一点，必须懂得如何对手头工作进行排序，最简单的就是focus on high priority work，这也就是Scrum过程中安排work item并对它们排序的意义。 无论如何，Scrum过程只是一个工具，而非目的，任何事情还是以delivery为重，流程本身执行是否合乎规范完全是次要的。&lt;img src=&quot;http://www1.feedsky.com/t1/513995832/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995832/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>工作</category><category>团队管理</category><category>敏捷开发</category><category>Scrum</category><category>项目流程</category><pubDate>Fri, 09 Apr 2010 01:44:04 +0800</pubDate><author>Huan Du</author><comments>http://huandu.me/2010/04/09/608/#comments</comments><guid isPermaLink="false">http://www.realdodo.com/?p=608</guid><dc:creator>Huan Du</dc:creator><fs:srclink>http://huandu.me/2010/04/09/608/</fs:srclink><fs:srcfeed>http://huandu.me/feed</fs:srcfeed><fs:itemid>feedsky/realdodo/~8631699/513995832/6015014</fs:itemid></item><item><title>一个时代的终结</title><link>http://item.feedsky.com/~feedsky/realdodo/~8631699/513995833/6015014/1/item.html</link><content:encoded>&lt;p&gt;&lt;a href=&quot;http://baike.baidu.com/view/997998.htm#1&quot; target=&quot;_blank&quot;&gt;明远&lt;/a&gt;正式宣布离开，他的时代终结了。&lt;/p&gt;
&lt;p&gt;我在明远手下做事也有一年多了，回忆一下，我们从未有过正式的交谈，他大概也不会认识我。他是我老大的老大的老大，他是一个产品人，他喜欢足球，而我只是一个沉默的程序员，对任何体育运动都不感冒，我们没有聊头。不过我还是得感谢他，在我看来，是他让我在工作中找到了久违的激情。&lt;/p&gt;
&lt;p&gt;明远很喜欢在季度沟通大会上不断激励我们的创业激情，讲述自己对未来、对竞争对手的分析，鼓励我们勇于接受挑战，实现心中理想。他的演讲很有特点，语调平缓，语速适中，整个过程很少带有废字，逻辑十分清楚，能用平淡的语言激起他人共鸣。他在演讲方面的老成与他的年龄很不相称，哪看得出他比我还小啊。&lt;/p&gt;
&lt;p&gt;在团队活动中，我印象最深的是有次部门全体员工集体出游的时候他穿着短裤拖鞋混迹于员工之中的形象。明远没有架子，在团队活动中他就是个小孩，不修边幅，很不起眼。这个样子的明远才是最真实的，而他的内心也许根本没有那么成熟，站在讲台上的那个过分成熟的身影终究会成为限制他自身发展的禁锢。&lt;/p&gt;
&lt;p&gt;由于种种我不知道的原因，明远选择了离开，有点突然。&lt;a href=&quot;http://hi.baidu.com/dongbao&quot; target=&quot;_blank&quot;&gt;东宝&lt;/a&gt;为明远在公司这段惊心动魄的创业历程写了篇《为了即将忘却的纪念》，可惜我只经历了这段创业旅程的最后一段。（东宝的文章已经看不到，可以看其他媒体的转载）&lt;/p&gt;
&lt;p&gt;明远，愿你在未来能永葆激情，创造更大辉煌。我看好你。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/513995833/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995833/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://huandu.me/2010/03/12/603/feed/</wfw:commentRss><slash:comments>5</slash:comments><description>明远正式宣布离开，他的时代终结了。 我在明远手下做事也有一年多了，回忆一下，我们从未有过正式的交谈，他大概也不会认识我。他是我老大的老大的老大，他是一个产品人，他喜欢足球，而我只是一个沉默的程序员，对任何体育运动都不感冒，我们没有聊头。不过我还是得感谢他，在我看来，是他让我在工作中找到了久违的激情。 明远很喜欢在季度沟通大会上不断激励我们的创业激情，讲述自己对未来、对竞争对手的分析，鼓励我们勇于接受挑战，实现心中理想。他的演讲很有特点，语调平缓，语速适中，整个过程很少带有废字，逻辑十分清楚，能用平淡的语言激起他人共鸣。他在演讲方面的老成与他的年龄很不相称，哪看得出他比我还小啊。 在团队活动中，我印象最深的是有次部门全体员工集体出游的时候他穿着短裤拖鞋混迹于员工之中的形象。明远没有架子，在团队活动中他就是个小孩，不修边幅，很不起眼。这个样子的明远才是最真实的，而他的内心也许根本没有那么成熟，站在讲台上的那个过分成熟的身影终究会成为限制他自身发展的禁锢。 由于种种我不知道的原因，明远选择了离开，有点突然。东宝为明远在公司这段惊心动魄的创业历程写了篇《为了即将忘却的纪念》，可惜我只经历了这段创业旅程的最后一段。（东宝的文章已经看不到，可以看其他媒体的转载） 明远，愿你在未来能永葆激情，创造更大辉煌。我看好你。&lt;img src=&quot;http://www1.feedsky.com/t1/513995833/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995833/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>有啊</category><category>工作&amp;生活</category><category>工作</category><category>互联网</category><category>百度</category><category>电子商务</category><category>创业</category><pubDate>Fri, 12 Mar 2010 00:46:07 +0800</pubDate><author>Huan Du</author><comments>http://huandu.me/2010/03/12/603/#comments</comments><guid isPermaLink="false">http://www.realdodo.com/?p=603</guid><dc:creator>Huan Du</dc:creator><fs:srclink>http://huandu.me/2010/03/12/603/</fs:srclink><fs:srcfeed>http://huandu.me/feed</fs:srcfeed><fs:itemid>feedsky/realdodo/~8631699/513995833/6015014</fs:itemid></item><item><title>Firefox插件（plugins）开发实用指南</title><link>http://item.feedsky.com/~feedsky/realdodo/~8631699/513995834/6015014/1/item.html</link><content:encoded>&lt;p&gt;Firefox插件可实现强大功能，但其中麻烦事情不少。写这个实用指南首先是为了方便自己记忆，免得以后再次栽倒一些坑里面，如果能帮助其他人，则是更好。这个指南不是为了手把手教读者开发插件，而是&lt;strong&gt;作为一个FAQ，解决各种诡异问题&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;Firefox拥有众多的扩展（Extension），开发扩展也非常容易，不过有一些事情还是无法用扩展解决，需要访问操作系统的底层功能，这就需要写插件（plugins）。例如flash就是一个插件而不是扩展。&lt;/p&gt;
&lt;p&gt;Mozilla提供了一系列的教程和文档，虽然很不详尽，众多重要的API语焉不详，但至少是一个好的开始。&lt;/p&gt;
&lt;p&gt;最需要阅读的是&lt;a href=&quot;https://developer.mozilla.org/en/Gecko_Plugin_API_Reference&quot;&gt;plugins API和使用入门&lt;/a&gt;。这是一个相当长的文档，如果看完所有的内容会花费大量的时间而且还会很晕，这里列一些重点供参考。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en/Gecko_Plugin_API_Reference:Plug-in_Basics&quot;&gt;plugins基础概念&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en/Gecko_Plugin_API_Reference:Plug-in_Development_Overview#Writing%20Plug-ins&quot;&gt;写第一个插件&lt;/a&gt;（只需要关注Writing Plug-ins这一节所谈到的内容）&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/En/Developer_Guide/Source_Code/Mercurial#mozilla-1.9.2_%28Firefox_3.6%29&quot;&gt;获得一份firefox的源码&lt;/a&gt;，比如firefox 3.6。plugins的例子可以在源码里找到（modules/plugin/sdk/samples），如果出了问题还可以自己编译一个debug版的firefox来调试。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en/Gecko_Plugin_API_Reference:Browser_Side_Plug-in_API&quot;&gt;了解浏览器能提供什么功能&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;制作插件的安装程序，推荐用&lt;a href=&quot;https://developer.mozilla.org/en/Bundles&quot;&gt;扩展的方式安装插件&lt;/a&gt;，有无数的好处&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;完成以上这些内容以后差不多就已经可以实现自己的插件了，一般而言，参照着例子来做开发不会有什么问题，只是有不少细节需要留意。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Firefox plugins开发的众多奇怪的约定（假设plugins已经被正确安装）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;有些约定非常奇怪，不要问我为什么，天晓得开发firefox的牛人们怎么想的。&lt;/p&gt;
&lt;p&gt;在Windows下，plugins必须满足以下条件才能被firefox检测到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;插件的名字必须是np*.dll，也就是必须以np开头，.dll结尾&lt;/li&gt;
&lt;li&gt;插件dll资源的语言必须为LANG_ENGLISH，code page必须为1252。在rc文件里是这么写的：&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US&lt;br /&gt;
#pragma code_page(1252)&lt;br /&gt;
&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;插件dll的VERSION_INFO里面必须包含以下值：&lt;code&gt;
&lt;p&gt;VALUE &quot;MIMEType&quot;, &quot;application/x-your-mimetype&quot;&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
这个MIME就是&amp;lt;object&amp;gt;标签引用插件的唯一凭证。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在Linux下，plugins必须满足以下条件才能被检测到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;插件的名字必须是lib*plugin.so，即以lib开头，plugin.so结尾&lt;/li&gt;
&lt;li&gt;插件必须实现&lt;a href=&quot;https://developer.mozilla.org/En/NP_GetMIMEDescription&quot;&gt;NP_GetMIMEDescription&lt;/a&gt;和NP_GetPluginVersion，并返回合适MIME字符串。注意，这个字符串并不是普通的MIME，是有特殊规则的，详见前面这个链接的内容。&lt;/li&gt;
&lt;li&gt;插件so不要静态链接gtk、opensll、pthread、z等系统库，这会在不同linux平台上因为符号表的问题遇到各种运行时错误&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;特别需要说明的是，NP_GetPluginVersion、NP_GetEntryPoints等关键函数没有任何官方文档介绍它们，只能根据例子来猜，反正没事就别改它们的实现，copy例子中的代码就好。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;firefox插件开发注意事项&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;写firefox插件的一个基本习惯是，经常编译代码并运行它，保证你的插件还能工作。只要firefox无法加载dll/so，或者加载出现任何错误，都会悄无声息的忽略这个插件。时常关注一下&lt;a href=&quot;about:plugins&quot;&gt;about:plugins&lt;/a&gt;，看看插件是不是还在这个列表里。&lt;/p&gt;
&lt;p&gt;firefox插件从窗口模式上可分为&lt;a href=&quot;https://developer.mozilla.org/en/Gecko_Plugin_API_Reference:Drawing_and_Event_Handling#Windowless_Plug-ins&quot;&gt;windowless&lt;/a&gt;和&lt;a href=&quot;https://developer.mozilla.org/en/Gecko_Plugin_API_Reference:Drawing_and_Event_Handling#Windowed_Plug-ins&quot;&gt;windowed&lt;/a&gt;两种。其中，windowless模式的文档较多较全，是firefox比较推荐的模式，坑比较少，这里就不多说了。windowed模式则相反，需要好好说说。&lt;/p&gt;
&lt;p&gt;无论在Windows还是Linux上，windowed的插件都拥有独立于浏览器页面的窗口。firefox会通过插件的&lt;a href=&quot;https://developer.mozilla.org/en/NPP_SetWindow&quot;&gt;NPP_SetWindow&lt;/a&gt;来告诉插件当前窗口的情况。&lt;/p&gt;
&lt;p&gt;关于windowed插件有两个诡异问题需要注意：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows平台下，插件窗口默认会响应WM_CTLCOLOREDIT、WM_CTLCOLORLISTBOX、WM_CTLCOLORBTN、WM_CTLCOLORSTATIC消息，并设置一个默认的背景色。这本来没问题，但在Windows XP下，这个颜色居然永远是黑色，而不是默认系统背景色（通常是白色）。最好subclass这个窗口并且拦截这些消息，不要让firefox去处理它们。对于插件来说，firefox处理这些消息只是帮倒忙而已。至于firefox还帮了哪些倒忙，可以去源码widget/src/windows/nsWindows.cpp的nsWindow::ProcessMessage()去围观。&lt;/li&gt;
&lt;li&gt;Linux平台下，NPP_SetWindow传入的NPWindow指针中虽然有一个ws_info成员，这个成员里面也确实有一个display变量指向X Window的Display结构，但绝对不要真正使用它，否则可能会导致firefox直接退出，&lt;a href=&quot;http://www.mail-archive.com/xorg@lists.freedesktop.org/msg04770.html&quot;&gt;据说&lt;/a&gt;这可能是firefox的一个bug。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;测试firefox插件小技巧，测试方面的高手可以无视&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;测试插件前建议先在firefox里面&lt;a href=&quot;https://developer.mozilla.org/en/Setting_up_extension_development_environment&quot;&gt;创建一个新的profile（帐号）&lt;/a&gt;。这样可以创造一个最干净的开发环境，避免被其他扩展/插件干扰。&lt;/p&gt;
&lt;p&gt;默认的profile名叫default，在命令行里输入&lt;code&gt;firefox -p default&lt;/code&gt;就可以使用这个profile。如果只是输入&lt;code&gt;firefox -p&lt;/code&gt;，会弹出一个对话框用于选择profile。这个命令在Windows和Linux下都可使用。&lt;/p&gt;
&lt;p&gt;无论是哪个平台，调试插件的方法都很类似。&lt;/p&gt;
&lt;p&gt;Windows下可以用VC以调试方式启动firefox，载入插件时调试器会自动载入对应的符号，捕捉发生的异常或者设断点都很方便。&lt;/p&gt;
&lt;p&gt;Linux下直接用gdb就好，细节应该不用多说。有一点需要注意，系统默认安装的firefox命令（默认放在/usr/bin/firefox）是一个shell脚本，真正的可执行文件名字需要打开这个脚本自行查找。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实现firefox插件的基本功能&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;firefox为插件提供的接口十分原始，很多功能默认没有实现，下面提供了一些思路和方法。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;让插件接受焦点：默认情况下，&amp;lt;object&amp;gt;标签不能获得焦点，必须指定tabindex。&lt;/li&gt;
&lt;li&gt;在插件中使用tab键跳到下一个element：没有好办法，必须自己手动将焦点还给浏览器窗口（Linux下不必如此），然后自己用NPN_*系列函数找到应该获得focus的DOM element，然后调用这个element的focus()方法。&lt;/li&gt;
&lt;li&gt;隐藏和显示插件：直接设置&amp;lt;object&amp;gt;标签的style.display = &amp;#8220;none&amp;#8221;即可，但这里有个严重的副作用，firefox会调用插件的NS_PluginShutdown，销毁这个插件。如果不期望造成这种效果，要么别用这种方式隐藏插件，要么把插件状态保存在js里，再次显示的时候把状态设回去。&lt;/li&gt;
&lt;li&gt;触发DOM事件：firefox没有提供任何便利的方法触发DOM事件，要在插件中做到这点，必须自己模拟js触发DOM事件的过程。例如，对于HTML事件，假设self是DOM element，js会这么做。&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;
evt = document.createEvent(&quot;KeyboardEvent&quot;);&lt;br /&gt;
evt.initKeyEvent(&lt;br /&gt;
&quot;blur&quot;,      //  in DOMString typeArg,&lt;br /&gt;
false,       //  in boolean canBubbleArg,&lt;br /&gt;
false);      //  in boolean cancelableArg,&lt;br /&gt;
self.dispatchEvent(evt);&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
对应的C代码就是&lt;/li&gt;
&lt;/ul&gt;
&lt;pre name=&quot;code&quot; class=&quot;cpp&quot;&gt;
void FireHTMLEvent(NPP npp, const string &amp;#038; name)
{
    NPVariant result;
    NPObject *window;
    NPVariant vDoc;

    NPN_GetValue(npp, NPNVWindowNPObject, &amp;#038;window);

    // 也许页面已经跳转了……
    if (!window) {
        return;
    }

    NPIdentifier sDocument = NPN_GetStringIdentifier(&quot;document&quot;);
    NPN_GetProperty(npp, window, sDocument, &amp;#038;vDoc);
    NPN_ReleaseObject(window);

    // evt = document.createEvent(&quot;KeyboardEvent&quot;);
    NPVariant evt;
    NPObject* npDoc = NPVARIANT_TO_OBJECT(vDoc);
    NPIdentifier createEvent = NPN_GetStringIdentifier(&quot;createEvent&quot;);
    NPVariant eventArgs[1];
    STRINGZ_TO_NPVARIANT(&quot;HTMLEvents&quot;, eventArgs[0]);
    NPN_Invoke(npp, npDoc, createEvent, eventArgs, 1, &amp;#038;evt);
    NPN_ReleaseObject(npDoc);

    // evt.initKeyEvent(
    //    &quot;blur&quot;,      //  in DOMString typeArg,
    //    false,            //  in boolean canBubbleArg,
    //    false);            //  in boolean cancelableArg,
    NPObject * npEvt = NPVARIANT_TO_OBJECT(evt);
    NPIdentifier initKeyEvent = NPN_GetStringIdentifier(&quot;initEvent&quot;);
    NPVariant initArgs[3];
    STRINGZ_TO_NPVARIANT(name.c_str(), initArgs[0]);
    BOOLEAN_TO_NPVARIANT(false, initArgs[1]);
    BOOLEAN_TO_NPVARIANT(false, initArgs[2]);
    NPN_Invoke(npp, npEvt, initKeyEvent, initArgs, 3, &amp;#038;result);
    NPN_ReleaseVariantValue(&amp;#038;result);

    // this.dispatchEvent(evt);
    NPObject * self;
    NPN_GetValue(npp, NPNVPluginElementNPObject, &amp;#038;self);
    NPIdentifier dispatchEvent = NPN_GetStringIdentifier(&quot;dispatchEvent&quot;);
    NPVariant dispatchArgs[1];
    dispatchArgs[0] = evt;
    NPN_Invoke(npp, self, dispatchEvent, dispatchArgs, 1, &amp;#038;result);
    NPN_ReleaseVariantValue(&amp;#038;result);
    NPN_ReleaseObject(npEvt);

    NPN_ReleaseObject(self);
}
&lt;/pre&gt;
&lt;p&gt;暂时先写到这里，如果还有什么疑难杂症，欢迎评论，我继续添加。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/513995834/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995834/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://huandu.me/2010/02/11/595/feed/</wfw:commentRss><slash:comments>7</slash:comments><description>Firefox插件可实现强大功能，但其中麻烦事情不少。写这个实用指南首先是为了方便自己记忆，免得以后再次栽倒一些坑里面，如果能帮助其他人，则是更好。这个指南不是为了手把手教读者开发插件，而是作为一个FAQ，解决各种诡异问题。 Firefox拥有众多的扩展（Extension），开发扩展也非常容易，不过有一些事情还是无法用扩展解决，需要访问操作系统的底层功能，这就需要写插件（plugins）。例如flash就是一个插件而不是扩展。 Mozilla提供了一系列的教程和文档，虽然很不详尽，众多重要的API语焉不详，但至少是一个好的开始。 最需要阅读的是plugins API和使用入门。这是一个相当长的文档，如果看完所有的内容会花费大量的时间而且还会很晕，这里列一些重点供参考。 plugins基础概念 写第一个插件（只需要关注Writing Plug-ins这一节所谈到的内容） 获得一份firefox的源码，比如firefox 3.6。plugins的例子可以在源码里找到（modules/plugin/sdk/samples），如果出了问题还可以自己编译一个debug版的firefox来调试。 了解浏览器能提供什么功能 制作插件的安装程序，推荐用扩展的方式安装插件，有无数的好处 完成以上这些内容以后差不多就已经可以实现自己的插件了，一般而言，参照着例子来做开发不会有什么问题，只是有不少细节需要留意。 Firefox plugins开发的众多奇怪的约定（假设plugins已经被正确安装） 有些约定非常奇怪，不要问我为什么，天晓得开发firefox的牛人们怎么想的。 在Windows下，plugins必须满足以下条件才能被firefox检测到： 插件的名字必须是np*.dll，也就是必须以np开头，.dll结尾 插件dll资源的语言必须为LANG_ENGLISH，code page必须为1252。在rc文件里是这么写的： LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) 插件dll的VERSION_INFO里面必须包含以下值： VALUE &quot;MIMEType&quot;, &quot;application/x-your-mimetype&quot; 这个MIME就是&amp;#60;object&amp;#62;标签引用插件的唯一凭证。 在Linux下，plugins必须满足以下条件才能被检测到： 插件的名字必须是lib*plugin.so，即以lib开头，plugin.so结尾 插件必须实现NP_GetMIMEDescription和NP_GetPluginVersion，并返回合适MIME字符串。注意，这个字符串并不是普通的MIME，是有特殊规则的，详见前面这个链接的内容。 插件so不要静态链接gtk、opensll、pthread、z等系统库，这会在不同linux平台上因为符号表的问题遇到各种运行时错误 特别需要说明的是，NP_GetPluginVersion、NP_GetEntryPoints等关键函数没有任何官方文档介绍它们，只能根据例子来猜，反正没事就别改它们的实现，copy例子中的代码就好。 firefox插件开发注意事项 写firefox插件的一个基本习惯是，经常编译代码并运行它，保证你的插件还能工作。只要firefox无法加载dll/so，或者加载出现任何错误，都会悄无声息的忽略这个插件。时常关注一下about:plugins，看看插件是不是还在这个列表里。 firefox插件从窗口模式上可分为windowless和windowed两种。其中，windowless模式的文档较多较全，是firefox比较推荐的模式，坑比较少，这里就不多说了。windowed模式则相反，需要好好说说。 无论在Windows还是Linux上，windowed的插件都拥有独立于浏览器页面的窗口。firefox会通过插件的NPP_SetWindow来告诉插件当前窗口的情况。 关于windowed插件有两个诡异问题需要注意： Windows平台下，插件窗口默认会响应WM_CTLCOLOREDIT、WM_CTLCOLORLISTBOX、WM_CTLCOLORBTN、WM_CTLCOLORSTATIC消息，并设置一个默认的背景色。这本来没问题，但在Windows XP下，这个颜色居然永远是黑色，而不是默认系统背景色（通常是白色）。最好subclass这个窗口并且拦截这些消息，不要让firefox去处理它们。对于插件来说，firefox处理这些消息只是帮倒忙而已。至于firefox还帮了哪些倒忙，可以去源码widget/src/windows/nsWindows.cpp的nsWindow::ProcessMessage()去围观。 Linux平台下，NPP_SetWindow传入的NPWindow指针中虽然有一个ws_info成员，这个成员里面也确实有一个display变量指向X Window的Display结构，但绝对不要真正使用它，否则可能会导致firefox直接退出，据说这可能是firefox的一个bug。 测试firefox插件小技巧，测试方面的高手可以无视 测试插件前建议先在firefox里面创建一个新的profile（帐号）。这样可以创造一个最干净的开发环境，避免被其他扩展/插件干扰。 默认的profile名叫default，在命令行里输入firefox -p default就可以使用这个profile。如果只是输入firefox -p，会弹出一个对话框用于选择profile。这个命令在Windows和Linux下都可使用。 无论是哪个平台，调试插件的方法都很类似。 Windows下可以用VC以调试方式启动firefox，载入插件时调试器会自动载入对应的符号，捕捉发生的异常或者设断点都很方便。 Linux下直接用gdb就好，细节应该不用多说。有一点需要注意，系统默认安装的firefox命令（默认放在/usr/bin/firefox）是一个shell脚本，真正的可执行文件名字需要打开这个脚本自行查找。 实现firefox插件的基本功能 firefox为插件提供的接口十分原始，很多功能默认没有实现，下面提供了一些思路和方法。 [...]&lt;img src=&quot;http://www1.feedsky.com/t1/513995834/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995834/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>工作</category><category>插件</category><category>FAQ</category><category>程序</category><category>技术文章</category><category>Firefox</category><pubDate>Thu, 11 Feb 2010 19:21:20 +0800</pubDate><author>Huan Du</author><comments>http://huandu.me/2010/02/11/595/#comments</comments><guid isPermaLink="false">http://www.realdodo.com/?p=595</guid><dc:creator>Huan Du</dc:creator><fs:srclink>http://huandu.me/2010/02/11/595/</fs:srclink><fs:srcfeed>http://huandu.me/feed</fs:srcfeed><fs:itemid>feedsky/realdodo/~8631699/513995834/6015014</fs:itemid></item><item><title>我这程序员的一年</title><link>http://item.feedsky.com/~feedsky/realdodo/~8631699/513995835/6015014/1/item.html</link><content:encoded>&lt;p&gt;2008年12月30日，正是我在微软发farewell letter的日子。我当时已经拿到百度的offer，正在准备把自己的角色从微软的项目经理转换成百度的技术研发。角色的转变背后往往藏着各种故事，我当然也不例外。&lt;/p&gt;
&lt;p&gt;从微软到百度，我只觉得这是我的幸运，并没什么值得夸耀的地方。我在微软的一年半时间里，技术上逐渐荒废，连自己也觉得堕落不已，有劲使不出。离开微软并非自我选择，但尝试走进百度，则是当时一个勇敢的决定，我重新抱起书本，打开已经陌生的Visual Studio，从头开始准备。&lt;/p&gt;
&lt;p&gt;我参加的第一个百度面试是在普天大厦，部门是NS，当时有两个面试官同时面我。我用刻意的沉稳与简洁来掩盖自己的不安，整个过程好似梦魇，令我疲惫不堪。郁闷的是，也许因为HR之间没有沟通好，前一轮面试结束1小时后，我还要赶到信威通信，参加百度第二个面试，电子商务部的面试。本来在上一个面试中我已经把斗志与自信消磨的差不多了，我只好用自己的本色来面对这第二个面试。很奇怪，第二个面试反而比第一个轻松，我发现我的思维开始活跃起来，沉睡已久的程序员的细胞开始复苏，讲起各种技术竟也能变得流畅而不刻板了。&lt;/p&gt;
&lt;p&gt;再之后，我觉得自己像撞了大运一般，两边的面试竟都走到了最后一轮，而且都还通过了，这个面试的经历为我今年的程序员之路开了个好头。&lt;/p&gt;
&lt;p&gt;值得一提的是，在电子商务部最后一轮面试的时候，老大问我对未来的规划时，我犹豫了。我曾想，做项目经理、做管理似乎是一个程序员的必然发展道路，但对于我真的适合么？我已经厌烦那种push团队前进、营造团队氛围、制定远景方向这种事情，我更想这几年踏踏实实的做事，完善自己的知识体系。但当时我还无法打破自己的思维惯性，还是支支吾吾的说希望成为研发经理云云。直到加入百度半年后，我才坚定自己的想法，做一个简单可依赖的程序员，先从技术做起。&lt;/p&gt;
&lt;p&gt;2009年1月，我加入了百度，这程序员的一年开始了，然后很快，这一年结束了。&lt;/p&gt;
&lt;p&gt;我从没想过时间会过的这么快，这么紧张有趣。我从对Linux一窍不通，到现在都开始习惯完全用vi编码、在命令行中调试、负责服务器程序的优化，这种变化我自己都感到惊讶。&lt;/p&gt;
&lt;p&gt;粗略统计了一下，这一年大约写了10K的代码，这个数字比一年前0行代码比起来当然是无穷大，但和原来本科七年写100K代码比起来，似乎也不算那么多。我当年做简历的时候就很惊讶，自己参与过的各种项目、自己写的各种小玩意，居然有那么多行代码，到今天我终于明白，其实这并不难，如果不是今年我把很多时间用在摸索上面，恐怕代码行数还得翻番。&lt;/p&gt;
&lt;p&gt;我今年最大的收获是激活了程序员的基因，手指终于开始适应写代码啦，这是一个很好的开始。&lt;/p&gt;
&lt;p&gt;本文写到这里似乎还没开始说这一年到底发生了什么，但确实已经要结束了。无论是接手康神留下的系统，还是从信威搬到百度大厦，这些都是外在的一些挑战与变化，相比自己重新选择未来道路这件事情来说，真的是微不足道。我这程序员的一年，恰好就是选择结果的体现，到现在我已经可以说，这个选择没有错，至少我比原来快乐。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/513995835/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995835/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://huandu.me/2009/12/30/592/feed/</wfw:commentRss><slash:comments>15</slash:comments><description>2008年12月30日，正是我在微软发farewell letter的日子。我当时已经拿到百度的offer，正在准备把自己的角色从微软的项目经理转换成百度的技术研发。角色的转变背后往往藏着各种故事，我当然也不例外。 从微软到百度，我只觉得这是我的幸运，并没什么值得夸耀的地方。我在微软的一年半时间里，技术上逐渐荒废，连自己也觉得堕落不已，有劲使不出。离开微软并非自我选择，但尝试走进百度，则是当时一个勇敢的决定，我重新抱起书本，打开已经陌生的Visual Studio，从头开始准备。 我参加的第一个百度面试是在普天大厦，部门是NS，当时有两个面试官同时面我。我用刻意的沉稳与简洁来掩盖自己的不安，整个过程好似梦魇，令我疲惫不堪。郁闷的是，也许因为HR之间没有沟通好，前一轮面试结束1小时后，我还要赶到信威通信，参加百度第二个面试，电子商务部的面试。本来在上一个面试中我已经把斗志与自信消磨的差不多了，我只好用自己的本色来面对这第二个面试。很奇怪，第二个面试反而比第一个轻松，我发现我的思维开始活跃起来，沉睡已久的程序员的细胞开始复苏，讲起各种技术竟也能变得流畅而不刻板了。 再之后，我觉得自己像撞了大运一般，两边的面试竟都走到了最后一轮，而且都还通过了，这个面试的经历为我今年的程序员之路开了个好头。 值得一提的是，在电子商务部最后一轮面试的时候，老大问我对未来的规划时，我犹豫了。我曾想，做项目经理、做管理似乎是一个程序员的必然发展道路，但对于我真的适合么？我已经厌烦那种push团队前进、营造团队氛围、制定远景方向这种事情，我更想这几年踏踏实实的做事，完善自己的知识体系。但当时我还无法打破自己的思维惯性，还是支支吾吾的说希望成为研发经理云云。直到加入百度半年后，我才坚定自己的想法，做一个简单可依赖的程序员，先从技术做起。 2009年1月，我加入了百度，这程序员的一年开始了，然后很快，这一年结束了。 我从没想过时间会过的这么快，这么紧张有趣。我从对Linux一窍不通，到现在都开始习惯完全用vi编码、在命令行中调试、负责服务器程序的优化，这种变化我自己都感到惊讶。 粗略统计了一下，这一年大约写了10K的代码，这个数字比一年前0行代码比起来当然是无穷大，但和原来本科七年写100K代码比起来，似乎也不算那么多。我当年做简历的时候就很惊讶，自己参与过的各种项目、自己写的各种小玩意，居然有那么多行代码，到今天我终于明白，其实这并不难，如果不是今年我把很多时间用在摸索上面，恐怕代码行数还得翻番。 我今年最大的收获是激活了程序员的基因，手指终于开始适应写代码啦，这是一个很好的开始。 本文写到这里似乎还没开始说这一年到底发生了什么，但确实已经要结束了。无论是接手康神留下的系统，还是从信威搬到百度大厦，这些都是外在的一些挑战与变化，相比自己重新选择未来道路这件事情来说，真的是微不足道。我这程序员的一年，恰好就是选择结果的体现，到现在我已经可以说，这个选择没有错，至少我比原来快乐。&lt;img src=&quot;http://www1.feedsky.com/t1/513995835/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995835/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>工作&amp;生活</category><category>工作</category><category>微软</category><category>百度</category><category>程序</category><category>职业规划</category><pubDate>Wed, 30 Dec 2009 13:35:02 +0800</pubDate><author>Huan Du</author><comments>http://huandu.me/2009/12/30/592/#comments</comments><guid isPermaLink="false">http://www.realdodo.com/?p=592</guid><dc:creator>Huan Du</dc:creator><fs:srclink>http://huandu.me/2009/12/30/592/</fs:srclink><fs:srcfeed>http://huandu.me/feed</fs:srcfeed><fs:itemid>feedsky/realdodo/~8631699/513995835/6015014</fs:itemid></item><item><title>架构成长之路</title><link>http://item.feedsky.com/~feedsky/realdodo/~8631699/513995836/6015014/1/item.html</link><content:encoded>&lt;p&gt;产生软件架构设计的动机是代码复用，当一段重复的代码在多个不同的模块之中重复多次、每个人都为这段代码写的吐血的时候，大家自然而然就想到了——我们要有一个“架构”，或者说准确的说是一个&lt;strong&gt;框架&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;最原始框架一定是最基本的。从广义上来说，操作系统就是一个框架，它封装了每一个程序需要完成的基本任务，管理内存、磁盘IO、中断、输入输出等。不过操作系统能封装的东西有限，最关键的是，封装总是略显简单。比如，需要写一系列服务器程序，每个程序都要访问网络、保持长连接、管理连接池等，这些机制并不是几行代码就可以实现，需要一个稍微高一点层次的框架来解决这个问题。于是很自然的，&lt;strong&gt;层次&lt;/strong&gt;的概念就出来了。&lt;/p&gt;
&lt;p&gt;当框架有了层次概念之后，大家就可以各司其职，为完善各自的框架而奋斗，直到最终框架已经极为好用，每写一个程序就像冲杯奶茶一样容易。大家都很高兴。&lt;/p&gt;
&lt;p&gt;到现在为止，软件架构似乎还非常的简单直白，但随着时间发展，复杂的需求涌现出来了，架构也就突破原来软件框架的范畴，变成更复杂的东西。&lt;/p&gt;
&lt;p&gt;还是拿那一系列服务器程序为例，假设它们是一个在线交易平台的后台服务。某个程序员完成了个人转账业务的代码，另一个程序员完成了商户转账业务的代码，互相一比对发现，嘿，大家怎么写的大同小异，应该优化。不过这样的优化存在着风险，因为谁都不能完全确定，未来这两个业务会怎么发展。&lt;/p&gt;
&lt;p&gt;保守的程序员会让这两份代码独立，静观其变，反正框架已经足够好，重写一份这种业务也花不了多少时间。这并没有错，不过会错失让架构突破框架限制的机会。&lt;/p&gt;
&lt;p&gt;喜欢挑战的程序员会仔细思考这两个&lt;strong&gt;业务逻辑&lt;/strong&gt;，从&lt;strong&gt;业务&lt;/strong&gt;的层面来分析其中的异同以及未来变化的趋势。&lt;/p&gt;
&lt;p&gt;比如转账这件事，无论是个人业务还是商户业务，创建交易、记账这些事情总少不了吧，这或许可以抽象出来，但是检查账户权限、验证密码这些事情肯定会不一样，这些东西保持尽可能的可变。如此这般，业务层面的封装逐渐成形，所谓的架构终于有了架构的意思。&lt;/p&gt;
&lt;p&gt;在引入业务之前，前面提到的“架构”只是一个放之四海皆可用的程序员的玩具；引入业务之后，架构终于得以走向成熟，摆脱程序员的束缚，反过来影响程序员的思维。&lt;/p&gt;
&lt;p&gt;真正让架构腾飞的是引入领域知识（domain knowledge）的概念。作为一个具有业务功能的系统，要维护业务的纯正性不容易。还是拿转账来说事，如果提供了创建交易和记账的业务抽象，再假设转账是一气呵成的，从代码上来说，先创建交易还是先记账其实没有太大区别。但这事搁在包含了领域知识的架构里面可不行，因为账务要求，必须先有单据再有流水。&lt;/p&gt;
&lt;p&gt;领域知识把程序员变成业务专家，渐渐的程序员就更能明白什么是业务、什么算框架，也可以开始用业务接口包装子系统，理解子系统之间的逻辑，让架构日臻完善。&lt;/p&gt;
&lt;p&gt;P.S. 上面这些文字并没有涉及具体的设计原则、方法。&lt;/p&gt;
&lt;p&gt;P.S.S. 再完美的架构也需要持续改进，况且这世界上恐怕还不存在所谓“完美”的架构。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/513995836/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995836/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://huandu.me/2009/12/29/590/feed/</wfw:commentRss><slash:comments>2</slash:comments><description>产生软件架构设计的动机是代码复用，当一段重复的代码在多个不同的模块之中重复多次、每个人都为这段代码写的吐血的时候，大家自然而然就想到了——我们要有一个“架构”，或者说准确的说是一个框架。 最原始框架一定是最基本的。从广义上来说，操作系统就是一个框架，它封装了每一个程序需要完成的基本任务，管理内存、磁盘IO、中断、输入输出等。不过操作系统能封装的东西有限，最关键的是，封装总是略显简单。比如，需要写一系列服务器程序，每个程序都要访问网络、保持长连接、管理连接池等，这些机制并不是几行代码就可以实现，需要一个稍微高一点层次的框架来解决这个问题。于是很自然的，层次的概念就出来了。 当框架有了层次概念之后，大家就可以各司其职，为完善各自的框架而奋斗，直到最终框架已经极为好用，每写一个程序就像冲杯奶茶一样容易。大家都很高兴。 到现在为止，软件架构似乎还非常的简单直白，但随着时间发展，复杂的需求涌现出来了，架构也就突破原来软件框架的范畴，变成更复杂的东西。 还是拿那一系列服务器程序为例，假设它们是一个在线交易平台的后台服务。某个程序员完成了个人转账业务的代码，另一个程序员完成了商户转账业务的代码，互相一比对发现，嘿，大家怎么写的大同小异，应该优化。不过这样的优化存在着风险，因为谁都不能完全确定，未来这两个业务会怎么发展。 保守的程序员会让这两份代码独立，静观其变，反正框架已经足够好，重写一份这种业务也花不了多少时间。这并没有错，不过会错失让架构突破框架限制的机会。 喜欢挑战的程序员会仔细思考这两个业务逻辑，从业务的层面来分析其中的异同以及未来变化的趋势。 比如转账这件事，无论是个人业务还是商户业务，创建交易、记账这些事情总少不了吧，这或许可以抽象出来，但是检查账户权限、验证密码这些事情肯定会不一样，这些东西保持尽可能的可变。如此这般，业务层面的封装逐渐成形，所谓的架构终于有了架构的意思。 在引入业务之前，前面提到的“架构”只是一个放之四海皆可用的程序员的玩具；引入业务之后，架构终于得以走向成熟，摆脱程序员的束缚，反过来影响程序员的思维。 真正让架构腾飞的是引入领域知识（domain knowledge）的概念。作为一个具有业务功能的系统，要维护业务的纯正性不容易。还是拿转账来说事，如果提供了创建交易和记账的业务抽象，再假设转账是一气呵成的，从代码上来说，先创建交易还是先记账其实没有太大区别。但这事搁在包含了领域知识的架构里面可不行，因为账务要求，必须先有单据再有流水。 领域知识把程序员变成业务专家，渐渐的程序员就更能明白什么是业务、什么算框架，也可以开始用业务接口包装子系统，理解子系统之间的逻辑，让架构日臻完善。 P.S. 上面这些文字并没有涉及具体的设计原则、方法。 P.S.S. 再完美的架构也需要持续改进，况且这世界上恐怕还不存在所谓“完美”的架构。&lt;img src=&quot;http://www1.feedsky.com/t1/513995836/realdodo/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/realdodo/~8631699/513995836/6015014/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>工作</category><category>架构</category><category>程序</category><category>技术文章</category><category>领域知识</category><category>框架</category><category>业务</category><pubDate>Tue, 29 Dec 2009 10:30:14 +0800</pubDate><author>Huan Du</author><comments>http://huandu.me/2009/12/29/590/#comments</comments><guid isPermaLink="false">http://www.realdodo.com/?p=590</guid><dc:creator>Huan Du</dc:creator><fs:srclink>http://huandu.me/2009/12/29/590/</fs:srclink><fs:srcfeed>http://huandu.me/feed</fs:srcfeed><fs:itemid>feedsky/realdodo/~8631699/513995836/6015014</fs:itemid></item></channel></rss>
