<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Some reminiscences, some memories &#187; Stardy &amp; Research</title>
	<atom:link href="http://www.mikespook.com/index.php/category/stardy-research/feed" rel="self" type="application/rss+xml" />
	<link>http://www.mikespook.com</link>
	<description>Just another boring day</description>
	<lastBuildDate>Wed, 08 Feb 2012 09:28:30 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>[翻译]1K 玫瑰花</title>
		<link>http://www.mikespook.com/2012/02/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%911k-%e7%8e%ab%e7%91%b0%e8%8a%b1/</link>
		<comments>http://www.mikespook.com/2012/02/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%911k-%e7%8e%ab%e7%91%b0%e8%8a%b1/#comments</comments>
		<pubDate>Wed, 08 Feb 2012 09:22:14 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Stardy & Research]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[rose]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=1202</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
这篇文章介绍了使用 canvas 的绘图功能绘制一个 3D 玫瑰。很有特色，随翻译于此。

在 Valentine&#8217;s Day 即将来临之际，希望能给诸位死 coder 一点点好运气……

原文在此：http://www.romancortes.com/blog/1k-rose/

<span class="readmore"><a href="http://www.mikespook.com/2012/02/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%911k-%e7%8e%ab%e7%91%b0%e8%8a%b1/" title="[翻译]1K 玫瑰花">阅读全文——共5559字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>这篇文章介绍了使用 canvas 的绘图功能绘制一个 3D 玫瑰。很有特色，随翻译于此。<br />
在 Valentine&#8217;s Day 即将来临之际，希望能给诸位死 coder 一点点好运气……</p>
<p>原文在此：<a href="http://www.romancortes.com/blog/1k-rose/" target="_blank">http://www.romancortes.com/blog/1k-rose/</a></p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;-翻译分隔线&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
<img class="alignright" src="http://www.romancortes.com/ficheros/rose.jpg" alt="" width="510" height="310" /><br />
我参加了 <a title="js1k" href="http://js1k.com/2012-love/" target="_blank">js1k</a> 组织的关于“爱”的第四次主题活动。提交了一个静态图片，由程序生成的 3D 玫瑰花。你可以<a href="http://js1k.com/2012-love/demo/1022" target="_blank">在这里找到它</a>。</p>
<p>它是用蒙特卡洛方法显式分段采样生成 3D 曲面。我将在这个文章中逐步解释所有的要点。<br />
<span id="more-1202"></span></p>
<h1>蒙特卡洛方法的简短说明</h1>
<p>蒙特卡洛方法是一个强大到令人难以置信的工具。我经常在各种函数最优化和采样问题中使用它，当你可以运行大量 CPU 计算，但是没有设计和编写算法的时间的时候，它们工作起来就像魔法一样。在玫瑰花的例子中，它是优化代码体积的有用工具。</p>
<p>如果你对于蒙特卡洛方法不怎么了解，可以在 Wikipedia 上阅读这篇<a href="http://en.wikipedia.org/wiki/Monte_carlo_method" target="_blank">相当棒的文章</a>。</p>
<h1>显式曲面和采样/绘制</h1>
<p>为了定义玫瑰花的形状，我使用了多种显式定义曲面。一共使用了 31 个曲面：24 个花瓣，4 个萼片（花瓣周围的小叶子），2 个叶子，还有一个用于玫瑰花的枝干。</p>
<p>那么这些显式曲面是如何工作的呢？很简单，接下来展示一个 2D 例子：</p>
<p>首先定义显式曲面函数：</p>
<pre class="brush: jscript; title: ; notranslate">
function surface(a, b) {  // a 和 b 参数范围从 0 到 1。
    return {
        x: a*50,
        y: b*50
    };
    // 这个曲面将是一个 50x50 单位尺寸的正方形。
}
</pre>
<p>然后是绘制的代码：</p>
<pre class="brush: jscript; title: ; notranslate">
var canvas = document.body.appendChild(document.createElement(&quot;canvas&quot;)),
    context = canvas.getContext(&quot;2d&quot;),
    a, b, position;

// 现在将用参数 a 和 b 对曲面按照 .1 间隔进行采样：

