<?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>apt-blog.net   无证程序员的PT桑</title>
	<atom:link href="http://apt-blog.net/feed" rel="self" type="application/rss+xml" />
	<link>http://apt-blog.net</link>
	<description>潜逃中。</description>
	<lastBuildDate>Fri, 18 May 2012 11:25:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>An OpenVPN configuration menu based generator</title>
		<link>http://apt-blog.net/an-openvpn-configuration-menu-based-generator</link>
		<comments>http://apt-blog.net/an-openvpn-configuration-menu-based-generator#comments</comments>
		<pubDate>Fri, 18 May 2012 11:10:26 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[openvpn]]></category>
		<guid isPermaLink="false">https://apt-blog.net/?p=1228</guid>
		<description><![CDATA[Setting up an openvpn server isn't a diffcult thing, but you have to deal with root certificates, rsa keys, confs, subnet addresses, iptables ... such trifles are annoying. So I wrote this script to make my life easier, only to provide basic infomation like the server IP, and those configuraion will be done at the [...]]]></description>
			<content:encoded><![CDATA[<p>Setting up an openvpn server isn't a diffcult thing, but you have to deal with root certificates, rsa keys, confs, subnet addresses, iptables ... such trifles are annoying.</p>
<p>So I wrote this script to make my life easier, only to provide basic infomation like the server IP, and those configuraion will be done at the background, then tared packages are ready there for both server and clients.</p>
<p><a href="http://apt-blog.net/wp-content/uploads/2012/05/ovpn_menu.png" rel="lightbox[1228]" title="ovpn_menu.png"><img title="ovpn_menu.png" alt="ovpn_menu.png" src="http://apt-blog.net/wp-content/uploads/2012/05/ovpn_menu.png" class="aligncenter" width="65%" /></a></p>
<p>Source is available at <a href="http://code.google.com/p/ptcoding/source/browse/ovpn_menu.sh">Google Code</a>.</p>
<p><strong>Script is recommanded running at your work station, and then upload only the server part tared file to the server, for secure concideration.</strong></p>
<h3>Features</h3>
<ul>
<li>Provide tared config which ready for any server distribution.</li>
<li>Random VPN subnet will be generated to avoid conflicting.</li>
<li>Random digital subffixed server/client CommonName will be assigned (if you don't provide one) for clearer management.</li>
<li>Configuration file is copied from the distributed OpenVPN in your system, which include full explnations of different options when you have to edit them. Default values is set by sed.</li>
<li>tls-auth enabled by default. </li>
</ul>
<h3>Usage</h3>
<p>For new setup:</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;">.<span style="color: #000000; font-weight: bold;">/</span>ovpn_menu.sh</pre></td></tr></table></div>
</p>
<p>Just do as promoted. When select 5 to exit, all the files will be packed into a single <code>NAME-all.tar.gz</code>, you should save it to somewhere safe. And if you want to sign some more certificate from this root ca, put this tar file as the argument.</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;">.<span style="color: #000000; font-weight: bold;">/</span>ovpn_menu.sh <span style="color: #000000; font-weight: bold;">/</span>path<span style="color: #000000; font-weight: bold;">/</span>to<span style="color: #000000; font-weight: bold;">/</span>YOUR-VPN-all.tar.gz</pre></td></tr></table></div>
<p>At last the script also provide you iptables commands that should be useful to setup the server.</p>
<h3>Download</h3>
<p>Direct download via wget/curl should be ok:</p>
<p><a href="http://ptcoding.googlecode.com/git/ovpn_menu.sh">http://ptcoding.googlecode.com/git/ovpn_menu.sh</a></p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/an-openvpn-configuration-menu-based-generator/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&quot;Leave password blank if dont want to change&quot; in a django admin field</title>
		<link>http://apt-blog.net/leave-password-blank-if-dont-want-to-change-in-a-django-admin</link>
		<comments>http://apt-blog.net/leave-password-blank-if-dont-want-to-change-in-a-django-admin#comments</comments>
		<pubDate>Fri, 27 Apr 2012 11:29:22 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[web技术]]></category>
		<category><![CDATA[django]]></category>
		<guid isPermaLink="false">https://apt-blog.net/?p=1214</guid>
		<description><![CDATA[Django is a complicated but useful python web-framework, comparing to light weight frameworks like Bottle, web.py. I recently switch from Bottle, making use of it's powerful admin site to build a Email Account Management system. An AdminSite offer interfaces to manage databases. It includes traditional authentications, permissions, data display and POST saving mechanisms, etc. , [...]]]></description>
			<content:encoded><![CDATA[<p>Django is a complicated but useful python web-framework, comparing to light weight frameworks like <code>Bottle</code>, <code>web.py</code>. I recently switch from Bottle, making use of it's powerful <a href="https://docs.djangoproject.com/en/1.4/ref/contrib/admin/">admin site</a> to build a Email Account Management system.</p>
<p>An AdminSite offer interfaces to manage databases. It includes traditional authentications, permissions, data display and POST saving mechanisms, etc. , which are nasty trifles if you try implement them from scratch using <code>bottle</code> or other things.</p>
<p>Now I can build a decent system in about 100 lines code, after many many document reading and code digging, I want to share the story how I solve the problem encountered while implementing the "Leave password blank if don't want to change it" requirement. As commonly seen in other applications when user try to update their profile.</p>
<p>Firstly the background. My data Model:</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> Users<span style="color: black;">&#40;</span>Model<span style="color: black;">&#41;</span>:
    account = models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length=<span style="color: #ff4500;">128</span>, unique = <span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    password = models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>db_column = <span style="color: #483d8b;">'crypt'</span>,max_length=<span style="color: #ff4500;">384</span>, blank=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    name = models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length=<span style="color: #ff4500;">768</span><span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;">#...</span></pre></td></tr></table></div>
<p>The password is stored hashed in the DB <code>crypt</code> column. If I just let the AdminSite pickup things like that, it just show the password value in the plain input text.</p>
<p>Firstly I tell django to show it as a password input widget.</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> MailUsersForm<span style="color: black;">&#40;</span>forms.<span style="color: black;">ModelForm</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">class</span> Meta:
        model = Users
        widgets = <span style="color: black;">&#123;</span>
                <span style="color: #483d8b;">'password'</span>: forms.<span style="color: black;">PasswordInput</span><span style="color: black;">&#40;</span>render_value = <span style="color: #008000;">False</span><span style="color: black;">&#41;</span>,
        <span style="color: black;">&#125;</span>
<span style="color: #ff7700;font-weight:bold;">class</span> MailUsersAdmin<span style="color: black;">&#40;</span>admin.<span style="color: black;">ModelAdmin</span><span style="color: black;">&#41;</span>:
    form = MailUsersForm</pre></td></tr></table></div>
<p>Ah, overriding the <code>form</code> in the <code>ModelAdmin</code> does the trick.</p>
<p>Now in the edit page, the Password field shows an empty input widget.</p>
<p><a href="http://apt-blog.net/wp-content/uploads/2012/04/django-password-field.png" rel="lightbox[1214]" title="django-password-field.png"><img title="django-password-field.png" alt="django-password-field.png" src="http://apt-blog.net/wp-content/uploads/2012/04/django-password-field.png" class="aligncenter" /></a></p>
<p>Now I need to hash the password before commit to the DB. I found the <a href="https://docs.djangoproject.com/en/1.4/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_model">save_model method</a>.</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> MailUsersAdmin<span style="color: black;">&#40;</span>admin.<span style="color: black;">ModelAdmin</span><span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># ...</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> save_model<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, request, obj, form, change<span style="color: black;">&#41;</span>:
        new_psw = request.<span style="color: black;">POST</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;password&quot;</span><span style="color: black;">&#93;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>new_psw<span style="color: black;">&#41;</span>:
            obj.<span style="color: black;">password</span> = obj._hash_password<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
            obj.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<p>This works, but buggy. The password field is defaultly blank. It will clean the field in the DB without processing. The problem is, by the time in the <code>save_model</code> method is triggered, the <code>obj</code> is already updated by the values from the POST request. There's no way to get the old password value here. All I can do without changing the old password, is to skip the <code>obj.save()</code>. But what if I only want to update other things?</p>
<p>Through out the documents, I found some other places to approch the Model instance.</p>
<p>Firstly the <a href="https://docs.djangoproject.com/en/1.4/topics/forms/modelforms/#the-save-method">django.forms.ModelForm.save</a> method. The module instance is returned from this method (then passed to the <code>save_model</code>), so I inspect the object with <code>ipdb</code> to see if the data is updated here:</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> MailUsersForm<span style="color: black;">&#40;</span>forms.<span style="color: black;">ModelForm</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> save<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, <span style="color: #66cc66;">*</span>args, <span style="color: #66cc66;">**</span>kw<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">import</span> ipdb<span style="color: #66cc66;">;</span> ipdb.<span style="color: black;">set_trace</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        obj = <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>MailUsersForm, <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: #66cc66;">*</span>args, <span style="color: #66cc66;">**</span>kw<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> obj</pre></td></tr></table></div>
<p>Unfortunately Yes. The instanced obj from the derived <code>save</code> is already updated to the POST value.</p>
<p>And <a href="http://www.djangofoo.com/tag/pre_save">django signals, pre_save</a>, all the same way. (The signal thing is even emmited after the <code>forms.ModelForm.save</code>)</p>
<p>Googled around and nothing suitble, I decided to find the right way myself.</p>
<p>There must be somewhere in django codes that sets the attributes with values from the POST requsts. So I add a hook in the Model.</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> Users<span style="color: black;">&#40;</span>Model<span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;">#....</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__setattr__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, name, value<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> name == <span style="color: #483d8b;">&quot;password&quot;</span>:
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">self</span>.<span style="color: #008000;">id</span> == <span style="color: #ff4500;">370</span>:
                <span style="color: #ff7700;font-weight:bold;">import</span> ipdb<span style="color: #66cc66;">;</span> ipdb.<span style="color: black;">set_trace</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">return</span> Model.<span style="color: #0000cd;">__setattr__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, name, value<span style="color: black;">&#41;</span></pre></td></tr></table></div>
<p>The <code>if self.id</code> thing is for filtering the specific page I submit the save in the browser. Now ipdb let me in debug mode.</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">ipdb&gt; w
&nbsp;
#................. Many Many unrelated things
&nbsp;
  /usr/lib/python2.7/site-packages/django/forms/forms.py(272)full_clean()
    270         self._clean_fields()
    271         self._clean_form()
--&gt; 272         self._post_clean()
    273         if self._errors:
    274             del self.cleaned_data
&nbsp;
  /usr/lib/python2.7/site-packages/django/forms/models.py(309)_post_clean()
    307         opts = self._meta
    308         # Update the model instance with self.cleaned_data.
&nbsp;
--&gt; 309         self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude)
    310
    311         exclude = self._get_validation_exclusions()
&nbsp;
  /usr/lib/python2.7/site-packages/django/forms/models.py(51)construct_instance()
     49             file_field_list.append(f)
     50         else:
---&gt; 51             f.save_form_data(instance, cleaned_data[f.name])
     52
     53     for f in file_field_list:
&nbsp;
  /usr/lib/python2.7/site-packages/django/db/models/fields/__init__.py(454)save_form_data()
    452
    453     def save_form_data(self, instance, data):
--&gt; 454         setattr(instance, self.name, data)
#---------------^ this matters!
    455
    456     def formfield(self, form_class=forms.CharField, **kwargs):
&nbsp;
&gt; /home/boypt/Projects/maildbadmin/maildbadmin/models.py(55)__setattr__()
     53             if self.id == 370:
     54                 import ipdb; ipdb.set_trace()
---&gt; 55                 print &quot;set :&quot;, value
     56 #
     57 #</pre></div></div>
<p>From the call stack, the <code>setattr</code> call to the Model changes the data. I check <code>django/db/models/fields/__init__.py</code> source, find that this is a <code>Field</code> base class, so the obvious solution is to override this method.</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> PasswordCharField<span style="color: black;">&#40;</span>models.<span style="color: black;">CharField</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> save_form_data<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, instance, data<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> data <span style="color: #66cc66;">!</span>= u<span style="color: #483d8b;">''</span>:
            data = instance._hashed_pwd<span style="color: black;">&#40;</span>data<span style="color: black;">&#41;</span>
            <span style="color: #008000;">setattr</span><span style="color: black;">&#40;</span>instance, <span style="color: #008000;">self</span>.<span style="color: black;">name</span>, data<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> Users<span style="color: black;">&#40;</span>Model<span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;">#...</span>
    password = PasswordCharField<span style="color: black;">&#40;</span>db_column = <span style="color: #483d8b;">'crypt'</span>, max_length=<span style="color: #ff4500;">384</span>, blank=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">#.....</span></pre></td></tr></table></div>
<p>OK, now everything's done here. The <code>save_model</code> overriding is no more needed, for the Model instance's password will not be set to empty at all when the user left the field blank when they submit.</p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/leave-password-blank-if-dont-want-to-change-in-a-django-admin/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mysql Replication 最简单配置</title>
		<link>http://apt-blog.net/mysql-replication-easy-config</link>
		<comments>http://apt-blog.net/mysql-replication-easy-config#comments</comments>
		<pubDate>Tue, 24 Apr 2012 08:48:23 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Mysql]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[mysql]]></category>
		<guid isPermaLink="false">https://apt-blog.net/?p=1207</guid>
		<description><![CDATA[Google 一下Mysql Replication可以找到相关配置说明满地都是，作为双机热备方案很多时候需要用到，但是稍微看下这些资料发现都是乱七八糟的，所以果断去读MySQL 5.1 Reference Manual: 16.1.1. How to Set Up Replication。资料很长，不过最后总结的配置其实非常简单。 配置 1. In Master (例子IP 10.6.7.7) my.cnf 添加这两行： [mysqld] log-bin=mysql-bin server-id=1 终端中运行： 1 2 3 mysqldump -uroot -p --all-databases --master-data &#124; gzip -9 -c &#62; dbdump.db.gz scp dbdump.db.gz user@10.6.7.8:~ echo &#34;CREATE USER 'repl'@'%' IDENTIFIED BY 'slavepass'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';&#34; [...]]]></description>
			<content:encoded><![CDATA[<p>Google 一下<code>Mysql Replication</code>可以找到相关配置说明满地都是，作为双机热备方案很多时候需要用到，但是稍微看下这些资料发现都是乱七八糟的，所以果断去读<a href="http://dev.mysql.com/doc/refman/5.1/en/replication-howto.html">MySQL 5.1 Reference Manual: 16.1.1. How to Set Up Replication</a>。资料很长，不过最后总结的配置其实非常简单。</p>
<h2>配置</h2>
<h3>1. In Master (例子IP 10.6.7.7)</h3>
<p><code>my.cnf</code> 添加这两行：</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">[mysqld]
log-bin=mysql-bin
server-id=1</pre></div></div>
</p>
<p>终端中运行：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;">mysqldump <span style="color: #660033;">-uroot</span> <span style="color: #660033;">-p</span> <span style="color: #660033;">--all-databases</span> <span style="color: #660033;">--master-data</span> <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">gzip</span> <span style="color: #660033;">-9</span> <span style="color: #660033;">-c</span> <span style="color: #000000; font-weight: bold;">&gt;</span> dbdump.db.gz
<span style="color: #c20cb9; font-weight: bold;">scp</span> dbdump.db.gz user<span style="color: #000000; font-weight: bold;">@</span>10.6.7.8:~
<span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">&quot;CREATE USER 'repl'@'%' IDENTIFIED BY 'slavepass'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';&quot;</span> <span style="color: #000000; font-weight: bold;">|</span> mysql <span style="color: #660033;">-uroot</span> <span style="color: #660033;">-p</span></pre></td></tr></table></div>
<h3>2. In Slave (例子IP 10.6.7.8)</h3>
<p><code>my.cnf</code> 添加和Master不同的ID：</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">[mysqld]
server-id=1001</pre></div></div>
<p>终端中运行：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #007800;">MASTER_IP</span>=10.6.7.7
&nbsp;
<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">&quot;SLAVE STOP; CHANGE MASTER TO MASTER_HOST='<span style="color: #007800;">$MASTER_IP</span>', MASTER_USER='repl', MASTER_PASSWORD='slavepass';&quot;</span>; <span style="color: #c20cb9; font-weight: bold;">zcat</span> dbdump.db.gz;<span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">&quot;SLAVE START;&quot;</span><span style="color: #7a0874; font-weight: bold;">&#41;</span> <span style="color: #000000; font-weight: bold;">|</span> mysql <span style="color: #660033;">-uroot</span> <span style="color: #660033;">-p</span></pre></td></tr></table></div>
<p>OK， 收工。</p>
<h2>验证</h2>
<p>要验证同步，在Master执行：<code>CREATE DATABASE test_repl;</code>， 在Slave执行 <code>SHOW DATABASES;</code>，可以看到<code>test_repl</code>同步完成，在Master执行：<code>DROP DATABASE test_repl;</code>，Slave的也相应消失。</p>
<p>完整的启动LOG <code>/var/log/mysql/error.log</code>大致如下，可看到replication线程启动正常。</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">120424 16:34:51 [Note] Plugin 'FEDERATED' is disabled.
120424 16:34:52  InnoDB: Initializing buffer pool, size = 8.0M
120424 16:34:52  InnoDB: Completed initialization of buffer pool
120424 16:34:52  InnoDB: Started; log sequence number 0 1174665
120424 16:34:52 [Note] Slave SQL thread initialized, starting replication in log 'mysql-bin.000001' at position 1060, relay log './ub1110-relay-bin.000024' position: 251
120424 16:34:52 [Note] Event Scheduler: Loaded 0 events
120424 16:34:52 [Note] /usr/sbin/mysqld: ready for connections.
Version: '5.1.61-0ubuntu0.11.10.1'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  (Ubuntu)
120424 16:34:52 [Note] Slave I/O thread: connected to master 'repl@172.28.16.82:3306',replication started in log 'mysql-bin.000001' at position 1060</pre></div></div>
<h2>收尾</h2>
<p>Slave的<code>/var/log/mysql/error.log</code>可能会看到一个warnning</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">120423 18:01:41 [Warning] Neither --relay-log nor --relay-log-index were used; so replication may break when this MySQL server acts as a slave and has his host name changed!! Please use '--relay-log=XXXXX-relay-bin' to avoid this problem.</pre></div></div>
<p>如它所说，把这句加入到<code>my.cnf</code>的<code>[mysqld]</code>即可。</p>
<h2>日常维护</h2>
<p>如果数据库操作频繁，binlog消耗的磁盘空间挺大的，设置Master的<code>expire_logs_days</code>可以控制存储binlog的文件个数。</p>
<p>如果留下了大堆binlog需要清理，可以执行这句清理7天前的binlog：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;">mysql <span style="color: #660033;">-uroot</span> <span style="color: #660033;">-p</span> <span style="color: #660033;">-e</span> <span style="color: #ff0000;">&quot;PURGE MASTER LOGS BEFORE DATE_SUB( NOW(), INTERVAL 7 DAY);&quot;</span></pre></td></tr></table></div>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/mysql-replication-easy-config/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Midware beaker using Google App Engine Memcache API as backend</title>
		<link>http://apt-blog.net/midware-beaker-using-google-app-engine-memcache-api-as-backend</link>
		<comments>http://apt-blog.net/midware-beaker-using-google-app-engine-memcache-api-as-backend#comments</comments>
		<pubDate>Wed, 11 Apr 2012 10:47:16 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[web技术]]></category>
		<category><![CDATA[beaker]]></category>
		<category><![CDATA[bottle]]></category>
		<category><![CDATA[gae]]></category>
		<category><![CDATA[google app engine]]></category>
		<category><![CDATA[session]]></category>
		<guid isPermaLink="false">https://apt-blog.net/?p=1195</guid>
		<description><![CDATA[What Beaker is a library for caching and sessions for use with web applications and stand-alone Python scripts and applications. -- Beaker Offcial Document Why I recently writting a small app running on Google App Engine, Beaker is the ideal midware working together with light weight python web frameworks, eg bottle, web.py, to handle session [...]]]></description>
			<content:encoded><![CDATA[<h2>What</h2>
<p><code>Beaker</code> is a library for caching and sessions for use with web applications and stand-alone Python scripts and applications. -- <a href="http://beaker.readthedocs.org/en/latest/index.html">Beaker Offcial Document</a></p>
<h2>Why</h2>
<p>I recently writting a small app running on Google App Engine, <code>Beaker</code> is the ideal midware working together with light weight python web frameworks, eg bottle, web.py, to handle session things.</p>
<p>The GAE provides <a href="https://developers.google.com/appengine/docs/python/apis">a bunch of external services</a>, which are extremely convinent for building apps. <code>Beaker</code> supports <a href="http://beaker.readthedocs.org/en/latest/configuration.html#options-for-sessions-and-caching">using google's Datastore api as backend</a>, but operations over the <code>Datastore</code> is expensive (actually still free to me, hah), it would be more delightful to use the (currently) unlimited <code>memcache</code> service.</p>
<p>Beaker supports <code>memcached</code> backend, too, but only to those ordinary APIs, like <code>pylibmc</code>, <code>cmemcache</code> which connect to specified addresses as a client. The GAE <code>memcache</code> however, with exactly same interfaces, but no need to connect to somewhere before operating data. So, it needs some work over <code>Beaker</code>.</p>
<h2>Patch</h2>
<ol>
<li>Download this <a href="http://ptcoding.googlecode.com/git/gae-chatbot/beaker/ext/googlememcache.py">googlememcache.py</a> ext module and save as <code>beaker/ext/googlememcache.py</code></li>
<li>Apply the following patch in the beaker directory. (Minor modification over <code>cache.py</code>)</li>
</ol>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">diff -r 0bf25d5b4287 beaker/cache.py
--- a/beaker/cache.py   Mon Mar 12 14:19:57 2012 -0700
+++ b/beaker/cache.py   Wed Apr 11 17:29:30 2012 +0800
@@ -18,6 +18,7 @@
 import beaker.ext.database as database
 import beaker.ext.sqla as sqla
 import beaker.ext.google as google
+import beaker.ext.googlememcache as googlememcache
&nbsp;
 # Initialize the cache region dict
 cache_regions = {}
@@ -115,6 +116,7 @@
           'ext:database':database.DatabaseNamespaceManager,
           'ext:sqla': sqla.SqlaNamespaceManager,
           'ext:google': google.GoogleNamespaceManager,
+          'ext:googlememcache': googlememcache.GoogleMemcacheNamespaceManager,
           })</pre></div></div>
<h2>Usage</h2>
<p>Nothing much different from using the original <code>ext:google</code></p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">session_opts = <span style="color: black;">&#123;</span>
    <span style="color: #483d8b;">'session.cookie_expires'</span>: <span style="color: #008000;">True</span>,
    <span style="color: #483d8b;">'session.type'</span>: <span style="color: #483d8b;">'ext:googlememcache'</span>,
<span style="color: black;">&#125;</span>
&nbsp;
app = SessionMiddleware<span style="color: black;">&#40;</span>orig_app, session_opts<span style="color: black;">&#41;</span></pre></td></tr></table></div>
<h2>Upstream</h2>
<p>I submitted this patch to <a href="https://bitbucket.org/bbangert/beaker/issue/110/backend-for-memcache-in-google-app-engine">the Beaker offcial</a>, not sure if they will adopt it as a new feature in the later releases.</p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/midware-beaker-using-google-app-engine-memcache-api-as-backend/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>挫挫的工商银行网银密码器</title>
		<link>http://apt-blog.net/icbc_random_code_generator_sucks</link>
		<comments>http://apt-blog.net/icbc_random_code_generator_sucks#comments</comments>
		<pubDate>Sat, 31 Mar 2012 10:19:45 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[小玩意]]></category>
		<category><![CDATA[icbc]]></category>
		<category><![CDATA[网银]]></category>
		<guid isPermaLink="false">https://apt-blog.net/?p=1180</guid>
		<description><![CDATA[3月27号手机收到工行短信说网银密码卡可以免费换成密码器，期限是3月31日（还真有心，sigh）。刚好今天最后一天，有空就去银行换了回来。 密码器早就见过，很多年前就见过汇丰银行的密码器，只有一个按钮，用来显示随机密码，当时工行才在推U盾。 今天拿到工行的密码器，卧了个去： 感情这个密码器的设计人还学人当物理键盘控啊。 体积够宽大就算了，还！够！厚！ 第一次开机后，要输入业务单上的激活码，然后设置一个6位开机密码： 以后每次开机都要输入这个密码，输入错了有失败计数器。没看到有修改密码的功能，所以，第一次弄要小心点了。 更正：说明书上有修改密码的操作说明。 开机后，密码器处于输入状态。可以随便输入数字标点，按确定后会算出一个6位数字。 但是这不是网银要使用的功能，一般使用还是和其他密码器那样，使用基于时间参数的算法。 更正：网银有些地方，如转帐操作需要输入页面显示的号码来获取密钥，而其他地方只需输入以下时间随机码。 在密码器刚开机的输入状态直接按“确认”，就进入这个每60秒变化一次的动态密码界面： 总结 应该说ICBC终于意识到一直推广的U盾的硬伤，除了开发硬件，还得开发驱动程序处理各个操作系统(版本)的兼容，更重要的是去年出了几个案件是有用户忘了拔U盾被木马盗取网银的事情；采用和PC系统之间独立的密码器就避免了兼容问题和这个安全漏洞。 虽然说工行有安全意识，却一直使用ActivX的过时的不安全的技术来处理网银的输入框，这点就像U盾一样，既不安全，又影响用户体验，起码在MacOS和Linux等非windows桌面系统里面就无法使用；采用动态密码器其实完全可以去掉这些所谓的安全输入控件，因为密码的一次有效性，即使内容被第三方劫持，也没有再次使用的价值。]]></description>
			<content:encoded><![CDATA[<p>3月27号手机收到工行短信说网银密码卡可以免费换成密码器，期限是3月31日（还真有心，sigh）。刚好今天最后一天，有空就去银行换了回来。</p>
<p>密码器早就见过，很多年前就见过汇丰银行的密码器，只有一个按钮，用来显示随机密码，当时工行才在推U盾。</p>
<p><a href="http://apt-blog.net/wp-content/uploads/2012/03/hsbc-code.jpeg" rel="lightbox[1180]" title="hsbc-code.jpeg"><img title="hsbc-code.jpeg" alt="hsbc-code.jpeg" src="http://apt-blog.net/wp-content/uploads/2012/03/hsbc-code.jpeg" width="30%" class="aligncenter" /></a></p>
<p>今天拿到工行的密码器，卧了个去：</p>
<p><a href="http://apt-blog.net/wp-content/uploads/2012/03/icbc-passcode-1.jpg" rel="lightbox[1180]" title="icbc-passcode-1.jpg"><img title="icbc-passcode-1.jpg" alt="icbc-passcode-1.jpg" src="http://apt-blog.net/wp-content/uploads/2012/03/icbc-passcode-1.jpg" width="50%" class="aligncenter" /></a></p>
<p>感情这个密码器的设计人还学人当<strong>物理键盘控</strong>啊。</p>
<p>体积够宽大就算了，<strong>还！够！厚！</strong></p>
<p><a href="http://apt-blog.net/wp-content/uploads/2012/03/icbc-passcode-2.jpg" rel="lightbox[1180]" title="icbc-passcode-2.jpg"><img title="icbc-passcode-2.jpg" alt="icbc-passcode-2.jpg" src="http://apt-blog.net/wp-content/uploads/2012/03/icbc-passcode-2.jpg" width="50%" class="aligncenter" /></a></p>
<p>第一次开机后，要输入业务单上的激活码，然后设置一个6位开机密码：</p>
<p><a href="http://apt-blog.net/wp-content/uploads/2012/03/icbc-passcode-4.jpg" rel="lightbox[1180]" title="icbc-passcode-4.jpg"><img title="icbc-passcode-4.jpg" alt="icbc-passcode-4.jpg" src="http://apt-blog.net/wp-content/uploads/2012/03/icbc-passcode-4.jpg" width="50%" class="aligncenter" /></a></p>
<p><a href="http://apt-blog.net/wp-content/uploads/2012/03/icbc-passcode-5.jpg" rel="lightbox[1180]" title="icbc-passcode-5.jpg"><img title="icbc-passcode-5.jpg" alt="icbc-passcode-5.jpg" src="http://apt-blog.net/wp-content/uploads/2012/03/icbc-passcode-5.jpg" width="50%" class="aligncenter" /></a></p>
<p>以后<strong>每次开机</strong>都要输入这个密码，输入错了有失败计数器。<S>没看到有修改密码的功能，所以，第一次弄要小心点了。</S> 更正：说明书上有修改密码的操作说明。</p>
<p>开机后，密码器处于输入状态。可以随便输入数字标点，按确定后会算出一个6位数字。</p>
<p><a href="http://apt-blog.net/wp-content/uploads/2012/03/icbc-passcode-7.jpg" rel="lightbox[1180]" title="icbc-passcode-7.jpg"><img title="icbc-passcode-7.jpg" alt="icbc-passcode-7.jpg" src="http://apt-blog.net/wp-content/uploads/2012/03/icbc-passcode-7.jpg" width="50%" class="aligncenter" /></a></p>
<p><S>但是这不是网银要使用的功能，一般使用还是和其他密码器那样，使用基于时间参数的算法。</S> 更正：网银有些地方，如转帐操作需要输入页面显示的号码来获取密钥，而其他地方只需输入以下时间随机码。</p>
<p><a href="http://apt-blog.net/wp-content/uploads/2012/03/icbc-passcode-8.jpg" rel="lightbox[1180]" title="icbc-passcode-8.jpg"><img title="icbc-passcode-8.jpg" alt="icbc-passcode-8.jpg" src="http://apt-blog.net/wp-content/uploads/2012/03/icbc-passcode-8.jpg" class="aligncenter" /></a></p>
<p>在密码器刚开机的输入状态直接按“确认”，就进入这个每60秒变化一次的动态密码界面：</p>
<p><a href="http://apt-blog.net/wp-content/uploads/2012/03/icbc-passcode-6.jpg" rel="lightbox[1180]" title="icbc-passcode-6.jpg"><img title="icbc-passcode-6.jpg" alt="icbc-passcode-6.jpg" src="http://apt-blog.net/wp-content/uploads/2012/03/icbc-passcode-6.jpg" width="50%" class="aligncenter" /></a></p>
<h2>总结</h2>
<p>应该说ICBC终于意识到一直推广的U盾的硬伤，除了开发硬件，还得开发驱动程序处理各个操作系统(版本)的兼容，更重要的是<a href="https://www.google.com/search?q=%E5%B7%A5%E8%A1%8C+U%E7%9B%BE+%E6%9C%A8%E9%A9%AC">去年出了几个案件是有用户忘了拔U盾被木马盗取网银</a>的事情；采用和PC系统之间独立的密码器就避免了兼容问题和这个安全漏洞。</p>
<p>虽然说工行有安全意识，却一直使用ActivX的过时的不安全的技术来处理网银的输入框，这点就像U盾一样，既不安全，又影响用户体验，起码在MacOS和Linux等非windows桌面系统里面就无法使用；采用动态密码器其实完全可以去掉这些所谓的安全输入控件，因为密码的一次有效性，即使内容被第三方劫持，也没有再次使用的价值。</p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/icbc_random_code_generator_sucks/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>一次本博客的性能故障排查</title>
		<link>http://apt-blog.net/trace_on_a_performace_problem</link>
		<comments>http://apt-blog.net/trace_on_a_performace_problem#comments</comments>
		<pubDate>Sat, 31 Mar 2012 07:02:53 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Networking]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[运维技术]]></category>
		<guid isPermaLink="false">https://apt-blog.net/?p=1163</guid>
		<description><![CDATA[无关背景 Ubuntu 10.04 这个版本已经服役两年，虽说是LTS，但最近起发现已经有点力不从心，主要是ppa上一些比较重要的库，如PHP 5.3，ningx的团队已经停止维护，uwsgi则总是落后半年的样子。很大一个原因是这些包在新版的Ubuntu里面已经有官方维护，ppa的第三方维护会缺乏跟进。所以在一定意义上，可以宣布Ubuntu 10.04死亡了。 但Ubuntu的包维护策略就是那样，要么自己维护所有用到的包，要么每隔一段时间就跟着官方一次大升级。觉得不爽就干脆把VPS的系统也改成Archlinux。 现象 一番大迁移后所有的东西都正常上线，但直到一个多星期后的昨天晚上才注意到博客的性能问题。 因为博客那里有nginx直接读取静态的缓冲机制，所以动态执行非常慢之前都没留意到。 把缓冲关闭就非常明显了，任何一次点击的页面都要10秒才能打开。 排查 引起应用缓慢的因素是非常多的，概括来说有两种：IO慢、运算慢。 运算慢是不大常见的，虽说PHP性能一直受到诟病。但如果是这个问题是很明显的，top里面php的子进程会占满CPU，高居不下。此前排查过一个drupal的站点，是因为前端模板的组件方式存在循环引用，用profile过程看，正则替换的regrex函数占了绝大多的CPU时间。 在SSH看到，打开页面的10秒内php-fpm的子进程基本没占CPU，排除这个可能。 IO慢就复杂了，每个组件都有IO，得先确定IO的范围。 首先想到是数据库Mysql，arch的包太新，有bug？linode主机的磁盘快挂了，磁盘很慢？这些都不好判断，确定这些得借助工具。 Profiling是追踪一个应用的运行流程，记录所有的函数调用栈、记录调用时间的过程，是追查性能问题的最佳帮手。Python里面是有个repoze.profile的wsgi中间件很方便进行排查，但我没做过完整PHP开发，就暂时不大清楚有什么方便的profile方案 （以前弄过早忘了），但google一下还是有很多方案的 ，不过我首先想起newrelic的应用监控系统就提供了这个功能。 Newrelic针对WEB应用和服务器监控服务，其中的服务器监控是免费的，但对应用的监控只有14天试用；所以我赶紧重新申请个帐号，用来监控一下wordpress。安装好监控模块后，超过2s的响应会在他们的Transaction traces记录下来。 图表数据可以排除数据库问题，几十个数据库的操作都是在ms级别完成，而在apt-blog.net的耗时花了10秒。其实我一开始对这个报告也没看懂，newrelic的直观性还有待提高，其实在这里出现域名的意思是有网络请求，比如askimet的评论、插件的更新等都要和外部请求，这里就会出现域名。 而现在出现了自己博客的域名，那问题就是，程序里面某个地方需要请求自己的域名，可能是检查状态的操作，被卡住了，直到超时才返回。 本机程序访问不到本机，基本确定是iptables规则出问题了，在filter表的最后插入这样一句： -A INPUT -j LOG iptables的规则一般是，除非明文允许，否则拒绝，所以经过一系列的规则后如果还没有没ACCEPT的，在最后的都是被DROP了，把这句放最后可以看到究竟是什么包被DROP了。 查看/var/log/everything.log看到这样的记录： Mar 31 03:17:32 (none) kernel: [17554800.765033] IN=lo OUT= MAC=00:00:00:00:00:0 0:00:00:00:00:00:00:08:00 SRC=106.187.36.50 DST=106.187.36.50 LEN=60 TOS=0x00 PR EC=0x00 TTL=64 ID=31529 DF PROTO=TCP SPT=45594 DPT=80 WINDOW=32792 RES=0x00 [...]]]></description>
			<content:encoded><![CDATA[<h2>无关背景</h2>
<p>Ubuntu 10.04 这个版本已经服役两年，虽说是LTS，但最近起发现已经有点力不从心，主要是ppa上一些比较重要的库，如PHP 5.3，ningx的团队已经停止维护，uwsgi则总是落后半年的样子。很大一个原因是这些包在新版的Ubuntu里面已经有官方维护，ppa的第三方维护会缺乏跟进。所以在一定意义上，可以宣布Ubuntu 10.04死亡了。</p>
<p>但Ubuntu的包维护策略就是那样，要么自己维护所有用到的包，要么每隔一段时间就跟着官方一次大升级。觉得不爽就干脆把VPS的系统也改成Archlinux。</p>
<h2>现象</h2>
<p>一番大迁移后所有的东西都正常上线，但直到一个多星期后的昨天晚上才注意到博客的性能问题。 因为博客那里有nginx直接读取静态的缓冲机制，所以动态执行非常慢之前都没留意到。</p>
<p>把缓冲关闭就非常明显了，任何一次点击的页面都要10秒才能打开。</p>
<h2>排查</h2>
<p>引起应用缓慢的因素是非常多的，概括来说有两种：<strong>IO慢</strong>、<strong>运算慢</strong>。</p>
<p>运算慢是不大常见的，虽说PHP性能一直受到诟病。但如果是这个问题是很明显的，<code>top</code>里面php的子进程会占满CPU，高居不下。此前排查过一个drupal的站点，是因为前端模板的组件方式存在循环引用，用profile过程看，正则替换的regrex函数占了绝大多的CPU时间。</p>
<p>在SSH看到，打开页面的10秒内php-fpm的子进程基本没占CPU，排除这个可能。</p>
<p>IO慢就复杂了，每个组件都有IO，得先确定IO的范围。</p>
<p>首先想到是数据库Mysql，arch的包太新，有bug？linode主机的磁盘快挂了，磁盘很慢？这些都不好判断，确定这些得借助工具。</p>
<p>Profiling是追踪一个应用的运行流程，记录所有的函数调用栈、记录调用时间的过程，是追查性能问题的最佳帮手。Python里面是有个<a href="http://docs.repoze.org/profile/">repoze.profile</a>的wsgi中间件很方便进行排查，但我没做过完整PHP开发，就暂时不大清楚有什么方便的profile方案 （以前弄过早忘了），但<a href="https://www.google.com/search?q=PHP+profiling">google一下还是有很多方案的</a> ，不过我首先想起<code>newrelic</code>的应用监控系统就提供了这个功能。</p>
<p><a href="https://newrelic.com/">Newrelic</a>针对WEB应用和服务器监控服务，其中的服务器监控是免费的，但对应用的监控只有14天试用；所以我赶紧重新申请个帐号，用来监控一下wordpress。安装好监控模块后，超过2s的响应会在他们的<code>Transaction traces</code>记录下来。</p>
<p><a href="http://apt-blog.net/wp-content/uploads/2012/03/newrelic-profile-apt-blog.png" rel="lightbox[1163]" title="newrelic-profile-apt-blog.png"><img title="newrelic-profile-apt-blog.png" alt="newrelic-profile-apt-blog.png" src="http://apt-blog.net/wp-content/uploads/2012/03/newrelic-profile-apt-blog.png" class="aligncenter" width="80%" /></a></p>
<p>图表数据可以排除数据库问题，几十个数据库的操作都是在ms级别完成，而在<code>apt-blog.net</code>的耗时花了10秒。其实我一开始对这个报告也没看懂，newrelic的直观性还有待提高，其实在这里出现域名的意思是有网络请求，比如askimet的评论、插件的更新等都要和外部请求，这里就会出现域名。</p>
<p>而现在出现了自己博客的域名，那问题就是，程序里面某个地方需要请求自己的域名，可能是检查状态的操作，被卡住了，直到超时才返回。</p>
<p>本机程序访问不到本机，基本确定是iptables规则出问题了，在filter表的<strong>最后</strong>插入这样一句：</p>
<pre><code>-A INPUT -j LOG
</code></pre>
<p>iptables的规则一般是，<strong>除非明文允许，否则拒绝</strong>，所以经过一系列的规则后如果还没有没ACCEPT的，在最后的都是被DROP了，把这句放最后可以看到究竟是什么包被DROP了。</p>
<p>查看<code>/var/log/everything.log</code>看到这样的记录：</p>
<pre><code>Mar 31 03:17:32 (none) kernel: [17554800.765033] IN=lo OUT= MAC=00:00:00:00:00:0 0:00:00:00:00:00:00:08:00 SRC=106.187.36.50 DST=106.187.36.50 LEN=60 TOS=0x00 PR EC=0x00 TTL=64 ID=31529 DF PROTO=TCP SPT=45594 DPT=80 WINDOW=32792 RES=0x00 SYN URGP=0
</code></pre>
<p>这里透露了重要信息：<code>IN=lo SRC=106.187.36.50 DST=106.187.36.50</code>，PHP确实有访问本地网络，送了给lo网卡，SRC和DST都是本地的公网地址。赶紧检查iptables的规则，果然没了对lo设备的允许规则。一般配置机器我都是用记录在<a href="http://wiki.ptsang.net/Iptables配置">自己的Wiki的iptables那套规则</a>的。关于lo的这几句可能一时没仔细想其作用，迁移系统那天脑抽手贱就删掉了。</p>
<p>加入允许lo设备的这句：</p>
<pre><code>-A INPUT -i lo -j ACCEPT
</code></pre>
<p>至此，问题解决。</p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/trace_on_a_performace_problem/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OpenVPN over HTTP 突破Layer 7 QoS的宽带网速限制</title>
		<link>http://apt-blog.net/break-through-l7qos-with-openvpn-over-http</link>
		<comments>http://apt-blog.net/break-through-l7qos-with-openvpn-over-http#comments</comments>
		<pubDate>Sun, 04 Mar 2012 11:26:28 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Networking]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[openvpn]]></category>
		<category><![CDATA[polipo]]></category>
		<category><![CDATA[proxy]]></category>
		<guid isPermaLink="false">https://apt-blog.net/?p=1156</guid>
		<description><![CDATA[Why 我家里用的是一家三线的便宜小区宽带，标称有几个M的带宽，虽说有些资源确实能达到这个速度，但发现直连VPN的速度从来都没上去过，大概30k/s，不难猜测到，ISP在链路上做了手脚，即所谓的Layer 7 Priority QoS。因为入线是100M进宅然后PPPoE，不像ADSL那样直接物理链路就限制了连接速度，在链路层做QoS也很合理。平时HTTP打开网页、下载到合适链路的话很容易达到满速，而OpenVPN的TCP/UDP包估计就被当成P2P流量被限制了，所以网速一点都不给力。 用OpenVPN科学上网是最稳定最灵活的方式了，它基于udp/tcp的协议，比pptp、l2tp等直接跑IP包的开销虽说大一点，但好处就是容易把数据流重新封装，避开链路的关卡。显然它也支持HTTP代理，所以把OpenVPN变成HTTP协议，就可以在家里跑上满速的VPN。 How OpenVPN要过HTTP代理，只能用TCP协议，这个需要服务端和客户端都要稍作修改。 Polipo as Tunnel Proxy 首先在VPS上安装一个http proxy，我选择polipo，比较轻量级。 apt-get install polipo 配置也是很简单的，编辑/etc/polipo/config，原来的配置文件基本全都是注释，直接在文件底部加上： proxyAddress = &#34;0.0.0.0&#34; proxyPort = 8128 authCredentials = &#34;user:password3.141592654&#34; tunnelAllowedPorts = 1194 polipo默认只监听本地的127.0.0.1，要拿来做服务就要监听外网 polipo默认监听8123，为了不让扫代理的盯上，自己随便写个端口 加上http basic验证，这个密码不要紧，后面让openvpn自动应答 允许管道模式连接openvpn的1194端口 OpenVPN Server 服务端倒是不需要很多配置，确定是tcp模式监听连接（我是多开一个openvpn server，子网错开，个人喜欢吧） Client 客户端就要指定使用代理的方式： remote 127.0.0.1 1194 http-proxy YOUR.VPS.IP.HERE.com 8128 pw.txt http-proxy-retry 这个pw.txt是上述的HTTP的认证信息，用户名密码各一行。 现在连接OpenVPN，可以看到连接过程的Log有这么几句，基本就确定OpenVPN over HTTP成功了！ Sun Mar [...]]]></description>
			<content:encoded><![CDATA[<h1>Why</h1>
<p>我家里用的是一家三线的便宜小区宽带，标称有几个M的带宽，虽说有些资源确实能达到这个速度，但发现直连VPN的速度从来都没上去过，大概30k/s，不难猜测到，ISP在链路上做了手脚，即所谓的Layer 7 Priority QoS。因为入线是100M进宅然后PPPoE，不像ADSL那样直接物理链路就限制了连接速度，在链路层做QoS也很合理。平时HTTP打开网页、下载到合适链路的话很容易达到满速，而OpenVPN的TCP/UDP包估计就被当成P2P流量被限制了，所以网速一点都不给力。</p>
<p>用OpenVPN<strong><a href="https://www.google.com/search?ie=UTF-8&amp;q=%E7%A7%91%E5%AD%A6%E4%B8%8A%E7%BD%91">科学上网</a></strong>是最稳定最灵活的方式了，它基于udp/tcp的协议，比pptp、l2tp等直接跑IP包的开销虽说大一点，但好处就是容易把数据流重新封装，避开链路的关卡。显然它也支持HTTP代理，所以把OpenVPN变成HTTP协议，就可以在家里跑上满速的VPN。</p>
<h1>How</h1>
<p>OpenVPN要过HTTP代理，只能用TCP协议，这个需要服务端和客户端都要稍作修改。</p>
<h3>Polipo as Tunnel Proxy</h3>
<p>首先在VPS上安装一个http proxy，我选择polipo，比较轻量级。</p>
<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">apt-get</span> <span style="color: #c20cb9; font-weight: bold;">install</span> polipo</pre></div></div>
<p>配置也是很简单的，编辑<code>/etc/polipo/config</code>，原来的配置文件基本全都是注释，直接在文件底部加上：</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">proxyAddress = &quot;0.0.0.0&quot;
proxyPort = 8128
authCredentials = &quot;user:password3.141592654&quot;
tunnelAllowedPorts = 1194</pre></div></div>
<ul>
<li>polipo默认只监听本地的127.0.0.1，要拿来做服务就要监听外网</li>
<li>polipo默认监听8123，为了不让扫代理的盯上，自己随便写个端口</li>
<li>加上http basic验证，这个密码不要紧，后面让openvpn自动应答</li>
<li>允许管道模式连接openvpn的1194端口</li>
</ul>
<h3>OpenVPN</h3>
<h4>Server</h4>
<p>服务端倒是不需要很多配置，确定是tcp模式监听连接（我是多开一个openvpn server，子网错开，个人喜欢吧）</p>
<h4>Client</h4>
<p>客户端就要指定使用代理的方式：</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">remote 127.0.0.1 1194
http-proxy YOUR.VPS.IP.HERE.com 8128 pw.txt
http-proxy-retry</pre></div></div>
<p>这个pw.txt是上述的HTTP的认证信息，用户名密码各一行。</p>
<p>现在连接OpenVPN，可以看到连接过程的Log有这么几句，基本就确定OpenVPN over HTTP成功了！</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">Sun Mar  4 19:12:58 2012 Attempting to establish TCP connection with 199.101.103.107:8192 [nonblock]
Sun Mar  4 19:12:59 2012 TCP connection established with [IPADDR]:8128
Sun Mar  4 19:12:59 2012 Send to HTTP proxy: 'CONNECT 127.0.0.1:1194 HTTP/1.0'
Sun Mar  4 19:12:59 2012 Attempting Basic Proxy-Authorization
Sun Mar  4 19:13:00 2012 HTTP proxy returned: 'HTTP/1.1 200 Tunnel established'</pre></div></div>
<h1>Related</h1>
<p>最后透露一下我的<strong>科学上网</strong>环境是在跑OpenWRT的路由器上跑VPN，然后配合<a href="http://code.google.com/p/chnroutes/">chnroute</a>的路由表，当然dnsmasq也经过配置负责把国内常用域名的解释交给国内的<code>114.114.114.114</code>服务器，这样基本一回家手机kindle电脑等全都是翻墙环境，而且速度非常良好。</p>
<h1>Reference</h1>
<ul>
<li><a href="http://www.pps.jussieu.fr/~jch/software/polipo/polipo.html">The Polipo Manual</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/break-through-l7qos-with-openvpn-over-http/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>x11vnc - 远程工作站桌面</title>
		<link>http://apt-blog.net/x11vnc_run_vncserver_in_workstation_remotely</link>
		<comments>http://apt-blog.net/x11vnc_run_vncserver_in_workstation_remotely#comments</comments>
		<pubDate>Sun, 05 Feb 2012 10:31:22 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[DE]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[vnc]]></category>
		<guid isPermaLink="false">https://apt-blog.net/?p=1151</guid>
		<description><![CDATA[Gnome Desktop Environment的VNC服务开启很容易，仅需点开Desktop Sharing，打个勾就完了。 但是有时候某台工作站没有打开这个VNC服务，只有SSH，但是又必须通过桌面来操作，这时候自带的vino server就好像不容易跑起来。 x11vnc这个小工具就可以派上用场，它对当前的x11 session建立VNC服务，很简单直接运行x11vnc，就等你连接5900端口了。当然，如果一定要指定一个session，有个-display参数可以指定，或者看看x11vnc --help，好长好长～～～]]></description>
			<content:encoded><![CDATA[<p>Gnome Desktop Environment的VNC服务开启很容易，仅需点开<code>Desktop Sharing</code>，打个勾就完了。</p>
<p><a href="http://apt-blog.net/wp-content/uploads/2012/02/vino-preferences.png" rel="lightbox[1151]" title="vino-preferences Gnome默认的VNC配置界面"><img title="vino-preferences Gnome默认的VNC配置界面" alt="vino-preferences Gnome默认的VNC配置界面" src="http://apt-blog.net/wp-content/uploads/2012/02/vino-preferences.png" class="aligncenter" style="width:30%" /></a></p>
<p>但是有时候某台工作站没有打开这个VNC服务，只有SSH，但是又必须通过桌面来操作，这时候自带的vino server就好像不容易跑起来。</p>
<p>x11vnc这个小工具就可以派上用场，它对当前的x11 session建立VNC服务，很简单直接运行x11vnc，就等你连接5900端口了。当然，如果一定要指定一个session，有个<code>-display</code>参数可以指定，或者看看<code>x11vnc --help</code>，好长好长～～～</p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/x11vnc_run_vncserver_in_workstation_remotely/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>uWSGI的几个使用技巧</title>
		<link>http://apt-blog.net/tips_on_using_uwsgi</link>
		<comments>http://apt-blog.net/tips_on_using_uwsgi#comments</comments>
		<pubDate>Thu, 24 Nov 2011 11:49:16 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[web技术]]></category>
		<category><![CDATA[uwsgi]]></category>
		<guid isPermaLink="false">https://apt-blog.net/?p=1137</guid>
		<description><![CDATA[uWSGI基本上是python做web服务的不二选择了，但似乎项目的开发者比较热衷于其新功能开发，对其使用文档还是相当缺乏的。 安装： Ubuntu 添加ppa，安装。 目前只有Lucid, Maverick, Natty几个版本还需要添加，以后版本都在官方原，直接apt-get install即可。 1 2 3 add-apt-repository ppa:uwsgi/release apt-get update apt-get install uwsgi-python Debian 目前Debian sid已经收录了uwsgi，一般服务器安装的testing/stable，只能自己修改apt的配置，从sid里面port过来。 注意：以下操作可能毁掉您的整个debian包系统。以下命令仅在Debian wheezy，即目前的testing运行测试安装过，如果你的是squeeze，自己修改01all那里 Pin: release a=squeeze 一行，但不保证能够正常运行。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 echo &#34;deb http://ftp.tw.debian.org/debian sid main non-free contrib&#34; &#62;&#62; /etc/apt/sources.list cat &#62; /etc/apt/preferences.d/01all &#60;&#60; [...]]]></description>
			<content:encoded><![CDATA[<p>uWSGI基本上是python做web服务的不二选择了，但似乎项目的开发者比较热衷于其新功能开发，对其使用文档还是相当缺乏的。</p>
<h2>安装：</h2>
<h3>Ubuntu</h3>
<p>添加ppa，安装。 目前只有Lucid, Maverick, Natty几个版本还需要添加，以后版本都在官方原，直接<code>apt-get install</code>即可。</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;">add-apt-repository ppa:uwsgi<span style="color: #000000; font-weight: bold;">/</span>release
<span style="color: #c20cb9; font-weight: bold;">apt-get</span> update
<span style="color: #c20cb9; font-weight: bold;">apt-get</span> <span style="color: #c20cb9; font-weight: bold;">install</span> uwsgi-python</pre></td></tr></table></div>
</p>
<h3>Debian</h3>
<p>目前Debian sid已经收录了uwsgi，一般服务器安装的testing/stable，只能自己修改apt的配置，从sid里面port过来。</p>
<p>注意：以下操作可能毁掉您的整个debian包系统。以下命令仅在Debian wheezy，即目前的testing运行测试安装过，如果你的是squeeze，自己修改01all那里 <code>Pin: release a=squeeze</code> 一行，但不保证能够正常运行。</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">&quot;deb http://ftp.tw.debian.org/debian sid main non-free contrib&quot;</span> <span style="color: #000000; font-weight: bold;">&gt;&gt;</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>apt<span style="color: #000000; font-weight: bold;">/</span>sources.list
<span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>apt<span style="color: #000000; font-weight: bold;">/</span>preferences.d<span style="color: #000000; font-weight: bold;">/</span>01all <span style="color: #cc0000; font-style: italic;">&lt;&lt; EOF
Package: *
Pin: release a=testing
Pin-Priority: 1000
EOF</span>
&nbsp;
<span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>apt<span style="color: #000000; font-weight: bold;">/</span>preferences.d<span style="color: #000000; font-weight: bold;">/</span>02uwsgi <span style="color: #cc0000; font-style: italic;">&lt;&lt; EOF
Package: uwsgi
Pin: release a=sid
Pin-Priority: 1010
EOF</span>
&nbsp;
<span style="color: #c20cb9; font-weight: bold;">apt-get</span> update
<span style="color: #c20cb9; font-weight: bold;">apt-get</span> <span style="color: #c20cb9; font-weight: bold;">install</span> uwsgi uwsgi-core uwsgi-plugin-python</pre></td></tr></table></div>
<h3>Centos, RHEL, Fedora, ...</h3>
<p>You're on your own, linux user.</p>
<h2>配置</h2>
<p>常见配置文件：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>uwsgi<span style="color: #000000; font-weight: bold;">/</span>apps-enabled<span style="color: #000000; font-weight: bold;">/</span>test.ini <span style="color: #cc0000; font-style: italic;">&lt;&lt; EOF
[uwsgi]
chmod-socket = 666
limit-as = 256
processes = 6
max-request = 2000
memory-report = true
enable-threads = true
virtualenv = /somewhere/to/your/virtualenv
wsgi-file = /home/somewhere/app.py
EOF</span></pre></td></tr></table></div>
<p>用ini方式的配置是比xml的容易写多了。具体配置说明请看<a href="http://projects.unbit.it/uwsgi/wiki/Doc">Doc</a></p>
<p>默认的sock文件在<code>/var/run/uwsgi/test/socket</code></p>
<p>配合nginx使用的配置文件：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>nginx<span style="color: #000000; font-weight: bold;">/</span>sites-enabled<span style="color: #000000; font-weight: bold;">/</span><span style="color: #7a0874; font-weight: bold;">test</span> <span style="color: #cc0000; font-style: italic;">&lt;&lt; EOF
server {
        listen   80;
        server_name host.domain.com;
        location / {
                include uwsgi_params;
                uwsgi_pass unix:///var/run/uwsgi/test/socket;
        }
}
EOF</span></pre></td></tr></table></div>
<h3>切换Python版本</h3>
<p>系统中可能安装有多个版本的Python，又或者同个系统里面有的app跑python3, 有的跑python2.6 stackless, 有的跑python2.7...</p>
<p>uwsgi 是通过plugin的方式加载不同的python解析器的，debian的uwsgi-plugin-python包里面提供了python26_plugin.so, python27_plugin.so, 不过uwsgi如果在app配置里面没有<code>plugins</code>参数指定使用哪个时候，会尝试加载<code>/usr/lib/uwsgi/plugins/python_plugin.so</code>，这个文件在debian下是个链接文件，可以运行<code>update-alternatives --config uwsgi-plugin-python</code>命令来修改这个默认版本。</p>
<p>源里面没有提供stackless python的plugin，只能自己编译了，还好非常简单：</p>
<p><code>/path/to/your/stackless/python uwsgiconfig.py --plugin plugins/stackless</code></p>
<p>然后把生成的python_stackless.so放到<code>/usr/lib/uwsgi/plugins</code> 就能被uwsgi加载了。</p>
<p>其他plugin的编译基本都这样，看源代码里面的plugins目录。</p>
<h2>代码</h2>
<p>使用uwsgi运行代码的时候，可以import uwsgi模块，和uwsgi进行一定的交互。</p>
<p>uwsgi 的源代码包当中有个<code>uwsgidecorators.py</code>，提供了多数的uwsgi接口，<a href="http://projects.unbit.it/uwsgi/wiki/Decorators">描述文档在此</a>。以下是几个典型应用。</p>
<h3>自动重载</h3>
<p>好一些web框架都有检测修改了源文件就自动重新加载的功能，但是在uwsgi里面这个方法是不好用了。</p>
<h4>Django</h4>
<p>加入以下代码：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> uwsgi
<span style="color: #ff7700;font-weight:bold;">from</span> uwsgidecorators <span style="color: #ff7700;font-weight:bold;">import</span> timer
<span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">utils</span> <span style="color: #ff7700;font-weight:bold;">import</span> autoreload
&nbsp;
@timer<span style="color: black;">&#40;</span><span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> change_code_gracefull_reload<span style="color: black;">&#40;</span>sig<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> autoreload.<span style="color: black;">code_changed</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
        uwsgi.<span style="color: #008000;">reload</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<h4>Bottle</h4>
<p>Bottle 的reload是它内置基础web服务器的功能，在uwsgi里面只能自己写代码监控。</p>
<p>想要简单很难做到完美：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> uwsgidecorators <span style="color: #ff7700;font-weight:bold;">import</span> filemon
&nbsp;
filemon<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/tmp/foo'</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>uwsgi.<span style="color: #008000;">reload</span><span style="color: black;">&#41;</span>
filemon<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/tmp/foo2'</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>uwsgi.<span style="color: #008000;">reload</span><span style="color: black;">&#41;</span>
filemon<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/tmp/foo3'</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>uwsgi.<span style="color: #008000;">reload</span><span style="color: black;">&#41;</span>
filemon<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/tmp/foo4'</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>uwsgi.<span style="color: #008000;">reload</span><span style="color: black;">&#41;</span>
filemon<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/tmp/foo5'</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>uwsgi.<span style="color: #008000;">reload</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<p>于是这些文件出现修改的时候，就自动reload。其实你可以自己加入调用uwsgi.reload()的响应。</p>
<h4>pylons, web.py, cherrypy ..</h4>
<p>程序员自己研究去。</p>
<h3>threading</h3>
<p>原来使用内置线程做的异步程序会发现在uwsgi，里面的线程好像都没启动了、数据库没有连接了……原因是uwsgi加载了代码后，fork出多个子进程，子进程里面的线程如果没有被再度激活，是不工作的。</p>
<p>有两个方法，一是在这个app的配置里面加上：</p>
<p><code>lazy = true</code></p>
<p>这样代码会在子进程fork了之后才开始加载。</p>
<p>二是使用uwsgi模块的api，uwsgidecorators 里面有相应的装饰器，这样uwsgi会在fork之后自动调用你要重新启动的线程。</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> uwsgidecorators <span style="color: #ff7700;font-weight:bold;">import</span> postfork
&nbsp;
@postfork
<span style="color: #ff7700;font-weight:bold;">def</span> reconnect_to_db<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    myfoodb.<span style="color: black;">connect</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
@postfork
<span style="color: #ff7700;font-weight:bold;">def</span> restart_threads<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">global</span> thread1, thread2
    thread1 = MyThread1<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    thread1.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    thread2 = MyThread2<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    thread2.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<h2>监控</h2>
<p>虽然uwsgi 默认带了SNMP功能，可以给外部监控其运行情况，但uwsgi的源码包里面带了个<code>uwsgistatus.py</code>文件，里面简单的提供了读取uwsgi模块的属性形成的报告。 可以把里面的代码copy到app项目里面，作为一个简单的监控数据。</p>
<p><a href="http://apt-blog.net/wp-content/uploads/2011/11/2011-11-24_19-04-14.png" rel="lightbox[1137]" title="2011-11-24_19-04-14.png"><img title="2011-11-24_19-04-14.png" alt="2011-11-24_19-04-14.png" src="http://apt-blog.net/wp-content/uploads/2011/11/2011-11-24_19-04-14.png" style="width: 30%;" class="aligncenter" /></a></p>
<h2>Reference</h2>
<p>以上部分代码取自uWSGI Wiki页面</p>
<ul>
<li><a href="http://projects.unbit.it/uwsgi/wiki/Doc">Doc</a></li>
<li><a href="http://projects.unbit.it/uwsgi/wiki/Example">Example</a> </li>
<li><a href="http://projects.unbit.it/uwsgi/wiki/TipsAndTricks">TipsAndTricks</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/tips_on_using_uwsgi/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Python多重继承的异构构造器</title>
		<link>http://apt-blog.net/constructors_with_different_arguments_in_python_multiinheri_class</link>
		<comments>http://apt-blog.net/constructors_with_different_arguments_in_python_multiinheri_class#comments</comments>
		<pubDate>Tue, 22 Nov 2011 17:23:51 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[class]]></category>
		<category><![CDATA[inheritace]]></category>
		<category><![CDATA[继承]]></category>
		<guid isPermaLink="false">http://apt-blog.net/?p=1131</guid>
		<description><![CDATA[What 在Python里面，如果你使用上Qt，SQLAlchemy，Twisted之类各种大型类库时候，有时候多重继承Multiple Inheritance是个简单的解决方法，但是多重继承的复杂性总容易造成误解和疑惑。 一般“常识”说，使用super访问父类的属性/方法，这种说法在多重继承里面是不成立的，多重继承的类并没有父类的概念(There is no superclass in a MI world)。类似的博客在过去几年被人写了无数遍了，因为过去版本里面python官方文档对super的解释非常有限而且有误导解释，直到2.6以后的文档，才详细说明了super在单继承和多继承的两种不同工作方式。当时苦逼的程序员甚至不得不去翻看Python源码才搞清楚是什么回事。以致几年来很多人对python的多重继承保持怀疑态度。 Python多重继承使用Method Resolution Order的动态算法来解决一个方法名的调用顺序，mro其实说来简单，就是一个深度优先的继承列表，很易理解，但随之来的是遇到互不相同的构造器__init__参数的问题。 Codepad运行结果：http://codepad.org/qQNiMzBl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class A&#40;object&#41;: def __init__&#40;self, arg1&#41;: print &#34;init func in A, with arg1 '%s'&#34; % arg1 super&#40;A, self&#41;.__init__&#40;&#41; &#160; class B&#40;object&#41;: def __init__&#40;self, arg1, [...]]]></description>
			<content:encoded><![CDATA[<h2>What</h2>
<p>在Python里面，如果你使用上Qt，SQLAlchemy，Twisted之类各种大型类库时候，有时候多重继承Multiple Inheritance是个简单的解决方法，但是多重继承的复杂性总容易造成误解和疑惑。</p>
<p>一般“常识”说，使用super访问父类的属性/方法，这种说法在多重继承里面是不成立的，<a href="http://www.artima.com/weblogs/viewpost.jsp?thread=236275">多重继承的类并没有父类的概念(There is no superclass in a MI world)</a>。类似的博客在过去几年被人写了无数遍了，因为过去版本里面python官方文档对super的解释非常有限而且有误导解释，直到2.6以后的文档，才详细说明了<a href="http://docs.python.org/library/functions.html#super">super在单继承和多继承的两种不同工作方式</a>。当时苦逼的程序员甚至<a href="http://blog.csdn.net/johnsonguo/article/details/585193">不得不去翻看Python源码</a>才搞清楚是什么回事。以致几年来很多人对python的多重继承保持怀疑态度。</p>
<p>Python多重继承使用<a href="http://www.python.org/download/releases/2.3/mro/">Method Resolution Order</a>的动态算法来解决一个方法名的调用顺序，mro其实说来简单，就是一个深度优先的继承列表，很易理解，但随之来的是遇到互不相同的构造器<code>__init__</code>参数的问题。</p>
<p>Codepad运行结果：<a href="http://codepad.org/qQNiMzBl">http://codepad.org/qQNiMzBl</a></p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> A<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, arg1<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;init func in A, with arg1 '%s'&quot;</span> <span style="color: #66cc66;">%</span> arg1
        <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>A, <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> B<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, arg1, arg2<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;init func in B, with arg1'%s', arg2 '%s'&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>arg1, arg2<span style="color: black;">&#41;</span>
        <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>B, <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span>arg1<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> C<span style="color: black;">&#40;</span>B, A<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, arg1, arg2<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;init func in C, with arg1'%s', arg2 '%s'&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>arg1, arg2<span style="color: black;">&#41;</span>
        <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>C, <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span>arg1, arg2<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">print</span> C.__mro__
&nbsp;
c = C<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;C's arg1&quot;</span>, <span style="color: #483d8b;">&quot;C's arg2&quot;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<p>执行结果：</p>
<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">init func in C, with arg1'C's arg1', arg2 'C's arg2'
init func in B, with arg1'C's arg1', arg2 'C's arg2'
init func in A, with arg1 'C's arg1'
(&lt;class '__main__.C'&gt;, &lt;class '__main__.B'&gt;, &lt;class '__main__.A'&gt;, &lt;type 'object'&gt;)</pre></div></div>
<p>可见几个类的构造器的执行顺序正是mro列表的顺序。重点是多重继承的各个类的构造器<code>__init__</code>之所以能够执行，是因为每个构造器里面都有一句super()，这个super完成mro列表中<strong>下一个类的构造器的调用</strong>。</p>
<p>这个事实听起来似乎很自然，但看代码，B的构造器还得必须知道A的构造器的参数？B需要知道自己将会被C同时继承A，并且调用A的构造？！！很荒谬，但不幸的这是mro的特点。代码是可以这么写，但不应该，为另一个不知道什么时候会被一起继承的类特地地写代码，跟面对对象设计的解耦原则相违背。</p>
<h2>How</h2>
<p>在mro方式的基础上，这个问题是不可能有效解决的，只能避免。概括起来大概有这么 两种方式。</p>
<h4>1.使用传统类的方式，显式调用<strong>被继承类</strong>的构造器，避开super的mro自动化工作。</h4>
<p>Codepad 看运行效果： <a href="http://codepad.org/XCVdKCPm">http://codepad.org/XCVdKCPm</a></p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> A<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, arg1<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;init func in A, with arg1 '%s'&quot;</span> <span style="color: #66cc66;">%</span> arg1
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> B<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, arg1, arg2<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;init func in B, with arg1'%s', arg2 '%s'&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>arg1, arg2<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> C<span style="color: black;">&#40;</span>A, B<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, arg1, arg2<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;init func in C, with arg1'%s', arg2 '%s'&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>arg1, arg2<span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;">#super(C, self).__init__(arg1) #这两行</span>
        A.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, arg1<span style="color: black;">&#41;</span>       <span style="color: #808080; font-style: italic;">#等价</span>
        B.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, arg1, arg2<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">print</span> C.__mro__
&nbsp;
c = C<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;C's arg1&quot;</span>, <span style="color: #483d8b;">&quot;C's arg2&quot;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<p>注意 C继承A，B的顺序已经改变。</p>
<p>要排除一个容易产生的误解。Python文档里面的super有个很显著的Note：super() only works for new-style classes. super只适用于新类。但<strong>新类并不必须使用super</strong>。</p>
<p>直接调用<code>被继承类</code>的<code>__init__</code>作为unbound方法调用，需要指定一个实例，如self作为参数，依次调用各个<code>被继承类</code>。缺点是若果这几个<code>被继承类</code>也在构造方法里面使用这样调用了同一个上级<code>被继承类</code>，会出现“爷爷类”的构造方法被调用多次的情况。</p>
<p>如果一定使用super，要注意继承列表的顺序。super(TYPE, self).method调用的是mro列表中第一个，也即继承列表第一个类的方法。</p>
<p>PyQt里面的类内部一般（未细究）都使用<code>__init__</code>的方式来初始化代码，因而很多PyQt的例子代码都是使用<code>QtGui.QMainWindow.__init__(self)</code>这样的代码来初始化。当然在单继承时候和super的写法是等价的，但最好使用统一的原则:</p>
<p>一个简单好记的原则：</p>
<ul>
<li>如果"被继承类"都使用<code>__init__</code>，"继承类"就使用<code>__init__</code>来初始化；</li>
<li>如果"被继承类"都使用<code>super</code>，"继承类"就使用<code>super</code>来初始化；</li>
</ul>
<h4>2.使用Composition / Association Pattern的设计模式（即'Is-A'转换成'Has-A'）来实现相同功能，避免多重继承。</h4>
<p>这个方法听起来未免有点让人不快（破坏了原有设计思维），但实际上很可能这是更好的方式，更清晰的代码，尤其是要继承的类里面混合了使用<code>super</code>，<code>__init__</code>两种初始化方式的时候。</p>
<h2>Reference</h2>
<ul>
<li><a href="http://stackoverflow.com/questions/4029550/python-correct-way-to-initialize-when-superclasses-accept-different-arguments">Python: Correct way to initialize when superclasses accept different arguments?</a></li>
<li><a href="http://www.artima.com/weblogs/viewpost.jsp?thread=236275">Things to Know About Python Super</a> <a href="http://www.artima.com/weblogs/viewpost.jsp?thread=236278">2</a> <a href="http://www.artima.com/weblogs/viewpost.jsp?thread=237121">3</a></li>
<li><a href="http://blog.csdn.net/johnsonguo/article/details/585193">关于Python的super用法研究</a></li>
<li><a href="http://docs.python.org/library/functions.html#super">Python Built-in Functions#super()</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/constructors_with_different_arguments_in_python_multiinheri_class/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 0.975 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2012-05-18 19:40:45 -->
<!-- Compression = gzip -->
