<?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; Python</title>
	<atom:link href="http://blog.ez2learn.com/tag/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.ez2learn.com</link>
	<description>Victor&#039;s個人部落格，關於程式設計與小提琴</description>
	<lastBuildDate>Mon, 30 Jan 2012 19:19:45 +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>李大師教初學者用C語言? MIT使用Python</title>
		<link>http://blog.ez2learn.com/2011/07/08/mit-use-python-for-beginne/</link>
		<comments>http://blog.ez2learn.com/2011/07/08/mit-use-python-for-beginne/#comments</comments>
		<pubDate>Fri, 08 Jul 2011 04:04:20 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[中文文章]]></category>
		<category><![CDATA[嘴砲]]></category>
		<category><![CDATA[程式語言]]></category>
		<category><![CDATA[language]]></category>
		<category><![CDATA[MIT]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1454</guid>
		<description><![CDATA[在前一篇文章 李大師您多久沒寫程式了 ? 一百個你不應該繼續用Dev C++的理由，有人提到李家同只是要教程式邏輯的思考方式，所以用Dev C++無關緊要，而且使用的語言是C，其實，光是用C語言說要教沒有基礎的學生程式的邏輯思考，甚至還要讓學生能有興趣，真的有那麼簡單嗎? 答案肯定是很困難的，C語言有很多特性讓初學會感到挫折，當然，使用Dev C++無疑的只是增加遇到問題的機會，對學習本身一點幫助都沒有 在我的看法如果想要教從來沒有經驗的初學者，C語言雖然是最扎實的一條路，也同時是最難走的一條路，以我自己的經驗來說，從國一到國三我自學了三年的VB，到了高中才開始學C/C++，我敢說如果沒有VB那樣簡易上手的那段時期，而一開始就學習如C/C++如此困難的程式語言，或許我會被挫折感打敗 程式語言的學習與思考是可以從高階的層次開始的，先忽略細節，避免太過困難、抽象的概念，有趣的實際小應用入門，C語言顯然都不具備有這樣的特性，與其使用C語言，Python、Basic、Ruby等語言才是更容易上手的，在網路上我所看到很多從5歲到10歲左右就開始學程式語言的高手，都是從Basic之類的語言開始的，但我從來沒有看過有5歲就能從C語言入門的，我想這就顯示出了簡易語言對於入門的重要性。 MIT使用Python做為CS入門程式設計課程 MIT用Python做為Computer science入們程式設計課程的語言，我想這已經不是新聞了，即使大師們活在巨塔中，至少也有網路吧? 這是一個宅在家裡都能學到最新事物的時代，接觸點新的事物好嗎? 很多人都學一招半式教一輩子，或許如果學的是古典音樂，這可能過個一百年都不會改變，但是不幸的是我們所在的是改變最快速的產業，不要期待學校能教多新的東西，但只希望不要跟世界脫節太多，或許你會反駁，很多東西過很久都不會變，像是概念上的東西，但是我會說，我用更現代的工具可以做得更好，一樣可以教不變的概念，只是看你有沒有心想要去做得好而已 這些課程都能在網路上免費找到，以下為MIT的6.00 Introduction to Computer Science and Programming上課的影片，在第一堂課33分左右，裡面的講者提到了程式語言，他說以前他接受的是lisp的訓練，可是他現在教的是Python，我想光這點就顯示出了國外一流大學和國內大學的心態差別，國內的李大師只會很開心的描述當年學了什麼東西，然後把這些十年前、二十年、三十年前學到的東西教給學生，更糟的是國內的李大師之流都沒有太豐富的實戰經驗，他們都以發論文為生，但國外的一流大學的老師其實是很多都有豐富的業界實戰經驗，他們懂得外面真正需要的是什麼 剩下還有很多堂課的影片，在網路上搜尋都找得到，所以就不在此列出]]></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++無關緊要，而且使用的語言是C，其實，光是用C語言說要教沒有基礎的學生程式的邏輯思考，甚至還要讓學生能有興趣，真的有那麼簡單嗎? 答案肯定是很困難的，C語言有很多特性讓初學會感到挫折，當然，使用Dev C++無疑的只是增加遇到問題的機會，對學習本身一點幫助都沒有</p>
<p>在我的看法如果想要教從來沒有經驗的初學者，C語言雖然是最扎實的一條路，也同時是最難走的一條路，以我自己的經驗來說，從國一到國三我自學了三年的VB，到了高中才開始學C/C++，我敢說如果沒有VB那樣簡易上手的那段時期，而一開始就學習如C/C++如此困難的程式語言，或許我會被挫折感打敗</p>
<p>程式語言的學習與思考是可以從高階的層次開始的，先忽略細節，避免太過困難、抽象的概念，有趣的實際小應用入門，C語言顯然都不具備有這樣的特性，與其使用C語言，Python、Basic、Ruby等語言才是更容易上手的，在網路上我所看到很多從5歲到10歲左右就開始學程式語言的高手，都是從Basic之類的語言開始的，但我從來沒有看過有5歲就能從C語言入門的，我想這就顯示出了簡易語言對於入門的重要性。</p>
<h2>MIT使用Python做為CS入門程式設計課程</h2>
<p>MIT用Python做為Computer science入們程式設計課程的語言，我想這已經不是新聞了，即使大師們活在巨塔中，至少也有網路吧? 這是一個宅在家裡都能學到最新事物的時代，接觸點新的事物好嗎? 很多人都學一招半式教一輩子，或許如果學的是古典音樂，這可能過個一百年都不會改變，但是不幸的是我們所在的是改變最快速的產業，不要期待學校能教多新的東西，但只希望不要跟世界脫節太多，或許你會反駁，很多東西過很久都不會變，像是概念上的東西，但是我會說，我用更現代的工具可以做得更好，一樣可以教不變的概念，只是看你有沒有心想要去做得好而已</p>
<p>這些課程都能在網路上免費找到，以下為MIT的<a href="http://mit600.mit.edu/blog/course-info/">6.00 Introduction to Computer Science and Programming</a>上課的影片，在第一堂課33分左右，裡面的講者提到了程式語言，他說以前他接受的是lisp的訓練，可是他現在教的是Python，我想光這點就顯示出了國外一流大學和國內大學的心態差別，國內的李大師只會很開心的描述當年學了什麼東西，然後把這些十年前、二十年、三十年前學到的東西教給學生，更糟的是國內的李大師之流都沒有太豐富的實戰經驗，他們都以發論文為生，但國外的一流大學的老師其實是很多都有豐富的業界實戰經驗，他們懂得外面真正需要的是什麼</p>
<p><iframe width="640" height="385" src="http://www.youtube.com/embed/k6U-i4gXkLM" frameborder="0" type="text/html"></iframe><div style="text-align:right;"><a style="color:#aaa;font-size:9px" href="http://www.clickonf5.org/" title="IFRAME Embed for Youtube Free WordPress Plugin" target="_blank">IFRAME Embed for Youtube</a></div><br />
<iframe width="640" height="385" src="http://www.youtube.com/embed/Pij6J0HsYFA" frameborder="0" type="text/html"></iframe><div style="text-align:right;"><a style="color:#aaa;font-size:9px" href="http://www.clickonf5.org/" title="IFRAME Embed for Youtube Free WordPress Plugin" target="_blank">IFRAME Embed for Youtube</a></div><br />
<iframe width="640" height="385" src="http://www.youtube.com/embed/X6ilT3uUOBo" frameborder="0" type="text/html"></iframe><div style="text-align:right;"><a style="color:#aaa;font-size:9px" href="http://www.clickonf5.org/" title="IFRAME Embed for Youtube Free WordPress Plugin" target="_blank">IFRAME Embed for Youtube</a></div><br />
<iframe width="640" height="385" src="http://www.youtube.com/embed/SXR9CDof7qw" frameborder="0" type="text/html"></iframe><div style="text-align:right;"><a style="color:#aaa;font-size:9px" href="http://www.clickonf5.org/" title="IFRAME Embed for Youtube Free WordPress Plugin" target="_blank">IFRAME Embed for Youtube</a></div><br />
<iframe width="640" height="385" src="http://www.youtube.com/embed/Pfo7r6bjSqI" frameborder="0" type="text/html"></iframe><div style="text-align:right;"><a style="color:#aaa;font-size:9px" href="http://www.clickonf5.org/" title="IFRAME Embed for Youtube Free WordPress Plugin" target="_blank">IFRAME Embed for Youtube</a></div></p>
<p>剩下還有很多堂課的影片，在網路上搜尋都找得到，所以就不在此列出</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2011/07/08/mit-use-python-for-beginne/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Zero-downtime service migration</title>
		<link>http://blog.ez2learn.com/2011/02/04/zero-downtime-service-migration/</link>
		<comments>http://blog.ez2learn.com/2011/02/04/zero-downtime-service-migration/#comments</comments>
		<pubDate>Fri, 04 Feb 2011 09:12:20 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[分享]]></category>
		<category><![CDATA[English Articles]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Unix-Like]]></category>
		<category><![CDATA[0-down-time]]></category>
		<category><![CDATA[hot code swap]]></category>
		<category><![CDATA[migration]]></category>
		<category><![CDATA[service]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1225</guid>
		<description><![CDATA[When I am running my website, now.in, I did encounter a trouble.  That is, when there is a bug in the production server, then I need to restart them.  Sounds fine, right? Just to restart a server.  Yes, for most &#8230; <a href="http://blog.ez2learn.com/2011/02/04/zero-downtime-service-migration/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>When I am running my website, now.in, I did encounter a trouble.  That is, when there is a bug in the production server, then I need to restart them.  Sounds fine, right? Just to restart a server.  Yes, for most of web HTTP servers, they are stateless,  it is fine to restart them whenever you want, but not true for now.in, they are audio streaming servers.  Here s a server stats diagram shows the problem:</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/02/server_diagram.png"><img class="alignnone size-medium wp-image-1217" title="server_diagram" src="http://blog.ez2learn.com/wp-content/uploads/2011/02/server_diagram-300x196.png" alt="" width="300" height="196" /></a></p>
<p>You can see there are some gaps in the plot, that&#8217;s caused by server restarting.  Of course, for users, that would definitely be bad experience.  Thus, I&#8217;m thinking how to solve this problem recently.  Before we go into the design, let&#8217;s look the reasons for restarting server first.</p>
<ul>
<li>To deploy new version of program</li>
<li>To fix bugs</li>
<li>The process is using to much memory</li>
<li>To reload environment, ulimit -n for example (the limit count of file descriptor under unix-like environment)</li>
<li>To migrate from host A to host B</li>
</ul>
<p>For simply employe new version of program, we can use reload function of Python to reload modules.  But there is some problems, reload function only rerun the module, those created instances are still there, it might work if the change is minor.  On the other hand,  reloading can&#8217;t solve memory usage problem, process environment change problem.  And here comes the final reason, to migrate service from host A to B.  Indeed, it is difficult not to make any down time for such migration, and there is little chance to do such migration, therefore, we&#8217;ll only focus on migration in same host.</p>
<h2>The idea</h2>
<p>The biggest challenge is, how to migrate those working connections?  My idea is simple, create a new process, and transfer those connection to the new process, and shut the old one down.  Following diagrams show my method.</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_011.png"><img class="alignnone size-medium wp-image-1226" title="mig_01" src="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_011-226x300.png" alt="" width="226" height="300" /></a></p>
<p>The Master is a process which is charge for managing migration and receiving command from other process.  And the process A is for running service.</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_02.png"><img class="alignnone size-medium wp-image-1220" title="mig_02" src="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_02-300x263.png" alt="" width="300" height="263" /></a></p>
<p>Before we perform the migration, the Manager startup process B, and wait it says it&#8217;s readly.</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_03.png"><img class="alignnone size-medium wp-image-1221" title="mig_03" src="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_03-300x263.png" alt="" width="300" height="263" /></a></p>
<p>When process B said "Hey! I&#8217;m ready", then manager tell process A to send the connection state descriptor to process B.  Process B receive the state, and take over the service.</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_04.png"><img class="alignnone size-medium wp-image-1222" title="mig_04" src="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_04-243x300.png" alt="" width="243" height="300" /></a></p>
<p>Finally, process B took over the service, then master tells process A "You are fired." and the process A rolls itself out.</p>
<p>That&#8217;s it, the service was migrated, and there is no down time.</p>
<h2>The problem &#8211; socket transfer</h2>
<p>The idea sounds good, right? But still, we have some technical problem to solve.  It is "How to transfer socket (file descriptor) from one process to another?".  To solve this problem, I have some study, and eventually I know two methods to achieve the goal.</p>
<h3>Child process</h3>
<p>For most of unix-like OS, child processes inherit file descriptors from their parent.  Of course we can use this feature to migrate our service, but however, it got its limitation.  You can only transfer file descriptors to child process.</p>
<h3>Sendmsg</h3>
<p>Another way to achieve same goal is, to use sendmsg through a unix domain socket to send the file descriptors.  With sendmsg, you can transfer file descriptors to almost any processes you like, that&#8217;s much flexible.</p>
<h2>A simple implementation</h2>
<p>To simplify the example, we only implement process A and process B here, it is quite enough for two process to complete the migration.  Before we go into the details, there is another problem to solve, which is sendmsg is not a standard function in Python.  Fortunately, there is a third party library named sendmsg provides this function.  To install sendmsg, it is easy, just type</p>
<p>easy_install sendmsg</p>
<p>And here you are.  Okay, following are the two programs.</p>
<p>a.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> <span style="color: #dc143c;">socket</span>
<span style="color: #ff7700;font-weight:bold;">import</span> sendmsg
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">struct</span>
&nbsp;
s = <span style="color: #dc143c;">socket</span>.<span style="color: #dc143c;">socket</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">socket</span>.<span style="color: black;">AF_INET</span>, <span style="color: #dc143c;">socket</span>.<span style="color: black;">SOCK_STREAM</span><span style="color: black;">&#41;</span>
s.<span style="color: black;">bind</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span>, <span style="color: #ff4500;">5566</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
s.<span style="color: black;">listen</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
conn, addr = s.<span style="color: black;">accept</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
conn.<span style="color: black;">send</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Hello, process %d is serving<span style="color: #000099; font-weight: bold;">\n</span>'</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><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Accept inet connection'</span>, conn
&nbsp;
us = <span style="color: #dc143c;">socket</span>.<span style="color: #dc143c;">socket</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">socket</span>.<span style="color: black;">AF_UNIX</span>, <span style="color: #dc143c;">socket</span>.<span style="color: black;">SOCK_STREAM</span><span style="color: black;">&#41;</span>
us.<span style="color: black;">bind</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'mig.sock'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
us.<span style="color: black;">listen</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
uconn, addr = us.<span style="color: black;">accept</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;">'Accept unix connection'</span>, uconn
&nbsp;
payload = <span style="color: #dc143c;">struct</span>.<span style="color: black;">pack</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'i'</span>, conn.<span style="color: black;">fileno</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
sendmsg.<span style="color: black;">sendmsg</span><span style="color: black;">&#40;</span>
    uconn.<span style="color: black;">fileno</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>, <span style="color: #483d8b;">''</span>, <span style="color: #ff4500;">0</span>, <span style="color: black;">&#40;</span><span style="color: #dc143c;">socket</span>.<span style="color: black;">SOL_SOCKET</span>, sendmsg.<span style="color: black;">SCM_RIGHTS</span>, payload<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Sent socket'</span>, conn.<span style="color: black;">fileno</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;">'Done.'</span></pre></div></div>

<p>b.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> <span style="color: #dc143c;">socket</span>
<span style="color: #ff7700;font-weight:bold;">import</span> sendmsg
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">struct</span>
&nbsp;
us = <span style="color: #dc143c;">socket</span>.<span style="color: #dc143c;">socket</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">socket</span>.<span style="color: black;">AF_UNIX</span>, <span style="color: #dc143c;">socket</span>.<span style="color: black;">SOCK_STREAM</span><span style="color: black;">&#41;</span>
us.<span style="color: black;">connect</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'mig.sock'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Make unix connection'</span>, us
&nbsp;
result = sendmsg.<span style="color: black;">recvmsg</span><span style="color: black;">&#40;</span>us.<span style="color: black;">fileno</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
identifier, flags, <span style="color: black;">&#91;</span><span style="color: black;">&#40;</span>level, <span style="color: #008000;">type</span>, data<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span> = result
<span style="color: #ff7700;font-weight:bold;">print</span> identifier, flags, <span style="color: black;">&#91;</span><span style="color: black;">&#40;</span>level, <span style="color: #008000;">type</span>, data<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
fd = <span style="color: #dc143c;">struct</span>.<span style="color: black;">unpack</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'i'</span>, data<span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Get fd'</span>, fd
&nbsp;
conn = <span style="color: #dc143c;">socket</span>.<span style="color: black;">fromfd</span><span style="color: black;">&#40;</span>fd, <span style="color: #dc143c;">socket</span>.<span style="color: black;">AF_INET</span>, <span style="color: #dc143c;">socket</span>.<span style="color: black;">SOCK_STREAM</span><span style="color: black;">&#41;</span>
<span style="color: #dc143c;">os</span>.<span style="color: black;">close</span><span style="color: black;">&#40;</span>fd<span style="color: black;">&#41;</span>
conn.<span style="color: black;">send</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Hello, process %d is serving<span style="color: #000099; font-weight: bold;">\n</span>'</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><span style="color: black;">&#41;</span>
<span style="color: #008000;">raw_input</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>The flow is simple, the a.py accept an inet socket and open an unix domain socket, wait b.py to take over the service.  And here we runs b.py, it connects to a.py, receives the fd of socket and takes over providing service.</p>
<h2>The result</h2>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/02/螢幕快照-2011-02-04-下午4.07.07.png"><img class="alignnone size-full wp-image-1223" title="螢幕快照 2011-02-04 下午4.07.07" src="http://blog.ez2learn.com/wp-content/uploads/2011/02/螢幕快照-2011-02-04-下午4.07.07.png" alt="" width="656" height="440" /></a></p>
<p>As the result shows, there is not down time between the service migration.</p>
<p>It is very useful to employ this technique in server programs.  You can even migrate service from Python server to a C/C++ server, or vice versa.  Also, to keep the memory usage low, you can also migrate the service to same program periodically.  I will try to employ this technique in my servers to achieve zero down time migration.  If you are interesting in this technique, have a try, it is fun and useful :)</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2011/02/04/zero-downtime-service-migration/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>無停機服務遷移</title>
		<link>http://blog.ez2learn.com/2011/02/04/zero-down-time-service-migration/</link>
		<comments>http://blog.ez2learn.com/2011/02/04/zero-down-time-service-migration/#comments</comments>
		<pubDate>Fri, 04 Feb 2011 08:15:30 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[中文文章]]></category>
		<category><![CDATA[分享]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[0-down-time]]></category>
		<category><![CDATA[伺服器]]></category>
		<category><![CDATA[網路連線]]></category>
		<category><![CDATA[無停機]]></category>
		<category><![CDATA[Network]]></category>
		<category><![CDATA[sendmsg]]></category>
		<category><![CDATA[Server]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1214</guid>
		<description><![CDATA[一直以來在營運now.in都有一個令人困擾的問題，就是每當伺服器更新或是出現問題時，就得關掉重開，雖然說大部份的伺服器我在設計上都做成重開也沒有關係，但有少部份一但重啟就會造成使用者斷線，參考下面這張伺服器狀態的圖 圖中被切斷的山峰都是伺服器重啟的時候，無疑的這對使用者來說是不好的體驗，對於一般網頁HTTP這類stateless的伺服器來說重啟是無所謂，但串流伺服器就不一樣，因此最近我在思考如何解決這樣的問題，首先想到的是伺服器重開的原因，不外乎有幾種 部署新版的程式 為了修正BUG 記憶體用量過高 重新讀取執行環境，例如ulimit -n，也就是檔案數量的限制大小 從主機A移到主機B 以Python的伺服器來說，如果只是單單只是為了部署新版的程式，Python有個reload函數可以重讀整個module，因此可以設個管理用的後門用來重載模組，但是這樣做有個問題，就是已經產生的instance還是一樣，其實說穿了等於是重新執行那個module，如果只是簡單的改版還好，但複雜的改版就可能牽扯到太多因素難以透過這樣來更新，而除此之外，很多重開的因素像是記憶體用量過高，或著執行的環境參數改變，這些都無可避免的一定得建新的process，至於從A主機移到B主機，基本上是難以避免的一定得重啟伺服器，除非有某個前端的伺服器保持連線，後端的伺服器進行交移工作才有可能，而主機轉移的狀況其實很少出現，因此這篇文章要探討的是如何辦到同一臺機器內的服務轉移 方法 最大的問題就在於，連線中的socket該如保持住連線，最簡單的想法就是建立新的process，將目前的連線交給新的process，新的process接手完所有的連線開始運作後舊的process就能終止了，當然所謂的連線還包函了服務的狀態，我們在者裡稱之為 CSD (Connection State Descriptor)，大致想法如下面圖片所示 首先Process A是正在運行的process，而Manager是負責控制遷移的process 此時Manager啟動了要負責接手的Process B 接著Manager通知A將連線狀態傳送給Process B，B接手後繼續提供服務，並且告知接收完成 在確定B能夠接手服務並且正確執行後，這時Manager就能將A中止，走到這裡服務就算是完整地移交了，中間沒有任何間斷 技術層面問題 &#8211; Socket的轉移 這樣的做法理論上看起來確實可行沒錯，但是最主要有技術上的問題得解決，就是如何將socket，或著是file descriptor轉移到另一個process，在我找過相關的資料後知道目前有兩個方法 child process 這個方法主要是因為child process在生成後會繼承parent process的file descriptor，但是缺點就是只限於child process，如果我們想將服務移交給另一個完整的獨立process這個方法就會行不通，因此使用上會有很多限制 sendmsg 另一個方法就是unix domain的socket有提供一個函數叫sendmsg，可以將file descriptor轉移到任何process，這樣一來實用性就比child process高很多，於是我決定採用此方法 簡易的實作 &#8230; <a href="http://blog.ez2learn.com/2011/02/04/zero-down-time-service-migration/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>一直以來在營運now.in都有一個令人困擾的問題，就是每當伺服器更新或是出現問題時，就得關掉重開，雖然說大部份的伺服器我在設計上都做成重開也沒有關係，但有少部份一但重啟就會造成使用者斷線，參考下面這張伺服器狀態的圖</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/02/server_diagram.png"><img class="alignnone size-medium wp-image-1217" title="server_diagram" src="http://blog.ez2learn.com/wp-content/uploads/2011/02/server_diagram-300x196.png" alt="" width="300" height="196" /></a></p>
<p>圖中被切斷的山峰都是伺服器重啟的時候，無疑的這對使用者來說是不好的體驗，對於一般網頁HTTP這類stateless的伺服器來說重啟是無所謂，但串流伺服器就不一樣，因此最近我在思考如何解決這樣的問題，首先想到的是伺服器重開的原因，不外乎有幾種</p>
<ul>
<li>部署新版的程式</li>
<li>為了修正BUG</li>
<li>記憶體用量過高</li>
<li>重新讀取執行環境，例如ulimit -n，也就是檔案數量的限制大小</li>
<li>從主機A移到主機B</li>
</ul>
<p>以Python的伺服器來說，如果只是單單只是為了部署新版的程式，Python有個reload函數可以重讀整個module，因此可以設個管理用的後門用來重載模組，但是這樣做有個問題，就是已經產生的instance還是一樣，其實說穿了等於是重新執行那個module，如果只是簡單的改版還好，但複雜的改版就可能牽扯到太多因素難以透過這樣來更新，而除此之外，很多重開的因素像是記憶體用量過高，或著執行的環境參數改變，這些都無可避免的一定得建新的process，至於從A主機移到B主機，基本上是難以避免的一定得重啟伺服器，除非有某個前端的伺服器保持連線，後端的伺服器進行交移工作才有可能，而主機轉移的狀況其實很少出現，因此這篇文章要探討的是如何辦到同一臺機器內的服務轉移</p>
<h2>方法</h2>
<p>最大的問題就在於，連線中的socket該如保持住連線，最簡單的想法就是建立新的process，將目前的連線交給新的process，新的process接手完所有的連線開始運作後舊的process就能終止了，當然所謂的連線還包函了服務的狀態，我們在者裡稱之為 CSD (Connection State Descriptor)，大致想法如下面圖片所示</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_01.png"><img class="alignnone size-medium wp-image-1219" title="Migration process 01" src="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_01-226x300.png" alt="" width="226" height="300" /></a></p>
<p>首先Process A是正在運行的process，而Manager是負責控制遷移的process</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_02.png"><img class="alignnone size-medium wp-image-1220" title="mig_02" src="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_02-300x263.png" alt="" width="300" height="263" /></a></p>
<p>此時Manager啟動了要負責接手的Process B</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_03.png"><img class="alignnone size-medium wp-image-1221" title="mig_03" src="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_03-300x263.png" alt="" width="300" height="263" /></a></p>
<p>接著Manager通知A將連線狀態傳送給Process B，B接手後繼續提供服務，並且告知接收完成</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_04.png"><img class="alignnone size-medium wp-image-1222" title="mig_04" src="http://blog.ez2learn.com/wp-content/uploads/2011/02/mig_04-243x300.png" alt="" width="243" height="300" /></a></p>
<p>在確定B能夠接手服務並且正確執行後，這時Manager就能將A中止，走到這裡服務就算是完整地移交了，中間沒有任何間斷</p>
<h2>技術層面問題 &#8211; Socket的轉移</h2>
<p>這樣的做法理論上看起來確實可行沒錯，但是最主要有技術上的問題得解決，就是如何將socket，或著是file descriptor轉移到另一個process，在我找過相關的資料後知道目前有兩個方法</p>
<h3>child process</h3>
<p>這個方法主要是因為child process在生成後會繼承parent process的file descriptor，但是缺點就是只限於child process，如果我們想將服務移交給另一個完整的獨立process這個方法就會行不通，因此使用上會有很多限制</p>
<h3>sendmsg</h3>
<p>另一個方法就是unix domain的socket有提供一個函數叫sendmsg，可以將file descriptor轉移到任何process，這樣一來實用性就比child process高很多，於是我決定採用此方法</p>
<h2>簡易的實作</h2>
<p>在這樣的轉移架構設計，Manager主要目的是要能夠對外有個統一個process能溝通，如果不需要的話其實兩個process就能達成服務轉移，為了實作簡單因此這個例子只用了兩個process進行轉移</p>
<p>還有另一個問題是Python標準函式庫裡沒有sendmsg，不過幸好有第三方函式庫已經準備好，就叫做sendmsg，安裝也很簡單，只要打</p>
<p>easy_install sendmsg</p>
<p>就可以了，接下來就是兩個範例程式</p>
<p>a.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> <span style="color: #dc143c;">socket</span>
<span style="color: #ff7700;font-weight:bold;">import</span> sendmsg
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">struct</span>
&nbsp;
s = <span style="color: #dc143c;">socket</span>.<span style="color: #dc143c;">socket</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">socket</span>.<span style="color: black;">AF_INET</span>, <span style="color: #dc143c;">socket</span>.<span style="color: black;">SOCK_STREAM</span><span style="color: black;">&#41;</span>
s.<span style="color: black;">bind</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span>, <span style="color: #ff4500;">5566</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
s.<span style="color: black;">listen</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
conn, addr = s.<span style="color: black;">accept</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
conn.<span style="color: black;">send</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Hello, process %d is serving<span style="color: #000099; font-weight: bold;">\n</span>'</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><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Accept inet connection'</span>, conn
&nbsp;
us = <span style="color: #dc143c;">socket</span>.<span style="color: #dc143c;">socket</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">socket</span>.<span style="color: black;">AF_UNIX</span>, <span style="color: #dc143c;">socket</span>.<span style="color: black;">SOCK_STREAM</span><span style="color: black;">&#41;</span>
us.<span style="color: black;">bind</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'mig.sock'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
us.<span style="color: black;">listen</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
uconn, addr = us.<span style="color: black;">accept</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;">'Accept unix connection'</span>, uconn
&nbsp;
payload = <span style="color: #dc143c;">struct</span>.<span style="color: black;">pack</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'i'</span>, conn.<span style="color: black;">fileno</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
sendmsg.<span style="color: black;">sendmsg</span><span style="color: black;">&#40;</span>
    uconn.<span style="color: black;">fileno</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>, <span style="color: #483d8b;">''</span>, <span style="color: #ff4500;">0</span>, <span style="color: black;">&#40;</span><span style="color: #dc143c;">socket</span>.<span style="color: black;">SOL_SOCKET</span>, sendmsg.<span style="color: black;">SCM_RIGHTS</span>, payload<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Sent socket'</span>, conn.<span style="color: black;">fileno</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;">'Done.'</span></pre></div></div>

<p>b.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> <span style="color: #dc143c;">socket</span>
<span style="color: #ff7700;font-weight:bold;">import</span> sendmsg
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">struct</span>
&nbsp;
us = <span style="color: #dc143c;">socket</span>.<span style="color: #dc143c;">socket</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">socket</span>.<span style="color: black;">AF_UNIX</span>, <span style="color: #dc143c;">socket</span>.<span style="color: black;">SOCK_STREAM</span><span style="color: black;">&#41;</span>
us.<span style="color: black;">connect</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'mig.sock'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Make unix connection'</span>, us
&nbsp;
result = sendmsg.<span style="color: black;">recvmsg</span><span style="color: black;">&#40;</span>us.<span style="color: black;">fileno</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
identifier, flags, <span style="color: black;">&#91;</span><span style="color: black;">&#40;</span>level, <span style="color: #008000;">type</span>, data<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span> = result
<span style="color: #ff7700;font-weight:bold;">print</span> identifier, flags, <span style="color: black;">&#91;</span><span style="color: black;">&#40;</span>level, <span style="color: #008000;">type</span>, data<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
fd = <span style="color: #dc143c;">struct</span>.<span style="color: black;">unpack</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'i'</span>, data<span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Get fd'</span>, fd
&nbsp;
conn = <span style="color: #dc143c;">socket</span>.<span style="color: black;">fromfd</span><span style="color: black;">&#40;</span>fd, <span style="color: #dc143c;">socket</span>.<span style="color: black;">AF_INET</span>, <span style="color: #dc143c;">socket</span>.<span style="color: black;">SOCK_STREAM</span><span style="color: black;">&#41;</span>
<span style="color: #dc143c;">os</span>.<span style="color: black;">close</span><span style="color: black;">&#40;</span>fd<span style="color: black;">&#41;</span>
conn.<span style="color: black;">send</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Hello, process %d is serving<span style="color: #000099; font-weight: bold;">\n</span>'</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><span style="color: black;">&#41;</span>
<span style="color: #008000;">raw_input</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>大致上的流程很簡單，首先a.py接收一個inet的連線，接著就開一個unix socket，等待b.py上線，然後把服務透過sendmsg轉移給b.py</p>
<h2>執行結果</h2>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/02/螢幕快照-2011-02-04-下午4.07.07.png"><img class="alignnone size-full wp-image-1223" title="螢幕快照 2011-02-04 下午4.07.07" src="http://blog.ez2learn.com/wp-content/uploads/2011/02/螢幕快照-2011-02-04-下午4.07.07.png" alt="" width="656" height="440" /></a></p>
<p>由執行結果我們可以看見，原本的連線是由process A提供的，然後轉交給process B，中間沒有任何中斷</p>
<p>有了這樣的技術基礎和想法，接著就剩下的是細節，基本上連線的狀態盡量以抽象的方式提供，如此一來任何程式只要能夠處理連線狀態就能夠接手，甚至可以由Python寫的伺服器轉移到由C/C++寫的伺服器，這些都可以辦到，中間服務都不會有間斷，而且也可以像HTTP伺服器一樣，處理到一定的量自動開新的process轉移給自己，如此一來可以保持低記憶體用量，有相當多的好處，未來我會試著將伺服器以這樣的想法改寫，應該就能做到無停機更新，有興趣的話也可以自己試試看</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2011/02/04/zero-down-time-service-migration/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Logy &#8211; a central logging system for Python</title>
		<link>http://blog.ez2learn.com/2011/01/01/logy-a-central-logging-system-for-python/</link>
		<comments>http://blog.ez2learn.com/2011/01/01/logy-a-central-logging-system-for-python/#comments</comments>
		<pubDate>Sat, 01 Jan 2011 09:30:46 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[分享]]></category>
		<category><![CDATA[English Articles]]></category>
		<category><![CDATA[Logging]]></category>
		<category><![CDATA[Open source]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1174</guid>
		<description><![CDATA[I&#8217;m glad to announce my new open source project:Logy. Logy is a simple lightweight central logging system for Python. When do you need a central logging system?  Well, when you have many servers online, and they are running different python &#8230; <a href="http://blog.ez2learn.com/2011/01/01/logy-a-central-logging-system-for-python/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m glad to announce my new open source project:Logy.  Logy is a simple lightweight central logging system for Python. When do you need a central logging system?  Well, when you have many servers online, and they are running different python application, you will want to have a central logging system for making sure everything is going fine.  And you might ask, why not just use email for error reports? Well, following screenshot explains:</p>
<p><img class="alignnone size-medium wp-image-1175" title="error_reports" src="http://blog.ez2learn.com/wp-content/uploads/2011/01/error_reports-300x238.png" alt="" width="300" height="238" /></p>
<p>I use email as error logging report, but however, as you can see if there is something wrong with my servers in same time, network outage for instance, there will be so many error report mails come in my mailbox like crazy.  Therefore think a central log might be a good idea, then I decide to develop a simple central logging system for my own servers.  Here is a screenshot of the Logy web page.</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2011/01/logy_screenshot01.png"><img class="alignnone size-full wp-image-1178" title="logy_screenshot01" src="http://blog.ez2learn.com/wp-content/uploads/2011/01/logy_screenshot01.png" alt="" width="732" height="617" /></a></p>
<p>It categorizes your logging records by IP address automatically, and also, you can set a application name for different applications which are running in same box.  And one important feature is, it is base on Flask :)</p>
<p>It is super easy to write records to Logy, here is an example:</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;">logging</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> ex_loghandlers
&nbsp;
rootLogger = <span style="color: #dc143c;">logging</span>.<span style="color: black;">getLogger</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span>
rootLogger.<span style="color: black;">setLevel</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">logging</span>.<span style="color: black;">DEBUG</span><span style="color: black;">&#41;</span>
&nbsp;
sream_handler = <span style="color: #dc143c;">logging</span>.<span style="color: black;">StreamHandler</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># set up the http handler which writes records to Logy server</span>
http_handler = ex_loghandlers.<span style="color: black;">ExHTTPHandler</span><span style="color: black;">&#40;</span>host=<span style="color: #483d8b;">'localhost:5000'</span>,
                                            url=<span style="color: #483d8b;">'/sink/TEST/myapp'</span>,
                                            method=<span style="color: #483d8b;">'POST'</span><span style="color: black;">&#41;</span>
rootLogger.<span style="color: black;">addHandler</span><span style="color: black;">&#40;</span>sream_handler<span style="color: black;">&#41;</span>
rootLogger.<span style="color: black;">addHandler</span><span style="color: black;">&#40;</span>http_handler<span style="color: black;">&#41;</span>
&nbsp;
logger1 = <span style="color: #dc143c;">logging</span>.<span style="color: black;">getLogger</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'myapp.area1'</span><span style="color: black;">&#41;</span>
logger2 = <span style="color: #dc143c;">logging</span>.<span style="color: black;">getLogger</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'myapp.area2'</span><span style="color: black;">&#41;</span>
&nbsp;
logger1.<span style="color: black;">debug</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Quick zephyrs blow, vexing daft Jim.'</span><span style="color: black;">&#41;</span>
logger1.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'How quickly daft jumping zebras vex.'</span><span style="color: black;">&#41;</span>
logger2.<span style="color: black;">warning</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Jail zesty vixen who grabbed pay from quack.'</span><span style="color: black;">&#41;</span>
logger2.<span style="color: black;">error</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'The five boxing wizards jump quickly.'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">try</span>:
    <span style="color: #ff7700;font-weight:bold;">raise</span> <span style="color: #008000;">Exception</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Boom&quot;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
    logger1.<span style="color: black;">fatal</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Huston, we've got a problem&quot;</span>, exc_info=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span> 
&nbsp;
logger1.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Everything goes fine, now.'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>You need to install my module named ex_loghandlers before you can run this script, it contains an ExHTTPHandler which provides extra information for Logy.</p>
<p>Here are some more information of Logy:</p>
<p>PyPi page: <a href="http://pypi.python.org/pypi/logy">http://pypi.python.org/pypi/logy</a></p>
<p>Bitbucket: <a href="https://bitbucket.org/victorlin/logy/src">https://bitbucket.org/victorlin/logy/src</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2011/01/01/logy-a-central-logging-system-for-python/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Po translate 自動化翻譯工具</title>
		<link>http://blog.ez2learn.com/2010/10/18/po-translate/</link>
		<comments>http://blog.ez2learn.com/2010/10/18/po-translate/#comments</comments>
		<pubDate>Mon, 18 Oct 2010 12:59:01 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[中文文章]]></category>
		<category><![CDATA[分享]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[工具]]></category>
		<category><![CDATA[國際化]]></category>
		<category><![CDATA[繁簡轉換]]></category>
		<category><![CDATA[自動翻譯]]></category>
		<category><![CDATA[gettext]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[simplified chinese]]></category>
		<category><![CDATA[tool]]></category>
		<category><![CDATA[tradition chinese]]></category>
		<category><![CDATA[translate]]></category>
		<category><![CDATA[zh-cn]]></category>
		<category><![CDATA[zh-tw]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1099</guid>
		<description><![CDATA[之前有寫一個 PO檔自動繁簡轉換程式，但是因為沒有把它傳到PyPi上，所以要到處copy貼上很麻煩，為了方便使用，我把它打包並且傳到了PyPi，以MIT License開放源始碼，不過只是一個很簡單的程式而已，我想應該沒有多少人會想改些什麼，但是當然有人有興趣的話，歡迎修改 這個程式主要是用來解決繁體中文的.PO檔和簡體之間的互換問題，因為翻譯完繁體中文，基本上和簡體沒有太大差別，用Google translate轉成簡體就可以，但是手工作起來很煩，這種事情當然是交給程式執行 安裝方法很簡單 easy_install po_translate 使用也很簡單 po_translate input.po output.po zh-TW zh-CN 像這樣就能輕鬆的透過Google translate把繁中的.PO檔轉成簡中的 相關連結 PyPi的連結 Bitbucket的Repo]]></description>
			<content:encoded><![CDATA[<p>之前有寫一個 <a href="http://blog.ez2learn.com/2010/04/28/po-file-zh_tw-to-zh_cn-program/">PO檔自動繁簡轉換程式</a>，但是因為沒有把它傳到PyPi上，所以要到處copy貼上很麻煩，為了方便使用，我把它打包並且傳到了PyPi，以MIT License開放源始碼，不過只是一個很簡單的程式而已，我想應該沒有多少人會想改些什麼，但是當然有人有興趣的話，歡迎修改</p>
<p>這個程式主要是用來解決繁體中文的.PO檔和簡體之間的互換問題，因為翻譯完繁體中文，基本上和簡體沒有太大差別，用Google translate轉成簡體就可以，但是手工作起來很煩，這種事情當然是交給程式執行</p>
<p>安裝方法很簡單</p>
<blockquote><p>easy_install po_translate</p></blockquote>
<p>使用也很簡單</p>
<blockquote><p>po_translate input.po output.po zh-TW zh-CN</p></blockquote>
<p>像這樣就能輕鬆的透過Google translate把繁中的.PO檔轉成簡中的</p>
<p>相關連結</p>
<p><a href="http://pypi.python.org/pypi/po_translate">PyPi的連結</a></p>
<p><a href="http://bitbucket.org/victorlin/po_translate">Bitbucket的Repo</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/10/18/po-translate/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>A tool for applying iptables safely: apply_firewall.</title>
		<link>http://blog.ez2learn.com/2010/08/26/a-tool-for-applying-iptables-safely-apply_firewall/</link>
		<comments>http://blog.ez2learn.com/2010/08/26/a-tool-for-applying-iptables-safely-apply_firewall/#comments</comments>
		<pubDate>Thu, 26 Aug 2010 10:39:11 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[English Articles]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[administration]]></category>
		<category><![CDATA[iptables]]></category>
		<category><![CDATA[tool]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1091</guid>
		<description><![CDATA[Have you ever done something stupid with iptables command which like blocking you self from accessing the SSH?  Yes, I have.  Most of administrator knows that is dangerous to change iptable rules remotely, with a little typo, you might have &#8230; <a href="http://blog.ez2learn.com/2010/08/26/a-tool-for-applying-iptables-safely-apply_firewall/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Have you ever done something stupid with iptables command which like blocking you self from accessing the SSH?  Yes, I have.  Most of administrator knows that is dangerous to change iptable rules remotely, with a little typo, you might have to restart the machine to access the SSH again.  For applying iptables sfaely, I wrote a simple tool which can be used to apply firewall rules safely.  It backups the original iptables configuration before applying it, and if you don&#8217;t type "yes" within specific time period, it will rollback to original iptables automatically.  Here is an example:</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2010/08/apply_firewall.png"><img class="alignnone size-full wp-image-1092" title="apply_firewall" src="http://blog.ez2learn.com/wp-content/uploads/2010/08/apply_firewall.png" alt="" width="453" height="231" /></a></p>
<p>It is written in Python, you can install it by</p>
<blockquote><p>easy_install apply_firewall</p></blockquote>
<p>Or you can download it here:</p>
<p><a href="http://pypi.python.org/pypi/apply_firewall/">http://pypi.python.org/pypi/apply_firewall/</a></p>
<p>I hope this could be helpful for linux administrators :D</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/08/26/a-tool-for-applying-iptables-safely-apply_firewall/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>淺談coroutine與gevent</title>
		<link>http://blog.ez2learn.com/2010/07/17/talk-about-coroutine-and-gevent/</link>
		<comments>http://blog.ez2learn.com/2010/07/17/talk-about-coroutine-and-gevent/#comments</comments>
		<pubDate>Sat, 17 Jul 2010 14:06:53 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[中文文章]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Coroutine]]></category>
		<category><![CDATA[Gevent]]></category>
		<category><![CDATA[Network]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1068</guid>
		<description><![CDATA[這篇文章是要大略介紹一下coroutine和Python的相關應用的函式庫gevent，在介紹coroutine前我們先來點情境，因為目前常見的coroutine應用都是在網路程式上，因此我們得先建立一些網路框架的模形再介紹coroutine會比較容易懂 不同的網路框架模型 網路隨著時代發展，已經成為現代生活中越來越重要的重要的基礎，而做為提供這些服務的伺服器，負載的連線也越來越多，因此網路程式的軟體、硬體架構也一直在演變，才有辦法承載越來越多的連線數量，有人希望在一台機器能夠同時處理1萬個連線以上，所以提出C10K問題，並且之後有不少新的的技術來達成這個目標，而在此我們只專注在於軟體的架構上，首先介紹最簡單的架構 阻塞式單一行程 這樣的網路程式非常的簡單，只有一個迴圈，單一個行程，處理完一個要求後才繼續處理下一個，理所當然這樣的效能非常差，因為連線在完成前其它的連線都無法被處理，現代的伺服器已經很少看見這樣的架構，但因為優點是簡單，如果沒有什麼大量同時連線要處理，其實這樣的架構就很足夠 阻塞式多行程 因為既然單一行程只能同時處理一個請求，那很簡單的想法就是每個請求開一個行程去處理，如此一來就能同時處理更多的請求，但是這樣做有缺點，行程的copy如果是fork的話，有os paging system在成本上其實還好，但是還是有，而且越多的連線就表示需要越多的context-switch，當連線量多到一定程度時，可能大部份的CPU時間都在忙著進行context-switch，如此一來這樣的架構在此情況下是沒有效率的，但是優點是寫伺服器的部份事實上和寫單一行程阻塞式不會差太多，一樣簡單好寫 阻塞式多行程多執行緒 除了多行程多阻塞式，有些程式為了減少process copy的成本，或是其它的考量，會在多個行程上開多個執行緒來處理請求，也有可能是單行程多執行緒，但是基本上和上面這幾種都沒有太大的差別，而引進了執行緒帶來了一些額外的問題，dead lock、race condition等等，當不同的執行緒如果在一起有共同的東西要處理，這些常見的同作問題就會出現，使得程式得寫得更小心 非阻塞式事件驅動 為了解決上面提到多行程和多執行緒等所帶來的問題，有一種做法是只有單一主要的迴圈來檢查有無網路IO的事件發生，再來決定要怎樣處理，這樣的好處在於省掉了context-switch、process copy等等成本，也不會有dead lock、race condition等問題，但缺點在於程式的部份會變複雜，因為當你一件事件被觸發，有事情還沒做完，你就得記下目前狀態，再下次事件觸發時再依先前的狀態來決定接下來要做什麼，不像上面是線性的程式執行那樣直觀，Twisted就是這樣的網路框架 非阻塞式Coroutine 那你或許會想，有沒有可能我們能有事件驅動的好處，和阻塞式那樣的直觀好處呢? 答案或許就是Coroutine，基本上它的本質也是事件驅動，只有單一的迴圈在檢查事件的發生，但是加上了coroutine的概念，而Gevent就是這樣的函式庫 Coroutine 講了這麼多次coroutine，我相信大部份的讀者可能還是不懂這到底是什麼鬼東西，對於大部份程式設計師而言這應該都算是較陌生的名字，對我而言，在一開始這也是個令人困惑的名詞，但事實上只要理解以後就會發現coroutine不是這麼的難懂，用簡單的一句話來說Coroutine，就是可以暫時中斷，之後再繼續執行的程序，我們來看一個例子，事實上Python就有最基礎的Coroutine，也就是generator # -*- coding: utf8 -*- def foo&#40;&#41;: for i in range&#40;10&#41;: # 丟資料並且把主控權交給呼叫者 yield i print &#8230; <a href="http://blog.ez2learn.com/2010/07/17/talk-about-coroutine-and-gevent/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>這篇文章是要大略介紹一下coroutine和Python的相關應用的函式庫gevent，在介紹coroutine前我們先來點情境，因為目前常見的coroutine應用都是在網路程式上，因此我們得先建立一些網路框架的模形再介紹coroutine會比較容易懂</p>
<h2>不同的網路框架模型</h2>
<p>網路隨著時代發展，已經成為現代生活中越來越重要的重要的基礎，而做為提供這些服務的伺服器，負載的連線也越來越多，因此網路程式的軟體、硬體架構也一直在演變，才有辦法承載越來越多的連線數量，有人希望在一台機器能夠同時處理1萬個連線以上，所以提出<a href="http://www.kegel.com/c10k.html">C10K問題</a>，並且之後有不少新的的技術來達成這個目標，而在此我們只專注在於軟體的架構上，首先介紹最簡單的架構</p>
<h3>阻塞式單一行程</h3>
<p>這樣的網路程式非常的簡單，只有一個迴圈，單一個行程，處理完一個要求後才繼續處理下一個，理所當然這樣的效能非常差，因為連線在完成前其它的連線都無法被處理，現代的伺服器已經很少看見這樣的架構，但因為優點是簡單，如果沒有什麼大量同時連線要處理，其實這樣的架構就很足夠</p>
<h3>阻塞式多行程</h3>
<p>因為既然單一行程只能同時處理一個請求，那很簡單的想法就是每個請求開一個行程去處理，如此一來就能同時處理更多的請求，但是這樣做有缺點，行程的copy如果是fork的話，有os paging system在成本上其實還好，但是還是有，而且越多的連線就表示需要越多的context-switch，當連線量多到一定程度時，可能大部份的CPU時間都在忙著進行context-switch，如此一來這樣的架構在此情況下是沒有效率的，但是優點是寫伺服器的部份事實上和寫單一行程阻塞式不會差太多，一樣簡單好寫</p>
<h3>阻塞式多行程多執行緒</h3>
<p>除了多行程多阻塞式，有些程式為了減少process copy的成本，或是其它的考量，會在多個行程上開多個執行緒來處理請求，也有可能是單行程多執行緒，但是基本上和上面這幾種都沒有太大的差別，而引進了執行緒帶來了一些額外的問題，dead lock、race condition等等，當不同的執行緒如果在一起有共同的東西要處理，這些常見的同作問題就會出現，使得程式得寫得更小心</p>
<h3>非阻塞式事件驅動</h3>
<p>為了解決上面提到多行程和多執行緒等所帶來的問題，有一種做法是只有單一主要的迴圈來檢查有無網路IO的事件發生，再來決定要怎樣處理，這樣的好處在於省掉了context-switch、process copy等等成本，也不會有dead lock、race condition等問題，但缺點在於程式的部份會變複雜，因為當你一件事件被觸發，有事情還沒做完，你就得記下目前狀態，再下次事件觸發時再依先前的狀態來決定接下來要做什麼，不像上面是線性的程式執行那樣直觀，<a href="http://twistedmatrix.com/trac/">Twisted</a>就是這樣的網路框架</p>
<h3>非阻塞式Coroutine</h3>
<p>那你或許會想，有沒有可能我們能有事件驅動的好處，和阻塞式那樣的直觀好處呢? 答案或許就是Coroutine，基本上它的本質也是事件驅動，只有單一的迴圈在檢查事件的發生，但是加上了coroutine的概念，而<a href="http://www.gevent.org/">Gevent</a>就是這樣的函式庫</p>
<h2>Coroutine</h2>
<p>講了這麼多次coroutine，我相信大部份的讀者可能還是不懂這到底是什麼鬼東西，對於大部份程式設計師而言這應該都算是較陌生的名字，對我而言，在一開始這也是個令人困惑的名詞，但事實上只要理解以後就會發現coroutine不是這麼的難懂，用簡單的一句話來說Coroutine，就是可以暫時中斷，之後再繼續執行的程序，我們來看一個例子，事實上Python就有最基礎的Coroutine，也就是generator</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># -*- coding: utf8 -*-</span>
<span style="color: #ff7700;font-weight:bold;">def</span> foo<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <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;">10</span><span style="color: black;">&#41;</span>:
        <span style="color: #808080; font-style: italic;"># 丟資料並且把主控權交給呼叫者</span>
        <span style="color: #ff7700;font-weight:bold;">yield</span> i
        <span style="color: #ff7700;font-weight:bold;">print</span> u<span style="color: #483d8b;">'foo: 主控又回到我手上了，打我阿笨蛋'</span>
&nbsp;
bar = foo<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># 執行coroutine</span>
<span style="color: #ff7700;font-weight:bold;">print</span> bar.<span style="color: black;">next</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> u<span style="color: #483d8b;">'main: 現在主控權在我們手上，做點雜事'</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'main:hello baby!'</span>
<span style="color: #808080; font-style: italic;"># 回到剛才foo這個coroutine中斷的地方繼續執行</span>
<span style="color: #ff7700;font-weight:bold;">print</span> bar.<span style="color: black;">next</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> bar.<span style="color: black;">next</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>執行結果:</p>
<pre>0
main: 現在主控權在我們手上，做點雜事
main:hello baby!
foo: 主控又回到我手上了，打我阿笨蛋
1
foo: 主控又回到我手上了，打我阿笨蛋
2</pre>
<p>看到了嗎? 我們的foo在執行的過程中被中斷又繼續了好幾次，這就是coroutine，你可能覺得這樣一點用都沒有，我在一開始也這樣想，thread的context-switch也是可以暫停然後再繼續執行，所以這樣的特性好處在哪裡? 有幾個重點在於</p>
<ul>
<li>thread之間需要context-switch，而且成本很高，但是coroutine之間的切換很快</li>
<li>coroutine的成本很低，可以很輕易的產生大量的coroutine</li>
<li>這些事情全是在同一個thread裡發生的，<STRIKE>因此不會有race condition等問題發生</STRIKE> (還是可能會有)</li>
<li>thread的context-switch雖然我們可以進行某種程度的控制，但是很多部份還是得靠OS來決定要先排程哪個thread，而coroutine的執行是由我們自己控制的</li>
</ul>
<p>接著我們用圖來說明coroutine和thread之間的差別</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2010/07/coroutine_diagram.jpg"><img class="alignnone size-full wp-image-1071" title="Coroutine Diagram" src="http://blog.ez2learn.com/wp-content/uploads/2010/07/coroutine_diagram.jpg" alt="" width="416" height="444" /></a></p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2010/07/thread_diagram.jpg"><img class="alignnone size-full wp-image-1072" title="Thread diagram" src="http://blog.ez2learn.com/wp-content/uploads/2010/07/thread_diagram.jpg" alt="" width="448" height="444" /></a></p>
<p>你可以發現，Coroutine所做的其實就只是在單一thread裡面不同的coroutine裡互相切換，本質上和thread很像，所以也有些coroutine叫做micro-thread，而切換通常都是我們主動去做的，看到下面這張，當你建立了不同的thread，context-switch未必是在你預期的情況下發生的，而且通常都是由OS引發的，除此之外，如果你老爸夠有錢，買了最新的Intel iCore 2000的一千核心CPU，那麼恭喜你，你的thread可能是由不同的processor執行，因此不同thread的程式真正的同時執行是有可能的</p>
<p>等等! 這時候你可能抓起你手邊的OS恐龍本敲你自己的頭，你這樣問道: 嘿! 恐龍本上面有提到FCFS、RR等等排程演算法，那Coroutine呢? 答案是如同我們前面提到，這由我們自己決定，這是好處之一，這時你可能又會抓著你的頭髮大叫 "網路呢? 網路呢? 講了半天我只看到Coroutine之間切來切去，我根本看不到在這裡面的半點網路成份，哪怕是一公克也好"</p>
<p>確實，當我一開始在讀相關資料時也很困惑，ˊ這樣跳來跳去又如何? 網路應用到底在哪裡? 現在就回到我們的主題來</p>
<h2>少了非同步IO的Coroutine，就像少了哇沙必的生魚片</h2>
<p>到目前為止都很難和網路有什麼相關的聯想，但是想到網路就會想到IO，而事實上這樣的優點就是在於有大量IO時會顯得特別好用，我們考慮一下當遇到IO時就將主控權交給別人</p>
<p><a href="http://blog.ez2learn.com/wp-content/uploads/2010/07/coroutine_io_diagram.jpg"><img class="alignnone size-full wp-image-1074" title="Coroutine IO diagram" src="http://blog.ez2learn.com/wp-content/uploads/2010/07/coroutine_io_diagram.jpg" alt="" width="424" height="504" /></a></p>
<p>Gevent的運作方式基本上就很類似這張圖，它的Coroutine是由greenlet實作的，而每個Coroutine都有一個parent，最頂層的Coroutine就是main thread或是當前的thread，每當Coroutine遇到IO的時候，就將主控權交給root coroutine，它會視哪些coroutine的IO event是已完成的，就將主控權交給他，其實就只是這樣而已，事實上程式寫起來完全和一般的阻塞式伺服器沒什麼兩樣，但是它千真萬確是非同步的，這就是它神奇的地方，我們來看點實際的例子，我們直接拿gevent範例裡的同步下載程式</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/usr/bin/python</span>
<span style="color: #808080; font-style: italic;"># Copyright (c) 2009 Denis Bilenko. See LICENSE for details.</span>
&nbsp;
<span style="color: #483d8b;">&quot;&quot;&quot;Spawn multiple workers and wait for them to complete&quot;&quot;&quot;</span>
&nbsp;
urls = <span style="color: black;">&#91;</span><span style="color: #483d8b;">'http://www.google.com'</span>, <span style="color: #483d8b;">'http://www.yandex.ru'</span>, <span style="color: #483d8b;">'http://www.python.org'</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> gevent
<span style="color: #ff7700;font-weight:bold;">from</span> gevent <span style="color: #ff7700;font-weight:bold;">import</span> monkey
&nbsp;
<span style="color: #808080; font-style: italic;"># patches stdlib (including socket and ssl modules) to cooperate with other greenlets</span>
monkey.<span style="color: black;">patch_all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">urllib2</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> print_head<span style="color: black;">&#40;</span>url<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Starting %s'</span> <span style="color: #66cc66;">%</span> url
    data = <span style="color: #dc143c;">urllib2</span>.<span style="color: black;">urlopen</span><span style="color: black;">&#40;</span>url<span style="color: black;">&#41;</span>.<span style="color: black;">read</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;">'%s: %s bytes: %r'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>url, <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>data<span style="color: black;">&#41;</span>, data<span style="color: black;">&#91;</span>:<span style="color: #ff4500;">50</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
jobs = <span style="color: black;">&#91;</span>gevent.<span style="color: black;">spawn</span><span style="color: black;">&#40;</span>print_head, url<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> url <span style="color: #ff7700;font-weight:bold;">in</span> urls<span style="color: black;">&#93;</span>
&nbsp;
gevent.<span style="color: black;">joinall</span><span style="color: black;">&#40;</span>jobs<span style="color: black;">&#41;</span></pre></div></div>

<p>這程式太簡單了對吧? 但它確實能做到極高效能的同步下載，我們做一點簡單的解釋，首先令人困惑的是monkey.patch_all()，會有這行，是因為Python內建的各種函式庫裡的IO函式庫、及會阻塞住的函數，例如Sleep都會讓整個程式卡住，而不是利用Selector/epoll之類的功能來處理，所以monkey這個函式庫就是負責將Python內建的函式庫取代成以gevent的非同步形式的函式，如此一來當執行到那些IO之類的動作，會切到MainThread的coroutine進行排程，而非直接卡在那裡等結果，而當IO動作真的完成了，gevent內部會將該coroutine標示為可執行的，因此下次有機會就會排到那個coroutine，看到下面的spawn，就是在產生coroutine，在這裡的coroutine因為事實上是greenlet這個Python函式庫題供的，所以事實上叫做greenlet</p>
<p>你可以在腦中想像一下，spawn首先呼產生了三個執行print_head的routine ，在joinall的地方，把主控權交給第一個print_head，而在函數裡遇到了urllib2.urlopen這個IO動作，因此它將自己設為等待狀態，並且將主控權交還給MainThread，而MainThread將主控權排給了第二個print_head，同樣的也遇到了urllib IO動作，第三個也是一樣，而這三個都在等待，主控權便再次回到了MainThread，它等待有哪個gevent的IO事件完成了，就將主控權交給它，如此一來，重覆這樣的過程，三個網頁的同步下載就在coroutine的切換之間完成了，當三函數return，joinall也會結束，整個程式就跑完了</p>
<p>一開始會很難理解，但一但弄清楚之後就會瞭解這樣寫法的簡單和實用性，你可以忘記它背後的原理，把它當作是一般的阻塞式網路程式來寫，就會很輕鬆，但又輕量、高效能，以上大概就談到這裡，有興趣可以玩玩看gevent</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/07/17/talk-about-coroutine-and-gevent/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Simple tool for rotating nginx log file</title>
		<link>http://blog.ez2learn.com/2010/07/17/simple-tool-for-rotating-nginx-log-file/</link>
		<comments>http://blog.ez2learn.com/2010/07/17/simple-tool-for-rotating-nginx-log-file/#comments</comments>
		<pubDate>Sat, 17 Jul 2010 11:02:28 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[English Articles]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Crontab]]></category>
		<category><![CDATA[Logging]]></category>
		<category><![CDATA[Nginx]]></category>
		<category><![CDATA[Rotate]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1062</guid>
		<description><![CDATA[#!/bin/env python &#34;&#34;&#34;Simple tool for rotating nginx log file &#160; @author: Victor Lin (bornstub@gmail.com) blog: http://blog.ez2learn.com &#34;&#34;&#34; import os import shutil import optparse import datetime import logging import subprocess &#160; log = logging.getLogger&#40;__name__&#41; &#160; def main&#40;&#41;: parser = optparse.OptionParser&#40;&#41; parser.add_option&#40;'-p', &#8230; <a href="http://blog.ez2learn.com/2010/07/17/simple-tool-for-rotating-nginx-log-file/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[
<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/bin/env python</span>
<span style="color: #483d8b;">&quot;&quot;&quot;Simple tool for rotating nginx log file
&nbsp;
@author: Victor Lin (bornstub@gmail.com) blog: http://blog.ez2learn.com
&quot;&quot;&quot;</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">shutil</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">optparse</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">datetime</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">logging</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">subprocess</span>
&nbsp;
log = <span style="color: #dc143c;">logging</span>.<span style="color: black;">getLogger</span><span style="color: black;">&#40;</span>__name__<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #dc143c;">parser</span> = <span style="color: #dc143c;">optparse</span>.<span style="color: black;">OptionParser</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'-p'</span>, <span style="color: #483d8b;">'--pid'</span>, dest=<span style="color: #483d8b;">'pidFile'</span>, metavar=<span style="color: #483d8b;">&quot;FILE&quot;</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">'/path/to/nginx.pid'</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'-l'</span>, <span style="color: #483d8b;">'--log'</span>, dest=<span style="color: #483d8b;">'logFile'</span>, metavar=<span style="color: #483d8b;">&quot;FILE&quot;</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">'/path/to/logfile'</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'-f'</span>, <span style="color: #483d8b;">'--format'</span>, dest=<span style="color: #483d8b;">'nameFormat'</span>,
        <span style="color: #008000;">help</span>=<span style="color: #483d8b;">'format of rotated log file name'</span> <span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'-o'</span>, <span style="color: #483d8b;">'--owner'</span>, dest=<span style="color: #483d8b;">'owner'</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">'the owner user of log file to set'</span><span style="color: black;">&#41;</span>
    <span style="color: black;">&#40;</span>options, args<span style="color: black;">&#41;</span> = <span style="color: #dc143c;">parser</span>.<span style="color: black;">parse_args</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">exists</span><span style="color: black;">&#40;</span>options.<span style="color: black;">logFile</span><span style="color: black;">&#41;</span>:
        log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'The log file %s does not exist'</span>, options.<span style="color: black;">logFile</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># move the log file</span>
    newName = <span style="color: #dc143c;">datetime</span>.<span style="color: black;">date</span>.<span style="color: black;">today</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">strftime</span><span style="color: black;">&#40;</span>options.<span style="color: black;">nameFormat</span><span style="color: black;">&#41;</span>
    log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Move log file %s to %s'</span>, options.<span style="color: black;">logFile</span>, newName<span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">subprocess</span>.<span style="color: black;">check_call</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'mv %s %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>options.<span style="color: black;">logFile</span>, newName<span style="color: black;">&#41;</span>, shell=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> options.<span style="color: black;">owner</span>:
        log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Set owner of %s to %s'</span>, newName, options.<span style="color: black;">owner</span><span style="color: black;">&#41;</span>
        <span style="color: #dc143c;">subprocess</span>.<span style="color: black;">check_call</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'chown %s %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>options.<span style="color: black;">owner</span>, newName<span style="color: black;">&#41;</span>, shell=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># tell nginx to reopen the file</span>
    log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Reopen log file'</span><span style="color: black;">&#41;</span>
    pid = <span style="color: #008000;">int</span><span style="color: black;">&#40;</span><span style="color: #008000;">open</span><span style="color: black;">&#40;</span>options.<span style="color: black;">pidFile</span>, <span style="color: #483d8b;">'rt'</span><span style="color: black;">&#41;</span>.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">os</span>.<span style="color: black;">kill</span><span style="color: black;">&#40;</span>pid, <span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span>
&nbsp;
    log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'done'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
    <span style="color: #dc143c;">logging</span>.<span style="color: black;">basicConfig</span><span style="color: black;">&#40;</span>level=<span style="color: #dc143c;">logging</span>.<span style="color: black;">INFO</span><span style="color: black;">&#41;</span>
    main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Usage:</p>
<blockquote><p>python nginx_log_rotate.py -p /usr/nginx/logs/nginx.pid -l "/usr/nginx/logs/YOURDOMAIN.log" -f "/home/USER/logs/YOURDOMAIN.%Y-%m-%d" -o OWNER_USER</p></blockquote>
<p>For example, you can set up a crontab task for rotating the log file like this:</p>
<blockquote><p>0 0 * * * python nginx_log_rotate.py -p /usr/nginx/logs/nginx.pid -l "/usr/nginx/logs/YOURDOMAIN.log" -f "/home/USER/logs/YOURDOMAIN.%Y-%m-%d" -o OWNER_USER</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/07/17/simple-tool-for-rotating-nginx-log-file/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>A simple workaround for installation problem of PIL under virtualenv</title>
		<link>http://blog.ez2learn.com/2010/05/01/a-simple-workaround-for-installation-problem-of-pil-under-virtualenv/</link>
		<comments>http://blog.ez2learn.com/2010/05/01/a-simple-workaround-for-installation-problem-of-pil-under-virtualenv/#comments</comments>
		<pubDate>Sat, 01 May 2010 07:49:25 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[English Articles]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[easy_install]]></category>
		<category><![CDATA[ImportError]]></category>
		<category><![CDATA[installation]]></category>
		<category><![CDATA[PIL]]></category>
		<category><![CDATA[virtual-environment]]></category>
		<category><![CDATA[virtualenv]]></category>
		<category><![CDATA[workaround]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1032</guid>
		<description><![CDATA[I encountered a problem when I am installing PIL under a virtualenv.  I installed it with easy_install, the output said it was installed, but however, I can&#8217;t import it under the virtual environment.  I got an ImportError when I tried &#8230; <a href="http://blog.ez2learn.com/2010/05/01/a-simple-workaround-for-installation-problem-of-pil-under-virtualenv/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I encountered a problem when I am installing PIL under a virtualenv.  I installed it with easy_install, the output said it was installed, but however, I can&#8217;t import it under the virtual environment.  I got an ImportError when I tried to import it.  Then I inspected the directory of PIL in site-packages directory,  I noticed that it didn&#8217;t contain egg information in the directory. Also, the path in .pth file to PIL is "PIL", and you can only see content of PIL in that directory.  What does this mean?  When you tried to import PIL, it looks every path in sys.path and see are there a package PIL? But the files Python can only see is something like this inside the directory:</p>
<blockquote><p>site-packages</p>
<p style="padding-left: 30px;">PIL</p>
<p style="padding-left: 60px;">__init__.py</p>
<p style="padding-left: 60px;">_imaging.pyd</p>
<p style="padding-left: 60px;">_imagingcms.pyd</p>
<p style="padding-left: 60px;">&#8230; and other files of PIL</p>
</blockquote>
<p>See? Python can only see __init__ and those stuff belong to PIL, but it can&#8217;t find the package PIL,  that&#8217;s why it failed.  To solve this issue, it is simple, just create another PIL directory in the original PIL directory, and move every in it into the new sub PIL directory.  They you will get something like this:</p>
<blockquote><p>site-packages</p>
<p style="padding-left: 30px;">PIL</p>
<p style="padding-left: 60px;">PIL</p>
<p style="padding-left: 90px;">__init__.py</p>
<p style="padding-left: 90px;">other PIL sutff here &#8230;</p>
</blockquote>
<p>That&#8217;s it!  Now python can see and find the PIL package.  Surely, the release of PIL  it is broken,  but fortunately, it is not difficult to fix.  Hope this article could be helpful for people who also encountered this problem.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/05/01/a-simple-workaround-for-installation-problem-of-pil-under-virtualenv/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PO檔自動繁簡轉換程式</title>
		<link>http://blog.ez2learn.com/2010/04/28/po-file-zh_tw-to-zh_cn-program/</link>
		<comments>http://blog.ez2learn.com/2010/04/28/po-file-zh_tw-to-zh_cn-program/#comments</comments>
		<pubDate>Tue, 27 Apr 2010 19:03:26 +0000</pubDate>
		<dc:creator>victor</dc:creator>
				<category><![CDATA[中文文章]]></category>
		<category><![CDATA[分享]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[國際化]]></category>
		<category><![CDATA[翻譯]]></category>
		<category><![CDATA[自動翻譯]]></category>
		<category><![CDATA[Google翻譯]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[po檔]]></category>

		<guid isPermaLink="false">http://blog.ez2learn.com/?p=1029</guid>
		<description><![CDATA[最近在更新我的網站Now.in，最麻煩的一項工作就是國際化，通常我都是先以英文寫網頁，接著用TurboGears2的i18n功能將字串訊息取出，而我的GUI程式的i18n流程也差不多，都是先寫英文，翻成繁體中文，其中一個最腦人的過程，就是把翻好的繁體中文po檔裡的字串剪下貼上到google翻譯將它變成簡體中文再貼到簡體中文的po檔裡，這些工作都是重覆性極高的機械性動作，在一開始句子還少時做還可以，當字詞越來越多，這就變成一件痛苦的工作，既然是高重覆性的工作，為什麼需要人力來做呢? 於是我就寫了一個小程式，可以自動把po檔的內容抓出來丟到google翻譯去，把結果寫到另一個po檔中 # -*- coding: utf8 -*- ''' Created on 2010/4/27 &#160; @author: Victor-mortal ''' &#160; import os import sys import urllib import json import logging import optparse import codecs import htmllib &#160; log = logging.getLogger&#40;__name__&#41; &#160; def unescape&#40;s&#41;: p &#8230; <a href="http://blog.ez2learn.com/2010/04/28/po-file-zh_tw-to-zh_cn-program/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>最近在更新我的網站<a href="http://now.in">Now.in</a>，最麻煩的一項工作就是國際化，通常我都是先以英文寫網頁，接著用TurboGears2的i18n功能將字串訊息取出，而我的GUI程式的i18n流程也差不多，都是先寫英文，翻成繁體中文，其中一個最腦人的過程，就是把翻好的繁體中文po檔裡的字串剪下貼上到google翻譯將它變成簡體中文再貼到簡體中文的po檔裡，這些工作都是重覆性極高的機械性動作，在一開始句子還少時做還可以，當字詞越來越多，這就變成一件痛苦的工作，既然是高重覆性的工作，為什麼需要人力來做呢? 於是我就寫了一個小程式，可以自動把po檔的內容抓出來丟到google翻譯去，把結果寫到另一個po檔中</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># -*- coding: utf8 -*-</span>
<span style="color: #483d8b;">''</span><span style="color: #483d8b;">'
Created on 2010/4/27
&nbsp;
@author: Victor-mortal
'</span><span style="color: #483d8b;">''</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">urllib</span>
<span style="color: #ff7700;font-weight:bold;">import</span> json
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">logging</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">optparse</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">codecs</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">htmllib</span>
&nbsp;
log = <span style="color: #dc143c;">logging</span>.<span style="color: black;">getLogger</span><span style="color: black;">&#40;</span>__name__<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> unescape<span style="color: black;">&#40;</span>s<span style="color: black;">&#41;</span>:
    p = <span style="color: #dc143c;">htmllib</span>.<span style="color: #dc143c;">HTMLParser</span><span style="color: black;">&#40;</span><span style="color: #008000;">None</span><span style="color: black;">&#41;</span>
    p.<span style="color: black;">save_bgn</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    p.<span style="color: black;">feed</span><span style="color: black;">&#40;</span>s<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> p.<span style="color: black;">save_end</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> translate<span style="color: black;">&#40;</span>text, sourceLanguage, destLanguage<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Translate a text
&nbsp;
    @param text: text to translate
    @param sourceLanguage: the language original text is in
    @param destLanguage: language to translate to
    &quot;&quot;&quot;</span>
    log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Translate %s from %s to %s'</span>, text, sourceLanguage, destLanguage<span style="color: black;">&#41;</span>
    query = <span style="color: #008000;">dict</span><span style="color: black;">&#40;</span>v=<span style="color: #483d8b;">'1.0'</span>,
                 q=text.<span style="color: black;">encode</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'utf8'</span><span style="color: black;">&#41;</span>,
                 langpair=<span style="color: #483d8b;">'%s|%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>sourceLanguage, destLanguage<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">file</span> = <span style="color: #dc143c;">urllib</span>.<span style="color: black;">urlopen</span><span style="color: black;">&#40;</span>
        <span style="color: #483d8b;">'http://ajax.googleapis.com/ajax/services/language/translate'</span>,
        data=<span style="color: #dc143c;">urllib</span>.<span style="color: black;">urlencode</span><span style="color: black;">&#40;</span>query<span style="color: black;">&#41;</span>
    <span style="color: black;">&#41;</span>
    result = <span style="color: #008000;">file</span>.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">file</span>.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    jsonResult = json.<span style="color: black;">loads</span><span style="color: black;">&#40;</span>result<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> jsonResult<span style="color: black;">&#91;</span><span style="color: #483d8b;">'responseData'</span><span style="color: black;">&#93;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">None</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> unescape<span style="color: black;">&#40;</span>jsonResult<span style="color: black;">&#91;</span><span style="color: #483d8b;">'responseData'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'translatedText'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #dc143c;">logging</span>.<span style="color: black;">basicConfig</span><span style="color: black;">&#40;</span>level=<span style="color: #dc143c;">logging</span>.<span style="color: black;">INFO</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #dc143c;">parser</span> = <span style="color: #dc143c;">optparse</span>.<span style="color: black;">OptionParser</span><span style="color: black;">&#40;</span>
        usage=<span style="color: #483d8b;">&quot;usage: %prog sourcePoFile destPoFile sourceLanguage destLanguage&quot;</span>
    <span style="color: black;">&#41;</span>
    <span style="color: black;">&#40;</span>_, args<span style="color: black;">&#41;</span> = <span style="color: #dc143c;">parser</span>.<span style="color: black;">parse_args</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    sourceFilePath = args<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
    destFilePath = args<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>
    sourceLang = args<span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span>
    destLang = args<span style="color: black;">&#91;</span><span style="color: #ff4500;">3</span><span style="color: black;">&#93;</span>
&nbsp;
    log.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Translate %s (in %s) to %s (in %s)'</span>,
             sourceFilePath, sourceLang, destFilePath, destLang<span style="color: black;">&#41;</span> 
&nbsp;
    result = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
    sourceFile = <span style="color: #dc143c;">codecs</span>.<span style="color: #008000;">open</span><span style="color: black;">&#40;</span>sourceFilePath, <span style="color: #483d8b;">'rt'</span>, encoding=<span style="color: #483d8b;">'utf8'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> sourceFile.<span style="color: black;">readlines</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> line.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'msgstr'</span><span style="color: black;">&#41;</span>:
            _, msg = line.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">' '</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
            msg = msg.<span style="color: black;">strip</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
            msg = msg<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span>:-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>
            translatedMsg = translate<span style="color: black;">&#40;</span>msg, sourceLang, destLang<span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> translatedMsg:
                translatedMsg = msg
            result.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'msgstr &quot;%s&quot;<span style="color: #000099; font-weight: bold;">\n</span>'</span> <span style="color: #66cc66;">%</span> translatedMsg<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">else</span>:
            result.<span style="color: black;">append</span><span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span>
    sourceFile.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    destFile = <span style="color: #dc143c;">codecs</span>.<span style="color: #008000;">open</span><span style="color: black;">&#40;</span>destFilePath, <span style="color: #483d8b;">'wt'</span>, encoding=<span style="color: #483d8b;">'utf8'</span><span style="color: black;">&#41;</span>
    destFile.<span style="color: black;">writelines</span><span style="color: black;">&#40;</span>result<span style="color: black;">&#41;</span>
    destFile.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
    main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p></code></p>
<p>使用方法很簡單</p>
<blockquote><p>python po_translate.py 來源po檔 目的po檔 來源語言 目的語言</p></blockquote>
<p>而語言的代碼，在Google translate的<a href="http://code.google.com/intl/zh-TW/apis/ajaxlanguage/documentation/reference.html#LangNameArray">API文件</a>中有寫，事實上它能做的不只有繁簡轉換，要翻成其它語言也可以，只是如果你能接受那品質的話</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.ez2learn.com/2010/04/28/po-file-zh_tw-to-zh_cn-program/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