for (a = 0; a &lt; 1; a += .1) {
    for (b = 0; b &lt; 1; b += .1) {
        position = surface(a, b);
        context.fillRect(position.x, position.y, 1, 1);
    }
}
</pre>
<p>结果为：</p>
<p><img src="http://www.romancortes.com/ficheros/rose-surface-drawing-1.gif" alt="" /></p>
<p>现在，让我们尝试用密度更高的采样间隔（更小间隔 = 更高密度）：</p>
<p><img src="http://www.romancortes.com/ficheros/rose-surface-drawing-2.gif" alt="" /></p>
<p>你已经看到了，当采样密度越来越高时，点和点之间越来越近，直到一个点和它临近的点之间的距离小于一个像素，这时屏幕上的曲面就完全被填充了（看 0.01）。这时，无论怎样提高密度也不会带来视觉上的变化，那么刚刚绘制的的区域就是已经被填充过的（比较 0.01 和 0.001 的结果）。</p>
<p>好，现在让我们重新定义曲面函数来绘制一个圆。有各种办法来实现，我会用这个公式：(x-x0)^2 + (y-y0)^2 < radius^2 这里的 (x0, y0) 是圆心：</p>
<pre class="brush: jscript; title: ; notranslate">
function surface(a, b) {
    var x = a * 100,
        y = b * 100,
        radius = 50,
        x0 = 50,
        y0 = 50;

    if ((x &#8211; x0) * (x &#8211; x0) + (y &#8211; y0) * (y &#8211; y0) &lt; radius * radius) {
        // 圆里
        return {
            x: x,
            y: y
        };
    } else {
        // 圆外
        return null;
    }
}
</pre>
<p>由于我屏蔽了圆外的点，所以要在采样中加入一个条件：</p>
<pre class="brush: jscript; title: ; notranslate">
if (position = surface(a, b)) {
    context.fillRect(position.x, position.y, 1, 1);
}
</pre>
<p>结果为：</p>
<p><img src="http://www.romancortes.com/ficheros/rose-surface-drawing-3.gif" alt="" /></p>
<p>刚才已经说了，有各种途径来定义圆，有些无需在采样中进行屏蔽。这里会展示其中一个办法，不过仅仅是一个提示；在接下来的文章中不会使用它：</p>
<pre class="brush: jscript; title: ; notranslate">
function surface(a, b) {
    // 使用圆的极坐标
    var angle = a * Math.PI * 2,
        radius = 50,
        x0 = 50,
        y0 = 50;

    return {
        x: Math.cos(angle) * radius * b + x0,
        y: Math.sin(angle) * radius * b + y0
    };
}
</pre>
<p><img src="http://www.romancortes.com/ficheros/rose-surface-drawing-3.gif" alt="" /></p>
<p>（这个方法需要比前一个方法更高的采样来填充这个圆）</p>
<p>好，现在来让圆变形，这样让它看起来更像一片花瓣：</p>
<pre class="brush: jscript; title: ; notranslate">
function surface(a, b) {
    var x = a * 100,
        y = b * 100,
        radius = 50,
        x0 = 50,
        y0 = 50;

    if ((x - x0) * (x - x0) + (y - y0) * (y - y0) &lt; radius * radius) {
        return {
            x: x,
            y: y * (1 + b) / 2 // 变形
        };
    } else {
        return null;
    }
}
</pre>
<p>结果为：</p>
<p><img src="http://www.romancortes.com/ficheros/rose-surface-drawing-4.gif" alt="" /></p>
<p>好，现在这个看起来更像是玫瑰花的花瓣的形状了。我建议你对变形多进行一些调整。你可以使用任何数学函数来实现，加减乘除、sin、 cos、乘方……任何函数。尝试修改这个函数，就能得到许多的形状（有的会很有趣）。</p>
<p>现在我为其增加一些颜色，对曲面添加颜色数据：</p>
<pre class="brush: jscript; title: ; notranslate">
function surface(a, b) {
    var x = a * 100,
        y = b * 100,
        radius = 50,
        x0 = 50,
        y0 = 50;

    if ((x - x0) * (x - x0) + (y - y0) * (y - y0) &lt; radius * radius) {
        return {
            x: x,
            y: y * (1 + b) / 2,
            r: 100 + Math.floor((1 - b) * 155), // 这会产生一个梯度
            g: 50,
            b: 50
        };
    } else {
        return null;
    }
}

for (a = 0; a &lt; 1; a += .01) {
    for (b = 0; b &lt; 1; b += .001) {
        if (point = surface(a, b)) {
            context.fillStyle = &quot;rgb(&quot; + point.r + &quot;,&quot; + point.g + &quot;,&quot; + point.b + &quot;)&quot;;
            context.fillRect(point.x, point.y, 1, 1);
        }
    }
}
</pre>
<p>结果为：</p>
<p><img src="http://www.romancortes.com/ficheros/rose-surface-drawing-5.jpg" alt="" /></p>
<p>铛！铛！铛！铛！隆重推出，有颜色的花瓣！</p>
<h1>3D 曲面和透视投影</h1>
<p>定义 3D 曲面是直接明了的：只要为曲面函数添加 z 属性。例如，接下来定义一个管道/圆柱体：</p>
<pre class="brush: jscript; title: ; notranslate">
function surface(a, b) {
    var angle = a * Math.PI * 2,
        radius = 100,
        length = 400;

    return {
        x: Math.cos(angle) * radius,
        y: Math.sin(angle) * radius,
        z: b * length - length / 2, // 通过减掉 length/2，使得这个管道的中心在 (0, 0, 0)
        r: 0,
        g: Math.floor(b * 255),
        b: 0
    };
}
</pre>
<p>现在，添加透视投影，首先定义一个摄影机：</p>
<p><img src="http://www.romancortes.com/ficheros/rose-camera.gif" alt="" /></p>
<p>我将摄影机放置于 (0, 0, cameraZ)，我将摄影机到画布的距离叫做“perspective”。我认为画布在 x/y 平面上，中心点是 (0, 0, cameraZ + perspective)。现在，每个采样点将会投影到画布：</p>
<pre class="brush: jscript; title: ; notranslate">
var pX, pY,  // 在画布上投影的 x 和 y 坐标
    perspective = 350,
    halfHeight = canvas.height / 2,
    halfWidth = canvas.width / 2,
    cameraZ = -700;

for (a = 0; a &lt; 1; a += .001) {
    for (b = 0; b &lt; 1; b += .01) {
        if (point = surface(a, b)) {
            pX = (point.x * perspective) / (point.z - cameraZ) + halfWidth;
            pY = (point.y * perspective) / (point.z - cameraZ) + halfHeight;
            context.fillStyle = &quot;rgb(&quot; + point.r + &quot;,&quot; + point.g + &quot;,&quot; + point.b + &quot;)&quot;;
            context.fillRect(pX, pY, 1, 1);
        }
    }
}
</pre>
<p>这个结果为：</p>
<p><img src="http://www.romancortes.com/ficheros/rose-tube1.jpg" alt="" /></p>
<h1>Z-buffer</h1>
<p>在计算机图形学中 z-buffer 是相当常见的技术，用于在远离摄影机的已经被绘制的点上绘制接近摄影机的点。它的工作方式是维护一个图像上已经画过的像素的数组。</p>
<p><img src="http://www.romancortes.com/ficheros/rose-z-buffer.jpg" alt="" /></p>
<p>这是玫瑰花的可视的 z-buffer，黑色是距离摄影机远的，白色是距离近的。</p>
<p>实现为：</p>
<pre class="brush: jscript; title: ; notranslate">
var zBuffer = [],
    zBufferIndex;

for (a = 0; a &lt; 1; a += .001) {
    for (b = 0; b &lt; 1; b += .01) {
        if (point = surface(a, b)) {
            pX = Math.floor((point.x * perspective) / (point.z - cameraZ) + halfWidth);
            pY = Math.floor((point.y * perspective) / (point.z - cameraZ) + halfHeight);
            zBufferIndex = pY * canvas.width + pX;
            if ((typeof zBuffer[zBufferIndex] === &quot;undefined&quot;) || (point.z &lt; zBuffer[zBufferIndex])) {
                zBuffer[zBufferIndex] = point.z;
                context.fillStyle = &quot;rgb(&quot; + point.r + &quot;,&quot; + point.g + &quot;,&quot; + point.b + &quot;)&quot;;
                context.fillRect(pX, pY, 1, 1);
            }
        }
    }
}
</pre>
<h1>旋转圆柱体</h1>
<p>你可以使用任何向量旋转方法。在玫瑰花的例子中，我使用 <a href="http://en.wikipedia.org/wiki/Euler_angles" target="_blank">Euler 旋转</a>。来实现一个基于 Y 轴的旋转：</p>
<pre class="brush: jscript; title: ; notranslate">
function surface(a, b) {
    var angle = a * Math.PI * 2,
        radius = 100,
        length = 400,
        x = Math.cos(angle) * radius,
        y = Math.sin(angle) * radius,
        z = b * length - length / 2,
        yAxisRotationAngle = -.4, // in radians!
        rotatedX = x * Math.cos(yAxisRotationAngle) + z * Math.sin(yAxisRotationAngle),
        rotatedZ = x * -Math.sin(yAxisRotationAngle) + z * Math.cos(yAxisRotationAngle);

    return {
        x: rotatedX,
        y: y,
        z: rotatedZ,
        r: 0,
        g: Math.floor(b * 255),
        b: 0
    };
}
</pre>
<p>结果为：</p>
<p><img src="http://www.romancortes.com/ficheros/rose-tube2.jpg" alt="" /></p>
<h1>蒙特卡洛采样</h1>
<p>在文章中已经使用了基于间隔的采样。它需要对每个曲面设定合适的间隔。如果间隔大，渲染会很快，但是曲面可能未被完全填充而存在空洞。另一方面，如果间隔过小，渲染会超过透视投影所需的时间。</p>
<p>所以，还是切换到蒙特卡洛采样吧：</p>
<pre class="brush: jscript; title: ; notranslate">
var i;

window.setInterval(function () {
    for (i = 0; i &lt; 10000; i++) {
        if (point = surface(Math.random(), Math.random())) {
            pX = Math.floor((point.x * perspective) / (point.z - cameraZ) + halfWidth);
            pY = Math.floor((point.y * perspective) / (point.z - cameraZ) + halfHeight);
            zBufferIndex = pY * canvas.width + pX;
            if ((typeof zBuffer[zBufferIndex] === &quot;undefined&quot;) || (point.z &lt; zBuffer[zBufferIndex])) {
                zBuffer[zBufferIndex] = point.z;
                context.fillStyle = &quot;rgb(&quot; + point.r + &quot;,&quot; + point.g + &quot;,&quot; + point.b + &quot;)&quot;;
                context.fillRect(pX, pY, 1, 1);
            }
        }
    }
}, 0);
</pre>
<p>现在，参数 a 和 b 被设置为两个随机值。对足够多的点进行采样，曲面就可以利用这种方法填充。多亏了间隔采样，我可以确定每次用 10000 个点绘制，然后更新屏幕。</p>
<p>特别说明一下，完全填充一个曲面仅仅需要保证伪随机数生成器的品质够好。在某些浏览器中，Math.random 是按照<a href="http://en.wikipedia.org/wiki/Linear_congruential_generator" target="_blank">线性一致生成器</a>实现的，而这可能在某些曲面上产生一些问题。如果你在采样中有较好的伪随机噪声生成的需求，你可以使用更高品质的如 Mersenne Twister （这里有其 JS 实现），或者在某些浏览器里可以使用的密码随机生成器。同样使用<a href="http://en.wikipedia.org/wiki/Low-discrepancy_sequence" target="_blank">低差异数序列</a>也是很好的解决方案。</p>
<h1>总结</h1>
<p>为了完成这个玫瑰花，花朵的每个部分，每个曲面，需要同时进行渲染。我为函数添加了第三个参数用于确定返回玫瑰花的哪个部分的点。数学上来说，这是一个分段函数，每个片段对应玫瑰花的一部分。在花瓣的部分，我使用旋转和拉伸/变形来创建所有花瓣。所有都是用文章中提及的方法混合来完成的。</p>
<p>当然通过采样建立显式曲面是众所周知的方法，并且是最古老的 3D 图形方法之一，在艺术用途中像我这样使用分段/蒙特卡洛/z-buffer 已经非常少见了。没什么创新，对于实际生活也不怎么有用，但是非常适合 js1k 这种简单并且小尺寸的要求。</p>
<p>通过这篇文章，我希望能够激发读者在计算机图形学上的兴趣来进行尝试，并且在不同的渲染方法中找到乐趣。在图形学的世界中探索和玩耍是一件令人兴奋的事情。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2012/02/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%911k-%e7%8e%ab%e7%91%b0%e8%8a%b1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>【翻译】App Engine Go 概述</title>
		<link>http://www.mikespook.com/2011/05/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%91app-engine-go-%e6%a6%82%e8%bf%b0/</link>
		<comments>http://www.mikespook.com/2011/05/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%91app-engine-go-%e6%a6%82%e8%bf%b0/#comments</comments>
		<pubDate>Wed, 11 May 2011 03:15:27 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Golang]]></category>
		<category><![CDATA[Stardy & Research]]></category>
		<category><![CDATA[gae]]></category>
		<category><![CDATA[golang]]></category>
		<category><![CDATA[google]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=977</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
就在我还寻思什么时候 Google 会在 NDK 里加入 Go 支持的时候，冷不丁的，Google 发力了。App Engine 支持 Go 了。下面是来自官方文档的概述。

&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;翻译分割线&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;

App Engine Go 概述

<span class="readmore"><a href="http://www.mikespook.com/2011/05/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%91app-engine-go-%e6%a6%82%e8%bf%b0/" title="【翻译】App Engine Go 概述">阅读全文——共1252字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>就在我还寻思什么时候 Google 会在 NDK 里加入 Go 支持的时候，冷不丁的，Google 发力了。App Engine 支持 Go 了。下面是来自官方文档的概述。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;翻译分割线&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<h1>App Engine Go 概述</h1>
<p>欢迎来到用于 Go 的 Google App Engine！<strong><span style="color: #ff0000;">实验性质</span></strong></p>
<p>利用 App Engine，可以使用<a href="http://www.golang.org/" target="_blank"> Go 编程语</a>言构建 Web 应用。Go 应用运行在 Google 的可伸缩的基础平台上，并使用大规模的持久化存储和服务。<br />
<span id="more-977"></span></p>
<h2>Go 运行时环境</h2>
<p>Go SDK 提供了与标准 Go <a href="http://golang.org/pkg/http/" target="_blank">http 包</a>相似的接口；编写 Go App Engine 应用与编写独立 Go Web 服务器类似。</p>
<p>Go 的运行时环境使用了 Go <a href="http://golang.org/doc/devel/release.html#r57" target="_blank">release r57.1</a>。SDK 包含了 Go 编译器和标准库，因此它无须额外的依赖。与 Java 和 Python 环境类似，并不是所有的标准库的功能都可以在沙箱中使用。例如，尝试打开一个套接字或向文件写入都会返回 <span style="color: #008000;"><strong>os.EINVAL</strong></span> 错误。</p>
<p>SDK 包含了自动构建服务来编译应用，因此无须自行包含编译器。同时，类似 Python SDK——当修改了源代码以后，应用会自动重新构建。</p>
<p>App Engine 的 Go 运行时环境提供了完整的 goroutine 支持，但并不是并行执行的：goroutine 在一个系统线程上调度。这个单线程的限制在未来的版本中可能会被移除。</p>
<p>Go 应用运行在一个有着简化库的安全“沙箱”环境中。例如，应用不能将数据写入本地文件系统或者随意建立网络连接。作为代替，应用使用 App Engine 提供的可伸缩的服务来保存数据或通过 Internet 进行通讯。</p>
<p>参阅运<a href="http://code.google.com/appengine/docs/go/runtime.html" target="_blank">行时环境</a>了解更多信息。</p>
<h2>数据存储和服务</h2>
<p>应用可以使用 App Engine <a href="http://code.google.com/appengine/docs/go/datastore/" target="_blank">数据存储</a>实现可信的、可伸缩的持久化的数据存储。Go 数据存储 API 提供了存取 Go 数据结构的原子语义。</p>
<p>App Engine <a href="http://code.google.com/appengine/docs/go/memcache/" target="_blank">Memcache</a> 提供了快速、临时性分布式存储，用于缓存数据存储查询和计算的结果。</p>
<p>应用使用 <a href="http://code.google.com/appengine/docs/go/urlfetch/" target="_blank">URL Fetch</a> 服务通过 Web 访问资源，使用 HTTP 和 HTTPS 协议与其他主机通讯。</p>
<p>应用可以使用 <a href="http://code.google.com/appengine/docs/go/users/" target="_blank">Google 帐号</a>进行身份验证。用户帐号的创建和登录由 Google 帐号负责，而已经拥有 Google 帐号的用户（诸如 GMail 帐号之类的）可以在你的应用中使用其帐号。应用可以检测到当前用户已经登录，并且可以访问用户的电子邮件地址。</p>
<h2>工具</h2>
<p>App Engine Go SDK 使用来自 Python SDK 的工具进行应用的测试和上传应用文件。</p>
<p><a href="http://code.google.com/appengine/docs/python/tools/devserver.html" target="_blank">开发服务器</a>在本地电脑上运行应用以便测试。该服务器模拟了 App Engine 数据存储、服务和沙盒限制。开发服务器同样可以基于测试，生成数据存储索引的配置，以便提升查询性能。</p>
<p>一个叫做 <a href="http://code.google.com/appengine/docs/python/tools/uploadinganapp.html" target="_blank">appcfg.py</a> 的有着多种用途的工具，处理所有将应用运行于 App Engine 的命令行交互。工具 appcfg.py 可以上传应用到 App Engine，或者仅更新数据存储索引配置，这样就可以在上传代码前构建新的索引。它也可以下载应用的日志，以便使用自己的工具分析应用的性能。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2011/05/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%91app-engine-go-%e6%a6%82%e8%bf%b0/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>【翻译】Godoc：文档化 Go 代码</title>
		<link>http://www.mikespook.com/2011/04/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%91godoc%ef%bc%9a%e6%96%87%e6%a1%a3%e5%8c%96-go-%e4%bb%a3%e7%a0%81/</link>
		<comments>http://www.mikespook.com/2011/04/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%91godoc%ef%bc%9a%e6%96%87%e6%a1%a3%e5%8c%96-go-%e4%bb%a3%e7%a0%81/#comments</comments>
		<pubDate>Fri, 01 Apr 2011 09:05:52 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Stardy & Research]]></category>
		<category><![CDATA[godoc]]></category>
		<category><![CDATA[golang]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=932</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
各位童鞋，愚人节好！由于鄙人愚钝，过不了这种高端节日，所以就不过节了。

所以今天即不会有鄙人要改名叫 mikeghost 的消息，也不会有诸如在 Android 上跑 iOS 应用的消息出现，当然，大家更不需要穿越的有木有来阅读本文。

生活还要继续……

<span class="readmore"><a href="http://www.mikespook.com/2011/04/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%91godoc%ef%bc%9a%e6%96%87%e6%a1%a3%e5%8c%96-go-%e4%bb%a3%e7%a0%81/" title="【翻译】Godoc：文档化 Go 代码">阅读全文——共2086字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>各位童鞋，愚人节好！由于鄙人愚钝，过不了这种高端节日，所以就不过节了。<br />
所以今天即不会有鄙人要改名叫 mikeghost 的消息，也不会有诸如在 Android 上跑 iOS 应用的消息出现，当然，大家更不需要穿越的有木有来阅读本文。<br />
生活还要继续……<br />
原文《Godoc: documenting Go code》在此：<a href="http://blog.golang.org/2011/03/godoc-documenting-go-code.html" target="_blank">http://blog.golang.org/2011/03/godoc-documenting-go-code.html</a><br />
&#8212;&#8212;&#8212;&#8212;&#8212;-翻译分割线&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>Go 项目对待文档的态度是严肃的。文档是让软件易于处理和维护的重要的组成部分。当然，它必须编写良好并且准确，而且必须容易编写和维护。理想情况，它应当同代码在一起，这样文档就可以伴随代码一起更新。程序员建立良好文档越简单，对所有人好处越多。</p>
<p>最终，我们开发出了<a href="http://golang.org/cmd/godoc/">godoc</a>文档工具。这个文章说明了用 godoc 生成文档的方法，以及解释了在你自己的项目中如何使用我们的约定和工具编写良好的文档。<br />
<span id="more-932"></span><br />
Godoc 解析 Go 代码，包括注释，然后生成如 HTML 或纯文本的文档。这使得文档同它描述的代码紧密的结合。例如，通过 godoc 的 Web 界面你可以通过轻轻一击，从函数的<a href="http://golang.org/pkg/strings/#HasPrefix">文档</a>跳转到其<a href="http://golang.org/src/pkg/strings/strings.go?s=6577:6614#L277">实现</a>。</p>
<p>Godoc 的概念同 Python 的 <a href="http://www.python.org/dev/peps/pep-0257/">Docstring</a> 和 Java 的 <a href="http://www.oracle.com/technetwork/java/javase/documentation/index-jsp-135444.html">Javadoc</a>类似，但是设计上更为简单。godoc 读取的注释不应该是语言结构（例如 Docstring 那样）或者必须拥有某种机器识别的标记（例如 Javadoc 那样）。Godoc 注释就是良好注释，即便是在没有 godoc 的情况下，也是短小易读的。</p>
<p>约定是简单的：对类型、变量、常量、函数或者包的注释，在其定义前编写普通的注释即可，不要插入空行。Godoc 将会把这些注释识别为对其后的内容的文档。例如，这是 fmt 包<a href="http://golang.org/pkg/fmt/#Fprint">Fprint 函数</a>的文档（这部分不翻译了，原味一点）：</p>
<pre class="brush: cpp; title: ; notranslate">
// Fprint formats using the default formats for its operands and writes to w.
// Spaces are added between operands when neither is a string.
// It returns the number of bytes written and any write error encountered.
func Fprint(w io.Writer, a ...interface{}) (n int, error os.Error) {
</pre>
<p>留意这些注释全部都是有它描述的元素名字的普通句子。这个重要的约定使我们可以生成各种不同格式的文档，从纯文本到 HTML，或者 UNIX 的 man 手册；或为了使其更容易阅读而进行截断，例如只显示第一行或者第一句的时候。</p>
<p>包上的注释会提供关于包大致信息的文档。这个注释可以很短，例如 sort 包的短短的描述：</p>
<pre class="brush: cpp; title: ; notranslate">
// The sort package provides primitives for sorting arrays
// and user-defined collections.
package sort
</pre>
<p>也可以使用类似 <a href="http://golang.org/pkg/gob/">gob package</a> 那样的详细描述。这个包需要大量的介绍性文档，而使用了其他约定：包的注释在其<a href="http://golang.org/src/pkg/gob/doc.go">doc.go</a>文件里，包含了注释和包的使用条款。</p>
<p>不论编写任何大小的注释，都要记得，第一句话将出现在 godoc 的 <a href="http://golang.org/pkg/">package list</a>中。</p>
<p>与顶级定义不相邻的注释，会被 godoc 的输出忽略，但有一个意外。以“BUG(who)”开头的顶级注释会被识别为已知的 bug，会被包含在包文档的“Bugs”部分。“who”应当是能提供更进一步信息的用户的名字。例如，这是来自于<a href="http://golang.org/pkg/bytes/#Bugs">bytes 包</a>的一个已知问题：</p>
<pre class="brush: cpp; title: ; notranslate">
// BUG(r): The rule Title uses for word boundaries does not handle Unicode punctuation properly.
</pre>
<p>Godoc  作为可执行命令时有些不同。它不会检查指定的源码，而是在属于 Go 源码的特殊的包“documentation”中寻找。“package  documentation”的注释作为命令文档。例如，查看<a href="http://golang.org/cmd/godoc/">godoc documentation</a>和与其对应的<a href="http://golang.org/src/cmd/godoc/doc.go">doc.go</a>文件。</p>
<p>用 Godoc 将注释转换为 HTML 时，有一些格式规则：</p>
<ul>
<li>紧接着下一行的文本被认为是同一段的；你必须空出一行，来分隔段落。</li>
<li>预格式化文本必须在注释符号内缩进（阅读gob的<a href="http://golang.org/src/pkg/gob/doc.go">doc.go</a>作为例子）。</li>
<li>无序额外符号，URL 会被转换为 HTML 链接。</li>
</ul>
<p>注意，这些规则没有哪个是需要你做超出通常要做的事情的范围的。</p>
<p>事实上，godoc 最棒的地方就是它的易用性。这样一来，许多 Go 代码，包括许多标准库，已经遵循这些约定。</p>
<p>可以用上面所描述的，将你自己的代码中的注释生成良好的文档。 任何安装在<code>$GOROOT/src/pkg/</code>的 Go 包，已经可以通过 godoc 的命令行和 HTTP 界面访问，你也可以通过 -path 标识添加指定的路径，或者在源代码目录执行“<code>godoc .</code>”。阅读<a href="http://golang.org/cmd/godoc/">godoc documentation</a>了解更多细节。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2011/04/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%91godoc%ef%bc%9a%e6%96%87%e6%a1%a3%e5%8c%96-go-%e4%bb%a3%e7%a0%81/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>【翻译】Gob 的数据</title>
		<link>http://www.mikespook.com/2011/03/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%91gob-%e7%9a%84%e6%95%b0%e6%8d%ae/</link>
		<comments>http://www.mikespook.com/2011/03/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%91gob-%e7%9a%84%e6%95%b0%e6%8d%ae/#comments</comments>
		<pubDate>Tue, 29 Mar 2011 07:58:46 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Stardy & Research]]></category>
		<category><![CDATA[gob]]></category>
		<category><![CDATA[golang]]></category>
		<category><![CDATA[protobuf]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=919</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
原文在此：http://blog.golang.org/2011/03/gobs-of-data.html，来自 Golang 官方博客。

Gob 是 Golang 的包中带的一个数据结构序列化的编/解码工具。在实际应用中，已经有不少的编解码工具/包/库了，为什么 Golang 还要新开发一个 Gob？又是一个重复的轮子？Gob 做了哪些工作？Gob 的优势是什么？本文做了一个较为全面的解释。

&#8212;&#8212;&#8212;&#8212;&#8212;-翻译分割线&#8212;&#8212;&#8212;&#8212;&#8212;-

<span class="readmore"><a href="http://www.mikespook.com/2011/03/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%91gob-%e7%9a%84%e6%95%b0%e6%8d%ae/" title="【翻译】Gob 的数据">阅读全文——共4659字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>原文在此：<a href="http://blog.golang.org/2011/03/gobs-of-data.html" target="_blank">http://blog.golang.org/2011/03/gobs-of-data.html</a>，来自 Golang 官方博客。</p>
<p>Gob 是 Golang 的包中带的一个数据结构序列化的编/解码工具。在实际应用中，已经有不少的编解码工具/包/库了，为什么 Golang 还要新开发一个 Gob？又是一个重复的轮子？Gob 做了哪些工作？Gob 的优势是什么？本文做了一个较为全面的解释。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;-翻译分割线&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<h1>Gob 的数据</h1>
<p>为了让某个数据结构能够在网络上传输或能够保存至文件，它必须被编码然后再解码。当然，已经有许多可用的编码方式了：JSON，XML，Google 的 protocol buffers，等等。而现在，又多了一种，由 Go 的 gob 包提供的方式。<br />
<span id="more-919"></span><br />
为什么定义新的编码？这要做许多繁重的工作。为什么不使用某个现成的格式？呃，无论如何，我们这样做了！Go 已经有刚才提到的所有编码方式的包（protocol buffer 包在另外一个代码库中，但它是下载得最多的包之一）。并且在许多情况下，包括同其他语言编写的工具和系统通讯，这些都是正确的选择。</p>
<p>但是在特定的 Go 环境中，例如在两个 Go 编写的服务之间通讯，这需要某些东西使得其更加容易使用，并且可能更加有效率。</p>
<p>Gobs 协同 Go 语言的工作方式，对于那些外部定义的、同语言无关的编码方式来说无法做到。同时，从现有的系统中也吸取了很多教训。</p>
<h2>目标</h2>
<p>gob 包有在设计时有许多目标。</p>
<p>首先，也是最显然的，它被设计成为非常容易使用的。一方面，由于 Go 有反射（reflection），就没有必要弄一个单独的接口定义语言或“协议编译器”。数据结构本身提供了编码和解码所需要的全部信息。 另一方面，这种方法也意味着 gob 永远无法良好的同其他语言协同工作，但这没问题：gob 是厚颜无耻的以 Go 为中心（译注：呃，以XXX为中心，坚决贯彻XXX的领导……）。</p>
<p>效率也是非常重要的。基于文本形式的，如 XML 和 JSON ，应用于高效通讯网络会太慢了。二进制编码是必须的（译注：二进制神马的，是必须的！回音：必须的……）！</p>
<p>Gob 流必须可以自解释。每个 gob 流，从开始读取，整个流将由包含足够的信息，以便在终端对其内容毫不知情的前提下对整个流加以解析。这一特性意味，即便你忘记了保存在文件中的 gob 流表示什么，也总是可以对其解码。</p>
<p>同样，这里有一些从 Google protocol buffers 获得的经验。</p>
<h2>Protocol buffer 的硬伤</h2>
<p>Protocol buffers 对 gob 的设计产生了主要的影响，但是有三个特性被谨慎的避开了。（暂且不说 protocol buffer 不是自解释的：如果你不知道 protocol buffer 编码时的数据的定义，你就无法解析它。）</p>
<p>首先，protocol buffer 仅工作于 Go 的 struct 数据类型。不能在最顶级编码一个整数或者数组，只可以将其至于 struct 中作为一个字段。 至少在 Go 中，这个限制似乎没有什么意义。如果你希望传输的仅仅是一个数组或者整数，为什么你要先将其放到 struct 中？</p>
<p>其次，可能 protocol buffer 的定义指定字段 T.x 和 T.y 需要解析，无论是在编码还是解码类型 T 的值。虽然，这样的必须字段看起来是个好主意，但是由于编解码器中必须含有用于编码和解码的特定的数据结构，用于报告必须字段是否丢失，实现的开销是大的。这同样也产生了问题。过一段时间后，某人可能希望修改数据定义，移除了必须的字段，但这导致现有接收数据的客户端崩溃。最好是在编码时就根本没有这些字段。（Protocol buffer 也有可选字段。但是，如果我们没有必须字段，所有的字段就是可选的。等一下还会针对可选字段进行一些讨论。）</p>
<p>第三个 protocol buffer 的硬伤是默认值。当 protocol buffer 在某个“默认”字段上设置了默认值，而解码后的结构就像那个字段被设置了某个值一样。这个想法在有 getter 和 setter 控制字段的访问的时候非常棒，但是当容器是一个原始结构的时候就很难控制其保持清晰了。必须的字段也存在同样的麻烦：在哪定义默认值，它们的类型是什么（是UTF-8文本？无符号字节串？在浮点型中有几位？）尽管有许多看起来很简单，protocol buffer 的设计和实现还是有许多伴随的问题。我们决定让这些都远离 gob，并且回到我们的 Go 旅程中，一个很有效率的默认规则：除非你设置了一些内容，否则就是那个类型的“零值”，而这个不需要被传输。</p>
<p>所以 gob 最终看起来是个更加通用、简单的 protocol buffer。它又是如何工作的呢？</p>
<h2>值</h2>
<p>编码后的 gob 数据不是 int8 或者 uint16 的串。作为代替，其看起来更象是 Go 的常量，不论是有符号的还是无符号的整数值是虚拟的、无大小定义的数字。当你编码一个 int8 的时候，其值被转换为一个无大小定义的变长整数。当你对 int64 编码时，其值也是转换为一个无大小定义的变长整数。（有符号和无符号是相同处理的，无大小定义也适用于无符号值。）如果都是值 7，在线传输的位是一致的。当接收者解码其值，它将其放入接收者变量中，可能是任意的一个整数类型。因此，编码方发送了一个来自 int8 的 7，而接收方可能将其保存在 int64 中。这没有问题：这个值永远匹配于一个整数。（如果不匹配，会产生错误。）在变量的大小上解偶，为编码提供了一些灵活性：我们可以随着软件演化扩展整数类型，但是仍然可以解码旧的数据。</p>
<p>这种灵活性对于指针同样有效。在传输前，所有指针都进行整理。int8、*int8、**int8、****int8等等的值，被传输为可能被存储于任何大小的 int，或者 *int，或者 ******int等等的整数值。这同样是一种灵活性。</p>
<p>同样的原因，在解码一个 struct，当其字段由编码方发送，存储于目标方的时候，也体现出这种灵活性。给出这样一个值</p>
<pre class="brush: cpp; title: ; notranslate">
type T struct { X, Y, Z int } // 只有导出字段（exported fields）被编码和解码。
var t = T{X: 7, Y: 0, Z: 8}
</pre>
<p>编码后仅发送 7 和 8。由于为零，Y 不会被发送；没有必要发送一个零值。</p>
<p>接收方可能用下面的结构解码：</p>
<pre class="brush: cpp; title: ; notranslate">
type U struct { X, Y *int8 } // 注意：int8 的指针
var u U
</pre>
<p>而获得的 u 的值只有 X （值为 7 的 int8 变量的地址）；Z 字段被忽略了——你应将其放到哪里呢？当解码一个 struct 的时候，字段会匹配其名字和类型，只有双方都有的字段会生效。这个简单的办法巧妙处理了“可选字段”问题：类型 T 添加了字段，过期的接收者仍然能处理它们知道的那部分。因此 gob 在可选字段上提供了重要的特性——无须任何额外的机制或标识。</p>
<p>从整数串可以构造其他类型：字节数组、字符串、数组、内存片段、Map，甚至浮点数组。IEEE 754 浮点位定义描述了浮点值存储为整数，在你知道其类型的时候，这会工作得很好，我们总是知道类型的吧。另外，这里的整数使用字节翻转的顺序发送，因为一般的浮点数字，就像是小整数数组，在低位上有许多个零是不用传递的。</p>
<p>gob 还有一个非常棒的特性是 Go 使得通过 GobEncoder 和 GobDecoder 接口使得自定义类型的编码成为可能，从某个意义上说类似于 JSON 包的 Marshaler 和 Unmarshaler，以及 fmt 包的 String 化接口。这个技巧使一些特殊功能成为可能，强制使用常量，或者传输数据的时候隐藏信息。阅读文档了解更多细节。</p>
<h2>类型的传输</h2>
<p>在第一次传输给定类型的时候，gob 包中包含了这个类型的描述。实际上，是这样的，编码器编码的是gob标准格式，而内部的 struct 则带有类型描述并给其标识一个唯一编号。（基本类型、类型描述结构的层级，在软件启动时已经定义好了。）在类型被描述后，它可以通过编号来引用。</p>
<p>因此，当我们发送类型 T 时，gob 编码器发送 T 的描述，并对其编号，例如 127。包括第一个数据包在内的所有的数据，都使用这个编号，所以 T 值的数据流看起来是这样：</p>
<pre class="brush: cpp; title: ; notranslate">
(&quot;define type id&quot; 127, definition of type T)(127, T value)(127, T value), ...
</pre>
<p>类型编号使得描述递归类型，以及发送这些类型的数据成为可能。因此，gob 可以对树状类型做编码：</p>
<pre class="brush: cpp; title: ; notranslate">
type Node struct {
Value int
Left, Right *Node
}
</pre>
<p>（这是一个让读者实践零默认值规则是如何工作的练习，尽管 gob 不会处理指针。）</p>
<p>带有了类型信息，gob 流就完全自说明了。除了那些初始类型，它们已经在开始的时候就定义好了。</p>
<h2>编译机</h2>
<p>在第一次传输给定类型的时候，gob 包会构造一个针对这个类型的小翻译机。在这个类型上使用了反射来构造这个翻译机，但是一旦翻译机构建完成，它就不再依赖反射。这个翻译机使用了 unsafe 和其他一些巧妙的机制来高速的将数据转化为编码后的字节流。也可以使用反射来避免 unsafe，但是会明显变慢。（受到 gob 实现的影响，Go 的 protocol buffer 使用了类似的机制提高速度。）而后的同样类型的值使用已经编译好的翻译机，这样就可以总是有一致的编码。</p>
<p>解码类似，但是略微复杂。当你解码一个数据，gob 包用一个字节片保存编码后的类型的值用于来解码，再加上得到解码的 Go 的值。gob 包构造一个翻译机用于这个过程：gob 类型在线传输用于 Go 类型的解码。一旦解码翻译机构造，一个没有反射的使用 unsafe 方法的引擎能提供最快的速度。</p>
<h2>使用</h2>
<p>在帽子里还有很多秘密，但是结果是得到一个用于数据传输的高效的，容易使用的编码系统。这里有一个完整的例子演示了不同类型的编码和解码。留意发送和接收数据是多么简单；你所需要做的一切，就是将值和变量置入 gob 包，然后它会完成所有的工作。</p>
<pre class="brush: cpp; title: ; notranslate">
package main

import (
    &quot;bytes&quot;
    &quot;fmt&quot;
    &quot;gob&quot;
    &quot;log&quot;
)

type P struct {
    X, Y, Z int
    Name string
}

type Q struct {
    X, Y *int32
    Name string
}

func main() {
    // 初始化编码器和解码器。通常 enc 和 dec 会绑定到网络连接，而编码器和解码器会运行在不同的进程中。
    var network bytes.Buffer //代替网络连接
    enc := gob.NewEncoder(&amp;network) // 将会写入网络
    dec := gob.NewDecoder(&amp;network) // 将会从网络中读取
    // 编码（发送）
    err := enc.Encode(P{3, 4, 5, &quot;Pythagoras&quot;})
    if err != nil {
        log.Fatal(&quot;encode error:&quot;, err)
    }
    // 解码（接收）
    var q Q
    err = dec.Decode(&amp;q)
    if err != nil {
        log.Fatal(&quot;decode error:&quot;, err)
    }
    fmt.Printf(&quot;%q: {%d,%d}\n&quot;, q.Name, *q.X, *q.Y)
}
</pre>
<p>你可以复制代码到 Go Playground 中，编译并执行这个例子。<br />
rpc 包在 gob 的基础上将网络上的方法调用的编码/解码自动化。这是另一篇随笔的主题了。</p>
<h2>细节</h2>
<p>gob 包文档，尤其是 doc.go 文件解释了本文所说的许多细节，并且包含了完整的可运行的例子，用来演示如何对数据进行编码。如果你对 gob 的实现感兴趣，这是个不错的起点。</p>
<p>- Rob Pike, 三月 2011</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2011/03/%e3%80%90%e7%bf%bb%e8%af%91%e3%80%91gob-%e7%9a%84%e6%95%b0%e6%8d%ae/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>用Go实现异步的Web开发</title>
		<link>http://www.mikespook.com/2011/03/%e7%94%a8go%e5%ae%9e%e7%8e%b0%e5%bc%82%e6%ad%a5%e7%9a%84web%e5%bc%80%e5%8f%91/</link>
		<comments>http://www.mikespook.com/2011/03/%e7%94%a8go%e5%ae%9e%e7%8e%b0%e5%bc%82%e6%ad%a5%e7%9a%84web%e5%bc%80%e5%8f%91/#comments</comments>
		<pubDate>Fri, 25 Mar 2011 07:39:42 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Stardy & Research]]></category>
		<category><![CDATA[golang]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[异步]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=915</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
不知道大家还记得不记得大约一年前，我的一个白日梦《关于Web编程异步模型的白日梦》，然后这个白日梦我又连续做了好几天《Web编程异步模型的PHP原生实现》、《Web编程异步模型的 Gearman 实现（残）》。

当时怎么也没相通，还死皮白赖的粘在PHP的异步实现上不肯放手。好吧，实现是繁琐的，应用是成功的，代码是容易写的，环境是要搭建的……

昨晚睡觉前突然觉得自己应该真正用Go实现一下异步的Web，哪怕是个例子也好啊。于是，边吃饭，边敲了一票代码搞了一个很简单的demo，分享给大家吧。在这里下载完整的代码：webdemo。

<span class="readmore"><a href="http://www.mikespook.com/2011/03/%e7%94%a8go%e5%ae%9e%e7%8e%b0%e5%bc%82%e6%ad%a5%e7%9a%84web%e5%bc%80%e5%8f%91/" title="用Go实现异步的Web开发">阅读全文——共3192字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>不知道大家还记得不记得大约一年前，我的一个白日梦《<a title="关于Web编程异步模型的白日梦" rel="bookmark" href="/index.php/archives/580">关于Web编程异步模型的白日梦</a>》，然后这个白日梦我又连续做了好几天《<a title=" Web编程异步模型的PHP原生实现" rel="bookmark" href="/index.php/archives/587">Web编程异步模型的PHP原生实现</a>》、《<a title="Web编程异步模型的 Gearman 实现（残）" rel="bookmark" href="/index.php/archives/597">Web编程异步模型的 Gearman 实现（残）</a>》。</p>
<p>当时怎么也没相通，还死皮白赖的粘在PHP的异步实现上不肯放手。好吧，实现是繁琐的，应用是成功的，代码是容易写的，环境是要搭建的……</p>
<p>昨晚睡觉前突然觉得自己应该真正用Go实现一下异步的Web，哪怕是个例子也好啊。于是，边吃饭，边敲了一票代码搞了一个很简单的demo，分享给大家吧。在这里下载完整的代码：<a href="http://www.mikespook.com/wp-content/uploads/2011/03/webdemo.zip">webdemo</a>。<br />
<span id="more-915"></span><br />
包含以下文件：</p>
<ul>
<li>async.go——异步Web</li>
<li>data.go——模拟数据</li>
<li> Makefile——不说了，你懂……</li>
<li>page.go——页面</li>
<li>sync.go——同步Web</li>
<li>timer.go——记录执行时间的日志</li>
<li>webdemo.go——主文件</li>
</ul>
<p>编译并运行</p>
<pre class="brush: bash; title: ; notranslate">
make run
</pre>
<p>通过浏览器分别访问同步方式的<a href="http://127.0.0.1:8888/sync">http://127.0.0.1:8888/sync</a>，和异步方式的<a href="http://127.0.0.1:8888/async">http://127.0.0.1:8888/async</a>。在控制台会输出请求处理的时长，实际上即便不看统计时长，两者之间的速度差异很直观的能体会出来。</p>
<pre class="brush: bash; title: ; notranslate">
mikespook@mikespook-desktop:~/Desktop/webdemo$ make run
8g -o _go_.8 timer.go data.go page.go async.go sync.go
rm -f _obj/webdemo.a
gopack grc _obj/webdemo.a _go_.8
cp _obj/webdemo.a &quot;/home/mikespook/bin/go/pkg/linux_386/webdemo.a&quot;
8g webdemo.go
8l -o webdemo webdemo.8
./webdemo
2011/03/25 15:06:35 async:	228623000
2011/03/25 15:06:57 sync:	20024992000
</pre>
<p>核心思路很简单，我在模拟数据请求的代码里用了 time.Sleep()，让每次数据请求都延迟 0.2 秒（一个优化得不太好的，很大的数据库表的一次很烂的查询所用时间）：</p>
<pre class="brush: cpp; title: ; notranslate">
// data.go
func GetContents(key string) string {
    time.Sleep(SLEEP) // 延迟
    return fmt.Sprintf(&quot;%s. The quick brown fox jumps over the lazy dog.&quot;, key)
}
</pre>
<p>对于同步数据请求来说，必须等上一次的数据请求完毕才能发起下一次数据请求（原始的PHP即是如此），那么如果100个0.2秒的数据请求，则最终耗时一定大于 20 秒。<br />
下面是请求100次数据的处理：</p>
<pre class="brush: cpp; title: ; notranslate">
// webdemo.go 同步数据请求
func syncHandler(w http.ResponseWriter, r *http.Request) {
    timer := webdemo.NewTimer(&quot;sync&quot;)
    defer timer.End()
    page := webdemo.NewSyncPage()
    for i := 0; i &lt; 100; i ++ {
        key := strconv.Itoa(i)
        page.SetContents(key)
    }
    page.Render(w)
}
</pre>
<p>下面是获取数据到页面，并渲染页面输出到http.ResonseWriter的方法：</p>
<pre class="brush: cpp; title: ; notranslate">
// sync.go
func (page *SyncPage) SetContents(key string) {
    page.contents[key] = GetContents(key)
}

func (page *SyncPage) Render(w http.ResponseWriter) {
    lines := &quot;&quot;
    for i := 0; i &lt; len(page.contents); i++ {
        key := strconv.Itoa(i)
        lines += fmt.Sprintf(TEMPLATE_LINE, page.contents[key])
    }
    block := fmt.Sprintf(TEMPLATE_BLOCK, lines)
    fmt.Fprintf(w, TEMPLATE_PAGE, block)
}
</pre>
<p>对于异步来说，总时间长度略大于单条数据请求时间长度。下面是异步请求的 handler 代码，其实跟同步并无区别。</p>
<pre class="brush: cpp; title: ; notranslate">
// webdemo.go 异步Handler
func asyncHandler(w http.ResponseWriter, r *http.Request) {
    timer := webdemo.NewTimer(&quot;async&quot;)
    defer timer.End()
    page := webdemo.NewAsyncPage()
    page.CountOut = 100
    for i:= 0; i &lt; page.CountOut; i++ {
        key := strconv.Itoa(i)
        page.SetContents(key)
    }
    page.Render(w)
}
</pre>
<p>为了实现异步的数据获取，在 async.go 的代码中，使用 go 语言强大的 channel 来实现。所以在 AsyncPage 结构中定义了一个 contents 是 chan。</p>
<pre class="brush: cpp; title: ; notranslate">
// async.go
// 页面内容
type AsyncContents struct {
    key, value string
}
// 页面
type AsyncPage struct {
    contents chan AsyncContents
    timeout chan bool
    CountOut int
    page Page
}
</pre>
<p>在异步页面的 SetContents 方法中，用 go 关键字建立一个 goroutines 向 chan 中输入内容。同时建立另一个 goroutines 作为 timeout（本例中不会出现 timeout 的情况，不过实际环境中这是必要的）。</p>
<pre class="brush: cpp; title: ; notranslate">
// async.go
func (page *AsyncPage) SetContents(key string) {
    // 异步的数据获取
    go func() {
        page.contents &lt;- AsyncContents{key, GetContents(key)}
    }()
    // 设置针对页面的超时
    go func() {
        time.Sleep(TIMEOUT)
        page.timeout &lt;- true
    }()
}
</pre>
<p>页面的渲染也跟同步方式不同，通过 select 将数据从chan 中取出，并渲染到模板。</p>
<pre class="brush: cpp; title: ; notranslate">
// async.go
func (page *AsyncPage) Render(w http.ResponseWriter) {

    lines := &quot;&quot;
    LOOP: for i := 0; i &lt; page.CountOut; i++{
        select {
            case line := &lt;-page.contents:
                lines = fmt.Sprintf(&quot;%s&quot; + TEMPLATE_LINE, lines, line.value)
                // 每获取一个数据，就去掉一个超时
                go func() {&lt;-page.timeout}()
            case &lt;-page.timeout:
                lines = fmt.Sprintf(&quot;%s&quot; + TEMPLATE_LINE, lines, &quot;Time Out&quot;)
                break LOOP
        }
    }
    block := fmt.Sprintf(TEMPLATE_BLOCK, lines)
    fmt.Fprintf(w, TEMPLATE_PAGE, block)
}
</pre>
<p>为了演示异步的结构，我并没有使用模板来渲染页面。不过用模板来渲染也差不多：异步的获取模板上叫做%VARn的变量，然后将数据集合渲染至模板上……</p>
<p>当数据的获取有依赖关系的时候情况会比较复杂，不过如果把 web 页面当作一个树（DOM Tree？）的话，让异步数据从叶节点开始获取，逐层上推是个不错的办法。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2011/03/%e7%94%a8go%e5%ae%9e%e7%8e%b0%e5%bc%82%e6%ad%a5%e7%9a%84web%e5%bc%80%e5%8f%91/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>gocode——VIM 和 Emacs 的 golang 代码自动补全</title>
		<link>http://www.mikespook.com/2011/03/gocode%e2%80%94%e2%80%94vim-%e5%92%8c-emacs-%e7%9a%84-golang-%e4%bb%a3%e7%a0%81%e8%87%aa%e5%8a%a8%e8%a1%a5%e5%85%a8/</link>
		<comments>http://www.mikespook.com/2011/03/gocode%e2%80%94%e2%80%94vim-%e5%92%8c-emacs-%e7%9a%84-golang-%e4%bb%a3%e7%a0%81%e8%87%aa%e5%8a%a8%e8%a1%a5%e5%85%a8/#comments</comments>
		<pubDate>Fri, 18 Mar 2011 03:33:00 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Stardy & Research]]></category>
		<category><![CDATA[autocompletion]]></category>
		<category><![CDATA[emacs]]></category>
		<category><![CDATA[gocode]]></category>
		<category><![CDATA[golang]]></category>
		<category><![CDATA[vim]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=908</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
虽然 golang 自身提供了 VIM 的语法高亮之类的脚本，但 autocompletion 并没有官方解决方案。无意之中发现 gocode 这个支持 VIM 和 Emacs 的 autocompletion daemon。

这里有个Flash 动画演示，展示了 gocode 的强大。我得说，用过之后，感觉速度确实够快。

下面是来自官方的部分介绍：

<span class="readmore"><a href="http://www.mikespook.com/2011/03/gocode%e2%80%94%e2%80%94vim-%e5%92%8c-emacs-%e7%9a%84-golang-%e4%bb%a3%e7%a0%81%e8%87%aa%e5%8a%a8%e8%a1%a5%e5%85%a8/" title="gocode——VIM 和 Emacs 的 golang 代码自动补全">阅读全文——共2704字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p><a href="http://www.mikespook.com/wp-content/uploads/2011/03/gocode.png"><img class="size-medium wp-image-909 alignright" title="gocode" src="http://www.mikespook.com/wp-content/uploads/2011/03/gocode-300x141.png" alt="" width="300" height="141" /></a>虽然 golang 自身提供了 VIM 的语法高亮之类的脚本，但 autocompletion 并没有官方解决方案。无意之中发现 <a href="https://github.com/nsf/gocode" target="_blank">gocode</a> 这个支持 VIM 和 Emacs 的 autocompletion daemon。</p>
<p>这里有个<a href="http://nsf.110mb.com/gocode-demo.swf">Flash 动画演示</a>，展示了 gocode 的强大。我得说，用过之后，感觉速度确实够快。</p>
<p>下面是来自官方的部分介绍：</p>
<p><span id="more-908"></span></p>
<blockquote><p><strong>用于 Go 编程语言的自动补全守护进程</strong></p>
<p>Gocode 是可以整合在如 vim 和 emacs 这样的代码编辑器中的辅助工具。它提供了一系列的高级功能，包括：</p>
<ul>
<li>上下文敏感的自动补全</li>
</ul>
<p>（译注：残念，还真是“一”系列啊！好吧，但是说实话单就这一个功能就很好用了。）</p>
<p>由于使用 client/server 架构的缓存形式，所以被称为守护进程。这使得自动补全非常快。通常，在缓存生效的情况下自动补全时间在 30ms，一般不会察觉这个延迟。</p></blockquote>
<p>gocode 的文档对安装说得很是详细，我这里仅对 VIM 做个简单的说明。另外，由于 golang 的 pkg 还不够稳定，不同版本可能会出现编译错误。实际上在我安装过程中，还是 fix 了 gocode 的兼容问题，才顺利安装的。下面会做详细说明。</p>
<p>首先，我假设你已经安装好了 go，设置好了 $GOBIN，并将其加入了 $PATH中。使用 8g -V 查看 go 版本，确保版本是【8g version weekly.2011-03-15 7807】。版本不同，在一些 go 的 pkg 的命名、方法等都会有不同。这可能会引起你在编译 gocode 时报错。</p>
<p>从 github 上获取 gocode 代码：</p>
<pre class="brush: bash; title: ; notranslate">git clone git://github.com/nsf/gocode.git</pre>
<p>进入 gocode 目录，编译并安装：</p>
<pre class="brush: bash; title: ; notranslate">cd gocode&amp;make install</pre>
<p>在我的编译过程中，出现如下错误：</p>
<pre class="brush: bash; title: ; notranslate">
mikespook@mikespook-desktop:~/Desktop/gocode$ make install
8g -o _go_.8 gocode.go autocompletefile.go package.go autocompletecontext.go server.go rpc.go decl.go apropos.go scope.go ripper.go config.go declcache.go os_posix.go
autocompletefile.go:230: undefined: ast.TypeCaseClause
autocompletefile.go:232: undefined: ast.TypeCaseClause
autocompletefile.go:246: typechecking loop
make: *** [_go_.8] 错误 1
</pre>
<p>这是由于 go/ast 中的 TypeCaseClause 已经改名为 CaseClause。用下面的 patch 即可解决这个问题：</p>
<pre class="brush: diff; title: ; notranslate">
diff --git a/autocompletefile.go b/autocompletefile.go
index e08e161..c0c83c4 100644
--- a/autocompletefile.go
+++ b/autocompletefile.go
@@ -227,17 +227,17 @@ func (f *AutoCompleteFile) processTypeSwitchStmt(a *ast.TypeSwitchStmt) {
                }
        }

