<?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>程式設計 遇上 小提琴 &#187; victor</title>
	<atom:link href="http://blog.ez2learn.com/author/victor/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.ez2learn.com</link>
	<description>Victor&#039;s個人部落格，關於程式設計與小提琴</description>
	<lastBuildDate>Tue, 07 Feb 2012 03:26:25 +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>Now.in 兩週年回顧</title>
		<link>http://blog.ez2learn.com/2012/01/31/now-in-2-years-review/</link>
		<comments>http://blog.ez2learn.com/2012/01/31/now-in-2-years-review/#comments</comments>
		<pubDate>Mon, 30 Jan 2012 19:19:45 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[中文文章]]></category>
		<category><![CDATA[Now.in]]></category>
		<category><![CDATA[now.in]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1737</guid>
		<description><![CDATA[時間過得很快，這兩天SSL的憑證過期了，忘了事先申請，上次申請才不過是去年的今天左右的時間，轉眼間Now.in已經上線超過兩年的時間了，從原本只有幾隻小貓到今天有上萬人在線上，頁面檢視數量也在最近超過了一億次 我們也朝我們的目標挺進了不少，在許多國家都有不少的使用者，而且也有成長的趨勢 我們的使用者來自全世界的每個有網路的角落 就我們所知，台灣從沒有一個網站可以走到這個地步，在全球的競爭下哪怕能打下一小片天地而不是只有國內的市場，我們希望有一天大家可以驕傲地說對全世界說 Now.in是來自台灣的網站，在這整個過程中我們用了最少的資源來達成最大的效益，這些靠的都是熱情，還記得曾有訪問中有一個問題是 &#8211; Now.in改版了多少次? 我的答案是無數次，而事實上也是如此，改進是持續的，而不是突然達成的，紀念Now.in上線兩週年，回顧一下這整個演進的過程 一切的開始 最初版本的網頁，非常地陽春，就只有最簡單的註冊、登入與電台、編輯個人電台和收聽功能而已，右邊的按鈕本來是預留想做好看的插圖按鈕，但是沒有美術能力，所以就兩個框框留在右邊，看起來挺奇怪的 收聽的頁面也只有單純的一個播放器而已，用的還不是自己寫的，是Google reader的音訊播放器 早期的Mr.DJ也只有單純的播曲子串流到伺服器的功能，而且UI的設計是將播放列表和電台控制與收音的面版放在一起的 加入聊天室 聊天室早在一開始就是計劃好的，但在2010年4月份才正式加入，功能非常地陽春，連加入聊天室的動作都不用做，只要打名稱就能聊天，不過這也有個問題在於，任何人都可以假冒別人的名稱說話，除了聊天室，社群網站的推薦按鈕也被加入了 為了解決聊天室名稱會被別人冒用的問題，如果直接顯示出使用者的IP會有隱私權和安全性的問題，而且也不好看，所以我就將使用者IP拿去做hash來產生顏色，如此一來不同的IP有不同的顏色，要冒充別人就比較困難，除此之外旁邊醜醜的按鈕也用CSS3弄好看了一些 播放器 因為Google reader的那種player是設計給一般的音訊檔案用的，然而Now.in的是串流，時間軸是沒有用的，為了有較好用的介面，這時加入了自己寫的Flash player 接著增加了電台個性圖編的功能，網頁的排版也進行了重新設計，雖然一樣還是一點美感都沒有 音樂資訊 大家在聽電台時一定會想知道目前播的到底是什麼曲子，所以即時更新曲目的功能就在2010年6月左右被實作了出來，這其實也是一開始就有想好要做的，而播放器的版本跟現今的已經差不了多少了 Mr.DJ2.0 為了支援傳送音樂曲目到伺服器，Mr.DJ也做了一些修改，同時也在這時來到了2.0版本，整個UI算是重新設計過，基本上的想法是播音樂和電台的功能想拆開來，原先的設計不管怎樣加東西進去都很難排版 網頁重寫 因為舊的網頁程式已經有點雜亂，加上對於TurboGears的一些毛病有些反感，同時也想試試新的網頁框架Pyramid，並好好寫自動化測試，除此之外也找人設計了logo，因此順手就將整個網頁重寫，網頁的設計上一開始還試了不同的樣式，但這最後沒有公開 但是最後還是重新設計過一次，就成了今天看到的這個版本 中間還經過了無數次細節修改，不同瀏覽器看起來的效果調整等等 Android和iPhone app 在手機上也能收聽這樣的功能一直也都是在一開始就有想好的，然而要扛起所有的開發我已經是分身乏術了，感謝兩位同學的幫忙，分別負責了iPhone和Android的app，從此之後在手機上也能收聽電台 積沙成塔 回顧舊的程式才發現原來已經經過了這麼多的改版，事實這些看得到的改進都還只是冰山一角而已，因為有些改進太小就沒有一一介紹，要跑老舊的程式實在太痛苦了，還有另外一大部份都是看不到的改進，那些才是最重要的，改進伺服器的穩定，改進整個系統的承載能力，改進聊天室的承載能力等等，這些都是無數的微小細累積起來的，惡魔都在細節裡，這也是為什麼這個行業沒有熱情是做不起來的，沒有熱情就只會想應付了事，東西能運作就不再改善，剩的就只有等產品死亡，不再有人使用 在看這些舊程式的過程中我心中有些疑問，開發過程中我到底送了多少個commit? 到底寫了多少行? 有空的話或許可以分析一下另外寫一篇文章也說不定 展望 雖然走到今天這個地步已經是很不容易，但事實上我們離目標還有很遠的一段距離，還有成堆如山的改進在前面等著我們去實行，但不管怎樣，感謝各位在過去兩年來對於Now.in的支持，今後也會更加努力向前邁進]]></description>
			<content:encoded><![CDATA[<p>時間過得很快，這兩天SSL的憑證過期了，忘了事先申請，上次申請才不過是去年的今天左右的時間，轉眼間Now.in已經上線超過兩年的時間了，從原本只有幾隻小貓到今天有上萬人在線上，頁面檢視數量也在最近超過了一億次</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/visitors.png"><img class="aligncenter size-full wp-image-1741" title="100 million pageviews" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/visitors.png" alt="100 million pageviews" width="344" height="331" /></a></p>
<p>我們也朝我們的目標挺進了不少，在許多國家都有不少的使用者，而且也有成長的趨勢</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/country_rank.png"><img class="aligncenter size-full wp-image-1742" title="Now.in Alexia country rank" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/country_rank.png" alt="Now.in Alexia country rank" width="332" height="414" /></a></p>
<p>我們的使用者來自全世界的每個有網路的角落</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/visitor_map.png"><img class="aligncenter size-medium wp-image-1765" title="visitor_map" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/visitor_map-300x185.png" alt="" width="300" height="185" /></a></p>
<p>就我們所知，台灣從沒有一個網站可以走到這個地步，在全球的競爭下哪怕能打下一小片天地而不是只有國內的市場，我們希望有一天大家可以驕傲地說對全世界說 Now.in是來自台灣的網站，在這整個過程中我們用了最少的資源來達成最大的效益，這些靠的都是熱情，還記得曾有訪問中有一個問題是 &#8211; Now.in改版了多少次? 我的答案是無數次，而事實上也是如此，改進是持續的，而不是突然達成的，紀念Now.in上線兩週年，回顧一下這整個演進的過程</p>
<h2>一切的開始</h2>
<p>最初版本的網頁，非常地陽春，就只有最簡單的註冊、登入與電台、編輯個人電台和收聽功能而已，右邊的按鈕本來是預留想做好看的插圖按鈕，但是沒有美術能力，所以就兩個框框留在右邊，看起來挺奇怪的</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/web01.png"><img class="aligncenter size-medium wp-image-1744" title="Now.in web screenshot" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/web01-300x156.png" alt="Now.in web screenshot" width="300" height="156" /></a></p>
<p>收聽的頁面也只有單純的一個播放器而已，用的還不是自己寫的，是Google reader的音訊播放器</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/web02.png"><img class="aligncenter size-medium wp-image-1745" title="Now.in web screenshot 2" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/web02-300x156.png" alt="Now.in web screenshot 2" width="300" height="156" /></a></p>
<p>早期的Mr.DJ也只有單純的播曲子串流到伺服器的功能，而且UI的設計是將播放列表和電台控制與收音的面版放在一起的</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/mrdj01.png"><img class="aligncenter size-full wp-image-1743" title="Mr.DJ screenshot" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/mrdj01.png" alt="Mr.DJ screenshot" width="460" height="499" /></a></p>
<h2>加入聊天室</h2>
<p>聊天室早在一開始就是計劃好的，但在2010年4月份才正式加入，功能非常地陽春，連加入聊天室的動作都不用做，只要打名稱就能聊天，不過這也有個問題在於，任何人都可以假冒別人的名稱說話，除了聊天室，社群網站的推薦按鈕也被加入了</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/web03.png"><img class="aligncenter size-medium wp-image-1748" title="Now.in chatroom screenshot" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/web03-300x224.png" alt="Now.in chatroom screenshot" width="300" height="224" /></a></p>
<p>為了解決聊天室名稱會被別人冒用的問題，如果直接顯示出使用者的IP會有隱私權和安全性的問題，而且也不好看，所以我就將使用者IP拿去做hash來產生顏色，如此一來不同的IP有不同的顏色，要冒充別人就比較困難，除此之外旁邊醜醜的按鈕也用CSS3弄好看了一些</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/web04.png"><img class="aligncenter size-medium wp-image-1749" title="Now.in screenshot" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/web04-300x216.png" alt="Now.in screenshot" width="300" height="216" /></a></p>
<h2>播放器</h2>
<p>因為Google reader的那種player是設計給一般的音訊檔案用的，然而Now.in的是串流，時間軸是沒有用的，為了有較好用的介面，這時加入了自己寫的Flash player</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/web05.png"><img class="aligncenter size-medium wp-image-1751" title="Now.in flash player" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/web05-300x245.png" alt="Now.in flash player" width="300" height="245" /></a></p>
<p>接著增加了電台個性圖編的功能，網頁的排版也進行了重新設計，雖然一樣還是一點美感都沒有</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/web07.png"><img class="aligncenter size-medium wp-image-1752" title="web07" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/web07-300x191.png" alt="" width="300" height="191" /></a></p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/web06.png"><img class="aligncenter size-medium wp-image-1753" title="web06" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/web06-300x276.png" alt="" width="300" height="276" /></a></p>
<h2>音樂資訊</h2>
<p>大家在聽電台時一定會想知道目前播的到底是什麼曲子，所以即時更新曲目的功能就在2010年6月左右被實作了出來，這其實也是一開始就有想好要做的，而播放器的版本跟現今的已經差不了多少了</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/web09.png"><img class="aligncenter size-medium wp-image-1754" title="web09" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/web09-300x275.png" alt="" width="300" height="275" /></a></p>
<p>Mr.DJ2.0</p>
<p>為了支援傳送音樂曲目到伺服器，Mr.DJ也做了一些修改，同時也在這時來到了2.0版本，整個UI算是重新設計過，基本上的想法是播音樂和電台的功能想拆開來，原先的設計不管怎樣加東西進去都很難排版</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/mrdj02.png"><img class="aligncenter size-medium wp-image-1755" title="mrdj02" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/mrdj02-300x152.png" alt="" width="300" height="152" /></a></p>
<h2>網頁重寫</h2>
<p>因為舊的網頁程式已經有點雜亂，加上對於TurboGears的一些毛病有些反感，同時也想試試新的網頁框架Pyramid，並好好寫自動化測試，除此之外也找人設計了logo，因此順手就將整個網頁重寫，網頁的設計上一開始還試了不同的樣式，但這最後沒有公開</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/web10.png"><img class="aligncenter size-medium wp-image-1756" title="web10" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/web10-300x81.png" alt="" width="300" height="81" /></a></p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/web11.png"><img class="aligncenter size-medium wp-image-1757" title="web11" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/web11-300x87.png" alt="" width="300" height="87" /></a></p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/web12.png"><img class="aligncenter size-medium wp-image-1758" title="web12" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/web12-300x93.png" alt="" width="300" height="93" /></a></p>
<p>但是最後還是重新設計過一次，就成了今天看到的這個版本</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/web13.png"><img class="aligncenter size-medium wp-image-1759" title="web13" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/web13-300x147.png" alt="" width="300" height="147" /></a></p>
<p>中間還經過了無數次細節修改，不同瀏覽器看起來的效果調整等等</p>
<p>Android和iPhone app</p>
<p>在手機上也能收聽這樣的功能一直也都是在一開始就有想好的，然而要扛起所有的開發我已經是分身乏術了，感謝兩位同學的幫忙，分別負責了iPhone和Android的app，從此之後在手機上也能收聽電台</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/android_screen_shot01.jpg"><img class="aligncenter size-medium wp-image-1762" title="android_screen_shot01" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/android_screen_shot01-180x300.jpg" alt="" width="180" height="300" /></a></p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2012/01/iphone_screen_shot02.jpg"><img class="aligncenter size-medium wp-image-1763" title="iphone_screen_shot02" src="http://blog.ez2learn.com/wp-content/uploads/2012/01/iphone_screen_shot02-200x300.jpg" alt="" width="200" height="300" /></a></p>
<h2>積沙成塔</h2>
<p>回顧舊的程式才發現原來已經經過了這麼多的改版，事實這些看得到的改進都還只是冰山一角而已，因為有些改進太小就沒有一一介紹，要跑老舊的程式實在太痛苦了，還有另外一大部份都是看不到的改進，那些才是最重要的，改進伺服器的穩定，改進整個系統的承載能力，改進聊天室的承載能力等等，這些都是無數的微小細累積起來的，惡魔都在細節裡，這也是為什麼這個行業沒有熱情是做不起來的，沒有熱情就只會想應付了事，東西能運作就不再改善，剩的就只有等產品死亡，不再有人使用</p>
<p>在看這些舊程式的過程中我心中有些疑問，開發過程中我到底送了多少個commit? 到底寫了多少行? 有空的話或許可以分析一下另外寫一篇文章也說不定</p>
<h2>展望</h2>
<p>雖然走到今天這個地步已經是很不容易，但事實上我們離目標還有很遠的一段距離，還有成堆如山的改進在前面等著我們去實行，但不管怎樣，感謝各位在過去兩年來對於Now.in的支持，今後也會更加努力向前邁進</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2012/01/31/now-in-2-years-review/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>新世紀通訊函式庫 &#8211; ZeroMQ</title>
		<link>http://blog.ez2learn.com/2011/12/31/transport-lib-of-new-era-zeromq/</link>
		<comments>http://blog.ez2learn.com/2011/12/31/transport-lib-of-new-era-zeromq/#comments</comments>
		<pubDate>Sat, 31 Dec 2011 09:53:30 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[中文文章]]></category>
		<category><![CDATA[分享]]></category>
		<category><![CDATA[分散式系統]]></category>
		<category><![CDATA[分散式運算]]></category>
		<category><![CDATA[單點失效]]></category>
		<category><![CDATA[Distributed system]]></category>
		<category><![CDATA[高可得性]]></category>
		<category><![CDATA[負載平衡]]></category>
		<category><![CDATA[雲端]]></category>
		<category><![CDATA[通訊]]></category>
		<category><![CDATA[High availability]]></category>
		<category><![CDATA[Mongrel]]></category>
		<category><![CDATA[Mongrel2]]></category>
		<category><![CDATA[single point failure]]></category>
		<category><![CDATA[socket]]></category>
		<category><![CDATA[ZeroMQ]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1678</guid>
		<description><![CDATA[2012年就快到了，離2015年第三次衝擊也快到了，聽不懂我在說什麼就算了，跟主題沒什麼關聯，在介紹主角之前，我總喜歡描述一下主角的背景 欲上雲端，必先分散 這幾年，雲端炒得正熱門，愛跟風的台灣人當然也不能錯過，不管是賣主機的聲稱他們是做雲端的，連賣房子的也稱他們的是雲端，哪天就連在路邊看見雲端雞排都請不要大驚小怪，因為那簡直和奈米光觸媒雞排一樣有異曲同工之妙，然而口口聲聲說雲端，但前陣子iPhone4S預購就給了各家電信商一個大巴掌，他們的網頁伺服器都在一瞬間被大量擁入的使用者塞暴了，更別說其中還有不乏自家推出雲端服務的電信商，說真的，連分散運算的基本功都做不到，跟人家雲什麼端? 你說是吧? 但今天的主角不在於雲端是什麼，或是什麼才算真正的雲端之類的無聊話題，今天的主軸是分散式運算，因為要達到規模的運算和高可得性就一定要扯到分散式的運算，而分散式運算一直以來都不是什麼簡單的議題，最麻煩的就要算是溝通上的問題了，如果你有 三個節點之間想要進行溝通，該怎麼做? 你說簡單，直接串在一起不就好了? 好吧，或許你只有固定的三個節點，這好辦，但是要是你有更多的節點呢? 考慮一下十個節點、一百個節點，光想像就能知道，隨著節點的增加，他們之間的連線數量呈指數在成長，不光是連線本身的問題，連這些節點要如何知道對方的存在以及管理都是相當麻煩的問題 中央集權 光想就令人頭皮發麻，為了解決這樣的問題，一個經典的模式被設計且大量使用，那就是所謂的 Broker (掮客)，也有稱Message Queue、Message bus等，簡單的概念，就是所有節點都連向中間的訊息交換伺服器，而溝通都透過這個中央的交換中心來進行，節點與節點之間無需去在意到底誰在和我溝通，只需要在意訊息的種類和內容即可 &#160; 雖然這樣的模式解決了一些問題，但同時也引入了新的問題，分散式系統的頭號公敵 &#8211; 單點失效，正因為所有節點都依賴著這個中間的掮客幫忙轉送訊息，這也意味著當掮客網路斷線、當機等等意外的發生，都會讓整個系統陷入停擺的狀態，除此之外還有另一個嚴重的問題，就是當節點數量增加，掮客的工作量也會一直往上升，在無法擴增的情況下會造成整個系統的擴展性受到限制，效能也會因為掮客受限制 各自為政 正因為中央集權造成了問題，所以有人提出了各自為政來解決問題，可以想見的，在分散式的系統裡，並不是所有的節點都需要和所有人進行溝通，他們通常只需要和特定的節點溝通，舉個例子，假設你設計的是一個多媒體檔案的處理系統，在第一個節點可能做的是Hash，用來產生該檔案的唯一識別編號，節點二做的是轉檔，節點三做的是儲存，節點四做的是歸檔，那麼你需要的就只是這樣的結構，如果以物件導向的設計模式來看，我們稱這樣的結構為責任鏈 &#160; 再看另一個例子，如果我們是氣象局，想發佈各種天氣的消息，那麼你需要的是一個伺服器，讓大家去訂閱他們有興趣的主題，這樣的結構以物件導向的觀點來看就叫做觀察者模式，與我們先前見到的Broker做的是一樣的事情，然而在這裡的重點在於該伺服器只負責發佈天氣消息，並不參與訊息的交換 整體的概念就是各自有不同的子系統，我們透過通訊的方式將它們串在一起，這樣做有個好處就是效能好，再來就是設計得當的話，某子系統雖然停擺，但不會影響到所有的系統 各自為政的代價 雖然各自為政的做法有其好處，但相對的也有它的代價，光是通訊的協定就是很腦人的問題，考慮一下各種不同的子系統間該用什麼協定來溝通，連線中斷了怎麼處理，負載平衡? 這些問題都要花相當大的心力來解決 而在不同的環境下，使用不同的通訊方式都各有好處，使用TCP/IP的話，不管你的節點在同一台機器或是遠端，都可以連線，缺點是在同一台機器會有一定的效能耗損，而且遇到廣播的訊息，同樣的訊息被傳送N次，就沒有UDP廣播來得划算，如果節點是在同一台機器上溝通，使用IPC的方式效率好，如果是在同一個thread裡，那麼分享記憶體的方式最快，IPC反而會拖慢速度，正因為有各種不同的考量，使得想要達成高效率的分散式系統是一件困難的事情 是主角登場的時候了 &#8211; ZeroMQ 講了半天，ZeroMQ到底是做什麼用的? 簡單的來說，它是一套網路通訊函式庫，用來解決上面所提到的問題，考慮一下上面所提到的物件導向設計模式，你想，即然那樣的模式一再出現，為什麼我們不能將這些常見的通訊方式變成可以輕易重覆使用的形式? ZeroMQ所做到的即是如此，它將常見的通訊方式定義成不同行為的socket，讓你可以輕易地重覆使用這些樣式去組出強健的分散式系統 老樣子 Hello world.. 不! 是Hello baby &#8230; <a href="http://blog.ez2learn.com/2011/12/31/transport-lib-of-new-era-zeromq/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>2012年就快到了，離2015年第三次衝擊也快到了，聽不懂我在說什麼就算了，跟主題沒什麼關聯，在介紹主角之前，我總喜歡描述一下主角的背景</p>
<h2>欲上雲端，必先分散</h2>
<p>這幾年，雲端炒得正熱門，愛跟風的台灣人當然也不能錯過，不管是賣主機的聲稱他們是做雲端的，連賣房子的也稱他們的是雲端，哪天就連在路邊看見雲端雞排都請不要大驚小怪，因為那簡直和奈米光觸媒雞排一樣有異曲同工之妙，然而口口聲聲說雲端，但前陣子iPhone4S預購就給了各家電信商一個大巴掌，他們的網頁伺服器都在一瞬間被大量擁入的使用者塞暴了，更別說其中還有不乏自家推出雲端服務的電信商，說真的，連分散運算的基本功都做不到，跟人家雲什麼端? 你說是吧?</p>
<p>但今天的主角不在於雲端是什麼，或是什麼才算真正的雲端之類的無聊話題，今天的主軸是分散式運算，因為要達到規模的運算和高可得性就一定要扯到分散式的運算，而分散式運算一直以來都不是什麼簡單的議題，最麻煩的就要算是溝通上的問題了，如果你有 三個節點之間想要進行溝通，該怎麼做? 你說簡單，直接串在一起不就好了?</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/12/Eva-Magi.jpg"><img class="aligncenter size-full wp-image-1680" title="Eva-Magi" src="http://blog.ez2learn.com/wp-content/uploads/2011/12/Eva-Magi.jpg" alt="" width="296" height="202" /></a></p>
<p>好吧，或許你只有固定的三個節點，這好辦，但是要是你有更多的節點呢? 考慮一下十個節點、一百個節點，光想像就能知道，隨著節點的增加，他們之間的連線數量呈指數在成長，不光是連線本身的問題，連這些節點要如何知道對方的存在以及管理都是相當麻煩的問題</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/12/mitinanime_eva.jpg"><img class="aligncenter size-full wp-image-1681" title="mitinanime_eva" src="http://blog.ez2learn.com/wp-content/uploads/2011/12/mitinanime_eva.jpg" alt="" width="522" height="291" /></a></p>
<h2>中央集權</h2>
<p>光想就令人頭皮發麻，為了解決這樣的問題，一個經典的模式被設計且大量使用，那就是所謂的 Broker (掮客)，也有稱Message Queue、Message bus等，簡單的概念，就是所有節點都連向中間的訊息交換伺服器，而溝通都透過這個中央的交換中心來進行，節點與節點之間無需去在意到底誰在和我溝通，只需要在意訊息的種類和內容即可</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/12/broker1.png"><img class="aligncenter size-full wp-image-1686" title="broker" src="http://blog.ez2learn.com/wp-content/uploads/2011/12/broker1.png" alt="" width="387" height="394" /></a></p>
<p>&nbsp;</p>
<p>雖然這樣的模式解決了一些問題，但同時也引入了新的問題，分散式系統的頭號公敵 &#8211; 單點失效，正因為所有節點都依賴著這個中間的掮客幫忙轉送訊息，這也意味著當掮客網路斷線、當機等等意外的發生，都會讓整個系統陷入停擺的狀態，除此之外還有另一個嚴重的問題，就是當節點數量增加，掮客的工作量也會一直往上升，在無法擴增的情況下會造成整個系統的擴展性受到限制，效能也會因為掮客受限制</p>
<h2>各自為政</h2>
<p>正因為中央集權造成了問題，所以有人提出了各自為政來解決問題，可以想見的，在分散式的系統裡，並不是所有的節點都需要和所有人進行溝通，他們通常只需要和特定的節點溝通，舉個例子，假設你設計的是一個多媒體檔案的處理系統，在第一個節點可能做的是Hash，用來產生該檔案的唯一識別編號，節點二做的是轉檔，節點三做的是儲存，節點四做的是歸檔，那麼你需要的就只是這樣的結構，如果以物件導向的設計模式來看，我們稱這樣的結構為責任鏈</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/12/flow-chain1.png"><img class="aligncenter size-full wp-image-1693" title="flow chain" src="http://blog.ez2learn.com/wp-content/uploads/2011/12/flow-chain1.png" alt="" width="394" height="88" /></a></p>
<p>&nbsp;</p>
<p>再看另一個例子，如果我們是氣象局，想發佈各種天氣的消息，那麼你需要的是一個伺服器，讓大家去訂閱他們有興趣的主題，這樣的結構以物件導向的觀點來看就叫做觀察者模式，與我們先前見到的Broker做的是一樣的事情，然而在這裡的重點在於該伺服器只負責發佈天氣消息，並不參與訊息的交換</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/12/weather-center.png"><img class="aligncenter size-full wp-image-1697" title="weather center" src="http://blog.ez2learn.com/wp-content/uploads/2011/12/weather-center.png" alt="" width="387" height="394" /></a></p>
<p>整體的概念就是各自有不同的子系統，我們透過通訊的方式將它們串在一起，這樣做有個好處就是效能好，再來就是設計得當的話，某子系統雖然停擺，但不會影響到所有的系統</p>
<h2>各自為政的代價</h2>
<p>雖然各自為政的做法有其好處，但相對的也有它的代價，光是通訊的協定就是很腦人的問題，考慮一下各種不同的子系統間該用什麼協定來溝通，連線中斷了怎麼處理，負載平衡? 這些問題都要花相當大的心力來解決</p>
<p>而在不同的環境下，使用不同的通訊方式都各有好處，使用TCP/IP的話，不管你的節點在同一台機器或是遠端，都可以連線，缺點是在同一台機器會有一定的效能耗損，而且遇到廣播的訊息，同樣的訊息被傳送N次，就沒有UDP廣播來得划算，如果節點是在同一台機器上溝通，使用IPC的方式效率好，如果是在同一個thread裡，那麼分享記憶體的方式最快，IPC反而會拖慢速度，正因為有各種不同的考量，使得想要達成高效率的分散式系統是一件困難的事情</p>
<h2>是主角登場的時候了 &#8211; ZeroMQ</h2>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/12/logo.gif"><img class="aligncenter size-full wp-image-1701" title="logo" src="http://blog.ez2learn.com/wp-content/uploads/2011/12/logo.gif" alt="" width="381" height="119" /></a></p>
<p>講了半天，<a href="http://www.zeromq.org/">ZeroMQ</a>到底是做什麼用的? 簡單的來說，它是一套網路通訊函式庫，用來解決上面所提到的問題，考慮一下上面所提到的物件導向設計模式，你想，即然那樣的模式一再出現，為什麼我們不能將這些常見的通訊方式變成可以輕易重覆使用的形式? ZeroMQ所做到的即是如此，它將常見的通訊方式定義成不同行為的socket，讓你可以輕易地重覆使用這些樣式去組出強健的分散式系統</p>
<h2>老樣子 Hello world.. 不! 是Hello baby</h2>
<p>雖然大部份程式範例都喜歡用Hello world，但我不喜歡和他們一樣，我喜歡Hello baby，我們來看一下簡單的Hello baby範例</p>
<p>Client端</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> zmq
&nbsp;
context = zmq.<span style="color: black;">Context</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #dc143c;">socket</span> = context.<span style="color: #dc143c;">socket</span><span style="color: black;">&#40;</span>zmq.<span style="color: black;">REQ</span><span style="color: black;">&#41;</span>
<span style="color: #dc143c;">socket</span>.<span style="color: black;">connect</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;tcp://127.0.0.1:7788&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #dc143c;">socket</span>.<span style="color: black;">send</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'hello'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #dc143c;">socket</span>.<span style="color: black;">recv</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Server端</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> zmq
&nbsp;
context = zmq.<span style="color: black;">Context</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #dc143c;">socket</span> = context.<span style="color: #dc143c;">socket</span><span style="color: black;">&#40;</span>zmq.<span style="color: black;">REP</span><span style="color: black;">&#41;</span>
<span style="color: #dc143c;">socket</span>.<span style="color: black;">bind</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;tcp://*:7788&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #dc143c;">socket</span>.<span style="color: black;">recv</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #dc143c;">socket</span>.<span style="color: black;">send</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'baby'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>有了這兩隻Python程式，你可以在本機執行，不管Client或Server端先執行都可，你可能覺得我在說笑，哪有Client先執行的道理，接著會解釋，他們輸出的結果會像這樣</p>
<p>Client端:</p>
<blockquote><p>baby</p></blockquote>
<p>Server端:</p>
<blockquote><p>hello</p></blockquote>
<p>所以這之間到底發生了什麼事? 很簡單的在Client方，以TCP的方式連接了本機端的7788 port，接著送了一個request，然後接收response然後印出來，而Server方以TCP的方式綁定了本地端的7788 port，然後讀了一個request後送了一個response回去，這有什麼特別? 目前為止 &#8230; 沒有，但是，讓我們看下去</p>
<h2>ZeroMQ的特異功能</h2>
<p>上面的兩隻程式整體看起來和一般的socket沒兩樣，而行為也沒太大差別，所以它的特異功能到底在哪裡? 難不成它可以用手指讀封包嗎? 事實上它的特別之處可多了，這兩個程式並沒有表現出太特別的地方，我們改寫上面的程式讓它變成更能突顯ZeroMQ特色的程式</p>
<p>req.py</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> zmq
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">random</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">time</span>
&nbsp;
context = zmq.<span style="color: black;">Context</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #dc143c;">socket</span> = context.<span style="color: #dc143c;">socket</span><span style="color: black;">&#40;</span>zmq.<span style="color: black;">REQ</span><span style="color: black;">&#41;</span>
<span style="color: #dc143c;">socket</span>.<span style="color: black;">bind</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;tcp://*:7788&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># wait all worker connected</span>
<span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">9</span><span style="color: black;">&#41;</span>:
    a = <span style="color: #dc143c;">random</span>.<span style="color: black;">randint</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">100</span><span style="color: black;">&#41;</span>
    b = <span style="color: #dc143c;">random</span>.<span style="color: black;">randint</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">100</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Compute %s + %s ...'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>a, b<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># send request to peer</span>
    <span style="color: #dc143c;">socket</span>.<span style="color: black;">send_multipart</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: #008000;">str</span><span style="color: black;">&#40;</span>a<span style="color: black;">&#41;</span>, <span style="color: #008000;">str</span><span style="color: black;">&#40;</span>b<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># receive response from peer</span>
    rep = <span style="color: #dc143c;">socket</span>.<span style="color: black;">recv</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">' ='</span>, rep</pre></div></div>

<p>rep.py</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">import</span> zmq
&nbsp;
context = zmq.<span style="color: black;">Context</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #dc143c;">socket</span> = context.<span style="color: #dc143c;">socket</span><span style="color: black;">&#40;</span>zmq.<span style="color: black;">REP</span><span style="color: black;">&#41;</span>
<span style="color: #dc143c;">socket</span>.<span style="color: black;">connect</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;tcp://localhost:7788&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Worker %s is running ...'</span> <span style="color: #66cc66;">%</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">getpid</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:
    <span style="color: #808080; font-style: italic;"># receive request</span>
    a, b = <span style="color: #dc143c;">socket</span>.<span style="color: black;">recv_multipart</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    a = <span style="color: #008000;">int</span><span style="color: black;">&#40;</span>a<span style="color: black;">&#41;</span>
    b = <span style="color: #008000;">int</span><span style="color: black;">&#40;</span>b<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Compute %s + %s and send response'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>a, b<span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">socket</span>.<span style="color: black;">send</span><span style="color: black;">&#40;</span><span style="color: #008000;">str</span><span style="color: black;">&#40;</span>a + b<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>接著我們先執行三個rep.py，然後再執行一個req.py，看發生了什麼事</p>
<p>req.py</p>
<pre>Compute 43 + 91 ...
 = 134
Compute 21 + 63 ...
 = 84
Compute 17 + 93 ...
 = 110
Compute 29 + 98 ...
 = 127
Compute 90 + 55 ...
 = 145
Compute 14 + 74 ...
 = 88
Compute 3 + 85 ...
 = 88
Compute 12 + 73 ...
 = 85
Compute 73 + 21 ...
 = 94</pre>
<p>rep.py 1</p>
<pre>Worker 7296 is running ...
Compute 43 + 91 and send response
Compute 29 + 98 and send response
Compute 3 + 85 and send response</pre>
<p>rep.py 2</p>
<pre>Worker 6532 is running ...
Compute 21 + 63 and send response
Compute 90 + 55 and send response
Compute 12 + 73 and send response</pre>
<p>rep.py 3</p>
<pre>Worker 5928 is running ...
Compute 17 + 93 and send response
Compute 14 + 74 and send response
Compute 73 + 21 and send response</pre>
<p>這兩個程式做的事情很簡單，req.py負責產生兩個亂數，將其當作request送給socket的另一端，而rep.py則是接收兩個亂數request，算出結果來送回給發出者，但是這好像哪裡不太對勁是嗎? 三個socket連到一個socket? 為什麼送request的一方居然變成bind而不是connect了? 為什麼connect的一方先執行居然也能連線? 我已經聽見你在電腦前的吼叫，讓我們來說明一下這到底是怎麼回事</p>
<h3>連線先後順序無關、自動重連機制</h3>
<p>ZeroMQ的socket有個特色就是對於誰先bind誰後connect之類的完全都不在乎，如果是connect先行，發線連線無法建立，ZeroMQ會自動重試，當bind也確立了，連線就會自動接上了，有了這樣的特性，只要管怎樣連接和對方的位址是什麼即可</p>
<p>通常bind那端都是位址為大家所熟知的那端，connect都是位址不為人知的，以我們這樣的例子，因為worker可能有很多個，所以我們將REQ端換成bind，而REP端換成connect，方便多個rep.py連接req.py</p>
<p>除此之外，REQ和REP兩種角色不管哪方bind哪方connect運算的方式都是一樣的</p>
<h3>多個Socket連線、多位址綁定</h3>
<p>ZeroMQ的socket之間可以多個互相連線，所以一個socket的另一端可能有N個節點連接，除此之外，同一個socket也可以綁在不同的位址上</p>
<h3>自動負載平衡</h3>
<p>如果你仔細觀察上面程式的輸出，就會發現request是依序分配給三個rep.py，這也是ZeroMQ的特色之一，REQ端會將send的message用Round-robin的方式分給所有的遠端連線，而你有多少個連線，他都會照一樣的規則分配</p>
<p>但因為先前有提到自動連線，因此會有個問題，當第一個連線接上時，其它連線還來不及連上，此時request可能已經大量分配給第一個連上的，為了解決這問題，我們安排了time.sleep(1)來等所有worker連上線，以避免早起的鳥兒吃光了蟲子</p>
<h3>訊息傳輸</h3>
<p>你可以發現我們送資料和處理的都是訊息，ZeroMQ可以提供你傳送多段資料在一筆訊息裡，因此我們在這看到的send_multipart和recv_multipart作用即為如此，有了這樣的特性，你可以不用擔心通訊協定的問題，只管專心處理訊息即可</p>
<h3>支援不同的通訊方式</h3>
<p>看見了我們在程式中所寫的 "tcp://*:7788&#8243; 和 "tcp://localhost:7788&#8243; 嗎? 它們暗示了TCP只不過只是支援的其中一種通訊方式而已，ZeroMQ還支援IPC，但目前只支援Linux下的domain socket，例如 "ipc:///tmp/req.socket"，甚至它還支援thread之間的通訊方式，因此如果你的兩個節點放在同一個process裡，為了效能考量，你可以用這種協定讓通訊效率最佳化</p>
<p>為了節省傳輸的封包，ZeroMQ甚至提供了基於UDP的廣播通訊方式，因此當你將節點放在同一個網路下，廣播的功能就能節省大量的重覆封包傳送</p>
<h3><span style="color: #000000;">支援N種語言</span></h3>
<p>雖然我們的範例都是用Python寫的，但ZeroMQ目前已經支援了幾乎所有你能在臺面上看到的主流語言，你喜歡用Lua寫子系統A? OK! 你喜歡用Perl寫系統B? 可以! C語言? 當然沒問題，你喜歡PHP? 厄&#8230; 也可以啦，總之，ZeroMQ讓你從不同語言的通訊中解藕開來，只要專注在於訊息的處理上即可</p>
<h2>更多的樣式</h2>
<p>我們做為範例的REQ/REP只不過是ZeroMQ所提供的樣式中的一種，它的特性就是一個request一個response，而REQ端會對所有的連線做fairly queue，也就是會公平地把request塞給REP端，借用官網的圖</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/12/fig18.png"><img class="aligncenter size-full wp-image-1719" title="fig18" src="http://blog.ez2learn.com/wp-content/uploads/2011/12/fig18.png" alt="" width="386" height="358" /></a></p>
<p>另一種常見的樣式是PUB/SUB，也就是我們先前提到的觀察者模式，它的特色是所有PUB發送的消息會廣播給所有SUB的連線，而且SUB可以設定只要某段字串開頭的訊息</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/12/fig4.png"><img class="aligncenter size-full wp-image-1720" title="fig4" src="http://blog.ez2learn.com/wp-content/uploads/2011/12/fig4.png" alt="" width="456" height="400" /></a></p>
<p>還有很常見的需求就是我們想將資料往某個方向負載平衡地推送，這時PUSH/PULL樣式就派上用場了，PUSH會將負載分散給PULL端，而且只能由PUSH推往PULL</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/12/fig5.png"><img class="aligncenter size-full wp-image-1721" title="fig5" src="http://blog.ez2learn.com/wp-content/uploads/2011/12/fig5.png" alt="" width="456" height="582" /></a></p>
<p>還有另一種樣式是跟一般socket沒兩樣的一對一連線，叫做PAIR</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/12/fig22.png"><img class="aligncenter size-full wp-image-1722" title="fig22" src="http://blog.ez2learn.com/wp-content/uploads/2011/12/fig22.png" alt="" width="244" height="484" /></a></p>
<h2>參在一起做撒尿牛丸</h2>
<p>事實上ZeroMQ還有更高級的樣式，但在本文就不介紹了，接著要大略介紹的是官網的一個例子，將這些樣式組合在一起，官網的例子是說當你需要分散式的Key/Value形式的伺服器，利用ZeroMQ來達成其組合方式就像這樣</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/12/fig69.png"><img class="aligncenter size-full wp-image-1723" title="fig69" src="http://blog.ez2learn.com/wp-content/uploads/2011/12/fig69.png" alt="" width="466" height="372" /></a></p>
<p>伺服器端負責儲存一份快取，且會透過PUB/SUB讓client保持更新，而Client端被更動時，透過PUSH丟給伺服器，如果新的Client上線了，就透過REQ向server要最新的一份快取</p>
<p>這樣就是用ZeroMQ實作分散式系統的一個簡易的實例，事實上它能做遠比這個還要更複雜的東西</p>
<h2>自從用了ZeroMQ，我人也高了，頭也壯了，考試都得一百分呢</h2>
<p>有了ZeroMQ，分散式系統從又難又腦人變成很難而已，因為通訊的部份由ZeroMQ來完成了，剩下的你只要專心來考慮節點之間的拓撲與連接方式、通訊方式，以及訊息的處理，不同語言的支持也讓你不再擔心要用什麼語言來實作，或是你的同事懂不懂某種語言，真正的心力被花費在設計上，分散式系統因此也能更輕易地設計與實作，而ZeroMQ的本意是用於即時地處理大量的金融資料，效率更是ZeroMQ的金字招牌之一，如果你需要複雜且高效的分散式系統，ZeroMQ絕對是你的好朋友</p>
<p>最後，值得一提的是有人基於ZeroMQ的特性設計了與程式語言無關的網頁框架，或著更精確的來說是與伺服器溝通的協定，類似CGI或WSGI那樣，但是透過ZeroMQ因此和語言無關，叫<a href="http://mongrel2.org/">Mongrel</a>，目前出到第二版，有興趣可以研究看看，或許哪天也會寫篇文章來介紹</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2011/12/31/transport-lib-of-new-era-zeromq/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>筆記: MySQL replication 問題 Client requested master to start replication from impossible position 解法</title>
		<link>http://blog.ez2learn.com/2011/11/28/mysql-1236-solution/</link>
		<comments>http://blog.ez2learn.com/2011/11/28/mysql-1236-solution/#comments</comments>
		<pubDate>Mon, 28 Nov 2011 04:36:48 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[筆記]]></category>
		<category><![CDATA[1236]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[note]]></category>
		<category><![CDATA[replication]]></category>
		<category><![CDATA[solution]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1669</guid>
		<description><![CDATA[今天遇到MySQL的master伺服器主機當機的情況，當master復原時，slaves都出現了  Slave I/O: Got fatal error 1236 from master when reading data from binary log: &#8216;Client requested master to start replication from impossible position&#8217;, Error_code: 1236 這樣的錯誤，問題起因很簡單，原本的slave正在pulling的master.bin因為當機的原因不正常關閉，當下次master資料庫終於正常重啟時，master會開另一個bin繼續寫，此時slave重練上後不知道有這件事，繼續向master要求當掉的bin的下一筆資料，就會出現上面的錯誤 只要確定是這種情況，解決方法就是看新的bin編號是到多少 mysql&#62; show master status\G *************************** 1. row *************************** File: master-bin.000073 Position: 21959116 &#8230; <a href="http://blog.ez2learn.com/2011/11/28/mysql-1236-solution/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>今天遇到MySQL的master伺服器主機當機的情況，當master復原時，slaves都出現了</p>
<blockquote><p> Slave I/O: Got fatal error 1236 from master when reading data from binary log: &#8216;Client requested master to start replication from impossible position&#8217;, Error_code: 1236</p></blockquote>
<p>這樣的錯誤，問題起因很簡單，原本的slave正在pulling的master.bin因為當機的原因不正常關閉，當下次master資料庫終於正常重啟時，master會開另一個bin繼續寫，此時slave重練上後不知道有這件事，繼續向master要求當掉的bin的下一筆資料，就會出現上面的錯誤</p>
<p>只要確定是這種情況，解決方法就是看新的bin編號是到多少</p>
<pre>mysql&gt; show master status\G
*************************** 1. row ***************************
            File: master-bin.000073
        Position: 21959116
    Binlog_Do_DB:
Binlog_Ignore_DB:
1 row in set (0.00 sec)</pre>
<p>把file和位置指向下一個新開的檔案</p>
<pre>slave stop;
CHANGE MASTER TO
    MASTER_LOG_FILE='master-bin.000073',
    MASTER_LOG_POS=0
;
slave start;
show slave status\G</pre>
<p>如此一來就解決了</p>
<p>參考資料 <a href="http://firelitdesign.blogspot.com/2011/04/how-to-fix-mysql-replication-error-1236.html">http://firelitdesign.blogspot.com/2011/04/how-to-fix-mysql-replication-error-1236.html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2011/11/28/mysql-1236-solution/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>那些台灣軟體產業所缺少的 &#8211; 開放源始碼</title>
		<link>http://blog.ez2learn.com/2011/11/25/taiwan-software-lacking-of-open-source/</link>
		<comments>http://blog.ez2learn.com/2011/11/25/taiwan-software-lacking-of-open-source/#comments</comments>
		<pubDate>Fri, 25 Nov 2011 15:43:19 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[中文文章]]></category>
		<category><![CDATA[作品]]></category>
		<category><![CDATA[嘴砲]]></category>
		<category><![CDATA[開源]]></category>
		<category><![CDATA[License]]></category>
		<category><![CDATA[Licensing]]></category>
		<category><![CDATA[Open source]]></category>
		<category><![CDATA[授權]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1644</guid>
		<description><![CDATA[前面幾篇談到了台灣軟體產業界常見的毛病，除了工具以外，還有一項令我感到相當意外的，就是我發現台灣業界對於開放源始碼的認知真的很有問題，例如我曾有和別人討論過，跟他們你們可以使用open source的現成資源來減低成本，但是得到的回應很常是 那不是讓你用但之後就要付錢嗎? 從這類的回應就可以大略知道，其實有很多人對於開放源始碼都有一些錯誤的認知，到底什麼時候該付錢、什麼情況可以使用都搞不清楚，因此這回我大算介紹一下一些常見的開源授權的常識 免責聲明 在讀本文前我得先聲明，我不是律師，這不是提供專業的法律見解，只是試著用較易懂的方式解釋授權，以我自己的經驗來說明，其中多少可能會有錯誤，請自行判斷，也歡迎指出錯誤，如有需要請洽詢專業的法律諮詢，在本文末會提到 認識授權 (License) 首先要從授權(License)的概念開始談起，開放源始碼通常不是只是單純把程式碼公開出來，而是一般都會搭配某種授權，而授權的意思，以白話來說，就是寫了一份聲明，裡面這樣提到 此程式任何人可以免費使用，但是使用前你必需遵守以下條款&#8230;. 有了這樣的聲明，使用開源的人就可以放心使用，當然前提是要遵守授權所提出的條款，基本上因為已經授權出來，所以就算是原作者反悔，也沒辦法控告你什麼，除非你違反他當初訂出來的條款，而一般人看見落落長的條款項目可能就怕了，更何況是用英文寫的，但是別擔心，事實上要注意的要點只有幾樣，都大同小異，同一類條款的性質都很類似 散佈(Redistribute) 在理解授權之前，首先要理解散佈，這是授權裡面一定會提到的重要關鍵動作，那麼什麼是散佈呢？ 簡單的來說，就是將軟體轉交給其它人，不管你是以原始碼的形式，或是編成二進制執行檔後，只要是轉交給其它法人，就算是散佈，舉個例子 把原始碼上傳供人下載 把原始碼拿來販售 把原始碼編成執行檔供人下載 把原始碼編成執行檔販售 以上都算是散佈的行為，所有的授權條款裡面都會提到散佈開源程式時你應盡的義務，當然，也有很多行為是稱不上散佈的，例如 將原始碼交給公司內部某個單位 將原始碼編譯成執行檔自己使用 在伺服器上以開源程式執行提功服務給使用者 像這樣沒有法人的經手，都不算是散佈的行為，對於散佈的行為介定是很重要的，等一下會解釋 授權(License)的種類 授權有(License)非常多種，我們在此大略將其分成三大類，第一類是GPL，第二類為BSD，而第三類為商業授權，是較為特別且少見的，其中GPL最不自由，而BSD最自由 &#160; 在這裡自由與不自由主要是指你在使用這些開源軟體時所要盡的義務的多和少 GPL GNU General Public License主要是由Linux陣營的開源軟體開發者為主在使用的，它有幾個特色 散佈要連修改的部份一起開源 病毒的感染性 排它性 散佈與修改 如同我們前面提到的散佈，最重要的重點是，如果你改了程式，而又要散佈程式，那麼你在散佈的同時也要把你修改的部份也公開出來，例如 修改了原始碼後拿來販售 修改了原始碼後編成執行檔供別人使用 上列行為都扯到了散佈，因此如果你程式有修改，你不能只給別人執行檔，要連改的部份一起開源出來，這條款的目的主要是在於GNU的社群，希望強迫使用者能回饋社群，因為一但你改了程式，想拿來賣錢，就得公開出來，避免有人改進了程式，拿來販售，但沒有公開程式的問題 &#8230; <a href="http://blog.ez2learn.com/2011/11/25/taiwan-software-lacking-of-open-source/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>前面幾篇談到了台灣軟體產業界常見的毛病，除了工具以外，還有一項令我感到相當意外的，就是我發現台灣業界對於開放源始碼的認知真的很有問題，例如我曾有和別人討論過，跟他們你們可以使用open source的現成資源來減低成本，但是得到的回應很常是</p>
<blockquote><p>那不是讓你用但之後就要付錢嗎?</p></blockquote>
<p>從這類的回應就可以大略知道，其實有很多人對於開放源始碼都有一些錯誤的認知，到底什麼時候該付錢、什麼情況可以使用都搞不清楚，因此這回我大算介紹一下一些常見的開源授權的常識</p>
<h2>免責聲明</h2>
<p>在讀本文前我得先聲明，我不是律師，這不是提供專業的法律見解，只是試著用較易懂的方式解釋授權，以我自己的經驗來說明，其中多少可能會有錯誤，請自行判斷，也歡迎指出錯誤，如有需要請洽詢專業的法律諮詢，在本文末會提到</p>
<h2>認識授權 (License)</h2>
<p>首先要從授權(License)的概念開始談起，開放源始碼通常不是只是單純把程式碼公開出來，而是一般都會搭配某種授權，而授權的意思，以白話來說，就是寫了一份聲明，裡面這樣提到</p>
<blockquote><p>此程式任何人可以免費使用，但是使用前你必需遵守以下條款&#8230;.</p></blockquote>
<p>有了這樣的聲明，使用開源的人就可以放心使用，當然前提是要遵守授權所提出的條款，基本上因為已經授權出來，所以就算是原作者反悔，也沒辦法控告你什麼，除非你違反他當初訂出來的條款，而一般人看見落落長的條款項目可能就怕了，更何況是用英文寫的，但是別擔心，事實上要注意的要點只有幾樣，都大同小異，同一類條款的性質都很類似</p>
<h2>散佈(Redistribute)</h2>
<p>在理解授權之前，首先要理解散佈，這是授權裡面一定會提到的重要關鍵動作，那麼什麼是散佈呢？ 簡單的來說，就是將軟體轉交給其它人，不管你是以原始碼的形式，或是編成二進制執行檔後，只要是轉交給其它法人，就算是散佈，舉個例子</p>
<blockquote><p>把原始碼上傳供人下載</p>
<p>把原始碼拿來販售</p>
<p>把原始碼編成執行檔供人下載</p>
<p>把原始碼編成執行檔販售</p></blockquote>
<p>以上都算是散佈的行為，所有的授權條款裡面都會提到散佈開源程式時你應盡的義務，當然，也有很多行為是稱不上散佈的，例如</p>
<blockquote><p>將原始碼交給公司內部某個單位</p>
<p>將原始碼編譯成執行檔自己使用</p>
<p>在伺服器上以開源程式執行提功服務給使用者</p></blockquote>
<p>像這樣沒有法人的經手，都不算是散佈的行為，對於散佈的行為介定是很重要的，等一下會解釋</p>
<h2>授權(License)的種類</h2>
<p>授權有(License)非常多種，我們在此大略將其分成三大類，第一類是GPL，第二類為BSD，而第三類為商業授權，是較為特別且少見的，其中GPL最不自由，而BSD最自由</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/11/licensing.png"><img class="aligncenter size-full wp-image-1651" title="licensing" src="http://blog.ez2learn.com/wp-content/uploads/2011/11/licensing.png" alt="" width="466" height="281" /></a></p>
<p>&nbsp;</p>
<p>在這裡自由與不自由主要是指你在使用這些開源軟體時所要盡的義務的多和少</p>
<h2>GPL</h2>
<p>GNU General Public License主要是由Linux陣營的開源軟體開發者為主在使用的，它有幾個特色</p>
<ul>
<li>散佈要連修改的部份一起開源</li>
<li>病毒的感染性</li>
<li>排它性</li>
</ul>
<h3>散佈與修改</h3>
<p>如同我們前面提到的散佈，最重要的重點是，如果你改了程式，而又要散佈程式，那麼你在散佈的同時也要把你修改的部份也公開出來，例如</p>
<blockquote><p>修改了原始碼後拿來販售</p>
<p>修改了原始碼後編成執行檔供別人使用</p></blockquote>
<p>上列行為都扯到了散佈，因此如果你程式有修改，你不能只給別人執行檔，要連改的部份一起開源出來，這條款的目的主要是在於GNU的社群，希望強迫使用者能回饋社群，因為一但你改了程式，想拿來賣錢，就得公開出來，避免有人改進了程式，拿來販售，但沒有公開程式的問題</p>
<p>當然，如果你的程式只是自己使用，或是公司內部使用，那麼你修改了程式但因為沒有散佈的問題，所以修改的部份也不用因此公開，還有一種情況是，你使用GPL授權的程式放在伺服器上提供服務，因為這過程沒有重新散佈，所以也不會有問題</p>
<p>除了修改以外，還有一個特性，就是病毒的感染性，除了修改程式，如果GPL的原始碼是函式庫，而你的程式連結了GPL授權的程式，不管是靜態連結或動態連結，都會因此被GPL感染，一但你要散佈你的程式，因為用了GPL的函式庫，因此你的主程式也被感染，變成你要把你的主程式一起開源出來，就因為這特性替GPL贏得了「病毒授權」的美名，就跟T病毒一樣，被感染了就會變成殭屍</p>
<p>就因為這樣病毒的特性，讓很多人又愛又恨，很多商業軟體想用某些開源的函式庫，但因為那些函式庫如果是GPL授權，會導至他們的產品本身也受到感染，而因此無法使用，變成非GPL和GPL這兩種可能性，除此之外，有些廠商為了避免被GPL感染，會用一些比較特別的手法來避開</p>
<p>因為這樣感染的特性，加上如此不自由的特質，使得很多可以用上GPL的場合卻因為感染性而無法達成，為了能解決感染的問題，它有推出另一種弱化版的GPL，叫LGPL (Less Generic Public Licence)，這個授權大致上和GPL是一樣的，差別就在於上面提到的連結受感染的問題，連結LGPL的函式庫並不會受到感染，如此一來就算是商業軟體也能安心地使用LGPL的函式庫</p>
<p>接著還有它的排它性，因為授權有很多種，GPL規定它的授權條款不能被修改，這表示你修改了程式要散佈被強迫要開源的話，你也只能選擇GPL的授權，除非你是原作者</p>
<p>你覺得GPL很不自由嗎? 事實上他們覺得GPL還不夠嚴格，正因為GPL陣營的人認為像Google之流的廠商，用了GPL的程式提供服務，不用公佈修改的部份，因此覺得心癢癢的，為此甚至增加了AGPL，更加嚴格的GPL，他的重新散佈的定義，擴增為包括提供服務，因此即使你用了AGPL的程式提功服務，沒有轉交程式給他人，就AGPL的定義，這就算是重新散佈，然而使用AGPL授權的程式其實非常少，像是MongoDB就是使用AGPL授權，正因為LGPL/GPL/AGPL強烈的限制特性，反而使它成為商業軟件開源的最愛授權，對手要修改販授的話也得公開，不想公開的話就得買另外的授權，這就是常見的雙授權商業模式，因此我個人喜歡戲稱GPL為 "商業友善授權"</p>
<h2>類BSD</h2>
<p>如果說GPL是邪惡的病毒授權，那麼類BSD就是自由又開放的授權，相較於GPL相當害怕別人用了GPL的程式不回饋，類BSD就大方許多，它最主要的條款就是，當你散佈修改過的類BSD授權下的程式，一樣不管是二進制的執行檔或原始碼，你要盡的義務就只有記得要把類BSD的授權一起轉交給別人就可以了，包含原作的姓名你也得一併加進去，不能自行亂改</p>
<p>舉個例子，你改了一個BSD授權的應用程式，你想編譯好成執行檔放到網路上供人下載，可以，只要連著當初的BSD授權一起散佈即可，不必把你改的部份也公開出來，因此你可以安心的用BSD授權的函式庫來寫商業軟體</p>
<p>下面的例子都是不違反BSD授權的做法</p>
<blockquote><p>修改BSD授權的程式編譯成執行檔來賣，只提供執行檔而非原始碼給使用者，原始的BSD授權條款也得一併給使用者</p>
<p>連結BSD授權的函式庫，主程式只提供執行檔進行販售，原始的BSD授權條款也得交給使用者</p></blockquote>
<p>而下面這些例子可能會違反BSD授權</p>
<blockquote><p>重新散佈BSD授權的程式，竄改並宣稱自己才是原作者</p>
<p>散佈BSD授權的程式，但不附帶BSD授權條款</p></blockquote>
<p>那你可能會問附帶BSD授權條款是怎樣辦到，很簡單，通常都只是一個LICENSE.txt檔案夾在壓縮檔裡之類的即可</p>
<p>而類BSD在此只是通稱，因為有很多授權都有這類主要的特性，大約列出常見的，像是</p>
<ul>
<li>MIT</li>
<li>Apache</li>
<li>zlib</li>
</ul>
<p><span><span class="Apple-style-span" style="line-height: 24px;">其中zlib最為自由，只要求不能亂改作者，不能聲稱修改的版本才是原始版本，以及不能移除授權，也就是散佈時一樣要附帶zlib授權，除此之外還有很多非常多種的授權，這只是常見的幾種，只要你看見他有寫 "BSD-like"，通常就是指它的特色跟上面描述的差不多，有些專案會特別量身訂作他們自己的授權，但精神大多都會跟這些主流授權差不多</span></span></p>
<h2>商業授權和其它授權</h2>
<p>除了類BSD和GPL，其實還有很多授權，是一些基於商業考量定出來的授權，通常較為少見，所以不多做討論</p>
<h2>常見的謎思 &#8211; MySQL要錢嗎?</h2>
<p>前一陣子常見到很多台灣資訊業界的人瘋狂轉貼MySQL要開始收費的消息，如果懂得上面提到的授權條款就會覺得這很好笑，以MySQL的例子來說，它的程式碼已經用GPL的形式開放出來了，就像潑出來的水收不回去了，即使他們想收錢都沒辦法了，MySQL官網上的授權費用，還有授權說明，都說得不清不楚，即使你打過去問他們到底什麼情況要收錢，他們也只會給你閃爍不清的說法，其目的就只是要騙不懂開源授權的笨蛋去買他們的商用授權，如果你的公司真的有過多預算當做贊助他們是不錯，但都已經被Sun之後又被甲骨文買下來事實上他們也不缺錢了</p>
<p>有興趣可以參考<a href="http://antbsd.twbbs.org/~ant/wordpress/?p=2259"> 探討 MySQL 授權</a></p>
<h2>常見的謎思 &#8211; 開源軟體找不到商業支援</h2>
<p>很多人也有個想法就是開源軟體找不到商業支援，但事實上這是錯的，正因為開源軟體免費的特質，它很多的商業模式其實都是在提供商業支援，例如 <a href="http://www.percona.com/">Percona</a>，他們提功的是強化版的MySQL InnoDB引擎，因為MySQL的GPL授權，導致他們修改的部份也得開源，因此他們當然不可能靠賣軟體獲利，而是靠商業諮詢和技術支援、專業訊練等等項目</p>
<p>因此，事實上很多開源軟體的商業資源遠比商業軟體還來得豐富，因為商業軟體的支援就只有他們提供，他們不幫你你就沒折，然而開源軟體因為是開放的，反而很多人可以解決同樣的問題，最糟的情況不過是自己下海解決而已，至少還不是商業軟體的無解</p>
<p>關於開源軟體的商業模式其實是一個很有趣的議題，有空的話可以另外寫一篇來介紹</p>
<h2>免費的開源授權諮詢</h2>
<p>好吧，講了這麼多，如果你問我你們公司有X專案，要用到Y授權的Z套件是否可行? 請別問我，請問中研院的<a href="http://www.openfoundry.org/">自由軟體鑄造場</a>的<a href="http://www.openfoundry.org/legal-consulting-services">免費開源法律問題諮詢</a>吧，有免錢的程式可以用就算了，還有免錢的諮詢可以問，好諮詢，不問嗎?</p>
<p>除此之外還可以參考他們比我更專業一百倍的<a href="http://www.openfoundry.org/tw/law-and-licensing">法律源地</a>部落格</p>
<h2>小結</h2>
<p>常常看見很多單位都在花大錢買商用軟體，但是一看卻發現這些功能遠比不用錢的開源軟體還差，像是以資料庫為例，就有看見某間學校買了某家公司的資料庫，限制連線人數，功能殘缺不齊，連稱得上現代的資料庫都有困難，然而授權都動不動數十萬數百萬，就覺得很好笑，有免錢的不用，要花大錢去買垃圾來用，使用開源的軟體，並不只是省錢而已，用現成的函式庫與軟體兜出需要的系統更是提升競爭力與減少開發成本的關鍵，台灣如果不能掌握開源的相關知識會比現在更加落後世界的腳步</p>
<p>而授權雖然條款很多看起來很嚇人，實際理解了之後其實也不會太難懂，陣營也很清楚，如果要比喻的話，GPL陣營認為人性本惡，把大家都當成是會開發後就不想把東西公開，每人都是自私的，所以條款強制性很重，而BSD則是相反，要不要開放是你自己的決定，而我認為如果要開源就不怕別人用，怕別人用的話就別開源</p>
<p>最後，我們這裡談到的只是從使用層面來看開源授權而已，台灣大多廠商目前的狀況是連使用上的認知都有困難，更別說是貢獻開源，以後有機會再來寫一些相關的文章</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2011/11/25/taiwan-software-lacking-of-open-source/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>那些台灣軟體產業所缺少的 &#8211; 自動化測試</title>
		<link>http://blog.ez2learn.com/2011/10/20/taiwan-software-lacking-of-auto-testing/</link>
		<comments>http://blog.ez2learn.com/2011/10/20/taiwan-software-lacking-of-auto-testing/#comments</comments>
		<pubDate>Thu, 20 Oct 2011 13:15:23 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[中文文章]]></category>
		<category><![CDATA[分享]]></category>
		<category><![CDATA[嘴砲]]></category>
		<category><![CDATA[資訊安全]]></category>
		<category><![CDATA[單元測試]]></category>
		<category><![CDATA[軟體工程]]></category>
		<category><![CDATA[軟體品質]]></category>
		<category><![CDATA[軟體測試]]></category>
		<category><![CDATA[自動化測試]]></category>
		<category><![CDATA[Jenkins]]></category>
		<category><![CDATA[整合測試]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1619</guid>
		<description><![CDATA[你是否有計算過，你在寫專案的過程中，測試過了多少次的程式? 我想是沒有，我也沒有，但是你是否有曾想過，或是感覺過，隨著專案的膨漲，你要測試的項目也跟著變多了? 這是理所當然的事情，當專案小，測試還算很輕鬆，因為程式的功能不外乎就那幾樣，一轉眼就測完了，常見的寫程式流程會像這樣 撰寫新功能 測試新功能 當然，也有修正bug的情況 修正bug 測試bug 如此一直循環，當你寫了新功能，理所當然地會去測試新功能，看是否如你預期地執行，那舊功能呢? 或許你記憶力不錯，在寫新功能的同時，想到先前某個舊功能是依賴現在改的東西，這麼一改可能會造成舊的功能出問題，於是你也順便測了一下舊的功能，當程式還小 撰寫新功能 測試新功能 測試舊功能 嘿，不怎麼樣吧? 只佔了開發時間的三分之一，好吧那如果有更多的舊功能要測呢? 撰寫新功能 測試新功能 測試舊功能 測試舊功能 測試舊功能 &#8230;. 發現了沒有? 隨著你的專案越來越大，如果要確保整個系統所有的功能都是正常運作的，無可避免地，在你修改程式之後要測試的項目會越來越多 這表示你每寫一行新程式的成本增加了，身為以減低成本為傲的島國 國民: 台灣人&#8230;，你說，簡單! 不要測舊功能不就好了? 是的，我想這可能就是最常見的情況，不要測試舊功能理所當然地，每寫一行的程式成本都保持一樣很低，但這代表著舊程式可能出錯的風險也跟著增加了，當你喜滋滋地覺得你幫公司省了成本，結果在一個月後因為舊程式缺乏測試，因改動了核心的部份造成舊的功能將所有資料外洩，公司損失慘重，這就是不重視軟體品質的後果 舉真實生活上發生過的例子，PTT曾經有過改程式未經好好地測試，造成每個人都能以管理員的權限登入的事情，知名的檔案同步平台Dropbox，也曾經發生過因為認證的程式改版有bug，造成任何人都可以登入別人帳號的事，我也有曾聽聞一些網站因為工程師為了測試方便，把認證的函數暫時改成 function authenticate&#40;user_id, password&#41; &#123; return true; // do authentication here // &#8230; <a href="http://blog.ez2learn.com/2011/10/20/taiwan-software-lacking-of-auto-testing/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>你是否有計算過，你在寫專案的過程中，測試過了多少次的程式? 我想是沒有，我也沒有，但是你是否有曾想過，或是感覺過，隨著專案的膨漲，你要測試的項目也跟著變多了? 這是理所當然的事情，當專案小，測試還算很輕鬆，因為程式的功能不外乎就那幾樣，一轉眼就測完了，常見的寫程式流程會像這樣</p>
<blockquote><p>撰寫新功能</p>
<p>測試新功能</p></blockquote>
<p>當然，也有修正bug的情況</p>
<blockquote><p>修正bug</p>
<p>測試bug</p></blockquote>
<p>如此一直循環，當你寫了新功能，理所當然地會去測試新功能，看是否如你預期地執行，那舊功能呢? 或許你記憶力不錯，在寫新功能的同時，想到先前某個舊功能是依賴現在改的東西，這麼一改可能會造成舊的功能出問題，於是你也順便測了一下舊的功能，當程式還小</p>
<blockquote><p>撰寫新功能</p>
<p>測試新功能</p>
<p>測試舊功能</p></blockquote>
<p>嘿，不怎麼樣吧? 只佔了開發時間的三分之一，好吧那如果有更多的舊功能要測呢?</p>
<blockquote><p>撰寫新功能</p>
<p>測試新功能</p>
<p>測試舊功能</p>
<p>測試舊功能</p>
<p>測試舊功能</p>
<p>&#8230;.</p></blockquote>
<p>發現了沒有? 隨著你的專案越來越大，如果要確保整個系統所有的功能都是正常運作的，無可避免地，在你修改程式之後要測試的項目會越來越多</p>
<p>這表示你每寫一行新程式的成本增加了，身為以減低成本為傲的島國 國民: 台灣人&#8230;，你說，簡單! 不要測舊功能不就好了? 是的，我想這可能就是最常見的情況，不要測試舊功能理所當然地，每寫一行的程式成本都保持一樣很低，但這代表著舊程式可能出錯的風險也跟著增加了，當你喜滋滋地覺得你幫公司省了成本，結果在一個月後因為舊程式缺乏測試，因改動了核心的部份造成舊的功能將所有資料外洩，公司損失慘重，這就是不重視軟體品質的後果</p>
<p>舉真實生活上發生過的例子，PTT曾經有過改程式未經好好地測試，造成每個人都能以管理員的權限登入的事情，知名的檔案同步平台Dropbox，也曾經<a href="http://blog.dropbox.com/?p=821">發生過因為認證的程式改版有bug，造成任何人都可以登入別人帳號的事</a>，我也有曾聽聞一些網站因為工程師為了測試方便，把認證的函數暫時改成</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">function authenticate<span style="color: black;">&#40;</span>user_id, password<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> true<span style="color: #66cc66;">;</span>
        // do authentication here
        // ....
<span style="color: black;">&#125;</span></pre></div></div>

<p>然後又不小心commit，因此讓任何人都通過認證的事情，但這些都不能只怪工程師本身，誰能無過? 人總會犯錯的，問題出問於工程本身的制度、專案的管理、和工具的使用上</p>
<p>在未來，網路的應用越來越多，而軟體的品質重要程度只會越來越高，所以，要如何維持軟體的品質又同時能不讓測試的成本隨著專案的擴張而跟著無限制地成長呢? 答案就是 &#8211; 自動化測試</p>
<h2>自動化測試</h2>
<p>自動化測試聽起來好像很美妙，讓電腦自動幫你測試程式? 有這麼好的事情嗎? 事實上不是那樣，自動化測試，是透過寫好的規則，自動對於程式進行測試，所以終究還是得需要人力的介入，那你或許會問，結果倒頭來還不是得用人力? 那到底有什麼好處? 答案就跟我們先前提到的一樣，如果你的專案很小，用人力測試其實可能就已足夠，但當你的專案夠大，如果沒有自動化測試，那麼光是在測舊的程式就是相當龐大的成本上</p>
<p>引入了自動化測試不代表程式就不會出錯，它不是萬能的，但是它至少保證了程式一定的品質，只要使用得當，就能降低測試的成本，也能讓大部份有經過自動測試的程式都不會出現太離譜的錯誤，至於要怎麼做，讓我們看下去</p>
<h2>單元測試</h2>
<p>最常見的測試，就是單元測試(Unit test)，通常是針對單一個或是少數類別，確保這些類別單獨運作是正確的，舉個例子，你寫了一個類別，是用來找輸入的地圖的最短路徑，那麼你就得替這類別，寫一個單元測試，餵入你準備好的資料，然後取得輸出的結果，看是否和你準備的預期答案是一樣的，舉一個最簡單的例子，一個用Python來將輸入文字拆解成一行一行的解析器</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> LineParser<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, newline=<span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>'</span>, remain=<span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">newline</span> = newline
        <span style="color: #008000;">self</span>._buffer = <span style="color: black;">&#91;</span>remain<span style="color: black;">&#93;</span>
        <span style="color: #008000;">self</span>._size = <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>remain<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> feed<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, data<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>._buffer.<span style="color: black;">append</span><span style="color: black;">&#40;</span>data<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>._size += <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>data<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> getLine<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        data = <span style="color: #483d8b;">''</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>._buffer<span style="color: black;">&#41;</span>
        index = data.<span style="color: black;">find</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">newline</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> index <span style="color: #66cc66;">!</span>= -<span style="color: #ff4500;">1</span>:
            line = data<span style="color: black;">&#91;</span>:index<span style="color: black;">&#93;</span>
            <span style="color: #008000;">self</span>._buffer = <span style="color: black;">&#91;</span>data<span style="color: black;">&#91;</span>index + <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">newline</span><span style="color: black;">&#41;</span>:<span style="color: black;">&#93;</span><span style="color: black;">&#93;</span>
            <span style="color: #008000;">self</span>.<span style="color: black;">length</span> = <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>._buffer<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">return</span> line
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> iterLines<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        line = <span style="color: #008000;">self</span>.<span style="color: black;">getLine</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">while</span> line <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">None</span>:
            <span style="color: #ff7700;font-weight:bold;">yield</span> line
            line = <span style="color: #008000;">self</span>.<span style="color: black;">getLine</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>它的單元測試就長這樣</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">unittest</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> TestLineParser<span style="color: black;">&#40;</span><span style="color: #dc143c;">unittest</span>.<span style="color: black;">TestCase</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> makeOne<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> LineParser<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> testParser<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        p = <span style="color: #008000;">self</span>.<span style="color: black;">makeOne</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
        p.<span style="color: black;">feed</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'abc'</span><span style="color: black;">&#41;</span>
        line = p.<span style="color: black;">getLine</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>line, <span style="color: #008000;">None</span><span style="color: black;">&#41;</span>
&nbsp;
        p.<span style="color: black;">feed</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>'</span><span style="color: black;">&#41;</span>
        line = p.<span style="color: black;">getLine</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>line, <span style="color: #483d8b;">'abc'</span><span style="color: black;">&#41;</span>
        line = p.<span style="color: black;">getLine</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>line, <span style="color: #008000;">None</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># write lots line</span>
        p.<span style="color: black;">feed</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'111<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>222<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>3333'</span><span style="color: black;">&#41;</span>
        lines = <span style="color: #008000;">list</span><span style="color: black;">&#40;</span>p.<span style="color: black;">iterLines</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'111'</span>, <span style="color: #483d8b;">'222'</span><span style="color: black;">&#93;</span>, lines<span style="color: black;">&#41;</span>
        line = p.<span style="color: black;">getLine</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>line, <span style="color: #008000;">None</span><span style="color: black;">&#41;</span>
&nbsp;
        p.<span style="color: black;">feed</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>text'</span><span style="color: black;">&#41;</span>
        line = p.<span style="color: black;">getLine</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>line, <span style="color: #483d8b;">'3333'</span><span style="color: black;">&#41;</span>
        line = p.<span style="color: black;">getLine</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>line, <span style="color: #008000;">None</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># write nothing</span>
        p.<span style="color: black;">feed</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span>
        line = p.<span style="color: black;">getLine</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>line, <span style="color: #008000;">None</span><span style="color: black;">&#41;</span>
&nbsp;
        p.<span style="color: black;">feed</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>'</span><span style="color: black;">&#41;</span>
        lines = <span style="color: #008000;">list</span><span style="color: black;">&#40;</span>p.<span style="color: black;">iterLines</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>lines, <span style="color: black;">&#91;</span><span style="color: #483d8b;">'text'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span><span style="color: #008000;">list</span><span style="color: black;">&#40;</span>p.<span style="color: black;">iterLines</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> suite<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    suite = <span style="color: #dc143c;">unittest</span>.<span style="color: black;">TestSuite</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    suite.<span style="color: black;">addTest</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">unittest</span>.<span style="color: black;">makeSuite</span><span style="color: black;">&#40;</span>TestLineParser<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> suite
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
    <span style="color: #dc143c;">unittest</span>.<span style="color: black;">main</span><span style="color: black;">&#40;</span>defaultTest=<span style="color: #483d8b;">'suite'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>很簡單的想法就是列出幾種常見的case，還有你能想到的特例代進去，好的測試資料要能夠測到每一行程式，但是要做到那樣需要花不少心力，其實能夠做到大部份常見的情況和常見的特例，就已經相當足夠</p>
<h2>整合測試</h2>
<p>有些程式，無可避免地會依賴其它程式，如果我們針對這兩個程式同時測試，會無法分出出錯到底是誰的錯，再者，很多依賴的部份可能會牽扯到IO或是其它系統資源，讓測試變得更複雜，例如有個類別是負責輸出文件到印表機的，那你要如何確認印表機印出來的東西是正確的? 答案就是做一個假的 (Mocking)印表機丟給那個類別去做列印的動作，再去讀取裡面的資料，確認跟你預期的一樣</p>
<p>雖然單元測試在相當單純的模擬環境下測過了我們的程式，然而世界並不是那樣的美好，總有些事情沒有經過真槍實彈操演過可能會有差錯，因此有時我們會引入部份受控制的真實環境來測試，例如你想測試網路連線，或許你可以寫一段script在Amazon EC2上建起幾個instance，並上傳程式到那些機器中，自動讓他們連線來確保這些功能是正常的，然而越真實的環境變因就越多，因此測試也就相對困難</p>
<h2>每日建構的好幫手 &#8211; Jenkins</h2>
<p>Joel有說過 <a href="http://chinese.joelonsoftware.com/Articles/DailyBuildsAreYourFriend.html">每日建構是你的朋友</a> (<a href="http://www.joelonsoftware.com/articles/fog0000000023.html">Daily build is your friend</a>)，也有提過 <a href="http://chinese.joelonsoftware.com/Articles/TheJoelTest.html">軟體開發成功的12個法則</a> (<a href="http://www.joelonsoftware.com/articles/fog0000000043.html">The Joel Test: 12 Steps to Better Code</a>)，裡面的daily build是指利用工具每天自動建構整個專案，通常對於編譯式的語言，如C語言寫的大型專案會較需要這類的工具，但是這樣的工具還有一個目的，在於確保程式是可以正常編譯的，並且讓測試員容易拿到最新的程式進行測試，然而自動化測試，同樣的也需要類似的工具，因為通常你在改完程式就會進行測試，那每次一改完程式就得跑一次測試指令，這不是一件很煩的事情嗎?</p>
<p>記得，工具是為我們服務的，不是我們為工具服務，這樣重複的瑣事理所當然也是由工具來幫忙，謝天謝地現今有好用又免費的工具，可以幫你做到這點，那就是<a href="http://jenkins-ci.org/">Jenkins</a>，它是一套基於網頁的自動化測試管理工具，它可以做到什麼呢? 它可以做到幫你定時去版本控制系統取程式回來，用預先設定好的流程進行測試，並且記錄測試的結果，如果有某個測試出錯了，當然也可以發Email通知你，以Now.in的開發為例，因為專案為數眾多，其中又有依賴關係，有了Jenkins的幫忙，程式只要一改送到BitBucket，它就會自動進行測試</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/10/jenkins.png"><img class="aligncenter size-full wp-image-1634" title="jenkins" src="http://blog.ez2learn.com/wp-content/uploads/2011/10/jenkins.png" alt="" width="746" height="470" /></a>如此一來就省下了大量的時間，同時，也可以專心在於開發上</p>
<p>Jenkins除了功能強大以外，他還有一項特色令我驚訝，就是非常簡單易用，從安裝到設定完所有的測試，除了clone hg檔案庫和設置測試環境以外，我從沒因為Jenkins打過一行指令，全部都可以透過它友善的網頁介面完成，同時它也有內建資料庫，也沒因此設定MySQL，在Windows下安裝更是容易，一個安裝檔執行完就是安裝完成，如果你希望有工具幫你自動定時測試或是建構，請不要懷疑，Jenkins是你最佳選擇</p>
<h2>部署前的自動化測試</h2>
<p>執行自動化測試的時機，除了剛改完程式，還有一個重要的時機，那就是在你把程式部署到伺服器以前，讓你的自動化部署的script先跑過一次自動測試，確認測試通過了再進行部署，為什麼要這樣做呢? 還記得先前提到的PTT和Dropbox以及一些網站對於authentication的return true慘劇嗎? 為了不讓那種事情發生，或著至少讓機會降低，在deploy前讓自動化測試跑過一次，確保測試的範圍內都是正確的，可以大大降低那種情況發生的機會，除此之外，也比較不會因為改出bug，自己沒發現，等到使用者來抱怨了才知道問題在哪</p>
<h2>測試的幾項重點</h2>
<p>自動化測試雖然是一項利器，但是得經過正確的使用才會有好的效果，自動化測試有所謂的覆蓋率，也就是你的程式裡以行為單位，有多少行是在跑測試時有執行過的? 這些工具都可以幫你統計出來，但是切記</p>
<blockquote><p><strong>不要為了追求高測試覆蓋率，替foo bar寫測試</strong></p></blockquote>
<p>這只是在浪費時間，如果某段程式已經簡單到沒測試的必要，你寫了也是多餘</p>
<p>除此之外，寫測試事實上也是成本，因此如果時間有限，請</p>
<blockquote><p><strong>優先針對重要的核心、資料模型、商業邏輯測試</strong></p></blockquote>
<p>因為就算你測再多無關緊要的程式，最重要的核心出錯了，可能整個系統就完蛋了，所以盡量以重要的程式做為測試的優先考量</p>
<blockquote><p><strong>優先針對安全性相關、存取權限、身份認證、常見攻擊手法測試</strong></p></blockquote>
<p>雖然身份認證這種事情算不上是核心，但這關係到你的系統會不會被輕易地攻擊，除此之外，如果你的程式是網站應用程式，SQL Injection、XSS、buffer overflow這類攻擊也會很常發生，因此，你也需要優先自行設計一些攻擊，針對這些常見的問題餵一些資料，雖然這無法保證一定不會犯錯，至少確保不會發生太低等級的錯誤，因為常見的case都已經有自動測試過了，搭配先前所提到的，deploy前跑過一次測試，如此一來就能將犯錯的機會降低許多</p>
<p>雖然你的程式可能大多都已經有自動化測試在幫你測試，但即使如此，你還是會發現新的bug，如果說，你直接改了bug，就這樣了事，很有可能在下幾次改版bug又回來了，因此</p>
<blockquote><p><strong>每當你發現你先前沒想到的bug，請加到你的測試中</strong></p></blockquote>
<p>如此一來，隨著你針對的bug測試case越多，你的程式品質就越高，未發現的bug也會越少，在未來確保這些bug不會再出現</p>
<h2>最後</h2>
<p>再一次，自動化測試不是萬能的，除此之外也需要正確的運用，如果台灣軟體業界能夠好好運用自動化測試，軟體的品質可以有所提升，開發者也不會因為除錯除到死加班到天亮，雖然寫測試是額外的負擔，但是對於大形專案長期看來是非常值得的投資</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2011/10/20/taiwan-software-lacking-of-auto-testing/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>那些台灣軟體產業所缺少的 &#8211; 版本控制系統</title>
		<link>http://blog.ez2learn.com/2011/10/20/taiwan-software-lacking-of-vcs/</link>
		<comments>http://blog.ez2learn.com/2011/10/20/taiwan-software-lacking-of-vcs/#comments</comments>
		<pubDate>Thu, 20 Oct 2011 04:02:15 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[中文文章]]></category>
		<category><![CDATA[分享]]></category>
		<category><![CDATA[嘴砲]]></category>
		<category><![CDATA[版本控制]]></category>
		<category><![CDATA[軟體工程]]></category>
		<category><![CDATA[軟體產業]]></category>
		<category><![CDATA[VCS]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1587</guid>
		<description><![CDATA[這幾年來，多多少少接觸了不少業界的人，雖然我自己還不算有真正待過業界太久，但是這期間看到不少業界的現象都令我挺驚訝的，例如在聊天時提到你們公司用的版本控制系統是什麼，有很多人都會回答 「那是什麼?」，一直以來這些在國外的主流開發環境都基本常識或是標準配備的東西台灣業界居然很多都連有那樣的工具存在都不知道，或著是對於某些東西有錯誤的認知，所以我想大略提一下常見的幾個問題 版本控制系統 我想這是最常見的毛病，很常發現很多公司在開發軟體時從來都不使用版本控制系統，最誇張的狀況就是管它三七二十一直接修改 &#160; 除此之外，常見的土法鍊鋼有聽說過資料夾複製，然後將資料夾名稱命名為版本1之類的方法，高級一點還有搭配Excel來記錄改過了什麼之類的 更進階的還有多人共同開發，還架了FTP來放這些檔案 但這些都有很大的問題，而且其會遇到的問題都正好是版本控制系統所要解決的，所以到底是什麼樣的問題非用版本控制系統不可? 首先，用資料夾copy有個很大的問題，一來是copy的過程很容易出錯，而且更糟的是出的錯很難發現，你怎麼從資料夾的內容來判斷這到底是哪個版本? 最常見的做法就是回想你到底在哪個版本改了什麼，然後去看對應的位置，是否有那些改動，但你有可能記得嗎? 要是程式不是你改的呢? 因此依賴資料夾名稱來得知檔案的版本是極度不可靠的做法，再者，如果你不幸改到錯誤的版本，辛辛苦苦改了半天，才發現改到舊版本了，那你要如何把你改的和正確的版本合在一起? 如果你只改了三行，這還好辦，但如果你改了三百行，那該怎麼辦? 用Excel來記錄改動的事項和版本一樣不會有幫助 那多人開發使用FTP來分享檔案呢? 老天，事情更慘了，原先只是你自己的開發，自己改錯了就算了，如今變成多人開發，有時出問題還不是你改的，這樣想好了，FTP上有個檔案 hello.py 今天張三載回去改了，變成 hello.py (張三版) 不幸的是，王五在張三上傳回FTP之前，也載回來改，變成 hello.py (王五版) 接著，張三把它的檔案上傳了，所以FTP上的檔案變成了 hello.py (張三版) 然後好戲發生了，王五也把它改的東西上傳了，所以FTP上的檔案被蓋掉，變成 hello.py (王五版) 發生了什麼事? 張三改的版本被蓋掉了，你可以想見張三在demo給老闆看時發現改的地方被蓋掉了，翻過辦公桌衝過去揍王五的情景了嗎? 像這樣還只是最簡單的情境，以這類土法鍊鋼的方式，還有太多太多預料不到的複雜情況會發生，什麼? 那你說，如果我們規定每人都得把資料夾以自己的名稱命名，加上版號，再上傳，這樣就不會錯了吧? hello_project-王五-rev123/ 拜託，何苦呢 ? 版本控制系統就是用來解決這些問題而開發出來的，學一套新工具有這麼難嗎? 常見的理由可能會有什麼沒時間學、不信任工具等等，事實上那些都不是理由，只要是程式碼的開發，都得使用版本控制系統，現在已經是2011年，如果你的軟體開發沒有使用版本控制系統，我說這不叫落後，這是原始 用了版本控制系統，最重要的好處是 你可以安心地放膽去改程式 &#8230; <a href="http://blog.ez2learn.com/2011/10/20/taiwan-software-lacking-of-vcs/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>這幾年來，多多少少接觸了不少業界的人，雖然我自己還不算有真正待過業界太久，但是這期間看到不少業界的現象都令我挺驚訝的，例如在聊天時提到你們公司用的版本控制系統是什麼，有很多人都會回答 「那是什麼?」，一直以來這些在國外的主流開發環境都基本常識或是標準配備的東西台灣業界居然很多都連有那樣的工具存在都不知道，或著是對於某些東西有錯誤的認知，所以我想大略提一下常見的幾個問題</p>
<h2>版本控制系統</h2>
<p>我想這是最常見的毛病，很常發現很多公司在開發軟體時從來都不使用版本控制系統，最誇張的狀況就是管它三七二十一直接修改</p>
<p>&nbsp;</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/10/directly_modify2.png"><img class="aligncenter size-large wp-image-1599" title="directly_modify" src="http://blog.ez2learn.com/wp-content/uploads/2011/10/directly_modify2-1024x324.png" alt="" width="640" height="202" /></a></p>
<p>除此之外，常見的土法鍊鋼有聽說過資料夾複製，然後將資料夾名稱命名為版本1之類的方法，高級一點還有搭配Excel來記錄改過了什麼之類的</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/10/copy_dir.png"><img class="aligncenter size-large wp-image-1600" title="copy_dir" src="http://blog.ez2learn.com/wp-content/uploads/2011/10/copy_dir-1024x269.png" alt="" width="640" height="168" /></a></p>
<p>更進階的還有多人共同開發，還架了FTP來放這些檔案</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/10/ftp.png"><img class="aligncenter size-large wp-image-1601" title="ftp" src="http://blog.ez2learn.com/wp-content/uploads/2011/10/ftp-1024x533.png" alt="" width="640" height="333" /></a></p>
<p>但這些都有很大的問題，而且其會遇到的問題都正好是版本控制系統所要解決的，所以到底是什麼樣的問題非用版本控制系統不可?</p>
<p>首先，用資料夾copy有個很大的問題，一來是copy的過程很容易出錯，而且更糟的是出的錯很難發現，你怎麼從資料夾的內容來判斷這到底是哪個版本? 最常見的做法就是回想你到底在哪個版本改了什麼，然後去看對應的位置，是否有那些改動，但你有可能記得嗎? 要是程式不是你改的呢? 因此依賴資料夾名稱來得知檔案的版本是極度不可靠的做法，再者，如果你不幸改到錯誤的版本，辛辛苦苦改了半天，才發現改到舊版本了，那你要如何把你改的和正確的版本合在一起? 如果你只改了三行，這還好辦，但如果你改了三百行，那該怎麼辦? 用Excel來記錄改動的事項和版本一樣不會有幫助</p>
<p>那多人開發使用FTP來分享檔案呢? 老天，事情更慘了，原先只是你自己的開發，自己改錯了就算了，如今變成多人開發，有時出問題還不是你改的，這樣想好了，FTP上有個檔案</p>
<blockquote><p>hello.py</p></blockquote>
<p>今天張三載回去改了，變成</p>
<blockquote><p>hello.py (張三版)</p></blockquote>
<p>不幸的是，王五在張三上傳回FTP之前，也載回來改，變成</p>
<blockquote><p>hello.py (王五版)</p></blockquote>
<p>接著，張三把它的檔案上傳了，所以FTP上的檔案變成了</p>
<blockquote><p>hello.py (張三版)</p></blockquote>
<p>然後好戲發生了，王五也把它改的東西上傳了，所以FTP上的檔案被蓋掉，變成</p>
<blockquote><p>hello.py (王五版)</p></blockquote>
<p>發生了什麼事? 張三改的版本被蓋掉了，你可以想見張三在demo給老闆看時發現改的地方被蓋掉了，翻過辦公桌衝過去揍王五的情景了嗎? 像這樣還只是最簡單的情境，以這類土法鍊鋼的方式，還有太多太多預料不到的複雜情況會發生，什麼? 那你說，如果我們規定每人都得把資料夾以自己的名稱命名，加上版號，再上傳，這樣就不會錯了吧?</p>
<blockquote><p>hello_project-王五-rev123/</p></blockquote>
<p>拜託，何苦呢 ? 版本控制系統就是用來解決這些問題而開發出來的，學一套新工具有這麼難嗎? 常見的理由可能會有什麼沒時間學、不信任工具等等，事實上那些都不是理由，只要是程式碼的開發，都得使用版本控制系統，現在已經是2011年，如果你的軟體開發沒有使用版本控制系統，我說這不叫落後，這是原始</p>
<p>用了版本控制系統，最重要的好處是</p>
<blockquote><p><span style="font-size: x-large;"><strong>你可以安心地放膽去改程式</strong></span></p></blockquote>
<h2>什麼是版本控制系統?</h2>
<p>講了那麼多，所以版本控制系統到底是什麼? 這麼來形容好了，如果你有玩過RPG遊戲，像是仙劍奇俠傳、軒轅劍等等，你一定知道遊戲打到一半可以存檔，然後也可以讀檔，接續先前的遊戲進度，如果你發現目前的等級太差了，打不贏魔王，你可以讀前面一點的檔案來重新練，或是前面的寶物忘記拿，也可以讀檔回去拿</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/10/game_save.png"><img class="aligncenter size-full wp-image-1603" title="game_save" src="http://blog.ez2learn.com/wp-content/uploads/2011/10/game_save.png" alt="" width="327" height="476" /></a></p>
<p>而版本控制系統做的事情，就和RPG裡的遊戲存檔是一樣的概念，你可以將目前程式開發的進度的快照存下來，當你發現改的檔案有問題，可以回朔到以前的版本，除此之外，它還可以幫助你找出兩個版本的差異，你再也不怕不小心改了哪一行結果都不知道，也不怕你的同事幫你偷改了幾行結果你卻不知道，甚至還可以合併兩個不同的分支</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/10/diff.png"><img class="aligncenter size-full wp-image-1607" title="diff" src="http://blog.ez2learn.com/wp-content/uploads/2011/10/diff.png" alt="" width="732" height="549" /></a></p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/10/hg_history.png"><img class="aligncenter size-full wp-image-1606" title="hg_history" src="http://blog.ez2learn.com/wp-content/uploads/2011/10/hg_history.png" alt="" width="684" height="376" /></a></p>
<h2>版本控制系統有哪些? 我該用哪一套?</h2>
<p>版本控制系統在幾十年前就已經有現成的工具，然而一直一路演進到今天，現在的版本控制系統已經相當先進且成熟，週邊的工具或是平臺也都相當齊全，從20年前的CVS看起，介紹一些最主流的</p>
<h3><a href="http://zh.wikipedia.org/wiki/%E5%8D%94%E4%BD%9C%E7%89%88%E6%9C%AC%E7%B3%BB%E7%B5%B1">CVS</a></h3>
<p>算是古董級的版本控制系統，主要用於open source的開發，特色是可以多人共同使用，缺點是如果多人要存取同個檔案，要先將檔案鎖起來，很多時候會鎖了忘了開，造成別人無法使用，雖然已經至古董了，但是因為很多大型的開源專案還在使用這系統，所以現在多少還看見有人使用，但不建議一般用途使用</p>
<h3><a href="http://zh.wikipedia.org/wiki/Subversion">Subversion</a></h3>
<p>Subversion簡稱svn，約十年前出現，是CVS的繼承者，主要是改進CVS的缺點，像是最重要的改進，就是檔案鎖定的問題，SVN以試著自動合併檔案的方式來解決同時有兩人修改同一個檔案的問題，現今很多開源專案也還在使用SVN</p>
<p><a href="http://zh.wikipedia.org/wiki/Git">Git</a> &amp; <a href="http://zh.wikipedia.org/wiki/Mercurial">Mercurial</a></p>
<p>接著時間來到了現代，分散式版本控制系統成為了新興專案的主流，在這些新的系統中，檔案庫不在是集中式的，而是分散式的，原本commit都要集中送到中央檔案庫，現在不同的檔案庫之間可以互相傳送對方沒有的commit，如此一來讓開發更加容易，即使沒有網路也可以在本地端commit之後再送給遠端的檔案庫，除此之外，它們引入的新特性也讓合併兩個分支之類的事變得更加容易，個人推薦如果你還沒有用過版本控制系統，可以直接學這些現代的工具，以前的工具大多都已經很少人在使用，可用的資源也在變少中</p>
<h2>Git 的簡介</h2>
<p>Git是Linus Torvalds開發用來管理Linux核心程式碼用的系統，特色是這套系統以效率為設計的考量，並且支援很多較低階的檔案歷史處理，支援的主流免費檔案庫有</p>
<p><a href="http://github.com">Github.com</a></p>
<p><a href="http://bitbucket.org">Bitbucket.org</a></p>
<p>原先Bitbucket只支援Mercurial，這幾天也支援Git了，而Github和Bitbucket的差別在於，Github如果想開私有的檔案庫要錢，而Bitbucket不用，而且是無限個數</p>
<p>如果你想學Git，個人推薦讀</p>
<p><a href="http://progit.org/">Git pro</a></p>
<p>裡面講得非常清楚而且詳細，也有圖說明系統的運作，相對的，如果是讀其它的文件，大多數都很難懂，因為都是寫給geek看的，看不懂是很正常的，整體看起來，Git比Mercurial還難學，除此之外還有一個缺點就是對於Windows的支援較差</p>
<h2>Mercurial的簡介</h2>
<p>Mercurial是基於Python的分散式版本控制系統，特色是以較簡易使用，並且很重視檔案修改的安全性為考量的工具，像是Git有很多指令，一下錯你改的檔案可能因此而遺失，這類情況在Mercurial中較少，大部份指令都會幫你自動備份，如果弄錯指令通常還有救</p>
<p>主流的檔案庫平台是</p>
<p><a href="http://bitbucket.org">Bitbucket.org</a></p>
<p>如果你想學Mercurial，個人推薦看Joel寫的</p>
<p><a href="http://hginit.com/">Hg Init 教學</a></p>
<p>個人較喜歡Mercurial的原因是它的GUI介面工具較為豐富，像是有</p>
<p><a href="http://tortoisehg.bitbucket.org/">TortoiseHg</a></p>
<p><a href="http://jasonfharris.com/machg/">MacHg</a></p>
<p><a href="http://www.sourcetreeapp.com/">SourceTree</a></p>
<p>等好用的GUI工具</p>
<h2>最後</h2>
<p>我在大學教過的<a href="http://www.slideshare.net/stubbornlin/mercurial-9779393">教學用投影片</a>，可以參考看看，未來有空的話或許我可以寫一些這方面的教學，因為畢竟中文資源很少，似乎也沒專門的書在講這些工具的使用</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2011/10/20/taiwan-software-lacking-of-vcs/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>淺談區域性 (locality)</title>
		<link>http://blog.ez2learn.com/2011/10/14/talk-about-locality/</link>
		<comments>http://blog.ez2learn.com/2011/10/14/talk-about-locality/#comments</comments>
		<pubDate>Fri, 14 Oct 2011 12:10:11 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[中文文章]]></category>
		<category><![CDATA[分享]]></category>
		<category><![CDATA[區域性]]></category>
		<category><![CDATA[連線延遲]]></category>
		<category><![CDATA[locality]]></category>
		<category><![CDATA[NoSQL]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1569</guid>
		<description><![CDATA[在設計不同的網路服務系統時，為了能夠有擴展性，通常都會設計成分散式的架構，然而除了架構上的設計，如何部署也是很重要的事，其中有個很重要的議題叫做區域性，因為沒有統一或明確的翻譯慣例，所以以英文來說明較為精確，在這裡指的區域性英文為locality，對於這個議題最近有一點心得 所以，回到主題，到底什麼是區域性? 簡單的來說，就是存取資料或資源時，很常存取或是相關的資料放在一起、或很近的地方的特性，舉個實例，例如假設我們有關於某個使用者的資料，但是使用者相關的資料分散在全球不同的資料庫裡，這時我們就會說，這資料庫的區域性不好，以圖來表示，我們假設使用者的資料分散在美西的DB1、美中的DB2和美東的DB3，而使用者在西雅圖要存取這些資料，就得走很遠的距離到三個很遠的地點存取資料 反言之，如果關於這使用者的資料，都存在離使用者很近的點，而且也都在同樣或是很接近的資料庫裡，那麼存取起來就會較快，這樣一來我們就可以說這樣的資料儲存方式它的區域性比較好，我們假設把同一使用者相關的資料都放在相近的地方，以圖為例，都放在加洲，這樣一來同樣是存取使用者的資料，其中所花的傳輸距離成本就遠比上一個例子來得少，反應速度也會因此較快 一般而言，區域性是越強越好，但是也有例外，那就是當考慮到可得性(availability)的時候，這樣的特性是指資料或資源隨時都可取得的機率高低，如果當我們把雞蛋放在同一個籃子裡，也就是資料都放在同樣的Datacenter裡，一但這個Datacenter對外的網路中斷，或是甚至遇到不可預料的災難時，那麼那些資料都會因此而無法取得，所以除了考慮到存取時的區域性，當資料有一定重要程度時，可得性也是很重要的考量，所以某些情況下，資料分散也是必要的 然而，區域性就表面看來，似乎只要將常用的、相關的資料都放在一起好像就能達成，然而經過仔細思考會發現其實並不是只有這樣，還有需要考慮到存取資料的距離，還有存取要求本身的高低階，在這篇文章我想分享的就是主要在於思考關於區域性設計上的一些理論的心得 請求的相依性與粒度 我發現並不是所有的情況下不佳的地區性都一定會嚴重影響到存取的效能，像是請求的相依性其實對於延遲的影響就非常大，如果說所有的情求都不能同時處理，一定得要上一個完成才能完成下一個的話，這樣一來就會造成每個request的請求都要額外花費一次連線的延遲成本，可以參考下圖 很明顯的，左邊的情況，傳輸距離所造成的影響，會是 請求數量 * (運算成本 + 延遲成本) 右邊的情況是 (請求數量 * 運算成本) + 延遲成本 因此光是請求是否能同步處理，並且是否有前後相依，就會造成相當大的差別，如果請求的數量越多，這樣的成本差距就越大，左邊的例子我們以NoSQL或是 SQL的請求為例子，通常下一道請求都是基於上一道請求的資料而決定的，如果說任務被拆散成很零碎的多道請求，像是有些key-value based的NoSQL資料庫，因為沒有高階的查詢指令，必然會有大量的請求，如此一來如果NoSQL資料庫放在很遠的地方，就會造成光是這之間的傳輸成本就會高得嚇人，而以SQL來看，因為可以盡可能地將多道SQL濃縮為少數幾道查詢，因此同樣的傳輸成本對於SQL資料庫來說，傳輸的成本造成的影響會小一點 而右邊的情況，通常是大量的資料傳輸，例如影音串流，因為上一筆資料無關下一筆資料，以這種情況來看，傳輸的距離不會是太大的問題，只有一開始會有的傳輸延遲 心得 一些簡單的心得就是，當請求是相依的，如果數量不大，那麼其實傳輸的延遲是可以被忽視的，又或著是大量連續的資料傳輸，距離的影響是較小的，如果不考慮連線的品質問題的話，但是如果是有相依特性的請求，數量又大的話，最好資料庫的部署要越接近越好，否則光是連線的延遲成本就相當驚人，就算你的NoSQL資料庫再怎麼快，也沒有任何幫助，甚至會比SQL資料庫還要慢]]></description>
			<content:encoded><![CDATA[<p>在設計不同的網路服務系統時，為了能夠有擴展性，通常都會設計成分散式的架構，然而除了架構上的設計，如何部署也是很重要的事，其中有個很重要的議題叫做區域性，因為沒有統一或明確的翻譯慣例，所以以英文來說明較為精確，在這裡指的區域性英文為locality，對於這個議題最近有一點心得</p>
<p>所以，回到主題，到底什麼是區域性? 簡單的來說，就是存取資料或資源時，很常存取或是相關的資料放在一起、或很近的地方的特性，舉個實例，例如假設我們有關於某個使用者的資料，但是使用者相關的資料分散在全球不同的資料庫裡，這時我們就會說，這資料庫的區域性不好，以圖來表示，我們假設使用者的資料分散在美西的DB1、美中的DB2和美東的DB3，而使用者在西雅圖要存取這些資料，就得走很遠的距離到三個很遠的地點存取資料</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/10/locality01.png"><img class="alignnone size-full wp-image-1574" title="Weak locality" src="http://blog.ez2learn.com/wp-content/uploads/2011/10/locality01.png" alt="" width="506" height="324" /></a></p>
<p>反言之，如果關於這使用者的資料，都存在離使用者很近的點，而且也都在同樣或是很接近的資料庫裡，那麼存取起來就會較快，這樣一來我們就可以說這樣的資料儲存方式它的區域性比較好，我們假設把同一使用者相關的資料都放在相近的地方，以圖為例，都放在加洲，這樣一來同樣是存取使用者的資料，其中所花的傳輸距離成本就遠比上一個例子來得少，反應速度也會因此較快</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/10/locality02.png"><img class="alignnone size-full wp-image-1576" title="Strong locality" src="http://blog.ez2learn.com/wp-content/uploads/2011/10/locality02.png" alt="" width="506" height="324" /></a></p>
<p>一般而言，區域性是越強越好，但是也有例外，那就是當考慮到可得性(availability)的時候，這樣的特性是指資料或資源隨時都可取得的機率高低，如果當我們把雞蛋放在同一個籃子裡，也就是資料都放在同樣的Datacenter裡，一但這個Datacenter對外的網路中斷，或是甚至遇到不可預料的災難時，那麼那些資料都會因此而無法取得，所以除了考慮到存取時的區域性，當資料有一定重要程度時，可得性也是很重要的考量，所以某些情況下，資料分散也是必要的</p>
<p>然而，區域性就表面看來，似乎只要將常用的、相關的資料都放在一起好像就能達成，然而經過仔細思考會發現其實並不是只有這樣，還有需要考慮到存取資料的距離，還有存取要求本身的高低階，在這篇文章我想分享的就是主要在於思考關於區域性設計上的一些理論的心得</p>
<h2>請求的相依性與粒度</h2>
<p>我發現並不是所有的情況下不佳的地區性都一定會嚴重影響到存取的效能，像是請求的相依性其實對於延遲的影響就非常大，如果說所有的情求都不能同時處理，一定得要上一個完成才能完成下一個的話，這樣一來就會造成每個request的請求都要額外花費一次連線的延遲成本，可以參考下圖</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/10/delay011.png"><img title="Locality delay chart" src="http://blog.ez2learn.com/wp-content/uploads/2011/10/delay011.png" alt="" width="547" height="360" /></a></p>
<p>很明顯的，左邊的情況，傳輸距離所造成的影響，會是</p>
<blockquote><p>請求數量 * (運算成本 + 延遲成本)</p></blockquote>
<p>右邊的情況是</p>
<blockquote><p>(請求數量 * 運算成本) + 延遲成本</p></blockquote>
<p>因此光是請求是否能同步處理，並且是否有前後相依，就會造成相當大的差別，如果請求的數量越多，這樣的成本差距就越大，左邊的例子我們以NoSQL或是 SQL的請求為例子，通常下一道請求都是基於上一道請求的資料而決定的，如果說任務被拆散成很零碎的多道請求，像是有些key-value based的NoSQL資料庫，因為沒有高階的查詢指令，必然會有大量的請求，如此一來如果NoSQL資料庫放在很遠的地方，就會造成光是這之間的傳輸成本就會高得嚇人，而以SQL來看，因為可以盡可能地將多道SQL濃縮為少數幾道查詢，因此同樣的傳輸成本對於SQL資料庫來說，傳輸的成本造成的影響會小一點</p>
<p>而右邊的情況，通常是大量的資料傳輸，例如影音串流，因為上一筆資料無關下一筆資料，以這種情況來看，傳輸的距離不會是太大的問題，只有一開始會有的傳輸延遲</p>
<h2>心得</h2>
<p>一些簡單的心得就是，當請求是相依的，如果數量不大，那麼其實傳輸的延遲是可以被忽視的，又或著是大量連續的資料傳輸，距離的影響是較小的，如果不考慮連線的品質問題的話，但是如果是有相依特性的請求，數量又大的話，最好資料庫的部署要越接近越好，否則光是連線的延遲成本就相當驚人，就算你的NoSQL資料庫再怎麼快，也沒有任何幫助，甚至會比SQL資料庫還要慢</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2011/10/14/talk-about-locality/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dev C++ 5.0.0.3釋出</title>
		<link>http://blog.ez2learn.com/2011/09/17/dev-c-5-0-0-3/</link>
		<comments>http://blog.ez2learn.com/2011/09/17/dev-c-5-0-0-3/#comments</comments>
		<pubDate>Sat, 17 Sep 2011 10:49:18 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[DevC++]]></category>
		<category><![CDATA[新版]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1564</guid>
		<description><![CDATA[還記得先前的吐嘈Dev C++用於教學嗎? 其實就在那文章張貼的時間點左右，有熱心的人替Dev C++更新了一個版本，從4.9.9.2到4.9.9.3，修了一些bug，除了那一版以外，又額外了出了好幾次的更新，每次更新都改善了一些東西，它的內建GCC編譯器更新為4.5.2，我自己在試過了之後，發現真的修掉了我列出的那些幾個最主要的大問題，雖然它的debugger對於變數顯示的功能還是很差，不過至少已經有在解決問題，並且持續改善，但我相信很多人都還是不知道有這樣的新版Dev C++可以使用，因為畢竟不是官方的更新，最近還推出到了5.0.0.3版，而且應該還會繼續更新 做為教學用的IDE，確實是小巧簡單上手的，Dev C++修掉那身臭蟲的話對於學生學習會遇到的問題也會較少，舉個例子，光是啟用除錯器，原本的Dev C++有bug，一定得到專案去設定而且還有奇怪的bug，弄半天才能進行debugging，可以參考我以前寫的 Dev C++ debugger教學 有了新版的Dev C++，要啟用debugger只要先編譯一次，再點除錯，它問你要不要加入除錯訊息，按是之後，再次按除錯，就可以進行除錯，簡化了非常多的步驟，如果你是助教或是教C/C++的老師，個人建議盡量使用這些最新版本的Dev C++ 下載最新版本的Dev C++，請到作者的部落格 Orwell&#8217;s Engine]]></description>
			<content:encoded><![CDATA[<p>還記得先前的<a href="http://blog.ez2learn.com/2011/07/08/please-do-not-use-dev-c/">吐嘈Dev C++用於教學</a>嗎? 其實就在那文章張貼的時間點左右，有熱心的人替Dev C++更新了一個版本，從4.9.9.2到4.9.9.3，修了一些bug，除了那一版以外，又額外了出了好幾次的更新，每次更新都改善了一些東西，它的內建GCC編譯器更新為4.5.2，我自己在試過了之後，發現真的修掉了我列出的那些幾個最主要的大問題，雖然它的debugger對於變數顯示的功能還是很差，不過至少已經有在解決問題，並且持續改善，但我相信很多人都還是不知道有這樣的新版Dev C++可以使用，因為畢竟不是官方的更新，最近還推出到了5.0.0.3版，而且應該還會繼續更新</p>
<p>做為教學用的IDE，確實是小巧簡單上手的，Dev C++修掉那身臭蟲的話對於學生學習會遇到的問題也會較少，舉個例子，光是啟用除錯器，原本的Dev C++有bug，一定得到專案去設定而且還有奇怪的bug，弄半天才能進行debugging，可以參考我以前寫的</p>
<p><a href="http://ez2learn.com/index.php/c-tutorials/dev-c-/203-dev-cdebugger">Dev C++ debugger教學</a></p>
<p>有了新版的Dev C++，要啟用debugger只要先編譯一次，再點除錯，它問你要不要加入除錯訊息，按是之後，再次按除錯，就可以進行除錯，簡化了非常多的步驟，如果你是助教或是教C/C++的老師，個人建議盡量使用這些最新版本的Dev C++</p>
<p>下載最新版本的Dev C++，請到作者的部落格 <a href="http://orwellengine.blogspot.com/">Orwell&#8217;s Engine</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2011/09/17/dev-c-5-0-0-3/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>我有一個夢想</title>
		<link>http://blog.ez2learn.com/2011/08/06/now-in-is-hiring/</link>
		<comments>http://blog.ez2learn.com/2011/08/06/now-in-is-hiring/#comments</comments>
		<pubDate>Sat, 06 Aug 2011 02:26:51 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[中文文章]]></category>
		<category><![CDATA[徵才]]></category>
		<category><![CDATA[now.in]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1538</guid>
		<description><![CDATA[我很幸運，出生在網路快速成長的年代，感謝網路的發明，我從國一就有機會開始學習寫程式，在這些年裡，心底一直都有一個疑問，我相信很多人都跟我一樣有同樣的疑問 「為什麼台灣沒有軟體服務產業?」 我們從新聞看見Yahoo成功、Google成功、Facebook成功，但是卻很少看見來自台灣的軟體服務產品能夠站上國際舞臺，台灣的硬體毫無疑問的是相當有競爭力的產業，然而隨著競爭國家的掘起、工資物料成本的提升，到後來就只能爭取幾毛錢的毛利，相對於硬體，軟體和服務才是能增加最多價值的產品，搭配台灣的硬體優勢將會是最強的組合，然而縱觀千百種不會成功的理由，不管是教育的問題、硬體產業的排擠效應、社會風氣的問題等等，這些都不是一朝一夕就可以改變的，因此我相信只有透過一些人來開路，先建立成功的典範，以這些人當種子散播就有機會帶動整個台灣軟體發展，不管是Google、Yahoo等等公司，有很多新創的公司都是從這些公司出去的員工 「那麼誰來做這些事?」 我知道，不管我有再多的想法，不去做都只是空想而已，我告訴我自己，不要期待別人來完成你的夢想，只有靠自己才有辦法改變這世界，我相信 「做的比說的大聲」 或許有人會說，這會失敗，我們的教育都傾向教我們一定要成功，卻沒有教我們要如何失敗，或許最終會以失敗收場，但又如何呢? 在我大學一年級決定開始Now.in的計劃時，我想過各種失敗的可能性，但是我這樣問自己 「如果因為怕失敗不去做，難道你要等到哪天看見別人做出你想做的東西，那時才大聲嚷嚷那是我的想法嗎?」 我不想成為最後只剩一張嘴的人，於是我開始動手，在大學上面講課時我在下面畫伺服器架構的設計圖，回到宿舍上網找適合的技術，當別人在寫程式作業時，我在寫Now.in的程式，當別人在打魔獸時 . . . . . . . . 我也在打魔獸，終於網站在2009年年底上線，隨著時間過去，從一開始只有最簡單的廣播功能，到後來漸漸增加了一些功能，看著使用人數越來越多，就像生了一個女兒看她長大一樣 她在 Alexa的排名也來到了全台458，全球57K 而在前陣子我們也在IDEAS Show得到不錯的成績，朝我們的夢想邁進了一小步，我們希望有一天我們可以成為廣播界的Youtube，然而隨著我們想做的東西越來越多，而技術人力只有我自己一個，很多使用者都以為我們是一個公司或團隊，當收到抱怨信說為什麼出問題時我們也只能無奈的賠不是，因為使用者不知道這其實是一個人做出來的，直到前陣子我找了我兩位同學幫忙寫iPhone和Android的app，才有其它的人加入，雖然手機只是我們很久以前就有的想法，但只因為人力不足，所以一直都沒辦法實現，甚至一開始我還打算連手機的app都自己完成，因此我想在這裡大聲地說 「我們需要你的幫助」 在擁抱美好夢想前，我想先把醜話說在前頭 「我們沒有資金」 很不幸的是我們並沒有太多的資金能夠雇用人力，我們沒有豪華的辦公室，沒有員工旅遊，沒有錢請全聯先生替我們代言，我們只能提供微薄的薪水雇用實習的員工，所以為什麼你要加入我們? 我們有技術 我們有的是以Python為核心的相關技術，在Now.in裡幾乎所有程式都是用相當新的Python技術寫的，而且範圍牽涉之廣，從桌面應用程式、網路伺服器、網頁應用程式甚至到伺服器管理都是，所以加入我們你可以學到技術，這些是花錢去補習班或請家教也學不到的 我們有使用者 身為一個程式設計師，我想最傷心的事情大概莫過於寫出來的程式沒有人使用，對於Now.in來說，現在每天有十萬個訪客，線上聽眾的鋒值可以達到一萬，電台可以達到一千個，我相信最開心的事情就是看到自己的程式有人使用，我同學推出的iPhone和Android App在短短幾週內都達到了五千次的下載，並且一直在成長，這成就感我想是金錢無法比擬的 我們有理想 就如同我們的口號 「廣播和收聽全世界」 我們希望能夠打造一個線上的廣播社群，讓任何人都可以有發聲的管道，除此之外我們還希望能夠讓人們可以更緊密地在我們的平臺上互動，還有太多太多我們想達成的目標，簡單的來說 「我們想改變世界」 這些理想都是在大公司很難找到的 我們需要你的參與 我們需要以下的人才 &#8230; <a href="http://blog.ez2learn.com/2011/08/06/now-in-is-hiring/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>我很幸運，出生在網路快速成長的年代，感謝網路的發明，我從國一就有機會開始學習寫程式，在這些年裡，心底一直都有一個疑問，我相信很多人都跟我一樣有同樣的疑問</p>
<p>「<strong>為什麼台灣沒有軟體服務產業?</strong>」</p>
<p>我們從新聞看見Yahoo成功、Google成功、Facebook成功，但是卻很少看見來自台灣的軟體服務產品能夠站上國際舞臺，台灣的硬體毫無疑問的是相當有競爭力的產業，然而隨著競爭國家的掘起、工資物料成本的提升，到後來就只能爭取幾毛錢的毛利，相對於硬體，軟體和服務才是能增加最多價值的產品，搭配台灣的硬體優勢將會是最強的組合，然而縱觀千百種不會成功的理由，不管是教育的問題、硬體產業的排擠效應、社會風氣的問題等等，這些都不是一朝一夕就可以改變的，因此我相信只有透過一些人來開路，先建立成功的典範，以這些人當種子散播就有機會帶動整個台灣軟體發展，不管是Google、Yahoo等等公司，有很多新創的公司都是從這些公司出去的員工</p>
<p>「<strong>那麼誰來做這些事?</strong>」</p>
<p>我知道，不管我有再多的想法，不去做都只是空想而已，我告訴我自己，不要期待別人來完成你的夢想，只有靠自己才有辦法改變這世界，我相信</p>
<p>「<strong>做的比說的大聲</strong>」</p>
<p>或許有人會說，這會失敗，我們的教育都傾向教我們一定要成功，卻沒有教我們要如何失敗，或許最終會以失敗收場，但又如何呢? 在我大學一年級決定開始Now.in的計劃時，我想過各種失敗的可能性，但是我這樣問自己</p>
<p>「<strong>如果因為怕失敗不去做，難道你要等到哪天看見別人做出你想做的東西，那時才大聲嚷嚷那是我的想法嗎?</strong>」</p>
<p>我不想成為最後只剩一張嘴的人，於是我開始動手，在大學上面講課時我在下面畫伺服器架構的設計圖，回到宿舍上網找適合的技術，當別人在寫程式作業時，我在寫Now.in的程式，當別人在打魔獸時 . . . . . . . . 我也在打魔獸，終於網站在2009年年底上線，隨著時間過去，從一開始只有最簡單的廣播功能，到後來漸漸增加了一些功能，看著使用人數越來越多，就像生了一個女兒看她長大一樣</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/08/google_analytics.png"><img class="alignnone size-medium wp-image-1540" title="google_analytics" src="http://blog.ez2learn.com/wp-content/uploads/2011/08/google_analytics-300x61.png" alt="" width="300" height="61" /></a></p>
<p>她在 <a href="http://www.alexa.com/siteinfo/now.in#">Alexa的排名</a>也來到了全台458，全球57K</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/08/nowin_alexa.png"><img class="alignnone size-full wp-image-1543" title="nowin_alexa" src="http://blog.ez2learn.com/wp-content/uploads/2011/08/nowin_alexa.png" alt="" width="687" height="272" /></a></p>
<p>而在前陣子我們也在IDEAS Show得到不錯的成績，朝我們的夢想邁進了一小步，我們希望有一天我們可以成為廣播界的Youtube，然而隨著我們想做的東西越來越多，而技術人力只有我自己一個，很多使用者都以為我們是一個公司或團隊，當收到抱怨信說為什麼出問題時我們也只能無奈的賠不是，因為使用者不知道這其實是一個人做出來的，直到前陣子我找了我兩位同學幫忙寫iPhone和Android的app，才有其它的人加入，雖然手機只是我們很久以前就有的想法，但只因為人力不足，所以一直都沒辦法實現，甚至一開始我還打算連手機的app都自己完成，因此我想在這裡大聲地說</p>
<p>「<strong>我們需要你的幫助</strong>」</p>
<p>在擁抱美好夢想前，我想先把醜話說在前頭</p>
<p>「<strong>我們沒有資金</strong>」</p>
<p>很不幸的是我們並沒有太多的資金能夠雇用人力，我們沒有豪華的辦公室，沒有員工旅遊，沒有錢請全聯先生替我們代言，我們只能提供微薄的薪水雇用實習的員工，所以為什麼你要加入我們?</p>
<h2>我們有技術</h2>
<p>我們有的是以Python為核心的相關技術，在Now.in裡幾乎所有程式都是用相當新的Python技術寫的，而且範圍牽涉之廣，從桌面應用程式、網路伺服器、網頁應用程式甚至到伺服器管理都是，所以加入我們你可以學到技術，這些是花錢去補習班或請家教也學不到的</p>
<h2>我們有使用者</h2>
<p>身為一個程式設計師，我想最傷心的事情大概莫過於寫出來的程式沒有人使用，對於Now.in來說，現在每天有十萬個訪客，線上聽眾的鋒值可以達到一萬，電台可以達到一千個，我相信最開心的事情就是看到自己的程式有人使用，我同學推出的iPhone和Android App在短短幾週內都達到了五千次的下載，並且一直在成長，這成就感我想是金錢無法比擬的</p>
<h2>我們有理想</h2>
<p>就如同我們的口號</p>
<p>「<strong>廣播和收聽全世界</strong>」</p>
<p>我們希望能夠打造一個線上的廣播社群，讓任何人都可以有發聲的管道，除此之外我們還希望能夠讓人們可以更緊密地在我們的平臺上互動，還有太多太多我們想達成的目標，簡單的來說</p>
<p>「<strong>我們想改變世界</strong>」</p>
<p>這些理想都是在大公司很難找到的</p>
<h2>我們需要你的參與</h2>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/08/we_want_you.jpg"><img class="alignnone size-full wp-image-1545" title="we_want_you" src="http://blog.ez2learn.com/wp-content/uploads/2011/08/we_want_you.jpg" alt="" width="446" height="599" /></a></p>
<p>我們需要以下的人才</p>
<h3>開發者</h3>
<p>開發者主要負責程式的設計與開發，我們的項目範圍牽涉很廣，所以可以有很多不同的選擇與可能性，需要有相當程度的熱情與學習能力，還有需要基本的程式設計能力，基本的英文讀寫能力，如果學習新的技術讓你感到開心，你想知道怎樣設計出好的架構，你想成為頂尖的程式設計師，這或許是你的最佳選擇</p>
<h3>系統管理員</h3>
<p>我們有很多台實體的伺服器和Amazon EC2上的機器，數量還在增加中，主要是負責照料這些伺服器，使用的OS主要是CentOS，如果你喜歡管理大量的伺服器，看著htop的CPU使用率跳動讓你感到開心，仔細檢視記錄檔讓你覺得興奮，寫script管理大量伺服器讓你覺得很有成就，或許你就是適合的人選</p>
<h3>網頁設計師</h3>
<p>如同你所見到的，很多人第一眼見到我們的網頁都說很醜，因為我個人不是學設計的，加上時間有限也無法花太多心力在調整網頁的面版上，所以都以最簡陋的形式程現，為了改善我們的網頁外觀，因此我們需要一個人負責網頁設計，你需要懂基本的HTML和CSS，你所做的設計每天都會有十萬人以上觀看和使用</p>
<h2>給有興趣的人</h2>
<p>我們並沒有辦法錄取每個人，因為就如同我在先前文章中講的，人越多越沒有效率，人月神話裡也有一個很好的啟示，就是增加人力也需要額外的成本，因此我們只能雇用適合我們的人，如果你有興趣，請將你的履歷寄到 <img src="http://blog.ez2learn.com/wp-content/themes/mistylook/img/email.png" alt="" width="155" height="21" /> ，我們可能沒有辦法每封信都一一回覆，開發者和網頁設計師請附上你的作品，我們不限種族、年齡、性別、學歷，最後，不管結果如何，感謝你們的參與</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2011/08/06/now-in-is-hiring/feed/</wfw:commentRss>
		<slash:comments>34</slash:comments>
		</item>
		<item>
		<title>那些在Now.in學到的 &#8211; Software engineering practices</title>
		<link>http://blog.ez2learn.com/2011/07/29/lesson-learned-fro-now-in/</link>
		<comments>http://blog.ez2learn.com/2011/07/29/lesson-learned-fro-now-in/#comments</comments>
		<pubDate>Fri, 29 Jul 2011 08:45:42 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[中文文章]]></category>
		<category><![CDATA[使用者經驗]]></category>
		<category><![CDATA[經驗分享]]></category>
		<category><![CDATA[產品]]></category>
		<category><![CDATA[軟體工程]]></category>
		<category><![CDATA[now.in]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1501</guid>
		<description><![CDATA[Now.in這個網站在2009年年底上線，在這幾天參加IDEAS Show之前終於來到了台灣前五百大，全球六萬名，這期間只花了一年半，而事實上這個網站早在我大學一年級約2006年左右就開始有這樣的想法，接著花了一兩年的時間開始構思和找相關的技術，2009年開始實作，從開始到上線只花了一年的時間，其中專案之龐大還有牽扯到的層面之廣，從架構的設計、後端伺服器的譔寫、前端網頁、Client端、伺服器管理、資料庫管理，很多人都不相信這只有我一個人完成的，而且還是在讀大學和碩士的課餘時間，直到最近上線的Android和iPhone才找了同學幫忙寫，感寫他們這幾週的辛苦 這整個過程的經驗，事實上多到可以寫一本書，不只是技術上的，之後有空我也會寫一些文章來分享我在其中所學到的一些東西，今天我要分享的是我在碩士一門軟體工程課以Now.in為主題所分享的一些經驗，主要是一些我在開發Now.in前後所體認到的一些經驗與想法，因為我發現很多人不是沒有技術，而是缺少對於服務、軟體工程等等的相關思維，或許這篇文章會有一些幫助，除此之外，我也希望能夠將我自己的想法記錄下來，當我要接受新的想法時，就可以回頭看看以前的自己為什麼會有這樣的想法 人越少，越有效 這樣的想法主要來自人月神話這本書，軟體開發不像是收割小麥，人越多越有效率，因為需要溝通，這意味著當你有越多人牽扯在這其中，所花的溝通成本也越高，每個人的平均效率就越低，除此之外，有一句話這麼說 讀程式比寫程式困難 相信有寫過真正程式的人都會有這樣的體認，寫程式的過程是將你的思想轉化為程式寫出來，而讀程式則是透過這些字句語法來理解程式原本的想法，就有如瞎子摸象一樣，因此當你的團隊裡有越多人寫同一份程式，光是在讀程式理解想法才能繼續寫的這整個過程中就損耗了不少生產力，因此甚至知名的電腦科學學者Dijkstra主張程式應該由單一個人來寫，因為它是一個人的想法 這樣的想法除了來自人月神話，也受最近出版的一本書Rework影響，它是來自37Signals公司的經驗，37Signals是一家小型軟體公司，雖然只有十幾個人的規模，但是推出的產品都很受歡迎，他們強調要找對的優秀開發者，還有開會是造成生產力浪費的原因之一，我認為Now.in之所以能以我一人之力完成，正因為我不需要開會做決策、不需要溝通，將我自己的生產力發揮到極致才有可能完成的 然而，如果是小型的專案能夠一個人寫當然是最有效率的，但是當專案的規模大到一定的程度，超過一個人或少數人能完成的地步，增加人力資源就是不可避免的事，不過在此之前，要慎重考量引入新的人力所造成的影響 保持敏捷 很常見的錯誤軟體開發方式就是一口氣推出一大堆功能，然後期待使用者會喜歡這些功能，但是可悲的事實是這些功能推出來通常都不是使用者所要的，就如投影片所表示的，一口氣在單一個開發循環中推出了一大堆的功能，這花了很多的時間與心力，但是卻發現使用者要的的只有一小部份和目前所做的有所重疊 在現實上，你總覺得你可以抓到使用者所想要的，但仔細想想會發現那是你自己想要的，並不一定每個人都跟你想要的是一樣的東西，一口氣在一個循環內開發如此多的功能，對於變化如此快速的網路世界來說，你永遠都趕不上變化 邊移動邊開火才是正確的方式，這有一部份的想法是來自約耳談軟體，如投影片所看到的，一開始先從最核心的功能做起就好，然後看使用者的反應與需求再來改版與新增功能，盡量保持每個開發循環很小，才能抓到使用者所要的 Now.in就是這樣的開發方式，就如投影片裡的抓圖，在一開始只有最核心的廣播功能，其餘的功能都是後來才加上去的，因我看見了需求，我看見使用者們邊聽廣播邊在PTT上推文討論，顯而易見的聊天是必需要有的功能，此後才開發了聊天室 右手邊這張圖是來自Rework裡的插畫，他們認為計劃就是在猜測，是的，沒錯，網路上充滿了創新與變化，很多東西是以前從來沒有人做過的，那麼你要如何計劃? 你沒辦法計劃，任何五年、十年計劃之類的東西都只是在賭博而已，不要浪費時間做太過長遠的計劃，邊移動邊開火才是正卻的做法，阿里巴巴的馬雲也說過，他們成功是因為 沒有資金、沒有技術、沒有計劃 這裡指的沒有計劃就是這麼一回事 老程式不死只是凋零 很多人會犯的一個毛病就是認為舊的程式都是很糟的，要完全砍掉重新寫才是好的，但是事實上從我的經驗告訴我，那些在線上跑經過多次修改的，遇過一大堆你在開發中想都想不到的詭異問題才存活到現今的，它們是如此的老練，有很多失敗的例子就是因為將舊有程式丟棄，直接採用全新的程式來面對實際的應用，事實上這樣就有如送沒見過世面的新兵上最慘烈的前線戰場送死是沒有兩樣的，我想或許Digg就是這樣的一個例子 Digg在去年推出全新的版本，但是不停地出現嚴重的問題，加上新的使用方式受到原本的使用者反彈，導至使用者大量且快速地流失，另一個例子是Netscape在推出新版時將舊的程式全部丟掉，在約耳談軟體的Things you should never do裡有提到 所以我們就一直使用舊程式就好了? 請別會錯我的意思，我想表達的是在前線跑的程式裡有太多是你重新寫不會想到的問題處理，請不要單純為了想砍掉重練而重寫，你需要的是重構而不是重頭寫過，就算你真的打算重新寫過，也請好好重視原本的寫法，以Now.in來說，有些東西就是從最早的版本傳承到現在的經驗 # XXX Workaround for fucking IE6, fuck you IE6! response.cache_control = &#34;no-store, no-cache, &#8230; <a href="http://blog.ez2learn.com/2011/07/29/lesson-learned-fro-now-in/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Now.in這個網站在2009年年底上線，在這幾天參加IDEAS Show之前終於來到了台灣前五百大，全球六萬名，這期間只花了一年半，而事實上這個網站早在我大學一年級約2006年左右就開始有這樣的想法，接著花了一兩年的時間開始構思和找相關的技術，2009年開始實作，從開始到上線只花了一年的時間，其中專案之龐大還有牽扯到的層面之廣，從架構的設計、後端伺服器的譔寫、前端網頁、Client端、伺服器管理、資料庫管理，很多人都不相信這只有我一個人完成的，而且還是在讀大學和碩士的課餘時間，直到最近上線的Android和iPhone才找了同學幫忙寫，感寫他們這幾週的辛苦</p>
<p>這整個過程的經驗，事實上多到可以寫一本書，不只是技術上的，之後有空我也會寫一些文章來分享我在其中所學到的一些東西，今天我要分享的是我在碩士一門軟體工程課以Now.in為主題所分享的一些經驗，主要是一些我在開發Now.in前後所體認到的一些經驗與想法，因為我發現很多人不是沒有技術，而是缺少對於服務、軟體工程等等的相關思維，或許這篇文章會有一些幫助，除此之外，我也希望能夠將我自己的想法記錄下來，當我要接受新的想法時，就可以回頭看看以前的自己為什麼會有這樣的想法</p>
<h2>人越少，越有效</h2>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p25.png"><img class="alignnone size-full wp-image-1503" title="nowin_p25" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p25.png" alt="" /></a></p>
<p>這樣的想法主要來自<a href="http://www.books.com.tw/exep/prod/booksfile.php?item=0010254508">人月神話</a>這本書，軟體開發不像是收割小麥，人越多越有效率，因為需要溝通，這意味著當你有越多人牽扯在這其中，所花的溝通成本也越高，每個人的平均效率就越低，除此之外，有一句話這麼說</p>
<blockquote><p>讀程式比寫程式困難</p></blockquote>
<p>相信有寫過真正程式的人都會有這樣的體認，寫程式的過程是將你的思想轉化為程式寫出來，而讀程式則是透過這些字句語法來理解程式原本的想法，就有如瞎子摸象一樣，因此當你的團隊裡有越多人寫同一份程式，光是在讀程式理解想法才能繼續寫的這整個過程中就損耗了不少生產力，因此甚至知名的電腦科學學者Dijkstra主張程式應該由單一個人來寫，因為它是一個人的想法</p>
<p>這樣的想法除了來自人月神話，也受最近出版的一本書<a href="http://37signals.com/rework/">Rework</a>影響，它是來自37Signals公司的經驗，37Signals是一家小型軟體公司，雖然只有十幾個人的規模，但是推出的產品都很受歡迎，他們強調要找對的優秀開發者，還有開會是造成生產力浪費的原因之一，我認為Now.in之所以能以我一人之力完成，正因為我不需要開會做決策、不需要溝通，將我自己的生產力發揮到極致才有可能完成的</p>
<p>然而，如果是小型的專案能夠一個人寫當然是最有效率的，但是當專案的規模大到一定的程度，超過一個人或少數人能完成的地步，增加人力資源就是不可避免的事，不過在此之前，要慎重考量引入新的人力所造成的影響</p>
<h2>保持敏捷</h2>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/26-1.png"><img class="alignnone size-full wp-image-1508" title="26-1" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/26-1.png" alt="" /></a></p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/26-2.png"><img class="alignnone size-full wp-image-1509" title="26-2" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/26-2.png" alt="" /></a></p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/26-3.png"><img class="alignnone size-full wp-image-1510" title="26-3" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/26-3.png" alt="" /></a></p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/26-4.png"><img class="alignnone size-full wp-image-1511" title="26-4" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/26-4.png" alt="" /></a></p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/26-5.png"><img class="alignnone size-full wp-image-1512" title="26-5" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/26-5.png" alt="" /></a></p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/26-6.png"><img class="alignnone size-full wp-image-1513" title="26-6" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/26-6.png" alt="" /></a></p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/26-7.png"><img class="alignnone size-full wp-image-1514" title="26-7" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/26-7.png" alt="" /></a></p>
<p>很常見的錯誤軟體開發方式就是一口氣推出一大堆功能，然後期待使用者會喜歡這些功能，但是可悲的事實是這些功能推出來通常都不是使用者所要的，就如投影片所表示的，一口氣在單一個開發循環中推出了一大堆的功能，這花了很多的時間與心力，但是卻發現使用者要的的只有一小部份和目前所做的有所重疊</p>
<p>在現實上，你總覺得你可以抓到使用者所想要的，但仔細想想會發現那是你自己想要的，並不一定每個人都跟你想要的是一樣的東西，一口氣在一個循環內開發如此多的功能，對於變化如此快速的網路世界來說，你永遠都趕不上變化</p>
<p><a href="http://chinesetrad.joelonsoftware.com/Articles/FireAndMotion.html">邊移動邊開火</a>才是正確的方式，這有一部份的想法是來自約耳談軟體，如投影片所看到的，一開始先從最核心的功能做起就好，然後看使用者的反應與需求再來改版與新增功能，盡量保持每個開發循環很小，才能抓到使用者所要的</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p27.png"><img class="alignnone size-full wp-image-1516" title="nowin_p27" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p27.png" alt="" /></a></p>
<p>Now.in就是這樣的開發方式，就如投影片裡的抓圖，在一開始只有最核心的廣播功能，其餘的功能都是後來才加上去的，因我看見了需求，我看見使用者們邊聽廣播邊在PTT上推文討論，顯而易見的聊天是必需要有的功能，此後才開發了聊天室</p>
<p>右手邊這張圖是來自Rework裡的插畫，他們認為計劃就是在猜測，是的，沒錯，網路上充滿了創新與變化，很多東西是以前從來沒有人做過的，那麼你要如何計劃? 你沒辦法計劃，任何五年、十年計劃之類的東西都只是在賭博而已，不要浪費時間做太過長遠的計劃，邊移動邊開火才是正卻的做法，阿里巴巴的馬雲也<a href="http://www.youtube.com/watch?v=QLT2KvtFpsI">說過</a>，他們成功是因為</p>
<blockquote><p>沒有資金、沒有技術、沒有計劃</p></blockquote>
<p>這裡指的沒有計劃就是這麼一回事</p>
<h2>老程式不死只是凋零</h2>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p28.png"><img class="alignnone size-full wp-image-1517" title="nowin_p28" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p28.png" alt="" /></a></p>
<p>很多人會犯的一個毛病就是認為舊的程式都是很糟的，要完全砍掉重新寫才是好的，但是事實上從我的經驗告訴我，那些在線上跑經過多次修改的，遇過一大堆你在開發中想都想不到的詭異問題才存活到現今的，它們是如此的老練，有很多失敗的例子就是因為將舊有程式丟棄，直接採用全新的程式來面對實際的應用，事實上這樣就有如送沒見過世面的新兵上最慘烈的前線戰場送死是沒有兩樣的，我想或許Digg就是這樣的一個例子</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p29.png"><img class="alignnone size-full wp-image-1518" title="nowin_p29" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p29.png" alt="" /></a></p>
<p>Digg在去年推出全新的版本，但是不停地出現嚴重的問題，加上新的使用方式受到原本的使用者反彈，導至使用者大量且快速地流失，另一個例子是Netscape在推出新版時將舊的程式全部丟掉，在約耳談軟體的<a href="http://www.joelonsoftware.com/articles/fog0000000069.html">Things you should never do</a>裡有提到</p>
<p>所以我們就一直使用舊程式就好了? 請別會錯我的意思，我想表達的是在前線跑的程式裡有太多是你重新寫不會想到的問題處理，請不要單純為了想砍掉重練而重寫，你需要的是重構而不是重頭寫過，就算你真的打算重新寫過，也請好好重視原本的寫法，以Now.in來說，有些東西就是從最早的版本傳承到現在的經驗</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># XXX Workaround for fucking IE6, fuck you IE6!</span>
response.<span style="color: black;">cache_control</span> = <span style="color: #483d8b;">&quot;no-store, no-cache, must-revalidate, post-check=0, pre-check=0&quot;</span></pre></div></div>

<p>像是腦殘IE會cache不該cache的東西，這樣的經驗只有真的有在前線跑過的程式才有辦法知道，除此之外還有你想不到的不同奇奇怪怪問題，因此老程式需要你的一些尊重</p>
<h2>效能通常不重要</h2>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p30.png"><img class="alignnone size-full wp-image-1519" title="nowin_p30" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p30.png" alt="" /></a></p>
<p>效能很重要嗎? 很重要，但是只有在很少的場合發生，身為技術人員有個壞毛病就是很愛計較效能，這個跑benchmark，那個也跑benchmark，總得要挑一個最有效的方式來實作，但是花了這麼大的心力在於效能上，在現實中效能並不是在所有情況都這麼重要的，有很多程式，例如按鈕的事件回應，你需要花很大的心力去最佳化那段程式嗎? 通常是不用，但是如果是最核心的演算法，會影響到整體表現甚至使用者經驗的，那就是得花心力去處理的部份，但即使如此，現在的硬體越來越便宜，同樣的價錢可以買到的運算資源越來越多，雖然不代表你可以因此隨便亂寫，但是代表你可以花更多心力在更重要的事情上。</p>
<p>除此之外，單一機器的運行效能以現今的觀點看來已經不是太重要了，因為更重要的是架構上的有效，擴展性遠比任何某一段程式的最佳化還來得重要，以Now.in為例，並沒有花太多心力在單一程式的最佳化上面，但是花了不少心力在於思考分散式的架構，如此一來才能增加機器來提升整體的承載能力。</p>
<h2>使用者經驗很重要</h2>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p31.png"><img class="alignnone size-full wp-image-1522" title="nowin_p31" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p31.png" alt="" /></a></p>
<p>我想技術人員開發的產品最常犯的一個毛病就是以自己的觀點來開發產品，很常見到很多產品都強調他們有多少功能，多強大，但是使用者界面卻出奇地難用，或是因為那些過盛的功能導至產品太複雜沒有人會使用，對我來說服務就是完全關於使用者經驗的事情，從使用者的角度來思考，就會發現其實使用者根本不在乎你的產品能不能提供多少強大的功能，他們只在乎好不好用，能不能解決他們自身的問題而已，我想最佳的例子就是Dropbox與Syncplicity</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p32.png"><img class="alignnone size-full wp-image-1523" title="nowin_p32" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p32.png" alt="" /></a></p>
<p>明明Syncplicity比Dropbox還早推出，甚至功能更強大，Dropbox只能同步一個資料夾，然而Syncplicity可以同步任何資料夾，可是Dropbox大成功，Syncplicity卻乏人問津，這其中最大的問題就在於，Syncplicity沒有考量到使用者經驗，有人在Quora上問了為什麼Dropbox會比Syncplicity成功，Syncplicity自己的CEO自己就跳出來說<a href="http://www.quora.com/Dropbox/Why-is-Dropbox-more-popular-than-other-programs-with-similar-functionality">為什麼</a>，簡單的來說就是以功能導向在思考產品而不是使用者經驗導向。</p>
<p>那什麼又是使用者經驗? 使用者經驗扯到的範圍很廣，從他發現這產品的開始，到他使用產品本身，這整個過程與體驗都是使用者經驗，比較Syncplicity與Dropbox，Syncplicity光是名稱就輸一截了，想取Simplicity的協音，然而卻是複雜的體現，名稱的發音對很多人都是一個問題，而功能上因為太強大，使用者不知道怎麼用，亂用一通，甚至拿來同步Windows資料夾，而Dropbox只有一個資料夾，要同步就拉檔案進去，簡單易懂，當然是簡單的受到使用者歡迎，而非功能強大的</p>
<h2>技術只不過是一張軟體樂園的入場卷</h2>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p35.png"><img class="alignnone size-full wp-image-1525" title="nowin_p35" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p35.png" alt="" /></a></p>
<p>身為技術人員，我會跟你說技術很重要，但重要到什麼地步? 只不過是一張入場卷，我會這麼說。</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p36.png"><img class="alignnone size-full wp-image-1526" title="nowin_p36" src="http://blog.ez2learn.com/wp-content/uploads/2011/07/nowin_p36.png" alt="" /></a></p>
<p>為什麼這樣說? 原因很簡單，你想進來這圈子玩一玩，沒有技術我會告訴你，門都沒有，然而你如果有很強的技術，恭喜你，你的入場卷比別人的大張，你可以享有優待，你可以玩最新最酷炫的遊樂設施，如果你的技術很爛&#8230;.，那麼很抱歉，你只能玩別人不想玩的無聊遊戲，沒人排隊的地方，玩玩就回家去吧，但是即使你有很大張的入場卷，也沒辦法滿載而歸，因為除了技術以外還有太多東西值得你去玩，不管是市場的遊樂設施、商業上的、金融上的，軟體的開發不是只有技術而已，光是你要去哪裡找人才，市場的定位等等，都不是那麼容易的事情，常常見到很多人有一身的技術，但最後淪為技匠，空有一身本領但是還是沒有舞臺，沒有很好的發揮，我自己很久之前就有這樣的體認，因此我對自己的期望不只有技術上的進步，我也花不少時間在接觸不同的事物上，思考技術以外的問題。</p>
<h2>沒有銀彈</h2>
<p>最後，套用一句軟體工程的名言 「<strong>沒有銀彈</strong>」 ，也就是沒有一種可以解決軟體工程上所有難題的方法、或是想法，這些是我在這過程中學到的經驗，並沒有辦法適用到任何的情況上，至今想法還是多少會有改變，但次每次的改變都是透過深刻的體認和思考才做出的結論，並不是隨波逐流的想法，我講的不一定是對的，在讀我文章的同時，希望也能仔細思考這其中的涵意和你們自己的體驗，找出最適合自己或團隊的想法，才是最重要的事。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2011/07/29/lesson-learned-fro-now-in/feed/</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
	</channel>
</rss>

