<?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>flykobe的技术与生活杂谈</title>
	<atom:link href="http://flykobe.com/index.php/feed/" rel="self" type="application/rss+xml" />
	<link>http://flykobe.com</link>
	<description></description>
	<lastBuildDate>Thu, 02 Feb 2012 09:12:44 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>[zz]About the Performance of Map Reduce Jobs</title>
		<link>http://flykobe.com/index.php/2012/02/02/zzabout-the-performance-of-map-reduce-jobs/</link>
		<comments>http://flykobe.com/index.php/2012/02/02/zzabout-the-performance-of-map-reduce-jobs/#comments</comments>
		<pubDate>Thu, 02 Feb 2012 01:21:41 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://flykobe.com/?p=1137</guid>
		<description><![CDATA[One of the big topics in the BigData community is Map/Reduce. There are a lot of good blogs that explain what Map/Reduce does and how it works logically, so I won’t repeat it (look here, here and here for a few). Very few of them however explain the technical flow of things, which I at least need, to [...]]]></description>
			<content:encoded><![CDATA[<p>One of the big topics in the BigData community is Map/Reduce. There are a lot of good blogs that explain what Map/Reduce does and how it works logically, so I won’t repeat it (look <a href="http://developer.yahoo.com/hadoop/tutorial/module4.html">here</a>, <a href="http://ayende.com/blog/4435/map-reduce-a-visual-explanation">here</a> and <a href="http://ksat.me/map-reduce-a-really-simple-introduction-kloudo/">here</a> for a few). Very few of them however explain the technical flow of things, which I at least need, to understand the performance implications. You can always throw more hardware at a map reduce job to improve the overall time. I don’t like that as a general solution and many Map/Reduce programs can be optimized quite easily, if you know what too look for. And optimizing a large map/reduce jobs can be instantly translated into ROI!</p>
<div>
<h1 lang="en-US">The Word Count Example</h1>
<p lang="en-US">I went over some blogs and tutorials about performance of Map/Reduce. <a href="http://www.cloudera.com/blog/2009/12/7-tips-for-improving-mapreduce-performance/">Here</a> is one that I liked. While there are a lot of good tips out there, none, except the one mentioned, talk about the Map/Reduce program itself. Most dive right into the various hadoop options to improve distribution and utilization. While this is important, I think we should start the actual problem we try to solve, that means the Map/Reduce Job.</p>
<p lang="en-US">To make things simple I am using Amazons Elastic Map Reduce. In my setup I started a new Job Flow with multiple steps for every execution. The Job Flow consisted of one master node and two task nodes. All of them were using the Small Standard instance.</p>
<p lang="en-US">While AWS Elastic Map/Reduce has its drawbacks in terms of startup and file latency (Amazon S3 has a high volatility), it is a very easy and consistent way to execute Map/Reduce jobs without needing to setup your own hadoop cluster. And you only pay for what you need! I started out with the word count example that you see in every map reduce documentation, tutorial or Blog. The result of the job always produces files that look something like this:</p>
<p lang="en-US">the: 5967<br />
all: 611<br />
a: 21586</p>
<p lang="en-US">That idea is to count the occurrence of every word in a large number of text files. I processed around 30 files totaling somewhere around 200MB in size. I ran the original python version and then made a very small change to it. Without touching the configuration of hadoop I cut the execution time in half:</p>
<p><strong>The Original Code:</strong></p>
<pre><code>#!/usr/bin/python
import sys
import re
def main(argv):
  line = sys.stdin.readline()
  pattern = re.compile("[a-zA-Z][a-zA-Z0-9]*")
  try:
    while line:
      for word in pattern.findall(line):
        print "LongValueSum:" + word.lower() + "\t" + "1"
      line = sys.stdin.readline()
  except "end of file":
    return None
if __name__ == "__main__":
  main(sys.argv)</code></pre>
<p><strong>The Optimized Code:</strong></p>
<pre><code>#!/usr/bin/python
import sys
import re
def main(argv):
  line = sys.stdin.readline()
  pattern = re.compile("[a-zA-Z][a-zA-Z0-9]*")
  map = dict()
  try:
    while line:
      for word in pattern.findall(line):
	<strong>map[word.lower()] = map.get(word.lower(), 0) + 1
      if ( len(map) &gt; 10000 ):
        for item in map.iteritems():
	  print "LongValueSum:" + item[0] + "\t" + str(item[1])
	map.clear()</strong>
      line = sys.stdin.readline()
    <strong>for item in map.iteritems():
      print "LongValueSum:" + item[0] + "\t" + str(item[1])</strong>
  except "end of file":
    return None
if __name__ == "__main__":
  main(sys.argv)</code></pre>
<p>Instead of “emitting” every word with value 1 to the OutputCollector, I did an internal reduce before emitting it. The result is that instead of emitted the word ‘a’ 1000 times with value 1, I emitted it 1 time with value 1000. The end result of the job is the same, but in half the time. To understand this we need to look at the execution flow of map reduce.</p>
<h1 lang="en-US">Execution Path and Distribution</h1>
<p lang="en-US">Look at the following Flow Diagram taken from the “Hadoop Tutorial from Yahoo!” (<a rel="cc:attributionURL" href="http://developer.yahoo.com/hadoop/">Yahoo! Inc.</a>) / <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a></p>
<div id="attachment_4765"><a href="http://developer.yahoo.com/hadoop/tutorial/module4.html#dataflow"><img title="Map Reduce Flow" src="http://cdn.dynatrace.com/blog/wp-content/Map-Reduce-Flow-600x388.png" alt="Map Reduce Flow" width="600" height="388" /></a>Map Reduce Flow</p>
</div>
<p lang="en-US">Elastic Map Reduce first schedules a Map Task task per file (or parts of the file). It then feeds each line of the file into the map function. The map function will emit each key/value, in this case each word of the line, to the OutputCollector. Each emitted key/value will be written to an intermediate file for later reduce. The Shuffle Process will make sure that each key, in this case each word, will be sent to the same reduce task (meaning hadoop node) for aggregation. If we emit the same word multiple times it also needs to be written and sent multiple times, which results in more I/O (disk and network). The logical conclusion is that we should „pre-reduce“ this on a per task node basis and send the minimal amount of data. This is what the Combiner is for, which is really a Reducer that is run locally on the same node after the Mapping. So we should be fine, right? Not really.</p>
<h2 lang="en-US">Inside of Amazons Elastic Map Reduce</h2>
<p lang="en-US">To get a better idea of where I spent the time, I deployed dynaTrace into Amazons Map Reduce environment. This can be done fully automated with a simple bootstrap action (I will publish the result as a Fastpack on our community at a later time).</p>
<p>The original python run lasted roughly 5 minutes each run (between 290 and 320 seconds), while the optimized ran around 3 minutes (160-170 seconds). I used dynaTrace to split those run times into their different components to get a feel for where we spend time. Some numbers have a rather high volatility which, as I found out, is due to Amazon S3 and to a smaller degree garbage collection. I executed it several times and the volatility did not have a big impact on the overall job execution time.</p>
<div>
<div id="attachment_4778"><a rel="lightbox" href="http://cdn.dynatrace.com/blog/wp-content/mapreduce1.png"><img title="Map Reduce Job Business Transaction that details where we spend our time" src="http://cdn.dynatrace.com/blog/wp-content/mapreduce1-600x89.png" alt="Map Reduce Job Business Transaction that details where we spend our time" width="600" height="89" /></a>Map Reduce Job Business Transaction that details where we spend our time</p>
</div>
</div>
<div>Click on the picture to analyze the details and you will see dramatic improvements on the mapping, combine and sort times. The Total Mapping time in this example is the overall execution time of all scheduled map tasks. The optimized code executed in less than 60% of the time. To a large degree this is due the map function itself (Map Only Time), which is actually quite surprising, after all we were not doing anything less really?</div>
<p lang="en-US">The next thing we see is that the combine time has dropped dramatically, we could say it nearly vanished! That makes sense after all we were making sure that we emitted less duplicates, thus less to combine. In fact it might make sense to stop combining at all as we will see later on. Another item that has dramatically improved is the sort. Again that makes a lot of sense, less data to sort. While the majority of the combine and sort happens in a separate thread, it still saves a lot of CPU and I/O time!</p>
<p lang="en-US">On the other hand neither shuffle nor reduce time itself have changed really. I identified the fluctuations the table does show, as being AWS S3 volatility issues via a hotspot analysis, so I ignored them. The fact that we see no significant improvements here makes sense. The resulting intermediate files of each map task do not look much different, whether we combine or use the optimized code.</p>
<p>So it really was the optimization of the map operation itself, that lead to overall improvement in job run time. While I might have achieved the same goal by doubling the number of map nodes, it would cost me more to do so.</p>
<h2 lang="en-US">What happens during mapping</h2>
<p lang="en-US">To understand why that simple change has such a large impact we need to look at what happens to emitted keys in a Map Job.</p>
<div id="attachment_4804"><a rel="lightbox" href="http://cdn.dynatrace.com/blog/wp-content/Map-Reduce-Combine2.png"><img title="flow of the data from the mapper to memory buffer, sort&amp;combine and finally the merge" src="http://cdn.dynatrace.com/blog/wp-content/Map-Reduce-Combine2.png" alt="flow of the data from the mapper to memory buffer, sort&amp;combine and finally the merge" width="500" height="455" /></a>flow of the data from the mapper to memory buffer, sort&amp;combine and finally the merge</p>
</div>
<p>What most Map/Reduce tutorials forget to mention is that the collect method called by the Mapper serializes the key/value directly to an in-memory buffer, as can be seen in the diagram above and the hotspot below.</p>
<div id="attachment_4786"><a rel="lightbox" href="http://cdn.dynatrace.com/blog/wp-content/map-reduce-map-function-write.png"><img title="When the Mapper emits a key via collect, it gets written to an in memory buffer" src="http://cdn.dynatrace.com/blog/wp-content/map-reduce-map-function-write-600x211.png" alt="When the Mapper emits a key via collect, it gets written to an in memory buffer" width="600" height="211" /></a>When the Mapper emits a key via collect, it gets written to an in memory buffer</p>
</div>
<p>Once that buffer has reached a certain saturation, the Spill Thread kicks in and writes the data to an intermediate file (this is controlled by several <em>io.sort.spill. options</em>). Map/Reduce normally deals with a large amount of potentially never repeating data, so it has to spill to file eventually.</p>
<div id="attachment_4790"><a rel="lightbox" href="http://cdn.dynatrace.com/blog/wp-content/map-reduce-spill-thread.png"><img title="The Spill Thread sorts, combines and writes the data to file in parallel to the mapping" src="http://cdn.dynatrace.com/blog/wp-content/map-reduce-spill-thread-600x285.png" alt="The Spill Thread sorts, combines and writes the data to file in parallel to the mapping" width="600" height="285" /></a>The Spill Thread sorts, combines and writes the data to file in parallel to the mapping</p>
</div>
<p lang="en-US">It is not enough to simple dump the data to file, the content has to be sorted and combined first. The sort is a preparation for the shuffle process and relative efficient (it sorts based on binary bytes, because the actual order is not important). The combine however needs to de-serialize the key and values again prior to writing.</p>
<div id="attachment_4791"><a rel="lightbox" href="http://cdn.dynatrace.com/blog/wp-content/map-reduce-combine-deserialize.png"><img title="The combine in the spill thread needs to deserialize the data again" src="http://cdn.dynatrace.com/blog/wp-content/map-reduce-combine-deserialize-600x211.png" alt="The combine in the spill thread needs to deserialize the data again" width="600" height="211" /></a>The combine in the spill thread needs to deserialize the data again</p>
</div>
<p lang="en-US">So emitting a key multiple times has</p>
<ol>
<li>a direct negative impact on the map time and CPU usage, due to more serialization</li>
<li>an indirect negative impact on CPU due to more spilling and additional deserialization in the combine step</li>
<li>a direct impact on the map task, due to more intermediate files, which makes the final merge more expensive</li>
</ol>
</div>
<div>
<p lang="en-US">Slower mapping obviously impacts the overall Job time directly. The more data we emit, the more CPU and I/O is consumed by the Spill Thread. If the SpillThread is too slow (e.g. expensive combine, slow disk), the in memory buffer might get fully saturated, in which case the map task has to wait (this can be improved by adjusting the <em>io.sort.spill.percent</em> hadoop option).</p>
<div id="attachment_4787"><a rel="lightbox" href="http://cdn.dynatrace.com/blog/wp-content/map-reduce-map-function-block.png"><img title="Showing that the Mapper can be slowed down by the Spill Thread if there are too many keys to sort or combine" src="http://cdn.dynatrace.com/blog/wp-content/map-reduce-map-function-block-600x358.png" alt="Showing that the Mapper can be slowed down by the Spill Thread if there are too many keys to sort or combine" width="600" height="358" /></a>The Mapper was paused by the Spill Thread, because there was too much data to sort and combine</p>
</div>
<p lang="en-US">Finally after the Map Task finishes the actual mapping, it writes, sorts and combines the remaining data to file. Finally it merges all intermediate files into a single output file (which it might combine again). More emitted key’s thus mean more intermediate files to merge as well.</p>
<div id="attachment_4788"><a rel="lightbox" href="http://cdn.dynatrace.com/blog/wp-content/map-reduce-flush.png"><img title="The complete Map Task shows the mapping itself and the flush at the end, which sorts, combines, merges and combines again" src="http://cdn.dynatrace.com/blog/wp-content/map-reduce-flush-600x303.png" alt="The complete Map Task shows the mapping itself and the flush at the end, which sorts, combines, merges and combines again" width="600" height="303" /></a>The complete Map Task shows the mapping itself and the flush at the end, which sorts, combines, merges and combines again</p>
</div>
<p>While the final flush only “slows” us down for 1.5 seconds, this still amounts to roughly 8 percent of the Mapper task. So we see it really does make a lot of sense to optimize the output of the map operation, prior to the combine or reduce step. It will save CPU, Disk and Network I/O and this of course means less Nodes are needed for the same work!</p>
<h2>The one million X factor</h2>
<p>Until now I have tried to explain the I/O and CPU implications of emitting many keys, but there is also another factor that should be considered when writing Map/Reduce jobs. The map function is executed potentially millions of times. Every ms consumed here can potentially lead to minutes in job time. Indeed most of the gain of my “optimization” came from speeding up the mapping itself and not from more effective combine and disk writes. On average each map method call had a little less to do and that paid off.</p>
<p>What that struck me when looking at Map/Reduce first, was that most samples and tutorials use scripting languages like python, perl or something else. This is realized via the Hadoop Streaming framework. While I understand that this lowers the barrier to write Map/Reduce jobs, it should not be used for serious tasks! To illustrate this I ran a randomly selected java version of the Word Count sample. The result is another 50-60% improvement on top of the optimized python (it might be even better, in a larger task).</p>
<div id="attachment_4783"><a rel="lightbox" href="http://cdn.dynatrace.com/blog/wp-content/mapreduce2.png"><img title="Several Java and Python Word Count Map Reduce Jobs, that show the vast differences in execution times" src="http://cdn.dynatrace.com/blog/wp-content/mapreduce2-600x134.png" alt="Several Java and Python Word Count Map Reduce Jobs, that show the vast differences in execution times" width="600" height="134" /></a>Several Java and Python Word Count Map Reduce Jobs, that show the vast differences in execution times</p>
</div>
<p>The table shows the various execution times for:</p>
<ul>
<li>Optimized Java: ~1.5 minutes job execution time<br />
The same trick as in python, if anybody really wants to have the code, it let me know.</li>
<li>Optimized Java with no Combiner: roughly 10 seconds faster than the optimized one<br />
As pointed out the pre-reduce in the map method makes the combine nearly redundant. The improvement in overall job time is minimal however due to the smallness of the job.</li>
<li>Original Java: ~2.5 minutes<br />
We see that all the times (mapping, combining, sorting, spilling) are a lot higher, as we came to expect</li>
<li>Optimized Python: ~3 minutes</li>
<li>Non-optimized python: ~5 minutes</li>
</ul>
<p>Java is faster than Python every time and the optimized version of Java is twice as fast as the optimized python version. Remember that this is a small example and that the hadoop parameters are the same for all runs . In addition CPU was never a limiting factor. If you execute the same small code millions of times, even small differences matter. The difference between a single line mapped in java and python is maybe not even measurable. With 200 MB of text it adds up to more than a minute! The same would be true for small changes in any java Map/Reduce job. The difference between the original and the optimized java version is still more than 60% improvement!</p>
<div>Word Count is very simple compared to some of the map/reduce jobs are out there, but it illustrates quite nicely that performance of our own code still matters. The key take away is that we still need to analyze and optimize the map task and our own code. Only after that is satisfactory, do we need to play around with the various hadoop options to improve distribution and utilization.</div>
<h2>Conclusion</h2>
<p>Map Reduce is a very powerful and elegant way to distribute processing of large amounts of data across many hosts. It is also a bit of a brute and it pays of to analyze and optimize the performance of the map and reduce tasks before we start playing with hadoop options. While Map/Reduce can reduce the job time by throwing more hardware the problem, easy optimizations often reach a similar effect. In the cloud and AWS Elastic Map Reduce that means less cost!</p>
</div>
<p><a name="reg"></a></p>
<form action="http://blog.dynatrace.com/2012/01/25/about-the-performance-of-map-reduce-jobs/#reg" method="post"></form>
<p>zz from: <a href="http://blog.dynatrace.com/2012/01/25/about-the-performance-of-map-reduce-jobs/">http://blog.dynatrace.com/2012/01/25/about-the-performance-of-map-reduce-jobs/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://flykobe.com/index.php/2012/02/02/zzabout-the-performance-of-map-reduce-jobs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>支付宝悬赏任务开发笔记</title>
		<link>http://flykobe.com/index.php/2012/01/31/%e6%94%af%e4%bb%98%e5%ae%9d%e6%82%ac%e8%b5%8f%e4%bb%bb%e5%8a%a1%e5%bc%80%e5%8f%91%e7%ac%94%e8%ae%b0/</link>
		<comments>http://flykobe.com/index.php/2012/01/31/%e6%94%af%e4%bb%98%e5%ae%9d%e6%82%ac%e8%b5%8f%e4%bb%bb%e5%8a%a1%e5%bc%80%e5%8f%91%e7%ac%94%e8%ae%b0/#comments</comments>
		<pubDate>Tue, 31 Jan 2012 09:58:57 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[php]]></category>
		<category><![CDATA[互联网]]></category>

		<guid isPermaLink="false">http://flykobe.com/?p=1129</guid>
		<description><![CDATA[测试环境的ip限制
由于其限制仅指定ip可访问接口，所以需要准备一批固定ip的服务器，并在这些服务器上配置淘宝要求的hosts。
我们由于种种原因，无法提供可用的固定ip服务器，所以在测试阶段是借用了一台没有root权限的服务器…不过，这些都可以通过ssh的端口转发、本地代理等方式解决。以下，将称呼该固定ip服务器为代理服务器。
为chrome安装proxy switch，并用putty建立一个到代理服务器的ssh代理通道，指定给proxy switch。在本地开发机上，将hosts配置为淘宝所需。这样就可以访问了。
解决完ip问题，继续开发。
绑定帐号
我们的功能需求是，定期给我们的用户打款，人和金额都不定。
由于支付宝接口中，用户都是用支付宝唯一用户号（2088开头，16位纯数字）来标识的，可以通过用户绑定的方式获取其支付宝id号（也有其他途径，比如taobao.user.get）。
例如我们的用户flykobe登录之后，希望绑定支付宝帐号，则页面会由站内跳转到alipay，flykobe在那里输入支付宝的帐号、密码手工授权，授权成功后，会跳转到return_url指定的我们的站内页面来，提示授权成功，并且该return_url会将绑定的结果（包括支付宝唯一用户号）记录在我们的本地存储里，留待后用。
sns.account.bind参数和返回值解释：
Args：sns_user_id &#8211; 第三方站内用户id；sns_user_name &#8211; 第三方站内用户名； type &#8211; 我使用的是common，因为不清楚buyer、seller、common之后的区别。注意：之后悬赏接口的outer_account_name和outer_account_id就对应这里的sns_user_name和sns_user_id。
Returns：alipay_login_id &#8211; 支付宝帐号，可能是邮箱、手机号等；key &#8211; 在解除绑定时需要，之后的悬赏接口未见使用；alipay_card_no &#8211; 支付宝用户号，16位纯数字，目前以2088开头。
需要注意的是，在测试阶段，使用的支付宝帐号是测试帐号，需要请支付宝帮忙生成，否则就会一直报“请求参数不正确。”
另外，对支付宝返回值进行sign verify的时候，仅需要将支付宝提供的参数作为verify的对象，而return_url等中自带的参数不需要！否则就会verify失败！
Linux curl函数代理
之前的绑定帐号，可以通过浏览器代理实现。但是之后的创建悬赏任务等，都是在server端执行的，需要设置linux下curl函数的sock代理。也是通过ssh代理实现：ssh -TnNv -D local-port -p ssh-proxy-port  user@ssh-proxy-ip # 如果需要后台执行，则ssh -qTfnN 。
之后，在代码里设置代理信息：
curl_setopt($process, CURLOPT_PROXY, &#8216;127.0.0.1:7070&#8242;);
curl_setopt($process, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
在测试的时候，有的时候会没有响应，这时可以换一个目标url试试。比如我之前用baidu测试，就没有响应，但是退出ssh代理时可以看到：
debug1: channel 2: free: direct-tcpip: listening port 7070 for 119.75.218.77 port 80, connect from 127.0.0.1 port 21856, nchannels 3
debug1: channel 3: free: direct-tcpip: listening port [...]]]></description>
			<content:encoded><![CDATA[<h5>测试环境的ip限制</h5>
<p>由于其限制仅指定ip可访问接口，所以需要准备一批固定ip的服务器，并在这些服务器上配置淘宝要求的hosts。</p>
<p>我们由于种种原因，无法提供可用的固定ip服务器，所以在测试阶段是借用了一台没有root权限的服务器…不过，这些都可以通过ssh的端口转发、本地代理等方式解决。以下，将称呼该固定ip服务器为代理服务器。</p>
<p>为chrome安装proxy switch，并用putty建立一个到代理服务器的ssh代理通道，指定给proxy switch。在本地开发机上，将hosts配置为淘宝所需。这样就可以访问了。</p>
<p>解决完ip问题，继续开发。</p>
<h5>绑定帐号</h5>
<p>我们的功能需求是，定期给我们的用户打款，人和金额都不定。</p>
<p>由于支付宝接口中，用户都是用支付宝唯一用户号（2088开头，16位纯数字）来标识的，可以通过用户绑定的方式获取其支付宝id号（也有其他途径，比如taobao.user.get）。</p>
<p>例如我们的用户flykobe登录之后，希望绑定支付宝帐号，则页面会由站内跳转到alipay，flykobe在那里输入支付宝的帐号、密码手工授权，授权成功后，会跳转到return_url指定的我们的站内页面来，提示授权成功，并且该return_url会将绑定的结果（包括支付宝唯一用户号）记录在我们的本地存储里，留待后用。</p>
<p>sns.account.bind参数和返回值解释：</p>
<p>Args：sns_user_id &#8211; 第三方站内用户id；sns_user_name &#8211; 第三方站内用户名； type &#8211; 我使用的是common，因为不清楚buyer、seller、common之后的区别。<strong>注意：之后悬赏接口的outer_account_name和outer_account_id就对应这里的sns_user_name和sns_user_id。</strong></p>
<p>Returns：alipay_login_id &#8211; 支付宝帐号，可能是邮箱、手机号等；key &#8211; 在解除绑定时需要，之后的悬赏接口未见使用；alipay_card_no &#8211; 支付宝用户号，16位纯数字，目前以2088开头。</p>
<p>需要注意的是，在测试阶段，使用的支付宝帐号是测试帐号，需要请支付宝帮忙生成，否则就会一直报“请求参数不正确。”</p>
<p>另外，对支付宝返回值进行sign verify的时候，仅需要将支付宝提供的参数作为verify的对象，而return_url等中自带的参数不需要！否则就会verify失败！</p>
<h5>Linux curl函数代理</h5>
<p>之前的绑定帐号，可以通过浏览器代理实现。但是之后的创建悬赏任务等，都是在server端执行的，需要设置linux下curl函数的sock代理。也是通过ssh代理实现：ssh -TnNv -D<em> local-port</em> -p <em>ssh-proxy-port  user@ssh-proxy-ip</em> # 如果需要后台执行，则ssh -qTfnN 。</p>
<p>之后，在代码里设置代理信息：</p>
<div id="_mcePaste">curl_setopt($process, CURLOPT_PROXY, &#8216;127.0.0.1:7070&#8242;);</div>
<div id="_mcePaste">curl_setopt($process, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);</div>
<p>在测试的时候，有的时候会没有响应，这时可以换一个目标url试试。比如我之前用baidu测试，就没有响应，但是退出ssh代理时可以看到：</p>
<pre>debug1: channel 2: free: direct-tcpip: listening port 7070 for 119.75.218.77 port 80, connect from 127.0.0.1 port 21856, nchannels 3
debug1: channel 3: free: direct-tcpip: listening port 7070 for 119.75.217.56 port 80, connect from 127.0.0.1 port 21857, nchannels 2
debug1: channel 4: free: direct-tcpip: listening port 7070 for 119.75.217.56 port 80, connect from 127.0.0.1 port 21864, nchannels 1</pre>
<p>说明还是连接上了，但是baidu没给影响而已。</p>
<h5>创建任务</h5>
<p>可以在<a href="https://shang.alipay.com/qiugou/index.htm">https://shang.alipay.com/qiugou/index.htm</a>看看支付宝的悬赏任务。我的理解是，由一些商家或者组织，发布任务提供赏金（即冻结这部分钱），一旦用户完成了这些任务，并由商家人工审核通过后，就可以标记用户，并自动完成支付赏金的过程。创建任务接口，顾名思义，就是创建一个新的悬赏任务。</p>
<p>在支付宝提供的悬赏接口中分为页面接口和系统接口。前者实际上是带着第三方提供的url参数跳转到支付宝里，需要人工输入一些密码、验证码等信息来完成的；后者则是可以完全代码后台运行，不需要人工干预的。</p>
<p>创建任务是一个页面接口，没有太特别的地方，仅解释一下参数和返回值。</p>
<p>Args：outer_task_id &#8211; 第三方的任务id，由第三方保证其唯一性；outer_task_freeze_no &#8211; 第三方流水号，也由第三方保证唯一性；task_amount &#8211; 赏金金额（总额）；task_type &#8211; 目前测试阶段，由支付宝提供了一个字符串作为我们的任务类型，不清楚之后应该传入什么值；task_title &#8211; 标题；task_expired_time &#8211; 截止时间；outer_account_name &#8211; 绑定的第三方用户名；outer_account_id &#8211; 绑定的第三方用户id。未使用增值服务，所以增值字段都置为空了。</p>
<p>Returns：仅多返回了paid_time &#8211; 支付时间。其他还返回了outer_task_id、outer_task_freeze_no、task_amount、additional_profit_amount、additional_profit_transfer_no。所以，具体支付的细节，需要在调用该create接口前，第三方自己记录下来。</p>
<h5>打款</h5>
<p>alipay.witkey.task.pay.by.platform接口是真正付款的接口，是一个系统接口。可以针对不同的用户，支付不同的金额。需要创建者的sns_user_id或者支付宝id，以及收款用户的支付宝id和sns_user_name，初步测试，收款用户可以不绑定帐号。支付宝id可以通过taobao.user.get获取（app授权后可用）。</p>
<p>在测试时，开始少传递了return_url和notify_url，则返回ILLEGAL_ARGUMENT错误。</p>
<p>该接口调用时，有以下几点需要注意的：</p>
<ol>
<li>请求参数必须使用POST方式传递</li>
<li>返回分为同步和异步两种：
<ol>
<li>同步返回：仅表示支付宝是否成功受理该请求，若成功，第三方可以修改支付为“处理中”的状态；若失败，则代表参数有问题，或者金额不足等，需要自行处理</li>
<li>异步返回：至notify_url。表示赏金是否支付成功，第三方可修改支付为“已支付”状态。并且，第三方必须打印“success”作为响应，否则支付宝会多次调用notify_url，直至超时。</li>
</ol>
</li>
</ol>
<p>解释下参数：</p>
<p>Args：outer_task_id &#8211; 对应create里的outer_task_id；outer_account_id &#8211; 创建者的第三方用户id；alipay_user_id &#8211; 创建者的支付宝id；transfer_detail &#8211; 字符串，放款明细。记录数不超过1000。 多条记录之间用（*@|$）分割， 记录内部属性之间用(~*@^)分割。 具体内容： 赏金分配流水号~*@^打款金额 ~*@^支付宝用户号~*@^合作网站用户昵称。其中，赏金分配流水号由第三方自行指定；打款金额以元为单位，精确到分。支付宝用户号是收款人的支付宝id；用户昵称是收款人的第三方用户名，不需要绑定。</p>
<p>另外，需要注意，失败时返回的错误提示码并不是很友好，比如，当传入的收款人不存在时，返回的是ILLEGAL_SIGN。</p>
]]></content:encoded>
			<wfw:commentRss>http://flykobe.com/index.php/2012/01/31/%e6%94%af%e4%bb%98%e5%ae%9d%e6%82%ac%e8%b5%8f%e4%bb%bb%e5%8a%a1%e5%bc%80%e5%8f%91%e7%ac%94%e8%ae%b0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>php sprintf易犯的错误</title>
		<link>http://flykobe.com/index.php/2012/01/19/php-sprintf%e6%98%93%e7%8a%af%e7%9a%84%e9%94%99%e8%af%af/</link>
		<comments>http://flykobe.com/index.php/2012/01/19/php-sprintf%e6%98%93%e7%8a%af%e7%9a%84%e9%94%99%e8%af%af/#comments</comments>
		<pubDate>Thu, 19 Jan 2012 06:39:28 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[php]]></category>
		<category><![CDATA[数据库]]></category>

		<guid isPermaLink="false">http://flykobe.com/?p=1122</guid>
		<description><![CDATA[在php代码里写sql句子，我喜欢用sprintf，这样看起来比较清晰，但是今天就遇到了一个隐藏很深的bug。
有这样一段代码：
        public function update_catogery_ad($id, array $info){/*{{{*/
                if (!$id &#124;&#124; !$info){
                        [...]]]></description>
			<content:encoded><![CDATA[<p>在php代码里写sql句子，我喜欢用sprintf，这样看起来比较清晰，但是今天就遇到了一个隐藏很深的bug。</p>
<p>有这样一段代码：</p>
<pre>        public function update_catogery_ad($id, array $info){/*{{{*/
                if (!$id || !$info){
                        return false;
                }

                $sql = null;
                foreach($info as $a){
                        list($type, $key, $val) = $a;
                        switch($type){
                                case 'int':
                                        $val = intval($val);
                                        break;
                                case 'string':
                                        $val = "'"._es($val)."'";
                                        break;
                        }
                        <strong>$sql .= sprintf(" `%s` = $val,", _es($key)); #1</strong>
                }
                $sql = substr($sql, 0, -1);

                <strong>$sql = sprintf("update %s set $sql where id = %d", $this-&gt;_get_catogery_ad(), intval($id)); #2</strong>
                return $this-&gt;db-&gt;update($sql);
        }/*}}}*/</pre>
<p>它执行的是，根据传入的info数组中的字段，更新某表。在测试环境，运行的很好，但是上线之后，有同事就反映无法更新数据，也不报错。</p>
<p>出问题的info数组是：</p>
<p>$info = array(</p>
<p>&#8216;a&#8217; =&gt; &#8216;bbbb&#8217;,</p>
<p>&#8216;c&#8217; =&gt; &#8216;&lt;area target=&#8221;_blank&#8221; alt=&#8221;" href=&#8221;http%3A%2F%2Fitry.try8.info%2Ftaobao%2Ftry%2F%3Fcatogery%3D%E4%BC%98%E8%B4%A8%E8%89%AF%E5%93%81&#8243; coords=&#8221;0,100,180,170&#8243; shape=&#8221;rect&#8221;&gt;&#8217;,</p>
<p>);</p>
<p>这里字段a可以被正常更新，但是字段c就维持原样。</p>
<p>仔细观察这段数据，可以看到字段c的value中含有%！在sprintf里，它会被当作特殊字符，于是第一条sql句子会报warning：Too few arguments，由于页面上display_errors关闭了，所以没有看到任何提示，这段代码会默默的执行下去，完成整个功能，看起来就好像c字段被忽略掉一样。</p>
<p>于是动手修改第一条sql句子为：$sql .= sprintf(&#8221; `%s` = %s,&#8221;, _es($key), $val);</p>
<p>再次运行，直接报sql句子为空！这时发现第二条sql句子也有同样的问题。于是将第二条句子修改为：$sql = sprintf(&#8220;update %s set %s where id = %d&#8221;, $this-&gt;_get_catogery_ad(), $sql, intval($id));</p>
<p>这些人为的bug会隐藏的很深！得多加小心，所以，编程习惯很重要！在使用sprintf的时候，如果变量中可能带有特殊字符，则一定要放在arg里，不能直接写入format里。</p>
]]></content:encoded>
			<wfw:commentRss>http://flykobe.com/index.php/2012/01/19/php-sprintf%e6%98%93%e7%8a%af%e7%9a%84%e9%94%99%e8%af%af/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>tokyo cabinet &amp; tyrant源码学习笔记</title>
		<link>http://flykobe.com/index.php/2012/01/11/tokyo-cabinet-tyrant%e6%ba%90%e7%a0%81%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0/</link>
		<comments>http://flykobe.com/index.php/2012/01/11/tokyo-cabinet-tyrant%e6%ba%90%e7%a0%81%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0/#comments</comments>
		<pubDate>Wed, 11 Jan 2012 06:29:39 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[php]]></category>
		<category><![CDATA[互联网]]></category>
		<category><![CDATA[数据库]]></category>

		<guid isPermaLink="false">http://flykobe.com/?p=1116</guid>
		<description><![CDATA[tokyo cabinet与tyrant一起，组成了一个可用的NoSQL数据库，cabinet是lib，tyrant是server。其性能和应用性毋庸置疑，希望可以学习，同时也对其稳定性持怀疑态度，希望可以看看是我用的不对，还是其源码有可改进的地方。
从tyrant/ttserver.c的main函数入手：
main(){
    ……
    g_serv = ttservnew();
    int rv = proc(dbname, host, port, thnum, tout, dmn, pidpath, kl, logpath,
                  ulogpath, ulim, uas, sid, mhost, mport, rtspath, ropts,
    [...]]]></description>
			<content:encoded><![CDATA[<p>tokyo cabinet与tyrant一起，组成了一个可用的NoSQL数据库，cabinet是lib，tyrant是server。其性能和应用性毋庸置疑，希望可以学习，同时也对其稳定性持怀疑态度，希望可以看看是我用的不对，还是其源码有可改进的地方。</p>
<p>从tyrant/ttserver.c的main函数入手：</p>
<pre>main(){
    ……
    g_serv = ttservnew();
    int rv = proc(dbname, host, port, thnum, tout, dmn, pidpath, kl, logpath,
                  ulogpath, ulim, uas, sid, mhost, mport, rtspath, ropts,
                  skelpath, mulnum, extpath, extpcs, mask);
    ttservdel(g_serv);
    if(extpcs) tclistdel(extpcs);
    return rv;
  }
}</pre>
<p>省略的部分是命令行参数处理，ttservnew函数对TTSERV进行初始化，其中初始化了4个锁：</p>
<pre>ttservnew(){
    ……
    if(pthread_mutex_init(&amp;serv-&gt;qmtx, NULL) != 0) tcmyfatal("pthread_mutex_init failed");
    if(pthread_cond_init(&amp;serv-&gt;qcnd, NULL) != 0) tcmyfatal("pthread_cond_init failed");
    if(pthread_mutex_init(&amp;serv-&gt;tmtx, NULL) != 0) tcmyfatal("pthread_mutex_init failed");
    if(pthread_cond_init(&amp;serv-&gt;tcnd, NULL) != 0) tcmyfatal("pthread_cond_init failed");
    ……
}</pre>
<pre>typedef struct _TTSERV {
    ……
    pthread_mutex_t qmtx;                  /* mutex for the queue */
    pthread_cond_t qcnd;                   /* condition variable for the queue */
    pthread_mutex_t tmtx;                  /* mutex for the timer */
    pthread_cond_t tcnd;                   /* condition variable for the timer */
    ……
}</pre>
<p>这里就牵涉到两个重要的概念，timer线程和worker线程，稍候会介绍。qmtx和qcnd主要是供主线程和worker线程使用，tmtx和tcnd主要供主线程和timer线程使用。</p>
<p>可以看出，真正的处理逻辑是在proc函数里。这个函数里进行了大量的稳定、log、容错等处理，先都忽略（但是这些才保证了一份好代码的存在！）。</p>
<p>proc函数中主要进行了以下工作：</p>
<ol>
<li>如果以daemon模式运行，则调用ttdaemonize函数，建立可运行的子子进程，父进程和子进程都退出</li>
<li>tcadbopen函数，根据数据库文件，为TCADB对象赋值，对于hash类型的db而言，会将数据库文件mmap到共享内存中</li>
<li>调用ttservaddtimedhandler函数，建立一个tserv的timer对象，其handler是do_slave</li>
<li>如果有外部脚本相关的，则再调用ttservaddtimedhandler函数，建立N个timer对象，其handler是do_extpc</li>
<li>调用ttservsettaskhandler函数，将tserv的do_task赋值为do_task函数</li>
<li>调用ttservsettermhandler函数，将tserv的do_term赋值为do_term函数，处理退出</li>
<li>do{ ttservstart(); }while(g_restart); 之类的g_restart值可以有signal改变，从而使ttserver可以再次执行ttservstart，以达到重启、reload的效果</li>
<li>在ttservstart函数中，是真正的业务逻辑</li>
</ol>
<p>以下简单看看ttservstart函数：</p>
<ol>
<li>根据host和port，建立socket，可以是unix原始套接口，也可以是TCP套接口</li>
<li>新建timer个数个线程，等待执行timer-&gt;do_timer函数</li>
<li>新建thnum个worker线程，以空的reqs初始化，等待执行timer-&gt;do_task函数</li>
<li>epoll_wait：
<ol>
<li>如果是请求建立连接，则accept连接，并将accept的socket端口加入到epoll的监听队列里</li>
<li>如果是其他请求，则请求qmtx锁，将该端口push到serv-&gt;queue里，释放qmtx锁，并发送qcnd信号，触发worker线程</li>
</ol>
</li>
</ol>
<p>worker线程的主体就是do_task函数，其读取socket中的数据，如果以magic word打头，则是二进制格式的命令；否则就是memcached兼容的命令格式。根据请求内容，再分别调用如do_put、do_get等函数进行处理。</p>
<p>以do_get函数为例，主要是调用tcadbget函数，读取到内容，再通过socket返回给客户端。这里的tcadbget函数，会根据数据库的类型，调用其get函数进行读取，比如hash database，就使用tchdbget方法。tchdbget中，首先请求HDBLOCKMETHOD 的read lock（允许其他读，但是不允许写），对整个db加锁，然后调用tchdbbidx函数计算出key对应在hash数组中的位置，和二次hash的key（为了解决hash碰撞，对于第一次hash key相同的元素，使用tree再进行存储）。调用tchdbgetimpl函数，进行真正的获取数据。</p>
<p>tchdbgetimpl函数中：由于hash database使用了on-memory hash database作为缓存，所以，如果cache不为空，则先会执行tcmdbget在cache中查找，如果找到则返回，否则继续在真正的hash database里查找。首先找到bucket的偏移位置，对于hash database来说，一部分数据会被map到共享内存，如果bucket属于这部分数据，则从map中读取；否则从数据文件读取（可能存在bucket的一部分数据在map，另一部分在数据文件的情况）。</p>
<p>bucket的header数据结构为：</p>
<p>magic word | hash | left child off | right child off | padding size | size of key | size of value |</p>
<p>buckey里的数据是以树存储的，用上面计算出的二次hash的key，与bucket key比较，若二次key较小，则向左子树找；若较大则向右子树找；否则相等时，比较关键字，若相等，则找到结果，返回；如果虽然二次hash的值相等，但是比较关键字不等，则需要解决二次 hash的碰撞。</p>
<p>二次hash碰撞的解决方法，是沿着当前节点的左子树或者右子树下行直至找到二次hash值等、关键字也等的节点，或者找不到匹配的节点，则返回失败。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- 小分割 &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<p>2012-1-19</p>
<p>今天想解决3个问题：</p>
<ol>
<li>复制数据库文件，与tcrmgr copy的区别；后者对锁的使用情况</li>
<li>B+tree数据文件格式，为什么异常退出会导致数据丢失？</li>
<li>尝试用c代码读取ulog和数据文件的内容</li>
</ol>
<h5>tcrmgr copy的奥秘</h5>
<p>tcrmgr copy的响应函数是proccopy，首先tcrdbnew数据库，并init了一个mutex锁，继而调用tcrdbcopy函数，先请求这个mutex锁，再进行具体的copy操作。</p>
<p><em>这里请求mutex锁的tcrdblockmethod函数，在多处被调用，比如get、put等等。这里是简单的CLI模式，每次数据库都是new的，mutex锁也是新建的，但是可以猜想，在多线程模式下，线程间的读写操作都是互斥的。（这里存疑，为什么用互斥锁，而非条件变量呢？）</em></p>
<p>tcrdbcopyimpl组装socket的copy包，通过tcrdbsend发送给ttserver。并通过ttsockgetc获取返回值。</p>
<p>可以看出，copy的奥秘不在tcrmgr这里，而是在ttserver怎样处理copy请求里。</p>
<p>ttserver.c的do_copy函数具体处理copy请求，针对B+tree，其实际处理函数为tcbdbcopy。</p>
<p>首先对数据库加写锁，排除一切的读写操作，从cache里读取叶节点和非叶节点，分别存储到lids和nids里。解锁。由于cache里的数据还未写入到数据文件里，所以针对每个节点，调用对应的save函数，写入到数据文件中（写入每个节点的前后加写锁、解锁）。</p>
<p>调用tcbdbtranbegin启动事务，加读锁，调用tchdbcopy函数（据说B+tree的底层存储其实是hash格式）将数据写到目的文件中，解锁。</p>
<p>实际执行复制操作的是tchdbcopyimpl，这里封装了两种操作，一种是直接调用tccopyfile进行复制，另一种是当目的文件以@开头，则执行命令。仅关注前者，很简单，就是将数据库文件中的内容读取出来，写入到目标文件。</p>
<p>那么tcrmgr copy和直接copy有哪些区别呢？</p>
<ul>
<li>tcrmgr copy会先将cache中的数据写回到文件中；而直接copy不会，所以cache中的数据就丢失了</li>
<li>tcrmgr copy写备份文件的时候，会加读锁，排斥写，保证备份文件的数据格式是正确的；而直接copy时没有锁，如果正好遇到cache中的数据同步到数据库文件，则备份文件的格式可能是损坏的</li>
</ul>
<h5>B+tree数据库异常情况分析</h5>
<p>从上面的copy过程，猜想，异常退出导致文件格式损坏的原因可能有：</p>
<ul>
<li>数据写在cache里，没有存入文件，cache里的新数据丢失</li>
<li>存入文件过程中，异常退出，导致文件格式损坏，可能很多数据甚至整个文件都会丢失</li>
</ul>
<p>具体需要看tcbdbputimpl函数。在该函数中，首先从history里查找key对应的叶节点的pid，如果找不到则从根节点开始二分查找，直至找到或者新建了一个leaf节点的pid（细节没看）。然后将新内容作为rec写入pid对应的leaf位置。<strong>之后如果需要，会对整个树进行调整</strong>。</p>
<p>如果该节点所在的recs数目超过了启动ttserver时的预设值lmemb，或者该节点的的size超过了lsmax，则调整。调整的具体过程忽略，可以参考算法中的B+ tree部分。</p>
<p>否则，如果该节点的recs数目&lt;1，则说明put失败，需要调用tcbdbleafkill函数切断该leaf节点与树的联系（疑问，这个leaf里应该还有其他旧数据，岂不是也丢失了？）。</p>
<p>如果不在事务里，则将数据写入cache。如果页节点的cache数目大于设定值lcnum，则需要将叶节点的cache内容写入到数据文件（又调用了hash的put函数，不清楚那边是否也有cache缓存），并清除叶节点cache的内容。同样处理非叶节点。那么，如果非正常退出，cache中的数据就有可能会丢失。</p>
<p>在将数据写入数据文件的过程中，注意到其rbuf的大小是BDBPAGEBUFSIZ，即32768，这就意味着，一个leaf的最大字节数是32768（数据部分应该是比这个要小）。</p>
<p>留个问题，cache里的数据难道不存在于数据文件中吗？</p>
]]></content:encoded>
			<wfw:commentRss>http://flykobe.com/index.php/2012/01/11/tokyo-cabinet-tyrant%e6%ba%90%e7%a0%81%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>php的mysql_connect与mysql_pconnect</title>
		<link>http://flykobe.com/index.php/2012/01/11/php%e7%9a%84mysql_connect%e4%b8%8emysql_pconnect/</link>
		<comments>http://flykobe.com/index.php/2012/01/11/php%e7%9a%84mysql_connect%e4%b8%8emysql_pconnect/#comments</comments>
		<pubDate>Wed, 11 Jan 2012 01:19:25 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[php]]></category>
		<category><![CDATA[互联网]]></category>
		<category><![CDATA[数据库]]></category>

		<guid isPermaLink="false">http://flykobe.com/?p=1112</guid>
		<description><![CDATA[在csdn上看到有人问：mysql_connect后是不是非要mysql_close，是不是自动释放？
大家伙们，PHP数据库连接是不是在页面执行完之后自己释放，即使不用mysql_close()也可以释放掉？
同理PDO连接到数据库后，是不是非要将PDO实例赋值为NULL，才能将PDO连接断掉？
在N久以前我查过资料，但己经模糊不清了，具说PHP处理每一个被请求的页面时，里面有MYSQL连接的，当页面处理完就立刻释放掉所有用到的资料。如：mysql_connect后的连接。这个说法对吗？PDO同理吗？
第一反应是CGI模式下，mysql_connect是当前请求结束自动释放，而mysql_pconnect是可以被同一web进程（线程）中的多个请求重复使用的。但是为什么呢？
查看php.net上的帮助：
mysql_pconnect() acts very much like mysql_connect() with two major differences.
First, when connecting, the function would first try to find a (persistent) link that&#8217;s already open with the same host, username and password. If one is found, an identifier for it will be returned instead of opening a new connection.
Second, the connection to the SQL server will [...]]]></description>
			<content:encoded><![CDATA[<p>在csdn上看到有人问：mysql_connect后是不是非要mysql_close，是不是自动释放？</p>
<blockquote><p>大家伙们，PHP数据库连接是不是在页面执行完之后自己释放，即使不用mysql_close()也可以释放掉？<br />
同理PDO连接到数据库后，是不是非要将PDO实例赋值为NULL，才能将PDO连接断掉？</p>
<p>在N久以前我查过资料，但己经模糊不清了，具说PHP处理每一个被请求的页面时，里面有MYSQL连接的，当页面处理完就立刻释放掉所有用到的资料。如：mysql_connect后的连接。这个说法对吗？PDO同理吗？</p></blockquote>
<p>第一反应是CGI模式下，mysql_connect是当前请求结束自动释放，而mysql_pconnect是可以被同一web进程（线程）中的多个请求重复使用的。但是为什么呢？</p>
<p>查看php.net上的帮助：</p>
<blockquote><p><strong>mysql_pconnect()</strong> acts very much like <a href="http://cn.php.net/manual/en/function.mysql-connect.php">mysql_connect()</a> with two major differences.</p>
<p>First, when connecting, the function would first try to find a (persistent) link that&#8217;s already open with the same host, username and password. If one is found, an identifier for it will be returned instead of opening a new connection.</p>
<p>Second, the connection to the SQL server will not be closed when the execution of the script ends. Instead, the link will remain open for future use (<a href="http://cn.php.net/manual/en/function.mysql-close.php">mysql_close()</a> will not close links established by <strong>mysql_pconnect()</strong>).</p>
<p>This type of link is therefore called &#8216;persistent&#8217;.</p></blockquote>
<p>首先，mysql_pconnect建立的链接不会在script结束后释放，那么对于CLI模式呢？从道理和实践代码中检测，都是会释放的。因为进程退出时，所有的资源都会被自动回收。通过以下的代码可以简单的验证：</p>
<blockquote>
<div id="_mcePaste">$db = mysql_pconnect(&#8216;localhost&#8217;, &#8216;mysql&#8217;, &#8216;pwd&#8217;);</div>
<div id="_mcePaste">sleep(100);</div>
<div id="_mcePaste">var_dump($db);</div>
<div id="_mcePaste">exit;</div>
<div>同时在mysql中执行：</div>
<div>
<div>mysql&gt; show processlist;  # sleep期间</div>
<div>+&#8212;-+&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+</div>
<div>| Id | User  | Host            | db     | Command | Time | State | Info             |</div>
<div>+&#8212;-+&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+</div>
<div>|  1 | mysql | localhost:63950 | tshare | Sleep   |    1 |       | NULL             |</div>
<div>| 68 | mysql | localhost       | NULL   | Query   |    0 | NULL  | show processlist |</div>
<div>| <strong>73 | mysql | localhost       | NULL   | Sleep   |   20 |       | NULL </strong> |</div>
<div>+&#8212;-+&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+</div>
<div>3 rows in set (0.01 sec)</div>
<div>mysql&gt; show processlist;  # 进程结束</div>
<div>+&#8212;-+&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+</div>
<div>| Id | User  | Host            | db     | Command | Time | State | Info             |</div>
<div>+&#8212;-+&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+</div>
<div>|  1 | mysql | localhost:63950 | tshare | Sleep   |    0 |       | NULL             |</div>
<div>| 68 | mysql | localhost       | NULL   | Query   |    0 | NULL  | show processlist |</div>
<div>+&#8212;-+&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+</div>
<div>2 rows in set (0.00 sec)</div>
</div>
</blockquote>
<p>那么CLI模式呢？将上面这段代码cp至一个web页面中，通过浏览器访问结束后，show processlist仍然可以看到有连接存在！这就使得多个web请求可以复用同一mysql链接，从而节省了建立连接的过程。但是这样也可能造成问题，就是当允许的web进程（线程）数目超过mysql的<span style="font-size: 12px; line-height: 18px; text-align: justify;"><span style="color: #000000;">max_connections时，会造成web进程连接mysql服务器的阻塞，因为每个web进程都不释放自己的mysql连接，从而可能造成too many connections的mysql错误。可以参考我之前的blog：</span></span><a href="http://flykobe.com/index.php/2011/01/20/mysql-too-many-connections%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/">http://flykobe.com/index.php/2011/01/20/mysql-too-many-connections%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/</a></p>
<p>那么它会存在多久呢？与mysqld的wait_timeout相关。如果一直没有被复用，那么它会存在wait_timeout时长，单位是秒。</p>
<div>
<blockquote>
<div>mysql&gt; show variables like &#8216;wait_timeout&#8217;;</div>
<div>+&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;-+</div>
<div>| Variable_name | Value |</div>
<div>+&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;-+</div>
<div>| wait_timeout  | 28800 |</div>
<div>+&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;-+</div>
<div>1 row in set (0.00 sec)</div>
</blockquote>
<div>当然，如果web server的进程（线程）结束，mysql_pconnect建立的连接也会结束。</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://flykobe.com/index.php/2012/01/11/php%e7%9a%84mysql_connect%e4%b8%8emysql_pconnect/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>我的2011总结</title>
		<link>http://flykobe.com/index.php/2012/01/09/%e6%88%91%e7%9a%842011%e6%80%bb%e7%bb%93/</link>
		<comments>http://flykobe.com/index.php/2012/01/09/%e6%88%91%e7%9a%842011%e6%80%bb%e7%bb%93/#comments</comments>
		<pubDate>Mon, 09 Jan 2012 04:02:30 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://flykobe.com/?p=1108</guid>
		<description><![CDATA[今天很有趣，我需要把一张mysql600多万数据、读写频繁的表拆分，趁着导测试数据的时间，总结一下我的2011.
2011年，最大的收获是亲近了佛法，完成了北京佛教研究所与北京广化寺开办的佛教初级和中级研修班的学习，并在正信、正见的道路上迈下了坚实的一步。
性格方面有了一些改变，之前我是个自认为蛮聪明、暴躁、急性子的人。而现在，走在路上，会突然警醒自己正在埋着头、飞快的迈着步子，于是刻意的放缓脚步、抬起头看看远方，发现生活中、道路上的种种美好。
在与人相处中，之前认为非黑即白，不可相容。而其实，善和恶，在一个短时间内是很难看得清楚的，强行的把我对善恶的观点加诸他人，其实不是因为我有多美好，而是我有多专制。
对于事业成功的定义，也有所变化。以金钱来衡量成功，其实很虚无。今年的高薪到了明年，可能就变成中下水平，而工作的愉悦程度、自身能力是否 稳步提高、企业文化才是略微真实和可把握的。
2011年是创业的一年。还行，公司能够保持正的资金流和一个相对稳定的发展态势。
2011年，我的技术进步可能稍慢，这一点比较遗憾。涉猎的范围比较杂乱， 部署了多台服务器，从apache转移到lighttpd，又转移到nginx；用了用NoSQL的cabinet存储，提高了性能，但是也遇到一些问题；做了mysql的主从备份，但是备份机没有发挥大的用途。做的最多的，还是业务层面的开发，年初对设计模式比较感兴趣，年末发现有些是过度设计，于是重构；遇到过性能瓶颈，从服务器、数据库和代码的角度进行了一些调优，但是也暴露出我经验不足的劣势。
2011年，家里又添了一只猫，现在有3个小家伙：噜噜、团团和咪宝。噜噜、团团仍然向肥胖不懈努力，咪宝在和病魔抗争，希望2012他可以健健康康！搬到了顺义，继续喂养小区里的流浪猫。也跟随公司在帮助几个流浪动物基地。
2011年，我的生活还不错，从空虚和追求享受，到学会从奉献和佛法中获取安定和快乐。
2012年，希望在修行的道路上能有进展，希望生活平静幸福，也希望技术方面可以有所突破。
]]></description>
			<content:encoded><![CDATA[<p>今天很有趣，我需要把一张mysql600多万数据、读写频繁的表拆分，趁着导测试数据的时间，总结一下我的2011.</p>
<p>2011年，最大的收获是亲近了佛法，完成了北京佛教研究所与北京广化寺开办的佛教初级和中级研修班的学习，并在正信、正见的道路上迈下了坚实的一步。</p>
<p>性格方面有了一些改变，之前我是个自认为蛮聪明、暴躁、急性子的人。而现在，走在路上，会突然警醒自己正在埋着头、飞快的迈着步子，于是刻意的放缓脚步、抬起头看看远方，发现生活中、道路上的种种美好。</p>
<p>在与人相处中，之前认为非黑即白，不可相容。而其实，善和恶，在一个短时间内是很难看得清楚的，强行的把我对善恶的观点加诸他人，其实不是因为我有多美好，而是我有多专制。</p>
<p>对于事业成功的定义，也有所变化。以金钱来衡量成功，其实很虚无。今年的高薪到了明年，可能就变成中下水平，而工作的愉悦程度、自身能力是否 稳步提高、企业文化才是略微真实和可把握的。</p>
<p>2011年是创业的一年。还行，公司能够保持正的资金流和一个相对稳定的发展态势。</p>
<p>2011年，我的技术进步可能稍慢，这一点比较遗憾。涉猎的范围比较杂乱， 部署了多台服务器，从apache转移到lighttpd，又转移到nginx；用了用NoSQL的cabinet存储，提高了性能，但是也遇到一些问题；做了mysql的主从备份，但是备份机没有发挥大的用途。做的最多的，还是业务层面的开发，年初对设计模式比较感兴趣，年末发现有些是过度设计，于是重构；遇到过性能瓶颈，从服务器、数据库和代码的角度进行了一些调优，但是也暴露出我经验不足的劣势。</p>
<p>2011年，家里又添了一只猫，现在有3个小家伙：噜噜、团团和咪宝。噜噜、团团仍然向肥胖不懈努力，咪宝在和病魔抗争，希望2012他可以健健康康！搬到了顺义，继续喂养小区里的流浪猫。也跟随公司在帮助几个流浪动物基地。</p>
<p>2011年，我的生活还不错，从空虚和追求享受，到学会从奉献和佛法中获取安定和快乐。</p>
<p>2012年，希望在修行的道路上能有进展，希望生活平静幸福，也希望技术方面可以有所突破。</p>
]]></content:encoded>
			<wfw:commentRss>http://flykobe.com/index.php/2012/01/09/%e6%88%91%e7%9a%842011%e6%80%bb%e7%bb%93/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>自省：如何为别人说法？</title>
		<link>http://flykobe.com/index.php/2012/01/04/%e8%87%aa%e7%9c%81%ef%bc%9a%e5%a6%82%e4%bd%95%e4%b8%ba%e5%88%ab%e4%ba%ba%e8%af%b4%e6%b3%95%ef%bc%9f/</link>
		<comments>http://flykobe.com/index.php/2012/01/04/%e8%87%aa%e7%9c%81%ef%bc%9a%e5%a6%82%e4%bd%95%e4%b8%ba%e5%88%ab%e4%ba%ba%e8%af%b4%e6%b3%95%ef%bc%9f/#comments</comments>
		<pubDate>Wed, 04 Jan 2012 02:43:49 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[佛]]></category>

		<guid isPermaLink="false">http://flykobe.com/?p=1105</guid>
		<description><![CDATA[休息十分钟，写写自己的感悟。
早晨看到一个朋友在工作群里感慨印度的见闻，落后、欺骗、不准时、贫穷等等，她的出发点是好的，描述这些情况，印证因果不爽，然后教导别人向善。
我作为被教育者之一，稍稍有些诧异。因为在我的观念里，印度是伟大佛陀的故乡，有很多久负盛名的禅修中心；当代的印度计算机业十分发达，有很多优秀的工程师；当然，也少不了《3 idiots》和《贫民窟的百万富翁》之类电影的熏陶。所以，我对印度还是充满了向往。而朋友言语描述的印度，充满了反差，也稍稍感觉有一些绝对。
恰逢最近在修《弟子规》功过格，自然而然的自省，我是否也有这样的以说教的口吻对待别人呢，并认为自己的看法是绝对正确、希望他人接受的呢？答案是肯定的！
《坛经》里说，若真修道人，不闻他人过。而我经常看到他人的过错，并揪着不放。比如挤公交车，我一般是始发站上车，都有位子，车子开到中途很挤的时候，很多人上车了就挤门口也不往里面走，外面的人就挤不上来。我看到这种情况，就会很愤慨的谴责别人，不为他人着想，只顾自己。甚至还为此与别人争吵过几次。事后，也都觉得自己是有理的，是善的，是清高的。而昨天，坐了另外一辆车，短途的，没几站我就要下了，于是我也不大愿意往后面走。当这个念头闪现的时候，就发觉，其实自己和他们没有区别。
当我指摘别人的时候，如果换位思考一下，也许结论就是不同的了。当然，坐公交车，礼让他人是肯定需要的，但是我指责的态度和做法都是非常有错误的！！我并不是代表着善，只是出于自私、我慢等心理在做这些事情。
经论和师父都说过，言语是苍白的，行动才是有力的。仍然以公交车为例，同学也说过，如果人多的时候，我先给年长者让个座位，然后自己走到最后面去，其实不需要在意其他人怎样。这些其实都是世间法，都是虚幻的，站在哪里、是否挤上这趟车、早晚，在广袤的时间和空间里都是没有意义的，因缘法皆空，重要的是，能否从这生活的点滴中，发掘出佛法的智慧？！
第一义谛中，没有善恶，只需要以清净平等心行住坐卧即可。
]]></description>
			<content:encoded><![CDATA[<p>休息十分钟，写写自己的感悟。</p>
<p>早晨看到一个朋友在工作群里感慨印度的见闻，落后、欺骗、不准时、贫穷等等，她的出发点是好的，描述这些情况，印证因果不爽，然后教导别人向善。</p>
<p>我作为被教育者之一，稍稍有些诧异。因为在我的观念里，印度是伟大佛陀的故乡，有很多久负盛名的禅修中心；当代的印度计算机业十分发达，有很多优秀的工程师；当然，也少不了《3 idiots》和《贫民窟的百万富翁》之类电影的熏陶。所以，我对印度还是充满了向往。而朋友言语描述的印度，充满了反差，也稍稍感觉有一些绝对。</p>
<p>恰逢最近在修《弟子规》功过格，自然而然的自省，我是否也有这样的以说教的口吻对待别人呢，并认为自己的看法是绝对正确、希望他人接受的呢？答案是肯定的！</p>
<p>《坛经》里说，若真修道人，不闻他人过。而我经常看到他人的过错，并揪着不放。比如挤公交车，我一般是始发站上车，都有位子，车子开到中途很挤的时候，很多人上车了就挤门口也不往里面走，外面的人就挤不上来。我看到这种情况，就会很愤慨的谴责别人，不为他人着想，只顾自己。甚至还为此与别人争吵过几次。事后，也都觉得自己是有理的，是善的，是清高的。而昨天，坐了另外一辆车，短途的，没几站我就要下了，于是我也不大愿意往后面走。当这个念头闪现的时候，就发觉，其实自己和他们没有区别。</p>
<p>当我指摘别人的时候，如果换位思考一下，也许结论就是不同的了。当然，坐公交车，礼让他人是肯定需要的，但是我指责的态度和做法都是非常有错误的！！我并不是代表着善，只是出于自私、我慢等心理在做这些事情。</p>
<p>经论和师父都说过，言语是苍白的，行动才是有力的。仍然以公交车为例，同学也说过，如果人多的时候，我先给年长者让个座位，然后自己走到最后面去，其实不需要在意其他人怎样。这些其实都是世间法，都是虚幻的，站在哪里、是否挤上这趟车、早晚，在广袤的时间和空间里都是没有意义的，因缘法皆空，重要的是，能否从这生活的点滴中，发掘出佛法的智慧？！</p>
<p>第一义谛中，没有善恶，只需要以清净平等心行住坐卧即可。</p>
]]></content:encoded>
			<wfw:commentRss>http://flykobe.com/index.php/2012/01/04/%e8%87%aa%e7%9c%81%ef%bc%9a%e5%a6%82%e4%bd%95%e4%b8%ba%e5%88%ab%e4%ba%ba%e8%af%b4%e6%b3%95%ef%bc%9f/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>开心赚宝技术日志</title>
		<link>http://flykobe.com/index.php/2011/12/31/%e5%bc%80%e5%bf%83%e8%b5%9a%e5%ae%9d%e6%8a%80%e6%9c%af%e6%97%a5%e5%bf%97/</link>
		<comments>http://flykobe.com/index.php/2011/12/31/%e5%bc%80%e5%bf%83%e8%b5%9a%e5%ae%9d%e6%8a%80%e6%9c%af%e6%97%a5%e5%bf%97/#comments</comments>
		<pubDate>Sat, 31 Dec 2011 03:11:31 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[工具类]]></category>

		<guid isPermaLink="false">http://flykobe.com/?p=1103</guid>
		<description><![CDATA[2011-12-31  开发中，积分系统；builder模式改造抽奖领奖流程
2011-12-31  待上线，修改代码，catch异常，即使tokyo崩溃，项目也可运行
2011-12-30  13：30人为操作导致tokyo server崩溃，数据丢失；15点左右，恢复一部分，使网页可运行；17点，全部恢复
]]></description>
			<content:encoded><![CDATA[<p>2011-12-31  开发中，积分系统；builder模式改造抽奖领奖流程</p>
<p>2011-12-31  待上线，修改代码，catch异常，即使tokyo崩溃，项目也可运行</p>
<p>2011-12-30  13：30人为操作导致tokyo server崩溃，数据丢失；15点左右，恢复一部分，使网页可运行；17点，全部恢复</p>
]]></content:encoded>
			<wfw:commentRss>http://flykobe.com/index.php/2011/12/31/%e5%bc%80%e5%bf%83%e8%b5%9a%e5%ae%9d%e6%8a%80%e6%9c%af%e6%97%a5%e5%bf%97/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>clushshell安装与使用</title>
		<link>http://flykobe.com/index.php/2011/12/28/clushshell%e5%ae%89%e8%a3%85%e4%b8%8e%e4%bd%bf%e7%94%a8/</link>
		<comments>http://flykobe.com/index.php/2011/12/28/clushshell%e5%ae%89%e8%a3%85%e4%b8%8e%e4%bd%bf%e7%94%a8/#comments</comments>
		<pubDate>Wed, 28 Dec 2011 05:54:26 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://flykobe.com/?p=1095</guid>
		<description><![CDATA[安装
从http://sourceforge.net/projects/clustershell/files/clustershell/1.5.1/ 下载源码安装。指定安装目录：
python setup.py install
新建配置文件：
mkdir /etc/clustershell
cp conf/* /etc/clustershell
新增配置文件groups：
[admin@v080049 apps]$ cat /etc/clustershell/groups
web: 192.168.80.49 192.168.80.105 192.168.80.106
db: 192.168.80.103 192.168.80.104

需要配置ssh的免密码登录。
使用
简单测试：clush -g web &#8216;ls&#8217;
部署代码：clush -g web &#8220;cd ~/prepare/happy_itry_v2/; echo &#8216;yes&#8217; &#124; ./taobao_push.sh &#8220;
]]></description>
			<content:encoded><![CDATA[<h5>安装</h5>
<p>从<a href="http://sourceforge.net/projects/clustershell/files/clustershell/1.5.1/">http://sourceforge.net/projects/clustershell/files/clustershell/1.5.1/</a> 下载源码安装。指定安装目录：</p>
<p>python setup.py install</p>
<p>新建配置文件：</p>
<p>mkdir /etc/clustershell</p>
<p>cp conf/* /etc/clustershell</p>
<p>新增配置文件groups：</p>
<div id="_mcePaste">[admin@v080049 apps]$ cat /etc/clustershell/groups</div>
<div id="_mcePaste">web: 192.168.80.49 192.168.80.105 192.168.80.106</div>
<div id="_mcePaste">db: 192.168.80.103 192.168.80.104</div>
<div></div>
<div>需要配置ssh的免密码登录。</div>
<h5>使用</h5>
<p>简单测试：clush -g web &#8216;ls&#8217;</p>
<p>部署代码：clush -g web &#8220;cd ~/prepare/happy_itry_v2/; echo &#8216;yes&#8217; | ./taobao_push.sh &#8220;</p>
]]></content:encoded>
			<wfw:commentRss>http://flykobe.com/index.php/2011/12/28/clushshell%e5%ae%89%e8%a3%85%e4%b8%8e%e4%bd%bf%e7%94%a8/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>zz 双网卡，双IP配置，加静态路由</title>
		<link>http://flykobe.com/index.php/2011/12/28/zz-%e5%8f%8c%e7%bd%91%e5%8d%a1%ef%bc%8c%e5%8f%8cip%e9%85%8d%e7%bd%ae%ef%bc%8c%e5%8a%a0%e9%9d%99%e6%80%81%e8%b7%af%e7%94%b1/</link>
		<comments>http://flykobe.com/index.php/2011/12/28/zz-%e5%8f%8c%e7%bd%91%e5%8d%a1%ef%bc%8c%e5%8f%8cip%e9%85%8d%e7%bd%ae%ef%bc%8c%e5%8a%a0%e9%9d%99%e6%80%81%e8%b7%af%e7%94%b1/#comments</comments>
		<pubDate>Wed, 28 Dec 2011 01:36:32 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://flykobe.com/?p=1093</guid>
		<description><![CDATA[
双网卡，双IP配置，加静态路由


张映 发表于 2011-12-23
分类目录： 服务器相关


北网通，南电信的问题是很让人郁闷的一件，这也是河蟹社会的一种特色吧。为了解决这个问题，我考虑过三种方案：
1,双网卡，双IP；或者单网卡，双IP。
这种方案，成本低，但是维护挺麻烦，并且速度比后面二个要慢。
2,BGP双线机房。
BGP的费用要比第一种方案要高，但是全国真正是BGP机房的到底有多少，应当就那么几家。其他假的比较多。用这种方案就不用在搞双IP了，一个IP就OK。
3,CDN加速
CDN的价格是最高，买的是dell r410的服务器，拖管在机房，带宽160元/m/月，还是熟人才拿到这价格。我和chinacache的客户经理当面谈过，刚开始的价格是400元/m/月，后来我说是我朋友推荐的，直接降到200元/m/月，这价格降的真是离谱。我有一个linux运维的QQ群，在里面问chinacache的CDN多少钱一个月一M，有得说100多元/m/月，有的说900多元/m/月，卧槽，这差别太大。感觉不规范，所以没用，现在和朋友一起创业，能省就省，所以我选择了第一种方案。扯了这么多，进入正题。
一，服务器服务安排
服务器，我准备了三台dell r410的机器，
1,web服务器
2,mysql服务器
3,文件服务器
每台服务器有二个网卡，eth1走内网，eth0走外网，并且网通和电信都走eth0，这样的方式我个人觉得比一个网卡走网通，一个网卡走电信要快。在交换机上设置二个vlan一个走内网，一个走外网，交换机设置就不在这儿多说了。
二，网络配置
1,cd /etc/sysconfig/network-scripts
2,修改 ifcfg-eth1


查看复制打印?


[root@localhost network-scripts]# cat ifcfg-eth1
DEVICE=&#8221;eth1&#8243;
NM_CONTROLLED=&#8221;yes&#8221;
ONBOOT=&#8221;yes&#8221;
TYPE=Ethernet
BOOTPROTO=none
IPADDR=192.168.1.2
PREFIX=24
DEFROUTE=yes
IPV4_FAILURE_FATAL=yes
IPV6INIT=no
NAME=&#8221;System eth1&#8243;
UUID=5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03
HWADDR=78:2B:CB:57:28:E5


3,修改ifcfg-eth0


查看复制打印?


[root@localhost network-scripts]# cat ifcfg-eth0
DEVICE=&#8221;eth0&#8243;
NM_CONTROLLED=&#8221;yes&#8221;
ONBOOT=&#8221;yes&#8221;
TYPE=Ethernet
BOOTPROTO=none
IPADDR=222.121.121.121
PREFIX=24
GATEWAY=222.121.121.1
NETMASK=255.255.255.128
DEFROUTE=yes
IPV4_FAILURE_FATAL=yes
IPV6INIT=no
NAME=&#8221;System eth0&#8243;
UUID=9c92fad9-6ecb-3e6c-eb4d-8a47c6f50c04
HWADDR=78:2B:CB:57:28:E6


4,cp ifcfg-eth0 ifcfg-eth0:0


查看复制打印?


[root@localhost network-scripts]# cat ifcfg-eth0:0
DEVICE=&#8221;eth0:0&#8243;
NM_CONTROLLED=&#8221;yes&#8221;
ONBOOT=&#8221;yes&#8221;
TYPE=Ethernet
BOOTPROTO=none
IPADDR=112.121.121.121
PREFIX=24
NETMASK=255.255.255.0
DEFROUTE=yes
IPV4_FAILURE_FATAL=yes
IPV6INIT=no
NAME=&#8221;System eth0&#8243;
UUID=9c92fad9-6ecb-3e6c-eb4d-8a47c6f50c04
HWADDR=78:2B:CB:57:28:E6


5,重起网络/etc/init.d/network restart
到这儿，双网卡，双IP基本上就配置好，但是网通和电信都是走的电信的网关，这样的话丢包会比较严重的。解决这个问题，有二个比较好的方法，一是加静态路由，一是加策略路由。我用的方法是加静态路由。
三，添加静态路由
route add -net 1.24.0.0 netmask 255.248.0.0 gw 60.12.105.145 dev eth0:0
route add -net 1.56.0.0 netmask 255.248.0.0 gw 60.12.105.145 dev eth0:0
上面是通过命令来加的，如果是双网卡的要加上dev的。因为静态路由有很多条，所以还是一起加比较好。
1,vim route.sh
2,把route add全部加到 route.sh里面
3,chmod +x route.sh
4,开机启动echo &#8220;sh /路径/route.sh&#8221; &#62;&#62; /etc/rc.local
下载网通路由，下载下来后，根据实际情况后修改
zz from:http://blog.51yip.com/server/1358.html

]]></description>
			<content:encoded><![CDATA[<div>
<h1>双网卡，双IP配置，加静态路由</h1>
</div>
<div>
<p>张映 发表于 2011-12-23</p>
<p>分类目录： <a title="查看 服务器相关 的全部文章" rel="category tag" href="http://blog.51yip.com/category/server">服务器相关</a></p>
</div>
<div>
<p>北网通，南电信的问题是很让人郁闷的一件，这也是河蟹社会的一种特色吧。为了解决这个问题，我考虑过三种方案：</p>
<p>1,双网卡，双IP；或者单网卡，双IP。</p>
<p>这种方案，成本低，但是维护挺麻烦，并且速度比后面二个要慢。</p>
<p>2,BGP双线机房。</p>
<p>BGP的费用要比第一种方案要高，但是全国真正是BGP机房的到底有多少，应当就那么几家。其他假的比较多。用这种方案就不用在搞双IP了，一个IP就OK。</p>
<p>3,CDN加速</p>
<p>CDN的价格是最高，买的是dell r410的服务器，拖管在机房，带宽160元/m/月，还是熟人才拿到这价格。我和chinacache的客户经理当面谈过，刚开始的价格是400元/m/月，后来我说是我朋友推荐的，直接降到200元/m/月，这价格降的真是离谱。我有一个linux运维的QQ群，在里面问chinacache的CDN多少钱一个月一M，有得说100多元/m/月，有的说900多元/m/月，卧槽，这差别太大。感觉不规范，所以没用，现在和朋友一起创业，能省就省，所以我选择了第一种方案。扯了这么多，进入正题。</p>
<p><strong>一，服务器服务安排</strong></p>
<p>服务器，我准备了三台dell r410的机器，</p>
<p>1,web服务器</p>
<p>2,mysql服务器</p>
<p>3,文件服务器</p>
<p>每台服务器有二个网卡，eth1走内网，eth0走外网，并且网通和电信都走eth0，这样的方式我个人觉得比一个网卡走网通，一个网卡走电信要快。在交换机上设置二个vlan一个走内网，一个走外网，交换机设置就不在这儿多说了。</p>
<p><strong>二，网络配置</strong></p>
<p>1,cd /etc/sysconfig/network-scripts</p>
<p>2,修改 ifcfg-eth1</p>
<div>
<div>
<div><a href="http://blog.51yip.com/server/1358.html#">查看</a><a href="http://blog.51yip.com/server/1358.html#">复制</a><a href="http://blog.51yip.com/server/1358.html#">打印</a><a href="http://blog.51yip.com/server/1358.html#">?</a></div>
</div>
<ol>
<li>[root@localhost network-scripts]# cat ifcfg-eth1</li>
<li>DEVICE=&#8221;eth1&#8243;</li>
<li>NM_CONTROLLED=&#8221;yes&#8221;</li>
<li>ONBOOT=&#8221;yes&#8221;</li>
<li>TYPE=Ethernet</li>
<li>BOOTPROTO=none</li>
<li>IPADDR=192.168.1.2</li>
<li>PREFIX=24</li>
<li>DEFROUTE=yes</li>
<li>IPV4_FAILURE_FATAL=yes</li>
<li>IPV6INIT=no</li>
<li>NAME=&#8221;System eth1&#8243;</li>
<li>UUID=5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03</li>
<li>HWADDR=78:2B:CB:57:28:E5</li>
</ol>
</div>
<p>3,修改ifcfg-eth0</p>
<div>
<div>
<div><a href="http://blog.51yip.com/server/1358.html#">查看</a><a href="http://blog.51yip.com/server/1358.html#">复制</a><a href="http://blog.51yip.com/server/1358.html#">打印</a><a href="http://blog.51yip.com/server/1358.html#">?</a></div>
</div>
<ol>
<li>[root@localhost network-scripts]# cat ifcfg-eth0</li>
<li>DEVICE=&#8221;eth0&#8243;</li>
<li>NM_CONTROLLED=&#8221;yes&#8221;</li>
<li>ONBOOT=&#8221;yes&#8221;</li>
<li>TYPE=Ethernet</li>
<li>BOOTPROTO=none</li>
<li>IPADDR=222.121.121.121</li>
<li>PREFIX=24</li>
<li>GATEWAY=222.121.121.1</li>
<li>NETMASK=255.255.255.128</li>
<li>DEFROUTE=yes</li>
<li>IPV4_FAILURE_FATAL=yes</li>
<li>IPV6INIT=no</li>
<li>NAME=&#8221;System eth0&#8243;</li>
<li>UUID=9c92fad9-6ecb-3e6c-eb4d-8a47c6f50c04</li>
<li>HWADDR=78:2B:CB:57:28:E6</li>
</ol>
</div>
<p>4,cp ifcfg-eth0 ifcfg-eth0:0</p>
<div>
<div>
<div><a href="http://blog.51yip.com/server/1358.html#">查看</a><a href="http://blog.51yip.com/server/1358.html#">复制</a><a href="http://blog.51yip.com/server/1358.html#">打印</a><a href="http://blog.51yip.com/server/1358.html#">?</a></div>
</div>
<ol>
<li>[root@localhost network-scripts]# cat ifcfg-eth0:0</li>
<li>DEVICE=&#8221;eth0:0&#8243;</li>
<li>NM_CONTROLLED=&#8221;yes&#8221;</li>
<li>ONBOOT=&#8221;yes&#8221;</li>
<li>TYPE=Ethernet</li>
<li>BOOTPROTO=none</li>
<li>IPADDR=112.121.121.121</li>
<li>PREFIX=24</li>
<li>NETMASK=255.255.255.0</li>
<li>DEFROUTE=yes</li>
<li>IPV4_FAILURE_FATAL=yes</li>
<li>IPV6INIT=no</li>
<li>NAME=&#8221;System eth0&#8243;</li>
<li>UUID=9c92fad9-6ecb-3e6c-eb4d-8a47c6f50c04</li>
<li>HWADDR=78:2B:CB:57:28:E6</li>
</ol>
</div>
<p>5,重起网络/etc/init.d/network restart</p>
<p><strong>到这儿，双网卡，双IP基本上就配置好，但是网通和电信都是走的电信的网关，这样的话丢包会比较严重的。解决这个问题，有二个比较好的方法，一是加静态路由，一是加策略路由。我用的方法是加静态路由。</strong></p>
<p><strong>三，添加静态路由</strong></p>
<p>route add -net 1.24.0.0 netmask 255.248.0.0 gw 60.12.105.145 dev eth0:0<br />
route add -net 1.56.0.0 netmask 255.248.0.0 gw 60.12.105.145 dev eth0:0</p>
<p>上面是通过命令来加的，<strong>如果是双网卡的要加上dev的</strong>。因为静态路由有很多条，所以还是一起加比较好。</p>
<p>1,vim route.sh</p>
<p>2,把route add全部加到 route.sh里面</p>
<p>3,chmod +x route.sh</p>
<p>4,开机启动echo &#8220;sh /路径/route.sh&#8221; &gt;&gt; /etc/rc.local</p>
<p>下载<a title="网通路由" href="http://blog.51yip.com/wp-content/uploads/2011/12/route.txt"><strong>网通路由</strong></a>，下载下来后，根据实际情况后修改</p>
<p>zz from:<a href="http://blog.51yip.com/server/1358.html">http://blog.51yip.com/server/1358.html</a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://flykobe.com/index.php/2011/12/28/zz-%e5%8f%8c%e7%bd%91%e5%8d%a1%ef%bc%8c%e5%8f%8cip%e9%85%8d%e7%bd%ae%ef%bc%8c%e5%8a%a0%e9%9d%99%e6%80%81%e8%b7%af%e7%94%b1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