-       var lastCursorAfter *ast.TypeCaseClause
+       var lastCursorAfter *ast.CaseClause
        for _, s := range a.Body.List {
-               if cc := s.(*ast.TypeCaseClause); f.cursor &gt; f.fset.Position(cc.Colon).Offset {
+               if cc := s.(*ast.CaseClause); f.cursor &gt; f.fset.Position(cc.Colon).Offset {
                        lastCursorAfter = cc
                }
        }

        if lastCursorAfter != nil {
                if tv != nil {
-                       if lastCursorAfter.Types != nil &amp;&amp; len(lastCursorAfter.Types) == 1 {
-                               tv.Type = lastCursorAfter.Types[0]
+                       if lastCursorAfter.List != nil &amp;&amp; len(lastCursorAfter.List) == 1 {
+                               tv.Type = lastCursorAfter.List[0]
                                tv.Value = nil
                        }
                        f.scope.addNamedDecl(tv)
</pre>
<p>完成后，安装程序会自动将编译好的 gocode 可执行文件复制到 $GOBIN 目录中。所以请务必确保 $GOBIN 设置正确。</p>
<p>对于 VIM 来说：</p>
<p>首先确保 $GOROOT/misc/vim 中的 Go VIM 脚本正确安装配置。可参考我的 vim 配置：<a href="https://bitbucket.org/mikespook/foobar/src/b7dbcbdbf3dd/vim/" target="_blank">https://bitbucket.org/mikespook/foobar/src/b7dbcbdbf3dd/vim/</a></p>
<p>然后安装 gocode 的 vim 脚本，可以简单执行 gocode 提供的 shell 脚本：</p>
<pre class="brush: bash; title: ; notranslate">cd gocode/vim &amp;&amp; ./update.bash</pre>
<p>实际上，这个脚本做了以下工作：</p>
<pre class="brush: bash; title: ; notranslate">
#!/usr/bin/env bash
mkdir -p ~/.vim/{autoload,ftplugin}
cp autoload/gocomplete.vim ~/.vim/autoload
cp ftplugin/go.vim ~/.vim/ftplugin
</pre>
<p>确保 vim 启用了 filetype 插件，.vimrc 中应当有：</p>
<pre class="brush: bash; title: ; notranslate">filetype plugin on</pre>
<p>好了，用 <C-x><C-o> 现在可以呼出 go 语言的自动补全了。</p>
<p>我为了方便，将 Ctrl+Space 映射为自动补全快捷键：</p>
<pre class="brush: bash; title: ; notranslate">imap &lt;C-Space&gt; &lt;C-x&gt;&lt;C-o&gt;</pre>
<p>由于我不用 Emacs，所以 Emacs 党请绕道官方文档吧……;)</p>
<p>【最新更新】</p>
<p>根据作者提供的线索，使用 golang 的 weekly 版本是不需要打补丁的。所以，正确的 golang 安装版本是【3b4e9c85b643 weekly/weekly.2011-03-15】</p>
<pre class="brush: bash; title: ; notranslate">
hg up -r weekly
hg id
</pre>
<p>在 7787 版本 CaseClause 才与 TypeCaseClause 合并到一起，成为 CaseClause。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2011/03/gocode%e2%80%94%e2%80%94vim-%e5%92%8c-emacs-%e7%9a%84-golang-%e4%bb%a3%e7%a0%81%e8%87%aa%e5%8a%a8%e8%a1%a5%e5%85%a8/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>go在stack上干了神马？</title>
		<link>http://www.mikespook.com/2011/03/go%e5%9c%a8stack%e4%b8%8a%e5%b9%b2%e4%ba%86%e7%a5%9e%e9%a9%ac%ef%bc%9f/</link>
		<comments>http://www.mikespook.com/2011/03/go%e5%9c%a8stack%e4%b8%8a%e5%b9%b2%e4%ba%86%e7%a5%9e%e9%a9%ac%ef%bc%9f/#comments</comments>
		<pubDate>Tue, 15 Mar 2011 04:58:46 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Stardy & Research]]></category>
		<category><![CDATA[golang]]></category>
		<category><![CDATA[morestack]]></category>
		<category><![CDATA[stackless]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=901</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
对这个话题已经有深入理解的童鞋请绕道；

对这个话题感兴趣，且有极强学习能力的同学请阅读这里，并且不用回来了。

其他和我一样愚笨的IT民工们，继续向前冲吧……

<span class="readmore"><a href="http://www.mikespook.com/2011/03/go%e5%9c%a8stack%e4%b8%8a%e5%b9%b2%e4%ba%86%e7%a5%9e%e9%a9%ac%ef%bc%9f/" title="go在stack上干了神马？">阅读全文——共1936字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<ol>
<li>对这个话题已经有深入理解的童鞋请绕道；</li>
<li>对这个话题感兴趣，且有极强学习能力的同学请阅读<a href="http://blog.nella.org/?p=849" target="_blank">这里</a>，并且不用回来了。</li>
<li>其他和我一样愚笨的IT民工们，继续向前冲吧……</li>
</ol>
<p>首先，来看一段神奇的 golang 代码：</p>
<pre class="brush: cpp; title: ; notranslate">
package main
var (
    i = 1
)
func main() {
    i = i + 1
    print(i, &quot;\n&quot;)
    main()
}
</pre>
<p><span id="more-901"></span><br />
熟悉 c 语言的人都知道，如果在 c 语言中编译执行类似的代码，程序最终会发生栈溢出（stack overflow），从而导致段错误（segmentation fault）。在 32 位环境下（我只有 32 位的实验环境）编译链接并执行这段代码，预期的“段错误”并没有出现。</p>
<p><a href="http://www.mikespook.com/wp-content/uploads/2011/03/go-stack.png"><img class="size-full wp-image-899 alignnone" title="go-stack" src="http://www.mikespook.com/wp-content/uploads/2011/03/go-stack.png" alt="" width="99" height="149" /></a></p>
<p>程序长时间稳定的执行。同时，利用 top 命令，会看到程序所使用的内存不断上升，丝毫没有减少的痕迹。</p>
<p><a href="http://www.mikespook.com/wp-content/uploads/2011/03/go-stack-top.png"><img class="alignnone size-full wp-image-900" title="go-stack-top" src="http://www.mikespook.com/wp-content/uploads/2011/03/go-stack-top.png" alt="" width="749" height="32" /></a></p>
<p>祭出神器 gdb，来看一下这个 go 程序到底做了什么。</p>
<pre class="brush: bash; title: ; notranslate">
gdb stackless
...
(gdb) b main.main
...
(gdb) r
...
(gdb) disassemble
</pre>
<p>当输入 disassemble 命令后，gdb 帮我们反编译了程序代码：</p>
<pre class="brush: bash; title: ; notranslate">
   0x08048c00 &lt;+0&gt;:	mov    %gs:0x0,%ecx
   0x08048c07 &lt;+7&gt;:	mov    -0x8(%ecx),%ecx
   0x08048c0a &lt;+10&gt;:	cmp    (%ecx),%esp
   0x08048c0c &lt;+12&gt;:	ja     0x8048c17 &lt;main.main+23&gt;
   0x08048c0e &lt;+14&gt;:	xor    %edx,%edx
   0x08048c10 &lt;+16&gt;:	xor    %eax,%eax
   0x08048c12 &lt;+18&gt;:	call   0x8049515 &lt;runtime.morestack&gt;
   0x08048c17 &lt;+23&gt;:	sub    $0x1c,%esp
   0x08048c1a &lt;+26&gt;:	mov    0x806c00c,%eax
   0x08048c20 &lt;+32&gt;:	inc    %eax
   0x08048c21 &lt;+33&gt;:	mov    %eax,0x806c00c
   0x08048c27 &lt;+39&gt;:	cltd
   0x08048c28 &lt;+40&gt;:	mov    %eax,(%esp)
   0x08048c2b &lt;+43&gt;:	mov    %edx,0x4(%esp)
   0x08048c2f &lt;+47&gt;:	call   0x804ef1b &lt;runtime.printint&gt;
   0x08048c34 &lt;+52&gt;:	lea    0x80557b0,%esi
   0x08048c3a &lt;+58&gt;:	lea    (%esp),%edi
   0x08048c3d &lt;+61&gt;:	cld
   0x08048c3e &lt;+62&gt;:	movsl  %ds:(%esi),%es:(%edi)
   0x08048c3f &lt;+63&gt;:	movsl  %ds:(%esi),%es:(%edi)
   0x08048c40 &lt;+64&gt;:	call   0x804f099 &lt;runtime.printstring&gt;
   0x08048c45 &lt;+69&gt;:	call   0x8048c00 &lt;main.main&gt;
   0x08048c4a &lt;+74&gt;:	add    $0x1c,%esp
   0x08048c4d &lt;+77&gt;:	ret
</pre>
<p>main.main+0到main.main+12处，我们看到取了两个值进行比较，当 (%ecx)大于%esp时，跳转到main.main+23。</p>
<p>main.main+23到main.main+64处，显然是我们程序的逻辑处理：变量i自加一、打印i、打印”\n”。</p>
<p>main.main+69再次调用地址 0x8048c00 的 main.main。</p>
<p>除了main.main+0到main.main+12的比较，main.main+14到main.main+18对 runtime.morestack 的调用外，这和一个普通的程序并无二至。</p>
<p>那么程序一开始比较两个数据和调用 runtime.morestack 是什么呢？</p>
<p>原来在程序一开始先比较了栈限制和已经使用的栈的大小（%esp）。当栈限制大于已经使用的栈的时候，说明空间充裕，则直接跳转到实际代码处执行（+23）。当栈限制小于等于时已经使用的栈时，执行  runtime.morestack（+18）申请更多的栈空间。</p>
<p>在 $(GOROOT)/src/pkg/runtime/stack.h 中，有略微详细的说明。</p>
<p>由此看来 golang 实现的并不是 stackless，而是在每次函数调用前判断栈空间是否足够，如果发现栈空间不够用，就立刻申请新的栈空间使用。当函数退出时，才释放这些栈空间。</p>
<p>还是在 gdb 中，执行：</p>
<pre class="brush: bash; title: ; notranslate">
(gdb) b runtime.morestack
...
(gdb) r
</pre>
<p>在函数调用 100 多次后（猜测 32 位跟 64 位应当不同，没试），触发了 runtime.morestack 申请新的栈空间。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2011/03/go%e5%9c%a8stack%e4%b8%8a%e5%b9%b2%e4%ba%86%e7%a5%9e%e9%a9%ac%ef%bc%9f/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>[翻译]飞翔的 gob</title>
		<link>http://www.mikespook.com/2011/03/%e7%bf%bb%e8%af%91%e9%a3%9e%e7%bf%94%e7%9a%84-gob/</link>
		<comments>http://www.mikespook.com/2011/03/%e7%bf%bb%e8%af%91%e9%a3%9e%e7%bf%94%e7%9a%84-gob/#comments</comments>
		<pubDate>Wed, 09 Mar 2011 01:20:32 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Stardy & Research]]></category>
		<category><![CDATA[gob]]></category>
		<category><![CDATA[golang]]></category>
		<category><![CDATA[net]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=891</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
这个题目的原文叫做《Gobs on the wire》，作者巧妙的用了“gob”这个词。gob本来是Golang的一个关于网络通信协议的包。而在这里，我感觉标题也可以翻译为《关于线上的那一大陀……》。好吧，我得承认，这么翻译实在不雅。

&#8212;&#8212;&#8212;&#8212;翻译分割线&#8212;&#8212;&#8212;&#8212;

飞翔的 gob

<span class="readmore"><a href="http://www.mikespook.com/2011/03/%e7%bf%bb%e8%af%91%e9%a3%9e%e7%bf%94%e7%9a%84-gob/" title="[翻译]飞翔的 gob">阅读全文——共4582字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>这个题目的原文叫做《<a href="http://blog.nella.org/?p=828" target="_blank">Gobs on the wire</a>》，作者巧妙的用了“gob”这个词。<a href="http://golang.org/pkg/gob" target="_blank">gob</a>本来是Golang的一个关于网络通信协议的包。而在这里，我感觉标题也可以翻译为《关于线上的那一大陀……》。好吧，我得承认，这么翻译实在不雅。</p>
<p>&#8212;&#8212;&#8212;&#8212;翻译分割线&#8212;&#8212;&#8212;&#8212;</p>
<h1>飞翔的 gob</h1>
<p>这周，我想跟大家谈谈如何用 Go 编写基于同步请求和异步事件通知的 Client/Server 系统。</p>
<p>为了帮助学习 Go，我克隆了一个<a href="http://conserver.com/" target="_blank">Conserver 命令行服务</a>。当然，现在对于世界来说没必要有另外一个命令行服务。然而由于Go语言带给开发者的特性，非常适合用来做命令行服务，所以这将会是一次非常有趣的体验。命令行服务的任务是从一个或者多个串口上汇总输出（或者说各种系统上的一个或者多个实现了使用TCP连接的Rtelnet协议的终端服务）。 它将输出记录到文件，同时让输出可以被别人实时的看到。它仅允许唯一一个用户进行读写，用于实际控制设备。<br />
<span id="more-891"></span><br />
Go版本的命令行服务被称作<a href="http://code.google.com/p/jra-go/source/browse/#hg%2Fcmd%2Fgocons" target="_blank">gocons</a>。先去看看，然后回到这里。跟以往一样，我先说说在编写这个的时候，我学到了Go的哪些东西。</p>
<p>最初，我尝试用<a href="http://golang.org/pkg/netchan" target="_blank">netchan</a>来构建这个。我想，如果客户端在某个通道（译注：channel，下同）上向服务器请求，当在某个命令行上有事件发生时接收通知这会是一件很酷的事情。但是，由于netchan不能处理多个通道，它彻底的崩溃了。 我认为，理论上是可能通过某种方法让netchan处理多个通道，但是这看起来确实非常难， 而且多个通道可能永远不同步。所以，我转向了正确的方向……</p>
<p>接下来是想使用<a href="http://golang.org/pkg/rpc">rpc</a>包实现客户端和服务器之间的协议。但是，这带来了另外一个问题：RPC，它的定义是同步的。它没有明确定义服务端如何使用RPC包下发异步事件到客户端，如“这些字节已经到达”。我需要自己编写协议，通过两个步骤，一个同步调用，例如“我可以收听这个吗？是的。”以及异步事件“你监视的命令行刚刚输出了这些字节”。 我之前已经编写了这些协议，这并不难，只是有一些繁琐。而Go提供的<a href="http://golang.org/pkg/gob">gob</a>包就是你开发一个协议所需要的全部。</p>
<p>而你需要做的就是描绘出协议消息，并且让gob负责处理打包和解包，就是计算消息的分隔。在我们的例子中，这个类型被称为connReq和connReply。在理想世界里，这些应当是某个库的公共类型，客户端和服务器端同时使用它们。在gocons中，我发懒了，只是复制并粘贴了它们。客户端在net.TCPConn上有gob.Decode，而结果就是connReq（如果不是的话，那说明有什么不对劲，客户端可以干掉连接或解码这个连接上的后续内容）。由于Go没有union（非类型安全的），connReq和connReply即便是给定的协议消息不使用的情况下，也应当包含所有的字段。我并没有仔细思考这个协议， 但由于未使用字段可能是字节段或字符串，不可能有很多；而且空的字节段将被编码为nil，而不是用零填充的整个字节缓冲区。</p>
<p>对此更加精致的设想是构造一个层次化的类型，最简单的类型（仅有一个int指名类型）作为基础，后面跟一个复杂的类型。但是要弄清楚给gob.Decode的是什么类型是非常难的；这就像你必须将协议拆分为两部分，并且分别对其调用go.Encode。第一个可以告诉这是什么类型，而第二个gob可以是包含数据的短语。无论如何，我不会在gocons里这么做。简单就好！</p>
<p>在服务器中，有两个代码片段很有趣。一个是使用JSON作为配置文件的格式。另一个是如何将新的数据发送到所有收听者。</p>
<p>第一个比较简单。仅仅是演示在不使用json.Unmarshall的情况下，如何从JSON文件中获取数据。我搞不清楚json.Unmarshall，所以在我尝试不用它的情况下，让json.Decode工作。我并不是说这么做好，但是它能运行，这可能可以帮助其他在Go中读取JSON的正在寻找例子的人。</p>
<p>希望的输入是这样的：</p>
<pre class="brush: jscript; title: ; notranslate">
{
    &quot;consoles&quot;: {
        &quot;firewall&quot;: &quot;ts.company.com:2070&quot;,
        &quot;web01&quot;: &quot;ts.company.com:2071&quot;,
        &quot;web02&quot;: &quot;ts.company.com:2072&quot;,
        &quot;web03&quot;: &quot;ts.company.com:2073&quot;
    }
}
</pre>
<p>目标是在consoles中对应的每个键值调用addConsole。</p>
<p>是这样做的，如果你不希望这样（或者知道如何做的话）使用json.Unmarshal吧：</p>
<pre class="brush: cpp; title: ; notranslate">
  r, err := os.Open(*config, os.O_RDONLY, 0)
  if err != nil {
    log.Exitf(&quot;Cannot read config file %v: %v&quot;, *config, err)
  }
  dec := json.NewDecoder(r)
  var conf interface{}
  err = dec.Decode(&amp;conf)
  if err != nil {
    log.Exit(&quot;JSON decode: &quot;, err)
  }
  hash, ok := conf.(map[string]interface{})
  if !ok {
    log.Exit(&quot;JSON format error: got %T&quot;, conf)
  }
  consoles, ok := hash[&quot;consoles&quot;]
  if !ok {
    log.Exit(&quot;JSON format error: key consoles not found&quot;)
  }
  c2, ok := consoles.(map[string]interface{})
  if !ok {
    log.Exitf(&quot;JSON format error: consoles key wrong type, %T&quot;, consoles)
  }
  for k, v := range c2 {
    s, ok := v.(string)
    if ok {
      addConsole(k, s)
    } else {
      log.Exit(&quot;Dial string for console %v is not a string.&quot;, k)
    }
  }
</pre>
<p>这里的模式大致如此，json.Decode提供了一个interface{}，然后根据结构使用类型选择器，然后获得你期望的在那里获得的内容。</p>
<p>更加简单的办法是使用json.Unmarshal。从文档中很难理解如何使用，幸好<a href="http://blog.golang.org/2011/01/json-and-go.html">这篇文章</a>让它看起来更加清晰。</p>
<p>服务器是在一个循环里处理i/o的一系列的goroutine组成。每个它监视的命令行拥有一个读goroutine和一个写goroutine。读的从其中获取字节，然后向所有收听的gocons客户端分发。它管理了一个包含客户端列表的链表，不过另外一种数据结构可能会工作得更好。不论是在net.TCPConn还是通道，客户端都是没有排序的。等待新数据的通道就像是客户端的代理goroutine。当每个客户端连接时，会创建一对goroutine，一个用于读，一个用于写。这允许我们在输入上实现阻塞读（例子可参看dec.Decode），而不用担心阻塞服务器上的其他任务。</p>
<p>利用一个独立的goroutine保证负责向TCP连接写入，这样可以不使用任何锁。作为练习，可以让多个命令行管理器同时说：“我有一些数据需要TCP连接的多路复用！”而无须关心它们向连接写入数据时相互干扰。（当前的实现一次只可以监听一个命令行。）</p>
<p>下面的片段演示了当有新的内容时，如何打包并且向所有命令行观看者发送通知：</p>
<pre class="brush: cpp; title: ; notranslate">
    select {
      // a bunch of other channels to monitor here...
      case data := &lt;-m.dataCh:
        if closed(m.dataCh) {
          break L
        }
        // multicast the data to the listeners
        for l := listeners; l != nil; l = l.next {
          if closed(l.ch) {
            // TODO: need to remove this node from the list, not just mark nil
            l.ch = nil
            log.Print(&quot;Marking listener &quot;, l, &quot; no longer active.&quot;)
          }
          if l.ch != nil {
            ok := l.ch &lt;- consoleEvent{data}
            if !ok {
              log.Print(&quot;Listener &quot;, l, &quot; lost an event.&quot;)
            }
          }
        }
</pre>
<p>这样，我们建立了一个新的consoleEvent，并且将其发送给了每个收听者。这有点浪费：它产生了许多垃圾，这意味着垃圾回收器需要更加努力的工作。可以创建一个consoleEvent，然后向所有的收听者发送这一个。但是，如果这样共享内存，需要开发者决定是让共享内存只读，还是使用互斥控制它的访问。在我们的例子中，使用了只读的方式，像这样：</p>
<pre class="brush: cpp; title: ; notranslate">
    // new event arrived from the console manager, so send it down the TCP connection
    case ev := &lt;-evCh:
      reply.code = Data
      reply.data = ev.data
      err := enc.Encode(reply)
      if err != nil {
        log.Print(&quot;connection &quot;, c.rw, &quot;, failed to send data: &quot;, err)
        break L
      }
</pre>
<p>在这个模式下，两个goroutine只负责读取和写入，这就像魔法。这梦幻般的降低了实现gocons需要的代码量。原先的命令行服务需要数百行复杂的代码，关于设置select掩码，等待select，检测fd是否需要accept()或者read()或者其他（同时要找到让fd可用的正确的数据结构）。在gocons中，以及其他Go程序，如http包实现的http服务，可以使用阻塞的读，让Go安排运行使得整个系统并未阻塞。</p>
<p>然而，如果考虑当向客户端TCP连接写入发生阻塞时的情况，会很有趣。当系统在写时发生阻塞，它最终会放弃，并且阻止从命令行上读取，阻塞其他所有客户端。为了对应这种情况，你需要在适当的地方建立防火墙：共享的资源不应当让个体阻塞它们。你需要在客户端代理goroutine和命令行管理者读取goroutine之间的通道设置一个队列，让其在通道上实现非阻塞写，并且当一个阻塞了，就处理它。例如，可以关闭通道，然后说“嘿，你的排水不畅，应该清理一下，然后再来找我。”</p>
<p>用Go编写并且调试这个服务器，让我学到了许多东西。而我仍然还有许多东西需要学习：代码中仍然有一些神秘的东西，例如为什么我需要runtime.Gosched()保证不会阻塞，以及如何处理关闭的通道在select中带来的麻烦。还有更多的隐藏在setOwner中的神秘工作，首要的是：如何发现将从一个地方转发到另一个地方的“pump goroutine”中的bug（在Go运行时环境，或者我能理解的情况下） </p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2011/03/%e7%bf%bb%e8%af%91%e9%a3%9e%e7%bf%94%e7%9a%84-gob/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>[翻译]Go语言开发苹果推送通知</title>
		<link>http://www.mikespook.com/2011/03/%e7%bf%bb%e8%af%91go%e8%af%ad%e8%a8%80%e5%bc%80%e5%8f%91%e8%8b%b9%e6%9e%9c%e6%8e%a8%e9%80%81%e9%80%9a%e7%9f%a5/</link>
		<comments>http://www.mikespook.com/2011/03/%e7%bf%bb%e8%af%91go%e8%af%ad%e8%a8%80%e5%bc%80%e5%8f%91%e8%8b%b9%e6%9e%9c%e6%8e%a8%e9%80%81%e9%80%9a%e7%9f%a5/#comments</comments>
		<pubDate>Wed, 02 Mar 2011 12:29:12 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[Stardy & Research]]></category>
		<category><![CDATA[APNS]]></category>
		<category><![CDATA[golang]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=870</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
原文在此：http://bravenewmethod.wordpress.com/2011/02/25/apple-push-notifications-with-go-language/

前两天正巧看到 APNS 没有 Go 的实现，还在琢磨怎么实现一个试试，这下我又省心了。文章本身并不怎么出色，代码倒是有些用途。翻译这篇东西纯粹是为了给自己后面的工作留个资料。大家有用则用，无用就无视吧。

&#8212;&#8212;&#8212;&#8212;&#8211;翻译分割线&#8212;&#8212;&#8212;&#8212;&#8211;

<span class="readmore"><a href="http://www.mikespook.com/2011/03/%e7%bf%bb%e8%af%91go%e8%af%ad%e8%a8%80%e5%bc%80%e5%8f%91%e8%8b%b9%e6%9e%9c%e6%8e%a8%e9%80%81%e9%80%9a%e7%9f%a5/" title="[翻译]Go语言开发苹果推送通知">阅读全文——共2453字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>原文在此：<a href="http://bravenewmethod.wordpress.com/2011/02/25/apple-push-notifications-with-go-language/" target="_blank">http://bravenewmethod.wordpress.com/2011/02/25/apple-push-notifications-with-go-language/</a></p>
<p>前两天正巧看到 APNS 没有 Go 的实现，还在琢磨怎么实现一个试试，这下我又省心了。文章本身并不怎么出色，代码倒是有些用途。翻译这篇东西纯粹是为了给自己后面的工作留个资料。大家有用则用，无用就无视吧。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8211;翻译分割线&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<h1>Go语言开发苹果推送通知</h1>
<p>我开始尝试学习并熟悉 <a href="http://golang.org/">Go 语言</a>，并且做了一些普通的常识，例如，发送苹果推送通知（Apple Push  Notifications）。这是我个人对一些开发环境的性能测试。迄今为止，已经有：<br />
<span id="more-870"></span></p>
<ul>
<li><a title="Apple Push Notifications with Node.js" href="http://bravenewmethod.wordpress.com/2010/12/09/apple-push-notifications-with-node-js/">使用 Node.js 的推送通知（Push notifications with Node.js）</a></li>
<li><a title="Apple Push Notifications with Erlang" href="http://bravenewmethod.wordpress.com/2011/02/16/apple-push-notifications-with-erlang/">使用 Erlang 的推送通知（Push notifications with Erlang）</a></li>
</ul>
<h2>第一步 准备</h2>
<p>获取并编译 Go。这里的例子是在 Ubuntu 10.04 LTS x64 上，基于 <a href="http://golang.org/doc/install.html">Go 上手指南（Go getting started guide）</a> 里的介绍进行安装。 <a title="Apple Push Notifications with Node.js" href="http://bravenewmethod.wordpress.com/2010/12/09/apple-push-notifications-with-node-js/"></a></p>
<ul>
<li>关于苹果推送和取得应用沙盒私钥的 .pem 文件的介绍在<a title="Apple Push Notifications with Node.js" href="http://bravenewmethod.wordpress.com/2010/12/09/apple-push-notifications-with-node-js/">这里</a>。</li>
<li>当然，你必须从 iOS 应用获取 32 位的推送令牌（push token）。</li>
</ul>
<h2>第二部 代码</h2>
<p>这里是完整的代码，复制并保存为文件 apn.go。</p>
<p>注意修改证书文件（cert.pem 和 key-noenc.pem）为你自己的证书文件。同时，替换推送令牌为你自己的推送令牌， 在这个例子里为了更加清楚，使用十六进制字符串书写。</p>
<pre class="brush: cpp; title: ; notranslate">
package main

import (
   &quot;crypto/tls&quot;
   &quot;fmt&quot;
   &quot;net&quot;
   &quot;json&quot;
   &quot;os&quot;
   &quot;time&quot;
   &quot;bytes&quot;
   &quot;encoding/hex&quot;
   &quot;encoding/binary&quot;
)

func main() {

   // 加载证书和配置文件
   cert, err := tls.LoadX509KeyPair(&quot;cert.pem&quot;, &quot;key-noenc.pem&quot;)
   if err != nil {
       fmt.Printf(&quot;error: %s\n&quot;, err.String())
       os.Exit(1)
   }
   conf := &amp;tls.Config {
        Certificates: []tls.Certificate{cert},
   }

   // 连接到 APNS 并且用 tls 客户端加密 socket
   conn, err := net.Dial(&quot;tcp&quot;, &quot;&quot;, &quot;gateway.sandbox.push.apple.com:2195&quot;)
   if err != nil {
      fmt.Printf(&quot;tcp error: %s\n&quot;, err.String())
      os.Exit(1)
   }
   tlsconn := tls.Client(conn, conf)

   // 强制握手，以验证身份握手被处理，否则会在第一次读写的时候进行尝试
   err = tlsconn.Handshake()
   if err != nil {
      fmt.Printf(&quot;tls error: %s\n&quot;, err.String())
      os.Exit(1)
   }
   // 调试信息
   state := tlsconn.ConnectionState()
   fmt.Printf(&quot;conn state %v\n&quot;, state)

   // 从 JSON 结构构造二进制的 payload
   payload := make(map[string]interface{})
   payload[&quot;aps&quot;] = map[string]string{&quot;alert&quot;: &quot;Hello Push&quot;}
   bpayload, err := json.Marshal(payload)

   // 将十六进制的设备令牌转为二进制的字节数组
   btoken, _ := hex.DecodeString(&quot;6b4628de9317c80edd1c791640b58fdfc46d21d0d2d1351687239c44d8e30ab1&quot;)

   // 构造 pdu
   buffer := bytes.NewBuffer([]byte{})
   // 命令
   binary.Write(buffer, binary.BigEndian, uint8(1))

   // 传输 id，optional
   binary.Write(buffer, binary.BigEndian, uint32(1))

   // 过期时间，1小时
   binary.Write(buffer, binary.BigEndian, uint32(time.Seconds() + 60*60))

   // 推送设备令牌
   binary.Write(buffer, binary.BigEndian, uint16(len(btoken)))
   binary.Write(buffer, binary.BigEndian, btoken)

   // 推送 payload
   binary.Write(buffer, binary.BigEndian, uint16(len(bpayload)))
   binary.Write(buffer, binary.BigEndian, bpayload)
   pdu := buffer.Bytes()

   // 写入 pdu
   _, err = tlsconn.Write(pdu)
   if err != nil {
      fmt.Printf(&quot;write error: %s\n&quot;, err.String())
      os.Exit(1)
   }

   // 等待 5 秒，以便 pdu 从 socket 返回错误
   tlsconn.SetReadTimeout(5*1E9)

   readb := [6]byte{}
   n, err := tlsconn.Read(readb[:])
   if n &gt; 0 {
     fmt.Printf(&quot;received: %s\n&quot;, hex.EncodeToString(readb[:n]))
   }

   tlsconn.Close()
}
</pre>
<h2>第三步 编译和运行</h2>
<p>简单</p>
<pre class="brush: bash; title: ; notranslate">
$ 6g apn.go
$ 6l apn.6
$ ./6.out
conn state {true 47}
$
</pre>
<p>如果所有都没有问题，程序会在几秒钟后退出，你会在你的 iPhone 上看到推送通知。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2011/03/%e7%bf%bb%e8%af%91go%e8%af%ad%e8%a8%80%e5%bc%80%e5%8f%91%e8%8b%b9%e6%9e%9c%e6%8e%a8%e9%80%81%e9%80%9a%e7%9f%a5/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>[翻译]Go 和 Python 的 Web 服务器性能对比</title>
		<link>http://www.mikespook.com/2011/02/%e7%bf%bb%e8%af%91go-%e5%92%8c-python-%e7%9a%84-web-%e6%9c%8d%e5%8a%a1%e5%99%a8%e6%80%a7%e8%83%bd%e5%af%b9%e6%af%94/</link>
		<comments>http://www.mikespook.com/2011/02/%e7%bf%bb%e8%af%91go-%e5%92%8c-python-%e7%9a%84-web-%e6%9c%8d%e5%8a%a1%e5%99%a8%e6%80%a7%e8%83%bd%e5%af%b9%e6%af%94/#comments</comments>
		<pubDate>Thu, 24 Feb 2011 04:46:27 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Stardy & Research]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[性能]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=857</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
原文在此：http://ziutek.github.com/web_bench/

由于是早上看到 鱼哥，在推上的推荐，我实在忍不住……这是中午的草率之举，所以 鱼哥 对本文的翻译负全责。

PS：别说我工作状态不饱满，我在等丫的程序执行完……

<span class="readmore"><a href="http://www.mikespook.com/2011/02/%e7%bf%bb%e8%af%91go-%e5%92%8c-python-%e7%9a%84-web-%e6%9c%8d%e5%8a%a1%e5%99%a8%e6%80%a7%e8%83%bd%e5%af%b9%e6%af%94/" title="[翻译]Go 和 Python 的 Web 服务器性能对比">阅读全文——共2501字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>原文在此：<a href="http://ziutek.github.com/web_bench/" target="_blank">http://ziutek.github.com/web_bench/</a></p>
<p>由于是早上看到 <a href="https://twitter.com/smallfish_xy" target="_blank">鱼哥</a>，在推上的推荐，我实在忍不住……这是中午的草率之举，所以 <a href="https://twitter.com/smallfish_xy" target="_blank">鱼哥</a> 对本文的翻译负全责。</p>
<p>PS：别说我工作状态不饱满，我在等丫的程序执行完……</p>
<p>&#8212;&#8212;&#8212;&#8212;翻译分割线&#8212;&#8212;&#8212;&#8212;</p>
<h1>Go 和 Python Web 服务器性能对比</h1>
<p>我通常使用 <a href="http://www.python.org/">Python</a> 来构建 Web 应用。一年前，在兴趣的驱使下，我开始学习 <a href="http://golang.org/">Go</a>。 在此期间，我重写了一些原本由 C 开发的 CGI 应用，包括运行于 <a href="http://en.wikipedia.org/wiki/Chroot">chroot</a> 环境下的同 <a href="http://www.acme.com/software/thttpd/">thttpd</a> 服务器一起的应用。我开始寻找可以开发易于 chroot、且内置 Web 服务器的独立 Web 应用的工具。那时，我开始玩 <a href="http://www.getwebgo.com/">web.go</a> 框架、<a href="https://github.com/hoisie/mustache.go">mustache.go</a> 模板、Go 原生 <a href="http://golang.org/pkg/http/">http</a> 包和 <a href="https://github.com/Philio/GoMySQL">GoMySQL</a> 数据库 API。我发现，有 http、mustache.go  GoMySQL 包的 Go 可以是我用来工作的不错的工具组合。因此，我决定使用 Go 编写我的应用。<br />
<span id="more-857"></span><br />
在工作过程中发现，我需要比 mustache.go 更加灵活，比 GoMySQL 更加成熟、没有那么多 Bug 的东西。最终，我使用 <a href="https://github.com/ziutek/kasia.go">Kasia.go</a> 模板和  <a href="https://github.com/ziutek/mymysql">MyMySQL</a> （为我的应用定制开发的包，不过我将其贡献给了 Go 社区）。重写的应用即便是在比以前的负载更高的运营环境下，也工作得很好。我开始思考这个问题：用 Go 实现独立 Web 应用比 Python 到底快了（或者是慢了）多少。我决定做一些各种框架和服务器不同的用途的测试。为了比较，我选择了下面的 Go 包：</p>
<ul>
<li>原始的 Go http包；</li>
<li>web.go 框架（它使用运行于独立模式[standalone mode] 的 http 包）；</li>
<li><a href="http://garyburd.github.com/twister/">twister</a> 框架 （它同样使用 http 包）。</li>
</ul>
<p>和下面的 Python Web服务器/框架：</p>
<ul>
<li>使用 <a href="http://www.cherrypy.org/">CherryPy</a> WSGI 服务器的<a href="http://webpy.org/"> web.py</a> 框架；</li>
<li>使用 <a href="http://trac.saddi.com/flup">flup</a> <a href="http://en.wikipedia.org/wiki/FastCGI">FastCGI</a> 做 <a href="http://wiki.nginx.org/Main">nginx</a> 服务器的后台处理的 web.py 框架；</li>
<li><a href="http://www.tornadoweb.org/">tornado</a> 异步服务器/框架；</li>
<li>nginx 做负载均衡的 tornado。</li>
</ul>
<p>每一个用例，我都编写了一个小应用，略微复杂一些的、传统的 Hello World 例子。任何应用都包括：</p>
<ul>
<li>使用正则表达式通过 URL 路径传递参数；</li>
<li>使用语句创建多行输出；</li>
<li>使用 printf 形式的格式化函数/表达式格式化输出。</li>
</ul>
<p>我想，这些都是在 Web 应用中常见的操作，所以应当包含在任何简易的性能对比测试中。所有测试应用的代码在下面的链接中：</p>
<ul>
<li> <a href="https://github.com/ziutek/web_bench/blob/master/http.go">Go http</a></li>
<li> <a href="https://github.com/ziutek/web_bench/blob/master/webgo.go">web.go</a></li>
<li> <a href="https://github.com/ziutek/web_bench/blob/master/twister.go">twister</a></li>
<li> <a href="https://github.com/ziutek/web_bench/blob/master/webpy.py">web.py</a></li>
<li> <a href="https://github.com/ziutek/web_bench/blob/master/trnado.py">tornado</a></li>
</ul>
<h2>测试环境</h2>
<p>测试环境包括两台 使用千兆以太网链接的PC （请求发起者和应用服务器）。</p>
<ul>
<li>请求发起者：2 x Xeon 2.6 GHz with hyperthreading, Debian SID,     kernel: 2.6.33.7.2-rt30-1-686 #1 SMP PREEMPT RT；</li>
<li>服务器: MSI Netbook with two core Intel U4100 1.30GHz, AC power     connected, 64-bit Ubuntu 10.10, kernel: 2.6.35-25-generic #44-Ubuntu SMP,     Python 2.6.6-2ubuntu2, web.py 0.34-2, flup 1.0.2-1, tornado 0.2-1, nginx     0.7.67-3ubuntu1；</li>
</ul>
<p>为了产生 HTTP 请求并且评估测试应用的性能，我使用 <a href="http://www.joedog.org/index/siege-home">siege</a> 性能测试工具。Siege 可以用多线程模拟多个用户。我使用了下面的命令产生请求：</p>
<pre class="brush: bash; title: ; notranslate">siege -c 200 -t 20s http://SERVER_ADDR:8080/Hello/100</pre>
<p>或者多个类似的命令，减少参数 -c 的量（在这个测试中，我同时运行了多个 Python 脚本）。它模拟了 200 用户的请求，并持续 20 秒。这个 URL 使得 Web 应用对每个请求都输出 100 行。Go 应用使用 Go 发布版 2011-02-01.1。</p>
<h2>结果</h2>
<p>GOMAXPROCS=1, 一个 Python 进程：</p>
<table style="border: 1px solid;">
<tbody>
<tr>
<th>框架</th>
<th colspan="2">请求速率 [1/sec]</th>
</tr>
<tr>
<td>Go http</td>
<td>1350</td>
<td></td>
</tr>
<tr>
<td>Twister</td>
<td>1324</td>
<td></td>
</tr>
<tr>
<td>Web.go</td>
<td>1141</td>
<td></td>
</tr>
<tr>
<td>Tornado</td>
<td>882</td>
<td></td>
</tr>
<tr>
<td>Tornado+nginx</td>
<td>862</td>
<td></td>
</tr>
<tr>
<td>Web.py+CheryPy</td>
<td>169</td>
<td></td>
</tr>
<tr>
<td>Web.py+nginx</td>
<td>114</td>
<td></td>
</tr>
</tbody>
</table>
<p>GOMAXPROCS=2, 两个 Python 并发进程：</p>
<table style="border: 1px solid;">
<tbody>
<tr>
<th>框架</th>
<th colspan="2">请求速率 [1/sec]</th>
</tr>
<tr>
<td>Go http</td>
<td>1768</td>
<td></td>
</tr>
<tr>
<td>Twister</td>
<td>1746</td>
<td></td>
</tr>
<tr>
<td>Tornado</td>
<td>1682</td>
<td></td>
</tr>
<tr>
<td>Web.go</td>
<td>1516</td>
<td></td>
</tr>
<tr>
<td>Tornado+nginx</td>
<td>1378</td>
<td></td>
</tr>
<tr>
<td>Web.py+CheryPy</td>
<td>308</td>
<td></td>
</tr>
<tr>
<td>Web.py+nginx</td>
<td>198</td>
<td></td>
</tr>
</tbody>
</table>
<p>GOMAXPROCS=4, 四个 Python 并发进程：</p>
<table style="border: 1px solid;">
<tbody>
<tr>
<th>框架</th>
<th colspan="2">请求速率 [1/sec]</th>
</tr>
<tr>
<td>Go http</td>
<td>2063</td>
<td></td>
</tr>
<tr>
<td>Twister</td>
<td>2020</td>
<td></td>
</tr>
<tr>
<td>Web.go</td>
<td>1753</td>
<td></td>
</tr>
<tr>
<td>Tornado</td>
<td>1662</td>
<td></td>
</tr>
<tr>
<td>Tornado+nginx</td>
<td>1364</td>
<td></td>
</tr>
<tr>
<td>Web.py+CheryPy</td>
<td>304</td>
<td></td>
</tr>
<tr>
<td>Web.py+nginx</td>
<td>211</td>
<td></td>
</tr>
</tbody>
</table>
<p>Web.py+nginx 工作的 flup FastCGI 选项：multiplexed=False, multithreaded=False。如果 multiplexed=True 它会运行得慢一些。如果 multithreaded=True 而只有一个进程服务于 nginx 服务器，会报下面的错误：</p>
<pre class="brush: bash; title: ; notranslate">[error] 18166#0: *66139 connect() to
unix:/home/michal/Programowanie/web_bench/socket failed (11: Resource
temporarily unavailable) while connecting to upstream</pre>
<p>FastCGI 的多进程由 <a href="http://redmine.lighttpd.net/projects/spawn-fcgi/wiki">spawn-fcgi</a> 产生。</p>
<h2>结论</h2>
<p>你可以看到 Go 赢得了几乎所有的测试用例。web.go 框架的那个不太理想的结果可能是由于它先尝试用指定的 URL 寻找静态页面，然后才会执行处理方法。让我惊讶的是 tornado Python 框架如此之高的性能，尤其是跟 web.py 框架相比而言。我同样对 CherryPy 服务器比 nginx+flup 快感到惊讶 (我使用 web.py+flup+nginx 跑几乎所有的 Python Web 应用)。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2011/02/%e7%bf%bb%e8%af%91go-%e5%92%8c-python-%e7%9a%84-web-%e6%9c%8d%e5%8a%a1%e5%99%a8%e6%80%a7%e8%83%bd%e5%af%b9%e6%af%94/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>

