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

<channel>
	<title>开源小站 &#187; www</title>
	<atom:link href="http://www.litrin.net/category/www/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.litrin.net</link>
	<description>It is Cool to OpenSource</description>
	<lastBuildDate>Fri, 03 Feb 2012 04:33:02 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Tornado的进程级别缓存用法</title>
		<link>http://www.litrin.net/2012/02/03/tornado%e7%9a%84%e8%bf%9b%e7%a8%8b%e7%ba%a7%e5%88%ab%e7%bc%93%e5%ad%98%e7%94%a8%e6%b3%95/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=tornado%25e7%259a%2584%25e8%25bf%259b%25e7%25a8%258b%25e7%25ba%25a7%25e5%2588%25ab%25e7%25bc%2593%25e5%25ad%2598%25e7%2594%25a8%25e6%25b3%2595</link>
		<comments>http://www.litrin.net/2012/02/03/tornado%e7%9a%84%e8%bf%9b%e7%a8%8b%e7%ba%a7%e5%88%ab%e7%bc%93%e5%ad%98%e7%94%a8%e6%b3%95/#comments</comments>
		<pubDate>Fri, 03 Feb 2012 03:20:48 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[nosql]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1665</guid>
		<description><![CDATA[之前一直在跟朋友调试一个基于tornado框架的长连接HTTP服务，并不断的优化尽量以最少的资源换取最大的连接数。我的想法是通过定期轮询一个Redis键，当键值发生改变后推送信息给客户端，并同时回写改变数据到redis以便下次比对。这种方法中规中矩，但显然每次变动都会牵扯两次Redis读写，网络开销的代价很大。即便采用了Redis pipline连接池，受通讯时间的影响，性能只能说“可以接受”而已。瓶颈在数据存储的级别。 记得在PHP开发过程中，经常会用到实例级别或者线程级别的变量缓存，带来的性能提升是巨大的。但对于tornado来说，由于每次访问都会实例化tornado.web.RequestHandler，不能满足要求，只能考虑在主线程上进行变量缓存。一旦成功将有可能直接抛弃redis。 种种原因，不公开最终实现的代码，这里只用一个页面计数器来模拟。通过一个页面http://localhost/get获得主页http://localhost/的Page View。 import tornado.httpserver import tornado.ioloop import tornado.options import tornado.httpclient import tornado.web import sys, os from tornado.options import define, options ##### class PageCounter(object): count = 0 #### #PageCounter = 0 这种采用全局变量的方法被证实无法实现。 class MainPage(tornado.web.RequestHandler): def get(self): PageCounter.count += 1 self.finish('Hello World!') class GetPageView(tornado.web.RequestHandler): def get(self): sPageView = str(PageCounter.count) self.write(sPageView) def main(port): if os.fork() [...]]]></description>
			<content:encoded><![CDATA[<p>之前一直在跟朋友调试一个<a href="http://www.litrin.net/2011/10/18/%E9%9D%9E%E9%98%BB%E5%A1%9E%E7%9A%84python-web%E6%A1%86%E6%9E%B6tornado/" target="_blank">基于tornado框架的长连接HTTP服务</a>，并不断的优化尽量以最少的资源换取最大的连接数。我的想法是通过定期轮询一个Redis键，当键值发生改变后推送信息给客户端，并同时回写改变数据到redis以便下次比对。这种方法中规中矩，但显然每次变动都会牵扯两次Redis读写，网络开销的代价很大。即便采用了Redis pipline连接池，受通讯时间的影响，性能只能说“可以接受”而已。瓶颈在数据存储的级别。</p>
<p>记得在PHP开发过程中，经常会用到实例级别或者线程级别的变量缓存，带来的性能提升是巨大的。但对于tornado来说，由于每次访问都会实例化tornado.web.RequestHandler，不能满足要求，只能考虑在主线程上进行变量缓存。一旦成功将有可能直接抛弃redis。</p>
<p><span id="more-1665"></span></p>
<p>种种原因，不公开最终实现的代码，这里只用一个页面计数器来模拟。通过一个页面http://localhost/get获得主页http://localhost/的Page View。</p>
<pre class="py" name=code>
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.httpclient
import tornado.web
import sys, os
from tornado.options import define, options

#####
class PageCounter(object):
    count = 0
####

#PageCounter = 0 这种采用全局变量的方法被证实无法实现。

class MainPage(tornado.web.RequestHandler):

    def get(self):
        PageCounter.count += 1
        self.finish('Hello World!')

class GetPageView(tornado.web.RequestHandler):
    def get(self):
        sPageView = str(PageCounter.count)
        self.write(sPageView)

def main(port):
    if os.fork() != 0:
        exit()
    define("port", default=port, help="run on the given port", type=int)
    application = tornado.web.Application([
        ("/", MainPage),
        ("/get", GetPageView),
    ])
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":

    if len (sys.argv) &gt; 1:
        port = sys.argv[1]
    else:
        port = 80
    main(port)</pre>
<p>这里要说的是object基类的PageCounter方法，可以定义一种数据结构以及对应的操作方法。当然这段代码只是Demo，写得不规范。由于PageCount是全局的，跟随进程实例化，所以可以很灵活在各个实例之间传递数据。</p>
<p><a href="http://www.litrin.net/2012/02/03/tornado%e7%9a%84%e8%bf%9b%e7%a8%8b%e7%ba%a7%e5%88%ab%e7%bc%93%e5%ad%98%e7%94%a8%e6%b3%95/homepage/" rel="attachment wp-att-1668"><img class="aligncenter size-medium wp-image-1668" title="homepage" src="http://www.litrin.net/wp-content/uploads/2012/02/homepage-300x218.png" alt="" width="300" height="218" /></a></p>
<p><a href="http://www.litrin.net/2012/02/03/tornado%e7%9a%84%e8%bf%9b%e7%a8%8b%e7%ba%a7%e5%88%ab%e7%bc%93%e5%ad%98%e7%94%a8%e6%b3%95/pageview/" rel="attachment wp-att-1669"><img class="aligncenter size-medium wp-image-1669" title="pageView" src="http://www.litrin.net/wp-content/uploads/2012/02/pageView-300x218.png" alt="" width="300" height="218" /></a></p>
<p>&nbsp;</p>
<p>类似这种方法，我们的项目性能提升了至少5倍。但这样实现的结果也非常明显，首先不能持久化，一旦进程终止，数据就被清空；其次是进程隔离，这种方法共享数据只能工作在tornado的一个进程中，对于习惯于多进程并行的Tornado部署会有问题，我们最终的做法是做唯一哈希；最后要注意的是内存释放的问题，数据保存在内存中，如果是大量的数据通过dict/list做映射的话需要进行定期清理，当然通过这种方法可以变态的做一个基于HTTP协议的Key-value数据库。</p>
<p># ab -n10000 -c1000 http://localhost/<br />
This is ApacheBench, Version 2.3 < $Revision: 655654 $><br />
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/<br />
Licensed to The Apache Software Foundation, http://www.apache.org/</p>
<p>Benchmarking localhost (be patient)<br />
Completed 1000 requests<br />
Completed 2000 requests<br />
Completed 3000 requests<br />
Completed 4000 requests<br />
Completed 5000 requests<br />
Completed 6000 requests<br />
Completed 7000 requests<br />
Completed 8000 requests<br />
Completed 9000 requests<br />
Completed 10000 requests<br />
Finished 10000 requests</p>
<p>Server Software:        TornadoServer/2.1.1<br />
Server Hostname:        localhost<br />
Server Port:            80</p>
<p>Document Path:          /<br />
Document Length:        12 bytes</p>
<p>Concurrency Level:      1000<br />
Time taken for tests:   8.921 seconds<br />
Complete requests:      10000<br />
Failed requests:        0<br />
Write errors:           0<br />
Total transferred:      1700000 bytes<br />
HTML transferred:       120000 bytes<br />
Requests per second:    1120.89 [#/sec] (mean)<br />
Time per request:       892.148 [ms] (mean)<br />
Time per request:       0.892 [ms] (mean, across all concurrent requests)<br />
Transfer rate:          186.09 [Kbytes/sec] received</p>
<p>Connection Times (ms)<br />
              min  mean[+/-sd] median   max<br />
Connect:        0  230 792.8      0    3012<br />
Processing:    14  134  73.9    114    1878<br />
Waiting:       14  134  73.9    114    1878<br />
Total:         86  364 815.1    114    3818</p>
<p>Percentage of the requests served within a certain time (ms)<br />
  50%    114<br />
  66%    115<br />
  75%    121<br />
  80%    141<br />
  90%    378<br />
  95%   3135<br />
  98%   3176<br />
  99%   3223<br />
 100%   3818 (longest request)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2012/02/03/tornado%e7%9a%84%e8%bf%9b%e7%a8%8b%e7%ba%a7%e5%88%ab%e7%bc%93%e5%ad%98%e7%94%a8%e6%b3%95/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>用Nginx和Redis搭建琐碎文件下载站</title>
		<link>http://www.litrin.net/2011/11/22/%e7%94%a8nginx%e5%92%8credis%e6%90%ad%e5%bb%ba%e7%90%90%e7%a2%8e%e6%96%87%e4%bb%b6%e4%b8%8b%e8%bd%bd%e7%ab%99/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e7%2594%25a8nginx%25e5%2592%258credis%25e6%2590%25ad%25e5%25bb%25ba%25e7%2590%2590%25e7%25a2%258e%25e6%2596%2587%25e4%25bb%25b6%25e4%25b8%258b%25e8%25bd%25bd%25e7%25ab%2599</link>
		<comments>http://www.litrin.net/2011/11/22/%e7%94%a8nginx%e5%92%8credis%e6%90%ad%e5%bb%ba%e7%90%90%e7%a2%8e%e6%96%87%e4%bb%b6%e4%b8%8b%e8%bd%bd%e7%ab%99/#comments</comments>
		<pubDate>Tue, 22 Nov 2011 09:46:48 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[nosql]]></category>
		<category><![CDATA[Redis]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1596</guid>
		<description><![CDATA[对于网站来说，琐碎文件是一个很鸡肋的问题。太多过小文件的管理总有这样那样的问题，特别是当文件数量到达临界时，甚至作一条ls或者rm都无法正常反应。而且正常情况下琐碎文件都会主动缓存，花费太多资源去实现意义也不大。 之前曾经通过nginx的memcache扩展，配合TokyoTyrant Server（每次都觉得很别嘴）这个即支持memcache协议，又可以持久化的Key-Value实现过琐碎文件的管理，但限于Memcache无法逾越的1M鸿沟，只能保存些小文件。 这些天看到一个Nginx第三方插件 HttpRedis, 本身仅仅只是计划用它来做一些短域名跳转之类的应用，忽然觉得Redis没有内容主体的限制，而且性能优良，完全可以用作琐碎文件的下载。 首先下载nginx——不知不觉nginx已经有了正式的1.0以上的版本了！ wget http://www.nginx.org/download/nginx-1.0.10.tar.gz 下载 http_redis: wget http://people.freebsd.org/~osa/ngx_http_redis-0.3.5.tar.gz 开始编译，没什么复杂的： tar vzxf nginx-1.0.10.tar.gz tar vzxf  ngx_http_redis-0.3.5.tar.gz cd nginx-1.0.10 ./configure &#8211;user=www &#8211;group=www &#8211;prefix=/app/nginx  &#8211;add-module=../ngx_http_redis-0.3.5 make -j4 &#38;&#38; make install //部分CPU无法通过编译，还是乖乖的make  &#38;&#38; make install 修改nginx的配置文件： 在http{}之中加入： server { listen 80; server_name www.test.com; location / { set $redis_key CACHE_$request_uri; redis_pass localhost:6379; default_type text/html; } } &#160; 安装redis，还是老一套： apt-get [...]]]></description>
			<content:encoded><![CDATA[<p>对于网站来说，琐碎文件是一个很鸡肋的问题。太多过小文件的管理总有这样那样的问题，特别是当文件数量到达临界时，甚至作一条ls或者rm都无法正常反应。而且正常情况下琐碎文件都会主动缓存，花费太多资源去实现意义也不大。</p>
<p>之前曾经通过nginx的memcache扩展，配合TokyoTyrant Server（每次都觉得很别嘴）这个即支持memcache协议，又可以持久化的Key-Value实现过琐碎文件的管理，但限于Memcache无法逾越的1M鸿沟，只能保存些小文件。</p>
<p><span id="more-1596"></span></p>
<p>这些天看到一个Nginx第三方插件 <a rel="nofollow" target="_blank" href="http://wiki.nginx.org/HttpRedis">HttpRedis</a>, 本身仅仅只是计划用它来做一些短域名跳转之类的应用，忽然觉得Redis没有内容主体的限制，而且性能优良，完全可以用作琐碎文件的下载。</p>
<p>首先下载nginx——不知不觉nginx已经有了正式的1.0以上的版本了！</p>
<p>wget http://www.nginx.org/download/nginx-1.0.10.tar.gz</p>
<p>下载 http_redis:</p>
<p>wget http://people.freebsd.org/~osa/ngx_http_redis-0.3.5.tar.gz</p>
<p>开始编译，没什么复杂的：</p>
<p>tar vzxf nginx-1.0.10.tar.gz<br />
tar vzxf  ngx_http_redis-0.3.5.tar.gz</p>
<p>cd nginx-1.0.10<br />
./configure &#8211;user=www &#8211;group=www &#8211;prefix=/app/nginx  &#8211;add-module=../ngx_http_redis-0.3.5</p>
<p>make -j4 &amp;&amp; make install //部分CPU无法通过编译，还是乖乖的make  &amp;&amp; make install</p>
<p>修改nginx的配置文件：</p>
<p>在http{}之中加入：</p>
<pre>server {

    listen       80;
    server_name  www.test.com;

    location / {
      set $redis_key CACHE_$request_uri;
      redis_pass     localhost:6379;

      default_type   text/html;
    }
}</pre>
<p>&nbsp;</p>
<p>安装redis，还是老一套：</p>
<p>apt-get install redis-serve<br />
/etc/init.d/redis-server start</p>
<p>比如你现在需要通过http://www.test.com/a.html访问一个页面，则可以直接通过下面的方法在redis中塞入一个值，依次类推：</p>
<p>redis-cli<br />
redis &gt; set CACHE_a.html “Hello World!”<br />
OK</p>
<p>redis &gt; set CACHE_/b/a.html “Hello World!”<br />
OK</p>
<p>调试无误后就可以使用程序端直接向redis中写入数据了。</p>
<p>当然，也可以通过设置不同的“default_type”存放图片或者其他类型的文件。 由于此扩展支持redis连接池，而且redis本身也支持master-slaver数据同步，完全可以实现redis端的负载均衡（考虑到redis的性能，我觉得意义不大，除非为了热备份）。需要注意的是Redis是内存数据库，尽管它有定时将数据回写磁盘的机制，但如果不启用vm的话，内存是一个硬性的限制。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2011/11/22/%e7%94%a8nginx%e5%92%8credis%e6%90%ad%e5%bb%ba%e7%90%90%e7%a2%8e%e6%96%87%e4%bb%b6%e4%b8%8b%e8%bd%bd%e7%ab%99/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>非阻塞的Python web框架tornado</title>
		<link>http://www.litrin.net/2011/10/18/%e9%9d%9e%e9%98%bb%e5%a1%9e%e7%9a%84python-web%e6%a1%86%e6%9e%b6tornado/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e9%259d%259e%25e9%2598%25bb%25e5%25a1%259e%25e7%259a%2584python-web%25e6%25a1%2586%25e6%259e%25b6tornado</link>
		<comments>http://www.litrin.net/2011/10/18/%e9%9d%9e%e9%98%bb%e5%a1%9e%e7%9a%84python-web%e6%a1%86%e6%9e%b6tornado/#comments</comments>
		<pubDate>Tue, 18 Oct 2011 08:10:12 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[服务器]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1581</guid>
		<description><![CDATA[公司项目中需要使用长链接方式的获取后端数据库——主要是Redis的实时数据。 由于项目本身是PHP的初次看到这个项目，首先想到的是Apache + mod_php的方式，配合php的ob_start()方式直接调用，就如同我之前的一篇东西所说的那样。可问题不这么简单： 系统是nginx + php-fpm方式，php-fpm“hold不住”过多的Http请求，而nginx需要调整响应时间。 用户数量很多，Apache的消耗很大。本身功能点很小，实现成本不合算。 说到并发，Apache采用的方式是大量的fork进程，通过“人多力量大”的方式应对多个请求，这样的基于进程（线程）模型的并发，一旦调用sleep，进程只是休眠而已，仍然占用着内存，仍然需要进程调度，资源始终得不到释放，资源自然无法得到控制。这些年nginx的流行大多都是因为nginx采用的epoll方式有效的解决这个问题——直接挂掉进程，然后再指定时间内重新启用。这就是传说中的“非阻塞”(asynchronous IO  AIO)。 多并发、轻量级应用，我首先想到的是Python，加上“非阻塞”关键字，得到的结果就是Tornado。 tornado原本是FriendFeed引擎的开源版本，本身就是为解决“C10K”问题而生的。 稍微研究了一下，coding也很简便： #!/usr/bin/env python #By Litrin J #http://www.litrin.net/ import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web import time, redis, hashlib,random from tornado.options import define, options define("port", default=8888, help="run on the given port", type=int) class LongPolling(tornado.web.RequestHandler): minWaitTime = 15 maxWaitTime = 900 RedisHost = [...]]]></description>
			<content:encoded><![CDATA[<p>公司项目中需要使用长链接方式的获取后端数据库——主要是Redis的实时数据。</p>
<p>由于项目本身是PHP的初次看到这个项目，首先想到的是Apache + mod_php的方式，配合php的ob_start()方式直接调用，就如同我之前的<a title="重新认识下PHP的输出" href="http://www.litrin.net/2011/01/16/%e9%87%8d%e6%96%b0%e8%ae%a4%e8%af%86%e4%b8%8bphp%e7%9a%84%e8%be%93%e5%87%ba/">一篇东西</a>所说的那样。可问题不这么简单：</p>
<ol>
<li>系统是nginx + php-fpm方式，php-fpm“hold不住”过多的Http请求，而nginx需要调整响应时间。</li>
<li>用户数量很多，Apache的消耗很大。本身功能点很小，实现成本不合算。<span id="more-1581"></span></li>
</ol>
<p>说到并发，Apache采用的方式是大量的fork进程，通过“人多力量大”的方式应对多个请求，这样的基于进程（线程）模型的并发，一旦调用sleep，进程只是休眠而已，仍然占用着内存，仍然需要进程调度，资源始终得不到释放，资源自然无法得到控制。这些年nginx的流行大多都是因为nginx采用的epoll方式有效的解决这个问题——直接挂掉进程，然后再指定时间内重新启用。这就是传说中的“非阻塞”(asynchronous IO  AIO)。</p>
<p>多并发、轻量级应用，我首先想到的是Python，加上“非阻塞”关键字，得到的结果就是<a rel="nofollow" target="_blank" href="http://www.tornadoweb.org/">Tornado</a>。</p>
<p>tornado原本是FriendFeed引擎的开源版本，本身就是为解决“C10K”问题而生的。</p>
<p>稍微研究了一下，coding也很简便：</p>
<pre class="py" name=code>#!/usr/bin/env python
#By Litrin J
#http://www.litrin.net/
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import time, redis, hashlib,random
from tornado.options import define, options

define("port", default=8888, help="run on the given port", type=int)

class LongPolling(tornado.web.RequestHandler):

    minWaitTime = 15
    maxWaitTime = 900

    RedisHost   = "172.18.194.98"
    RedisPrefix = "Q/MSG%s"

    SignSalt    = "salt"

    StopCode    = "{'stop':1}"

    @tornado.web.asynchronous
    def get(self):
        sUid        = self.get_argument("uid", None)
        sSign       = self.get_argument("sign", None)
        sJsonCall   = self.get_argument("jsonCallback", None)

        if (sUid is None or sSign is None
                or self.checkSign(sUid, sSign) == False):
            raise tornado.web.HTTPError(404)
            self.clear()

        else:
            self.doLongPolling(callback=self.onWaitting)

    def checkSign(self, sUid, sSign):
        return True

    def doLongPolling(self, callback):
        #Check if the client close the connection
        if self.request.connection.stream.closed():
            self.clear()

        sKey        =   self.RedisPrefix % self.uid
        res         =   redis.Redis(self.RedisHost)
        sMessage    =   res.rpop(sKey)
        del res #close the redis at 1st time

        callback(sMessage)

    def onWaitting(self, sMessage):
        if (sMessage is not None):
            self.onRespones(sMessage)
        else:
            iNextPollingTime = time.time() + self.minWaitTime
            if self.minWaitTime &lt; self.maxWaitTime:
                self.minWaitTime *= 2
                #Can't useing time.sleep for no-bloking mode
                tornado.ioloop.IOLoop.instance().add_timeout(
                iNextPollingTime ,
                lambda: self.doLongPolling(callback=self.onWaitting)
            )
            else:
                self.onRespones(self.StopCode)

    def onRespones(self, sMessage):
        sJsonCall   = self.get_argument("jsonCallback", None)
        if sJsonCall is not None:
            sMessage= sJsonCall + "( " + sMessage.decode('utf-8') + ")"

        self.write(sMessage)
        self.finish()

def main():
    tornado.options.parse_command_line()
    application = tornado.web.Application([
        (r"/", LongPolling),
    ])
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    main()</pre>
<p>一旦建立http连接，系统会每隔一段时间去轮询用户的队列中是否有数据，有数据时才向客户端发送请求，否则就是“长时间小菊花”状态。很简单，仅仅用了不到100行代码的样子。当然，为了安全因素和节省资源，代码里添加了很多限制性的操作，否则可能会更简洁高效。</p>
<p>需要注意的是，tornado的理念就是建立在“非阻塞”基础上的，你当然可以选择time.sleep(n)，但传统上的time.sleep(n)方式的休眠会导致IO阻塞，故只能采用tornado.ioloop.IOLoop.instance().add_timeout 的方法回调来回调去，将下次启动的时间转给系统。话说回来，这是我第一次在Java或JS以外使用回调函数……</p>
<p>初步测试了一下，借助Redis本身就很强的并发能力在台式机上可以每秒钟完成4K次请求以上，并可以保持同时4K以上的访问不掉线，此时的CPU load仍然保持在1以下，单是测试的结果就足以让Apache+Php组合汗颜。</p>
<p>部署的时候采用了单IP多端口方式，服务器有4个核心，决定开4个端口对应，分别是8885~8888，修改</p>
<pre>define("port", default=8888, help="run on the given port", type=int)</pre>
<p>即可</p>
<p>前端nginx用负责负载分发：</p>
<pre>upstream backend {
        server 127.0.0.1:8888;
        server 127.0.0.1:8887;
        server 127.0.0.1:8886;
        server 127.0.0.1:8885;
}
 server{
        listen  80;
        server_name message.test.com;
        keepalive_timeout 65;    #
        proxy_read_timeout 2000; #
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;

    location / {
        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;

        proxy_pass  http://backend;
        }
}</pre>
<p>需要注意的是，这样的httpSocket方式很可能被内核认为是SYN攻击，而且对于大量的keep alive而言，很可能超过核心限制，还要修改sysctl参数。</p>
<pre>#系统最大打开文件数
fs.file-max = 201510

#TCP连接的最长时间
net.ipv4.tcp_keepalive_time = 1800
#出现问题的尝试次数
net.ipv4.tcp_keepalive_probes = 15
#检查是否连接的等待时间
net.ipv4.tcp_keepalive_intvl = 60
#不进行SYN cookies防御
net.ipv4.tcp_syncookies = 0</pre>
<pre></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2011/10/18/%e9%9d%9e%e9%98%bb%e5%a1%9e%e7%9a%84python-web%e6%a1%86%e6%9e%b6tornado/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>开源小站的基础结构</title>
		<link>http://www.litrin.net/2011/08/12/%e5%bc%80%e6%ba%90%e5%b0%8f%e7%ab%99%e7%9a%84%e5%9f%ba%e7%a1%80%e7%bb%93%e6%9e%84/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e5%25bc%2580%25e6%25ba%2590%25e5%25b0%258f%25e7%25ab%2599%25e7%259a%2584%25e5%259f%25ba%25e7%25a1%2580%25e7%25bb%2593%25e6%259e%2584</link>
		<comments>http://www.litrin.net/2011/08/12/%e5%bc%80%e6%ba%90%e5%b0%8f%e7%ab%99%e7%9a%84%e5%9f%ba%e7%a1%80%e7%bb%93%e6%9e%84/#comments</comments>
		<pubDate>Fri, 12 Aug 2011 05:06:03 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[站长的blog]]></category>
		<category><![CDATA[硬件相关]]></category>
		<category><![CDATA[网络和安全]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1516</guid>
		<description><![CDATA[之前有朋友问过开源小站的架构是怎样的，我回答道：“标准的wordpress，稍微做了一些调整。”时隔一段时期，几经搬迁，这次重新整理了一下开源小站的结构，就当分享罢了。 “开源小站”最初是基于drupal构建，基础数据库是postgresql。后来由于drupal的性能问题以及框死了的postgresql，只能整体迁移到了wordpress+mysql。当然迁移好之后，考虑到drupal和wordpress的链接地址的不同，修改了wordpress的部分代码，让之前的外链不至于失效。后来索性就通过apache的mod_rewrite模块添加了301跳转，wordpress的版本也可以随着“持续演进”了。 由于wordpress或者这一类的CMS系统本身不是一个性能优化的产品，后来一段时间搬迁过一次，平台是vps的虚拟机。性能问题很突出，首页打开竟然需要2.7秒以上，加上传输耗时，超出了3秒的范围，严重影响用户体验。于是不得不在wordpress的代码上进行修改。页面全部缓存到了memcache。响应时间减少到1.5秒以下，基本可以满足大部分需求。但这样就无法跟随wordpress持续演进了。wp的版本只能定死在了某个版本号上。考虑到安全因素，我只能说，这是3.1之前的“某个版本”。 关于硬件环境，最初的drupal版本是一台相对很“牛”的机器，双核志强*2 + 4G RAM+ RAID5。所以当初提供了很多开源系统镜像下载。系统的瓶颈出现在网络带宽上，可谓“衣食无忧”。 之后的变故，加上域名备案的因素，使得网站一度只能跑在一台win2003的virtual server上，内存只有64M，从一个极端到了另一个极端。之后很长一段时间，开源小站始终运行在虚拟机或者vps之上。当然，由此带来的流量下滑不是一点点，但访客停留时间大大增加。也算抹平了。 后来，设法弄到了一点资源，与别人合用了一台主机，4核Xeon+4GRAM + RAID1，磁盘总是不够用，甚至为此都不保存访问日志。 后来，曾经狂热的支持了一把atom，于是自己用atom的平台搭建了一台服务器，1u高度标准机箱，atom-230,1.6G +2GRAM+160G的笔记本硬盘，操作系统选择了Ubuntu的804 Server，竟然跑得很流畅，这就是现在开源小站的主机。当然，也感谢某位朋友，免费提供了他们所代理的某款网络安全产品，用以作为我们小站的前端防火墙。在前端防火墙之后，通过端口映射的方式完成了网站80端口的指向。负担一部分iptable的任务，同时也作为SSH和远程重起主机的跳板。 之后，加入了xml-rpc服务，同时将文章推送到几个免费博客和litrinjiang.appspot.com，尽管格式全乱，但至少也算是备份吧。 &#160;]]></description>
			<content:encoded><![CDATA[<p>之前有朋友问过开源小站的架构是怎样的，我回答道：“标准的wordpress，稍微做了一些调整。”时隔一段时期，几经搬迁，这次重新整理了一下开源小站的结构，就当分享罢了。</p>
<p>“开源小站”最初是基于drupal构建，基础数据库是postgresql。后来由于drupal的性能问题以及框死了的postgresql，只能<a title="WordPress的家" href="http://www.litrin.net/2009/03/17/wordpress%e7%9a%84%e5%ae%b6/">整体迁移到了wordpress+mysql</a>。当然迁移好之后，考虑到drupal和wordpress的链接地址的不同，修改了wordpress的部分代码，让之前的外链不至于失效。后来索性就通过apache的mod_rewrite模块添加了301跳转，wordpress的版本也可以随着“持续演进”了。</p>
<p><span id="more-1516"></span>由于wordpress或者这一类的CMS系统本身不是一个性能优化的产品，后来一段时间搬迁过一次，平台是vps的虚拟机。性能问题很突出，首页打开竟然需要2.7秒以上，加上传输耗时，超出了3秒的范围，严重影响用户体验。于是不得不在wordpress的代码上进行修改。页面全部缓存到了memcache。响应时间减少到1.5秒以下，基本可以满足大部分需求。但这样就无法跟随wordpress持续演进了。wp的版本只能定死在了某个版本号上。考虑到安全因素，我只能说，这是3.1之前的“某个版本”。</p>
<p>关于硬件环境，最初的drupal版本是一台相对很“牛”的机器，双核志强*2 + 4G RAM+ RAID5。所以当初提供了很多开源系统镜像下载。系统的瓶颈出现在网络带宽上，可谓“衣食无忧”。</p>
<p>之后的变故，加上域名备案的因素，使得网站一度只能跑在一台win2003的virtual server上，内存只有64M，从一个极端到了另一个极端。之后很长一段时间，开源小站始终运行在虚拟机或者vps之上。当然，由此带来的流量下滑不是一点点，但访客停留时间大大增加。也算抹平了。</p>
<p>后来，设法弄到了一点资源，与别人合用了一台主机，4核Xeon+4GRAM + RAID1，磁盘总是不够用，甚至为此都不保存访问日志。</p>
<p><img class="aligncenter size-medium wp-image-1519" title="" src="http://www.litrin.net/wp-content/uploads/2011/08/20110301125-225x300.jpg" alt="" width="225" height="300" /></p>
<p>后来，曾经<a title="完工的ATOM" href="http://www.litrin.net/2009/07/16/%e5%ae%8c%e5%b7%a5%e7%9a%84atom/">狂热的支持了一把atom</a>，于是自己用atom的平台搭建了一台服务器，1u高度标准机箱，atom-230,1.6G +2GRAM+160G的笔记本硬盘，操作系统选择了Ubuntu的804 Server，竟然跑得很流畅，这就是现在开源小站的主机。当然，也感谢某位朋友，免费提供了他们所代理的某款网络安全产品，用以作为我们小站的前端防火墙。在前端防火墙之后，通过端口映射的方式完成了网站80端口的指向。负担一部分iptable的任务，同时也作为SSH和远程重起主机的跳板。</p>
<p><img class="aligncenter size-medium wp-image-1518" title="20110226121" src="http://www.litrin.net/wp-content/uploads/2011/08/20110226121-300x225.jpg" alt="" width="300" height="225" /></p>
<p>之后，加入了xml-rpc服务，同时将文章推送到几个免费博客和litrinjiang.appspot.com，尽管格式全乱，但至少也算是备份吧。</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2011/08/12/%e5%bc%80%e6%ba%90%e5%b0%8f%e7%ab%99%e7%9a%84%e5%9f%ba%e7%a1%80%e7%bb%93%e6%9e%84/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>LAMP的常用扩展安装</title>
		<link>http://www.litrin.net/2011/05/16/lamp%e7%9a%84%e5%b8%b8%e7%94%a8%e6%89%a9%e5%b1%95%e5%ae%89%e8%a3%85/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=lamp%25e7%259a%2584%25e5%25b8%25b8%25e7%2594%25a8%25e6%2589%25a9%25e5%25b1%2595%25e5%25ae%2589%25e8%25a3%2585</link>
		<comments>http://www.litrin.net/2011/05/16/lamp%e7%9a%84%e5%b8%b8%e7%94%a8%e6%89%a9%e5%b1%95%e5%ae%89%e8%a3%85/#comments</comments>
		<pubDate>Mon, 16 May 2011 01:57:56 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[www]]></category>
		<category><![CDATA[数据库应用]]></category>
		<category><![CDATA[DataBase]]></category>
		<category><![CDATA[FreeBSD]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[服务器]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1467</guid>
		<description><![CDATA[之前已经弄过许多篇关于LAMP搭建的东西，都是基于最简单包的，这次说说几个比较常用扩展的安装： FreeBSD + Apache + PHP + Mysql (恶搞版) FreeBSD + Apache + PHP + Mysql + Oracle Ubuntu + Apache + PHP + Mysql ，比较简单，正常server版安装结束的最后一步可以选择安装LAMP Ubuntu + nginx + php Ubuntu + php + Oracle Memcache 说起Memcache，这年头稍微大点的网站几乎是必不可少的。内存Key-value一对一数据库，但数据易失。新浪的团队也有个项目是memcachedb，就是以速度为代价，部分数据回写硬盘。但似乎不是memcache的本意，个人觉得不如直接写sql，仁者见仁吧。 For Ubuntu apt-get install memcached php5-memcache /etc/init.d/memcached start For FreeBSD cd /usr/ports/databases/memcached make install clean cd /usr/ports/databases/pecl-memcache make [...]]]></description>
			<content:encoded><![CDATA[<p>之前已经弄过许多篇关于LAMP搭建的东西，都是基于最简单包的，这次说说几个比较常用扩展的安装：</p>
<ul>
<li><a title="用FreeBSD的ports安装apache+php+mysql" href="http://www.litrin.net/2005/11/09/%e7%94%a8freebsd%e7%9a%84ports%e5%ae%89%e8%a3%85apachephpmysql/" target="_blank">FreeBSD + Apache + PHP + Mysql</a> (<a title="用FreeBSD的ports安装apache+php+mysql·改" href="http://www.litrin.net/2007/08/01/%e7%94%a8freebsd%e7%9a%84ports%e5%ae%89%e8%a3%85apachephpmysql%c2%b7%e6%94%b9/" target="_blank">恶搞版</a>)</li>
<li><a title="FreeBSD+apache+PHP+OCI支持Oracle" href="http://www.litrin.net/2009/06/15/freebsdapachephpoci%e6%94%af%e6%8c%81oracle/" target="_blank">FreeBSD + Apache + PHP + Mysql + Oracle</a></li>
<li>Ubuntu + Apache + PHP + Mysql ，比较简单，正常server版安装结束的最后一步可以选择安装LAMP</li>
<li><a title="Ubuntu下安装Nginx+php(FastCGI)" href="http://www.litrin.net/2010/10/27/ubuntu%e4%b8%8b%e5%ae%89%e8%a3%85nginxphpfastcgi/" target="_blank">Ubuntu + nginx + php</a></li>
<li><a title="配置Ubuntu的php支持OCI8" href="http://www.litrin.net/2009/09/15/%e9%85%8d%e7%bd%aeubuntu%e7%9a%84php%e6%94%af%e6%8c%81oci8/" target="_blank">Ubuntu + php + Oracle</a></li>
</ul>
<p><span id="more-1467"></span></p>
<p><strong>Memcache</strong></p>
<p>说起Memcache，这年头稍微大点的网站几乎是必不可少的。内存Key-value一对一数据库，但数据易失。新浪的团队也有个项目是memcachedb，就是以速度为代价，部分数据回写硬盘。但似乎不是memcache的本意，个人觉得不如直接写sql，仁者见仁吧。</p>
<ul>
<li>For Ubuntu<br />
apt-get install memcached php5-memcache<br />
/etc/init.d/memcached start</li>
<li>For FreeBSD<br />
cd /usr/ports/databases/memcached<br />
make install clean<br />
cd /usr/ports/databases/pecl-memcache<br />
make install clean<br />
echo”memcached_enable=YES” &gt;&gt; /etc/rc.conf<br />
/usr/local/etc/rc.d/memcached start</li>
</ul>
<p><strong>Redis</strong></p>
<p>也是一个Key-value数据库，比起Memcache速度慢一点，但会回写硬盘，数据不丢失。处理Feed之类SQL吃上劲的应用必备。</p>
<ul>
<li>For Ubuntu //11.04可行，8.04不行，官方似乎从904以后才加入的。之前的版本不妨用<a rel="nofollow" target="_blank" href="http://ftp.us.debian.org/debian/pool/main/r/redis/" target="_blank">debian包<br />
</a>apt-get install redis-server php-redis //同样的<a rel="nofollow" target="_blank" href="http://ftp.us.debian.org/debian/pool/main/p/php-radius/" target="_blank">debian包下载</a><br />
/etc/init.d/redis-server start</li>
<li>For Freebsd<br />
cd /usr/ports/databases/redis<br />
make install clean<br />
echo &#8216;redis_enable=”YES”&#8216; &gt;&gt; /etc/rc.conf<br />
/usr/local/etc/rc.d/redis restart<br />
cd /usr/ports/databases/php5-redis<br />
make install clean //敲的条件反射了</li>
</ul>
<p>说起redis的PHP支持类，由于redis是明文的传输，所以很多人会选择<a rel="nofollow" target="_blank" href="https://github.com/jdp/redisent" target="_blank">redisend</a>这类基于socket连接方式的php类——说白了就是模拟telnet 6379。根据实际测试的结果，C扩展至少有40%以上的提升，而且内存更少，加包和拆包的过程更迅速。更何况redisend的前一个版本一堆问题，后一个版本又是php5.3 only的天上一脚，地上一脚的维护也是麻烦。不是万不得已还是少用为妙。</p>
<p><strong>PHP-FPM</strong></p>
<p>这个是给Nginx做fastcgi扩展的，相比php-cgi, spawn-cgi不但性能上有提升，配置上也灵活的多，但FreeBSD不支持，Ubuntu也不官方推荐。这里使用了ubuntu的非官方库。</p>
<p>echo “deb <a rel="nofollow" target="_blank" href="http://php53.dotdeb.org">http://php53.dotdeb.org</a> stable all” &gt;&gt; /etc/apt/sources.list<br />
apt-get update; apt-get upgrade<br />
apt-get install php5-fpm</p>
<p>会直接覆盖之前官方支持的PHP包，请慎重考虑。</p>
<p>/etc/init.d/php5-fpm start</p>
<p><strong>PHP-GD</strong></p>
<p>最常见的缺失，但很容易。</p>
<ul>
<li>For Ubuntu<br />
apt-get install php5-gd</li>
<li>For FreeBSD<br />
cd /usr/ports/graphics/php5-gd<br />
make install clean</li>
</ul>
<p><strong>Minify</strong></p>
<p>Minify是一个基于PHP5的CSS/JS优化包，其实这不属于Php的扩展。</p>
<ol>
<li><a rel="nofollow" target="_blank" href="http://code.google.com/p/minify/">http://code.google.com/p/minify/</a> 下载最新的minify，解压后，将min目录拷贝至网站根目录下。</li>
<li>确认是否开启了apache 的mod_rewrite，Ubuntu默认不开启，需要a2enmod,选择rewrite后,/etc/init.d/apache2 restart 重起apache</li>
<li>网站根目录下编辑.htaccess内容如下：
<pre>RewriteEngine On
        RewriteBase /
         RewriteRule ^(.*\.(css|js))\S?$ min/index.php?f=$1 [L]</pre>
</li>
<li>如果系统已经安装了memcache包，不妨用MC替代文件方式存储优化后的信息，编辑min/config.php，末尾添加
<pre class="php">require 'lib/Minify/Cache/Memcache.php';
$memcache = new Memcache;
$memcache-&gt;connect('localhost', 11211); //根据本地配置调整
$min_cachePath = new Minify_Cache_Memcache($memcache);</pre>
</li>
</ol>
<p><strong>TokyoTyrant (ttServer)</strong></p>
<p>TykyoTyrant想必web game, social game的公司用的很多，大概是由于某家企鹅公司的框架所限吧。这东西就是一个“支持lua扩展并可持续化”的key-value。个人觉得完全可以通过memcache + redis两者取代。</p>
<ul>
<li>for Ubuntu (natty ok, hardy not support)<br />
apt-get install tokyotyrant<br />
/etc/init.d/tokyotyrant 52~53行稍作修改：<br />
52 #SERVERHOST=”$RUNDIR/$NAME.sock”<br />
53 SERVERPORT=1978<br />
即可通过/etc/init.d/tokyotyrant start命令启动1978端口的监听</li>
</ul>
<ul>
<li>for Freebsd<br />
cd /usr/ports/databases/tokyotyrant/<br />
make install clean</li>
</ul>
<div>tokyotyrant有自己的php模块，但习惯上都是通过memcache模块进行操作的，命令等价于memcahce的操作。</div>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2011/05/16/lamp%e7%9a%84%e5%b8%b8%e7%94%a8%e6%89%a9%e5%b1%95%e5%ae%89%e8%a3%85/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>从4sq宕机到本·拉登宕机</title>
		<link>http://www.litrin.net/2011/05/03/%e4%bb%8e4sq%e5%ae%95%e6%9c%ba%e5%88%b0%e6%9c%ac%c2%b7%e6%8b%89%e7%99%bb%e5%ae%95%e6%9c%ba/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e4%25bb%258e4sq%25e5%25ae%2595%25e6%259c%25ba%25e5%2588%25b0%25e6%259c%25ac%25c2%25b7%25e6%258b%2589%25e7%2599%25bb%25e5%25ae%2595%25e6%259c%25ba</link>
		<comments>http://www.litrin.net/2011/05/03/%e4%bb%8e4sq%e5%ae%95%e6%9c%ba%e5%88%b0%e6%9c%ac%c2%b7%e6%8b%89%e7%99%bb%e5%ae%95%e6%9c%ba/#comments</comments>
		<pubDate>Tue, 03 May 2011 02:29:07 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[站长的blog]]></category>
		<category><![CDATA[业界话题]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1461</guid>
		<description><![CDATA[一直在玩Android版本的FourSquar，前些日子忽然无法登录。作为一个身处中国的ITer，首先很自然的想到是由于“非技术因素”导致。没想到这次却是完完全全的技术故障。而且并非是4sq自己的故障，故障来自亚马逊的云托管服务。 同样的，前些天最让奥巴马同志欣慰的莫过于总算搞掉了本·拉登这块心病。美国人一窝蜂的愣是挤爆了CNN的手机网站。据说全美所有新闻网站的平均响应时间瞬间提升了6倍以上——可以说，都是处在了宕机的边缘。拉登果然是“恐怖之王”连他的死讯都能媲美一次完美的DDOS攻击。 两个新闻都与宕机有关，而且都与移动互联和宕机有关。 不管愿不愿意接受，整个互联网已经逐步向移动互联网过渡。不管是4SQ、微博这样的应用型访问还是CNN这样的资讯型访问，与手机的结合无疑于绑定了所有人的垃圾时间，带来可观的流量和利润——随时，随地，随性访问互联网真的很方便。然而在此乐观的背后是恐怖的瞬间爆发式访问。正如之前twitter不止一次的宕机事故，每一次都少不了热门事件的推手。 相信这些知名网站的架构设计绝对不是我能想象的完善，硬件投入也绝非“抠门”。唯一的可能就是瞬间提升的访问量导致服务器处理速度和集群带宽容量的饱和。于是加上现在无处不在的“云计算”，像4SQ这样的“成长型企业”很自然的选择了亚马逊以及其他的云服务。选择的背后无疑是将自己的全副家当交给了云，云也会有处理速度和带宽的限制，同样也有可能出现爆发式访问，而且很自然的会出现波及效应，导致云中所有的用户离线。当然，亚马逊对此次事故的评价为“前所未见的”，可以预见的是，一切才刚刚开始！ 在此之前我曾经认为移动互联短期内出现的瓶颈是在手机和基站之间的通讯，例如在人多拥挤的地方（事实上是“机多拥挤”）手机信号往往不好。以前年三十大家挤在一起发短信拜年，往往隔几个小时才能收到，对于数据的访问也应如此。后来我看到一篇华为的成功案例，觉得随着时间的推移，迟早这个瓶颈会消失。流量的压力迟早一股脑的扔给了服务端。 对于移动互联的架构师所要考虑的问题就是如何快速的处理“瞬间爆发”，没有任何侥幸心理。这似乎也只有更大的访问冗余可以解决。可以预见的是，现在加上了移动互联的概念后web2.0已经接近成熟，但目前的技术瓶颈已经出现。1.0时代的瓶颈来自带宽，2.0的瓶颈将会是在冗余和弹性上。而对于很多心存幻想的朋友，4SQ的宕机恰恰说明了另一点“云靠不住！”]]></description>
			<content:encoded><![CDATA[<p>一直在玩Android版本的FourSquar，前些日子忽然无法登录。作为一个身处中国的ITer，首先很自然的想到是由于“非技术因素”导致。没想到这次却是完完全全的技术故障。而且并非是4sq自己的故障，故障来自亚马逊的云托管服务。</p>
<p>同样的，前些天最让奥巴马同志欣慰的莫过于总算搞掉了本·拉登这块心病。美国人一窝蜂的愣是挤爆了CNN的手机网站。据说全美所有新闻网站的平均响应时间瞬间提升了6倍以上——可以说，都是处在了宕机的边缘。拉登果然是“恐怖之王”连他的死讯都能媲美一次完美的DDOS攻击。</p>
<p><span id="more-1461"></span></p>
<p><img class="aligncenter" src="http://www.it.com.cn/news/images/2011/05/03/09/news110503mxqtx1.jpg" alt="" width="475" height="437" /></p>
<p>两个新闻都与宕机有关，而且都与移动互联和宕机有关。</p>
<p>不管愿不愿意接受，整个互联网已经逐步向移动互联网过渡。不管是4SQ、微博这样的应用型访问还是CNN这样的资讯型访问，与手机的结合无疑于绑定了所有人的垃圾时间，带来可观的流量和利润——随时，随地，随性访问互联网真的很方便。然而在此乐观的背后是恐怖的瞬间爆发式访问。正如之前twitter不止一次的宕机事故，每一次都少不了热门事件的推手。</p>
<p>相信这些知名网站的架构设计绝对不是我能想象的完善，硬件投入也绝非“抠门”。唯一的可能就是瞬间提升的访问量导致服务器处理速度和集群带宽容量的饱和。于是加上现在无处不在的“云计算”，像4SQ这样的“成长型企业”很自然的选择了亚马逊以及其他的云服务。选择的背后无疑是将自己的全副家当交给了云，云也会有处理速度和带宽的限制，同样也有可能出现爆发式访问，而且很自然的会出现波及效应，导致云中所有的用户离线。当然，亚马逊对此次事故的评价为“前所未见的”，可以预见的是，一切才刚刚开始！</p>
<p>在此之前我曾经认为移动互联短期内出现的瓶颈是在手机和基站之间的通讯，例如在人多拥挤的地方（事实上是“机多拥挤”）手机信号往往不好。以前年三十大家挤在一起发短信拜年，往往隔几个小时才能收到，对于数据的访问也应如此。后来我看到一篇<a rel="nofollow" target="_blank" href="http://www.chinairn.com/doc/70290/100714.html" target="_blank">华为的成功案例</a>，觉得随着时间的推移，迟早这个瓶颈会消失。流量的压力迟早一股脑的扔给了服务端。</p>
<p>对于移动互联的架构师所要考虑的问题就是如何快速的处理“瞬间爆发”，没有任何侥幸心理。这似乎也只有更大的访问冗余可以解决。可以预见的是，现在加上了移动互联的概念后web2.0已经接近成熟，但目前的技术瓶颈已经出现。1.0时代的瓶颈来自带宽，2.0的瓶颈将会是在冗余和弹性上。而对于很多心存幻想的朋友，4SQ的宕机恰恰说明了另一点“云靠不住！”</p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2011/05/03/%e4%bb%8e4sq%e5%ae%95%e6%9c%ba%e5%88%b0%e6%9c%ac%c2%b7%e6%8b%89%e7%99%bb%e5%ae%95%e6%9c%ba/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ecshop的问题</title>
		<link>http://www.litrin.net/2011/04/28/ecshop%e7%9a%84%e9%97%ae%e9%a2%98/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=ecshop%25e7%259a%2584%25e9%2597%25ae%25e9%25a2%2598</link>
		<comments>http://www.litrin.net/2011/04/28/ecshop%e7%9a%84%e9%97%ae%e9%a2%98/#comments</comments>
		<pubDate>Thu, 28 Apr 2011 08:22:05 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[网络和安全]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1454</guid>
		<description><![CDATA[在看一台服务器的SQL-Slow日志，看到了如下的一纪录，惊出了一身冷汗！ SELECT pg.package_id, pg.goods_id, pg.goods_number, pg.admin_id, g.goods_sn, g.goods_name, g.market_price, g.goods_thumb, g.is_real, IFNULL(mp.user_price, g.shop_price * '1') AS rank_price FROM `ecs_`.`ecs_package_goods` AS pg LEFT JOIN `ecs_venusveil`.`ecs_goods` AS g ON g.goods_id = pg.goods_id LEFT JOIN `ecs_venusveil`.`ecs_member_price` AS mp ON mp.goods_id = g.goods_id AND mp.user_rank = '0' WHERE pg.package_id = -1 union all select 1,2,3,4,5,6,7,8,count(*),concat( (Select concat(0x5b,email,0x3a,user_name,0x5d) FROM ecs_users [...]]]></description>
			<content:encoded><![CDATA[<p>在看一台服务器的SQL-Slow日志，看到了如下的一纪录，惊出了一身冷汗！</p>
<pre class="sql" name='code'>
SELECT pg.package_id, pg.goods_id, pg.goods_number,
        pg.admin_id, g.goods_sn, g.goods_name, g.market_price, g.goods_thumb, g.is_real,
        IFNULL(mp.user_price, g.shop_price * '1') AS rank_price
FROM `ecs_`.`ecs_package_goods` AS pg
        LEFT JOIN `ecs_venusveil`.`ecs_goods` AS g ON g.goods_id = pg.goods_id
        LEFT JOIN `ecs_venusveil`.`ecs_member_price` AS mp ON mp.goods_id = g.goods_id AND mp.user_rank = '0'
WHERE pg.package_id = -1
union all
        select 1,2,3,4,5,6,7,8,count(*),concat(
                (Select concat(0x5b,email,0x3a,user_name,0x5d)
                        FROM ecs_users
                        LIMIT 9456,1), floor(rand(0)*2))x
         from information_schema.tables
         group by x
ORDER BY pg.package_id, pg.goods_id;</pre>
<p><span id="more-1454"></span><br />
不知看懂没有，反正这句SQL是解释不通的。由于网站跑在ECshop2.7.2的环境之上，问题肯定来自代码缺陷。之前似乎有发过<a href="http://www.litrin.net/2009/09/11/%E8%AF%B4%E8%AF%B4%E2%80%9C%E4%B8%93%E4%B8%9A%E2%80%9D/">ECShop的牢骚</a>，作为一个商业软件，而且是做交易平台的商业软件，竟然如此不专业种种的操作。特地浏览了一下代码，发觉代码里面的更加经典！几乎没有对任何变量进行校验和过滤！</p>
<p>由于程序不是我开发的，无法追溯源。只能根据日志进行反查，找到了根基</p>
<ol>
<li>flow.php中2157行中json来源可由客户端定义，导致2182行$package-&gt;package_id不可控。</li>
<li>include/lib_order.php中对include/lib_common.php 的function get_package_info($id)可以植入SQL</li>
<li>畸形SQL导致Mysql返回空信息</li>
<li>攻击者获得无限量优惠包，仅此而已。</li>
</ol>
<p>无话可说！引以为戒吧！</p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2011/04/28/ecshop%e7%9a%84%e9%97%ae%e9%a2%98/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>高负载的Lamp架构</title>
		<link>http://www.litrin.net/2011/04/20/%e9%ab%98%e8%b4%9f%e8%bd%bd%e7%9a%84lamp%e6%9e%b6%e6%9e%84/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e9%25ab%2598%25e8%25b4%259f%25e8%25bd%25bd%25e7%259a%2584lamp%25e6%259e%25b6%25e6%259e%2584</link>
		<comments>http://www.litrin.net/2011/04/20/%e9%ab%98%e8%b4%9f%e8%bd%bd%e7%9a%84lamp%e6%9e%b6%e6%9e%84/#comments</comments>
		<pubDate>Wed, 20 Apr 2011 04:23:26 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[数据库应用]]></category>
		<category><![CDATA[硬件相关]]></category>
		<category><![CDATA[DataBase]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[网络和安全]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1435</guid>
		<description><![CDATA[记得那时是2005年10月，开源小站刚上线不久的一篇文章。那时的我还仅仅将网站的高可用性和大负载，大流量集中在“堆硬件”的层面上。包括之后的一篇文档，似乎也没有逃脱这个范畴。之后由于工作内容的关系，始终没有再继续探讨这个问题。仅仅只在一篇关于GAE的文章中讲述了一下架构的趋势。 时隔了5年多，不妨回头重新从新的高度上说说LAMP结构的网站如何支撑尽可能大的负载。同样说明，本文代表站长Litrin的个人意见，欢迎共同探讨，但喷子慎入。 首先声明的是，以下一堆相对无趣的文字仅针对一系列负载到了至少10W pv以上级别，对于较小规模的网站大可不必遵守，还是提倡成本优先的原则，到了对应的PV级别想必也会有足够的财力和物力去改版了。 初期规划 这里主要还是指设计模型等前期工作。MVC的设计结构深入人心，不得不承认确实大多架构设计师首选的设计结构必是MVC。事实上，在一个网站上最容易获取，最不缺的资源是CPU和内存，最大的瓶颈都是出现在IO。 作为LAMP架构最大的问题就是MVC的代价太大，层层的封包意味着重复判断；打开大量的程序文件只会严重拖累性能。网上很多免费的、开源的PHP框架，用过的几个诸如ThinkPHP, ZF等应付中小型网站绝对没有问题，但都不适合大负载的网站。甚至包括Smarty这样单纯的模版引擎，大量的IO操作会拖累整体性能。如果不考虑其他因素，我甚至都考虑过All in 1的垃圾结构——这当然不现实。但恰恰是这种all in 1却正是大负载最适合的结构。奇怪的悖论。 受GAE的影响，曾经想过API+XML的方式实现网站架构，但后来打消了这个念头。XML本身的结构冗余度很多，这也就意味着信息承载率低。在内部几台服务器之间的通讯状况想必都没有必要搞XML，如果要搞类似的模式，CSV方式反倒是不错的选择。XML是为了开放给第三方用户的。 数据库设计 读写分离这个是众所周知的秘密了。Mysql 的Master + Slaver的设计真的很方便！最多就是多搞几重的读写分离。 拆表、拆库、分机器，大表做纵拆，宽表做横拆，不关联的分机器；纵拆是为了省时间，横拆是为了省内存，分机器是为了分负载。Mysql在几百万行数据级别以下性能是很不错的，超过这个数量级，就算机器再强悍都会死的很难看，mysql垃圾的锁表机制会展现无疑。没有人愿等到30秒以上的Respone，何况我曾经遭遇一个45分钟以上的Select操作（一重操作，没有用到Like，使用索引）。曾经用过mysql 的分区表，感觉这种方法更像是在打补丁——改变数据库结构去适应程序，很奇怪的流程，更何况即便逃过这次不拆，迟早有一天还会回来拆的。 索引，该有的全都有，但对于写操作频繁的表，索引反倒成了累赘，不要让索引数占到字段数的30%以上这是约定俗成的标准吧。 memcache，没什么可以多说的，这玩意基本上就是铺硬件换时间的。 说道非关系型数据库，好像近期很热门。怎么说呢？越是复杂的网站越是能体现出它的优势，但问题是大多数网站都不会像twitter或者Facebook这么复杂，况且大多数程序员的逻辑还是SQL模式，贸进很可能搞砸。 WWW服务器拓扑设计 F5这类的设备是必须的，之前曾经觉得LVS可以替代F5这样的设备。用过之后才发现，这等于是将整个网站的身价性命都扔到了一台服务器上，一旦宕机或者被侵入呢？ 对于Apache还是nginx的争论持续了很久。nginx很快速，可以将单独的PHP负载分离出去，但没有apache来的灵活。要根据需求再做评价。 异步操作和缓存机制 这个貌似也没有什么花头，调整好负载之后可以将一部分操作异步到数据库相对空闲的时段进行，达到移峰填谷的效果。配合memcache或者其他的缓存机制本身也是动转静的手段之一。 自动和手动的问题 属于代码级别了吧。良好的编程规范对系统的优化是很显著的。漫山遍野的select *， 大量的变量不清空是高负载系统的忌讳。纵然php的内存处理机制再出色，它永远也不会理解你设置的变量什么时候失效，减少这看似寥寥无几的空间乘上惊人的pv数也是很可观的。]]></description>
			<content:encoded><![CDATA[<p>记得那时是2005年10月，<a title="开源小站" href="http://www.litrin.net" target="_blank">开源小站</a>刚上线不久的<a title="中国顶级门户网站架构分析" href="http://www.litrin.net/2005/10/27/%e4%b8%ad%e5%9b%bd%e9%a1%b6%e7%ba%a7%e9%97%a8%e6%88%b7%e7%bd%91%e7%ab%99%e6%9e%b6%e6%9e%84%e5%88%86%e6%9e%90/" target="_blank">一篇文章</a>。那时的我还仅仅将网站的高可用性和大负载，大流量集中在“堆硬件”的层面上。包括之后的<a href="http://www.litrin.net/2005/11/06/%e5%8c%96%e7%ae%80%e4%b8%ba%e7%b9%81%e2%80%94%e2%80%94%e4%bc%98%e5%8c%96%e4%bd%a0%e7%9a%84%e7%bd%91%e7%ab%99%e5%b1%82%e6%ac%a1/" target="_blank">一篇文档</a>，似乎也没有逃脱这个范畴。之后由于工作内容的关系，始终没有再继续探讨这个问题。仅仅只在一篇关于<a href="http://www.litrin.net/2010/12/21/%e4%bb%8egae%e7%9c%8b%e7%bd%91%e7%ab%99%e6%9e%b6%e6%9e%84%e8%b6%8b%e5%8a%bf/" target="_blank">GAE的文章</a>中讲述了一下架构的趋势。</p>
<p>时隔了5年多，不妨回头重新从新的高度上说说LAMP结构的网站如何支撑尽可能大的负载。同样说明，本文代表站长Litrin的个人意见，欢迎共同探讨，但喷子慎入。</p>
<p><span id="more-1435"></span></p>
<p>首先声明的是，以下一堆相对无趣的文字仅针对一系列负载到了至少10W pv以上级别，对于较小规模的网站大可不必遵守，还是提倡成本优先的原则，到了对应的PV级别想必也会有足够的财力和物力去改版了。</p>
<h2>初期规划</h2>
<p>这里主要还是指设计模型等前期工作。MVC的设计结构深入人心，不得不承认确实大多架构设计师首选的设计结构必是MVC。事实上，在一个网站上最容易获取，最不缺的资源是CPU和内存，最大的瓶颈都是出现在IO。</p>
<p>作为LAMP架构最大的问题就是MVC的代价太大，层层的封包意味着重复判断；打开大量的程序文件只会严重拖累性能。网上很多免费的、开源的PHP框架，用过的几个诸如ThinkPHP, ZF等应付中小型网站绝对没有问题，但都不适合大负载的网站。甚至包括Smarty这样单纯的模版引擎，大量的IO操作会拖累整体性能。如果不考虑其他因素，我甚至都考虑过All in 1的垃圾结构——这当然不现实。但恰恰是这种all in 1却正是大负载最适合的结构。奇怪的悖论。</p>
<p>受GAE的影响，曾经想过API+XML的方式实现网站架构，但后来打消了这个念头。XML本身的结构冗余度很多，这也就意味着信息承载率低。在内部几台服务器之间的通讯状况想必都没有必要搞XML，如果要搞类似的模式，CSV方式反倒是不错的选择。XML是为了开放给第三方用户的。</p>
<h2>数据库设计</h2>
<p>读写分离这个是众所周知的秘密了。Mysql 的Master + Slaver的设计真的很方便！最多就是多搞几重的读写分离。</p>
<p>拆表、拆库、分机器，大表做纵拆，宽表做横拆，不关联的分机器；纵拆是为了省时间，横拆是为了省内存，分机器是为了分负载。Mysql在几百万行数据级别以下性能是很不错的，超过这个数量级，就算机器再强悍都会死的很难看，mysql垃圾的锁表机制会展现无疑。没有人愿等到30秒以上的Respone，何况我曾经遭遇一个45分钟以上的Select操作（一重操作，没有用到Like，使用索引）。曾经用过mysql 的分区表，感觉这种方法更像是在打补丁——改变数据库结构去适应程序，很奇怪的流程，更何况即便逃过这次不拆，迟早有一天还会回来拆的。</p>
<p>索引，该有的全都有，但对于写操作频繁的表，索引反倒成了累赘，不要让索引数占到字段数的30%以上这是约定俗成的标准吧。</p>
<p>memcache，没什么可以多说的，这玩意基本上就是铺硬件换时间的。</p>
<p>说道非关系型数据库，好像近期很热门。怎么说呢？越是复杂的网站越是能体现出它的优势，但问题是大多数网站都不会像twitter或者Facebook这么复杂，况且大多数程序员的逻辑还是SQL模式，贸进很可能搞砸。</p>
<h2>WWW服务器拓扑设计</h2>
<p>F5这类的设备是必须的，之前曾经觉得LVS可以替代F5这样的设备。用过之后才发现，这等于是将整个网站的身价性命都扔到了一台服务器上，一旦宕机或者被侵入呢？</p>
<p>对于Apache还是nginx的争论持续了很久。nginx很快速，可以将单独的PHP负载分离出去，但没有apache来的灵活。要根据需求再做评价。</p>
<h2>异步操作和缓存机制</h2>
<p>这个貌似也没有什么花头，调整好负载之后可以将一部分操作异步到数据库相对空闲的时段进行，达到移峰填谷的效果。配合memcache或者其他的缓存机制本身也是动转静的手段之一。</p>
<h2>自动和手动的问题</h2>
<p>属于代码级别了吧。良好的编程规范对系统的优化是很显著的。漫山遍野的select *， 大量的变量不清空是高负载系统的忌讳。纵然php的内存处理机制再出色，它永远也不会理解你设置的变量什么时候失效，减少这看似寥寥无几的空间乘上惊人的pv数也是很可观的。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2011/04/20/%e9%ab%98%e8%b4%9f%e8%bd%bd%e7%9a%84lamp%e6%9e%b6%e6%9e%84/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>google的下一代web协议spdy</title>
		<link>http://www.litrin.net/2011/04/14/google%e7%9a%84%e4%b8%8b%e4%b8%80%e4%bb%a3web%e5%8d%8f%e8%ae%aespdy/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=google%25e7%259a%2584%25e4%25b8%258b%25e4%25b8%2580%25e4%25bb%25a3web%25e5%258d%258f%25e8%25ae%25aespdy</link>
		<comments>http://www.litrin.net/2011/04/14/google%e7%9a%84%e4%b8%8b%e4%b8%80%e4%bb%a3web%e5%8d%8f%e8%ae%aespdy/#comments</comments>
		<pubDate>Thu, 14 Apr 2011 00:09:08 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[服务器]]></category>
		<category><![CDATA[网络和安全]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1433</guid>
		<description><![CDATA[之前听过Stanely发过一个牢骚：说在公司局域网内使用Chrome，很快收到了网管寄出的Email，大致上是通知他换用其他的浏览器，然后说了一堆Chrome种种的不好、不安全之类的言论。更让他郁闷的是邮件的收件人只是几个Chrome的用户，没有针对其他人，为什么能准确的辨别出谁使用了Chrome？ 我想，排除了其他因素，诸如网管可能有远程监视硬盘之类的技术，仅仅通过公司企业的前端路由——估计他们这种规模的企业，一定有深包分析之类的高级设备，完全可以清除的知道谁在使用Chrome，原因就是因为Chrome正在测试google下一代的web协议spdy。 单从发音上就知道spdy = speedy。快！它的核心是针对目前HTTP传输中对多重数据传输的低效，无优先排序功能重新构架的。google已经在自家的gmail等应用上偷偷支持了spdy协议，据说可以提升接近一半的传输速度。本想看看他们的协议白皮书，无奈，众所周知，这个网站并不存在。 大致上概括下，spdy协议可以让网页打开时优先传输html，完成后再去传输图片和js，css。而且就我理解下来，spdy是支持压缩和加密传输模式的，而且效率远高于http。 如果需要让您当前的网站支持spdy协议的传输，google提供了一个apache mod 。但说实话，由于spdy协议目前只是处在测试阶段，而且仅仅只能被Chrome支持，我还是不建议马上部署这个模块。 查看spdy的状态简单的方法是在Chrome中打开这个链接：chrome://net-internals 。 回答开始的话题，由于目前spdy尚处于测试阶段，大部分企业的防火墙都不会认为这是一个正常的http访问，兴许会作为一个攻击来源之类的抛出警报。网管们烦心，发出个邮件禁止一下也在情理之中。]]></description>
			<content:encoded><![CDATA[<p>之前听过Stanely发过一个牢骚：说在公司局域网内使用Chrome，很快收到了网管寄出的Email，大致上是通知他换用其他的浏览器，然后说了一堆Chrome种种的不好、不安全之类的言论。更让他郁闷的是邮件的收件人只是几个Chrome的用户，没有针对其他人，为什么能准确的辨别出谁使用了Chrome？</p>
<p>我想，排除了其他因素，诸如网管可能有远程监视硬盘之类的技术，仅仅通过公司企业的前端路由——估计他们这种规模的企业，一定有深包分析之类的高级设备，完全可以清除的知道谁在使用Chrome，原因就是因为Chrome正在测试google下一代的web协议spdy。</p>
<p><span id="more-1433"></span>单从发音上就知道spdy = speedy。快！它的核心是针对目前HTTP传输中对多重数据传输的低效，无优先排序功能重新构架的。google已经在自家的gmail等应用上偷偷支持了spdy协议，据说可以提升接近一半的传输速度。本想看看他们的协议白皮书，无奈，众所周知，这个网站并不存在。</p>
<p>大致上概括下，spdy协议可以让网页打开时优先传输html，完成后再去传输图片和js，css。而且就我理解下来，spdy是支持压缩和加密传输模式的，而且效率远高于http。</p>
<p>如果需要让您当前的网站支持spdy协议的传输，google提供了一个<a rel="nofollow" target="_blank" href="http://code.google.com/p/mod-spdy/" target="_blank">apache mod </a>。但说实话，由于spdy协议目前只是处在测试阶段，而且仅仅只能被Chrome支持，我还是不建议马上部署这个模块。</p>
<p>查看spdy的状态简单的方法是在Chrome中打开这个链接：<a rel="nofollow" target="_blank" href="chrome://net-internals/#events&amp;q=type:SPDY_SESSION%20is:active">chrome://net-internals</a> 。</p>
<p>回答开始的话题，由于目前spdy尚处于测试阶段，大部分企业的防火墙都不会认为这是一个正常的http访问，兴许会作为一个攻击来源之类的抛出警报。网管们烦心，发出个邮件禁止一下也在情理之中。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2011/04/14/google%e7%9a%84%e4%b8%8b%e4%b8%80%e4%bb%a3web%e5%8d%8f%e8%ae%aespdy/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>写在zipsite1.0发布之后</title>
		<link>http://www.litrin.net/2011/03/27/%e5%86%99%e5%9c%a8zipsite1-0%e5%8f%91%e5%b8%83%e4%b9%8b%e5%90%8e/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e5%2586%2599%25e5%259c%25a8zipsite1-0%25e5%258f%2591%25e5%25b8%2583%25e4%25b9%258b%25e5%2590%258e</link>
		<comments>http://www.litrin.net/2011/03/27/%e5%86%99%e5%9c%a8zipsite1-0%e5%8f%91%e5%b8%83%e4%b9%8b%e5%90%8e/#comments</comments>
		<pubDate>Sun, 27 Mar 2011 13:51:45 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[站长的blog]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[GAE]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1418</guid>
		<description><![CDATA[首先，大肆宣扬一番：今天zipsite正式发布了第一个里程碑式版本 Ver1.0 Release！ 话说去年11月份，刚接到一个项目，要在Android平台上开发一个基于手机通讯录的应用。众所周知的原因，Android开发者网站http://developer.android.com/ 在国内无法访问。一开始，我直接下载了本地版本的html全站镜像文件。作为一开始了解这个平台，读读SDK搭建方法一点问题也没有。后来对照几个example进行开发联系，发觉很多类型不知道何去何从，用了本地搜索发觉也是摸不着头脑。于是有了一个想法：为何不把手册放在网上，借助google来索引整个网站。 于是我马上切换到Geeker模式，浏览手册的目录，发觉所有的html都是很规范的相对路径，完全可以直接放在网上。而且主目录下已经有了部分GAE的文件，似乎可以很方便的上传到GAE。尝试了几次，大失所望，原来单是文件数目就有接近3000，超出了GAE 1000个文件的限制很多。 切换回程序员模式。想到之前曾经写过一个系统底层的Python脚本，为了方便翻阅归档的文件，通过python对于zip良好的支持和多线程操作，一次性diff多个版本的区别。用这种方式可以解决GAE的一系列限制，上传网站。 很快，最基本的代码完成了，网站也上传成功，大部分情况工作的也不错。于是有了想要把这个项目分享的想法。 既然要开源，只是基本的功能实现是不够的，而且代码也不够清晰。就算原理再简单，也应该给人清新的感觉，于是先重写了原型代码。想了很久有什么功能需要再添加，后来想到了GAE的CPU Time其实也是有限制的，zip解压又是CPU耗费型的。又通过GAE的Data Store和Memcache做了双重缓冲。大致上有点意思了，于是申请了google code的空间，通过SVN放出了第一个版本ver0.1作为测试。最后，为了显得有点可扩展性，写了一个接口，可以支持插件，其实主要是纯文本和HTML，最初的想法只是看到Android手册中的HTML有太多的空格和换行，很浪费带宽而已。 话说当时正巧我的广告帐户里面攒到了$80多之后，很久没有动静。始终无法达到最低的汇款金额$100。要知道，$80比$0还要折磨人。切换到商人模式，复杂化了插件，让他可以自动的制定位置插入广告。又复杂了一步。 本来这个项目只是为了想要借助google的本地索引来完成一些事情，谁知道上线很久以后，Google仍不能完成全站的收录。SEO吧。又写了sitemap的支持功能到项目中，发布了Ver0.2版本。 剩下的任务就是不断的修正bug而已。春节前几天，我觉得整个项目该有的功能都差不多了，于是这才开始整理feature——生命周期完全颠倒了。也正是这个原因，回头看看之前的代码，已经乱的差不多了。特别是插件的接口我觉得真的很垃圾，况且即便是程序员，也没有几个人愿意通过大量的正则表达式来实现功能，于是索性取消了。 为了给这个项目制作一个像样点的文档，我特地在Mac app store里面买了一套iwork，花了差不多$90大洋。呵呵，也算是开发费用的一部分吧。 然后就是Ver1.0 RC 版本，虽然这个项目没有什么人关注，但很庆幸，有几个人特地找到我，愿意参加测试，在此感谢下！ 今天终于咬牙发布了最终的Release版本。希望不要马上推出个什么紧急补丁之类的东西。 说到下一个版本，我觉得应该遵守GAE的习惯，采用整数版本号来表示Release版本，小数表示tag或者snap版本。2版本应该增加一个管理员界面，在后台上多做些文章；重新启用插件部分，当然表达方式越简单越好。Zipsite项目的最终目标是成为一个运行在GAE沙盒内部的一个快速、傻瓜化的网站搭建系统。]]></description>
			<content:encoded><![CDATA[<p>首先，大肆宣扬一番：今天<a rel="nofollow" target="_blank" title="ZipSite project" href="http://code.google.com/p/zipsite" target="_blank">zipsite</a>正式发布了第一个里程碑式版本 Ver1.0 Release！</p>
<p>话说去年11月份，刚接到一个项目，要在Android平台上开发一个基于手机通讯录的应用。众所周知的原因，Android开发者网站<a rel="nofollow" target="_blank" href="http://developer.android.com/">http://developer.android.com/</a> 在国内无法访问。一开始，我直接下载了本地版本的html全站镜像文件。作为一开始了解这个平台，读读SDK搭建方法一点问题也没有。后来对照几个example进行开发联系，发觉很多类型不知道何去何从，用了本地搜索发觉也是摸不着头脑。于是有了一个想法：为何不把手册放在网上，借助google来索引整个网站。</p>
<p><span id="more-1418"></span></p>
<p>于是我马上切换到Geeker模式，浏览手册的目录，发觉所有的html都是很规范的相对路径，完全可以直接放在网上。而且主目录下已经有了部分GAE的文件，似乎可以很方便的上传到GAE。尝试了几次，大失所望，原来单是文件数目就有接近3000，超出了GAE 1000个文件的限制很多。</p>
<p>切换回程序员模式。想到之前曾经写过一个系统底层的Python脚本，为了方便翻阅归档的文件，通过python对于zip良好的支持和多线程操作，一次性diff多个版本的区别。用这种方式可以解决GAE的一系列限制，上传网站。<br />
很快，最<a rel="nofollow" target="_blank" title="突破GAE文件数量的限制" href="http://www.litrin.net/2010/11/26/%e7%aa%81%e7%a0%b4gae%e6%96%87%e4%bb%b6%e6%95%b0%e9%87%8f%e7%9a%84%e9%99%90%e5%88%b6/" target="_blank">基本的代码</a>完成了，<a title="Android开发者手册" href="http://android-sdk.appspot.com/" target="_blank">网站也上传成功</a>，大部分情况工作的也不错。于是有了想要把这个项目分享的想法。<br />
既然要开源，只是基本的功能实现是不够的，而且代码也不够清晰。就算原理再简单，也应该给人清新的感觉，于是先重写了原型代码。想了很久有什么功能需要再添加，后来想到了GAE的CPU Time其实也是有限制的，zip解压又是CPU耗费型的。又通过GAE的Data Store和Memcache做了双重缓冲。大致上有点意思了，于是申请了google code的空间，通过SVN放出了第一个版本ver0.1作为测试。最后，为了显得有点可扩展性，写了一个接口，可以支持插件，其实主要是纯文本和HTML，最初的想法只是看到Android手册中的HTML有太多的空格和换行，很浪费带宽而已。</p>
<p>话说当时正巧我的广告帐户里面攒到了$80多之后，很久没有动静。始终无法达到最低的汇款金额$100。要知道，$80比$0还要折磨人。切换到商人模式，复杂化了插件，让他可以自动的制定位置插入广告。又复杂了一步。</p>
<p>本来这个项目只是为了想要借助google的本地索引来完成一些事情，谁知道上线很久以后，Google仍不能完成全站的收录。SEO吧。又写了sitemap的支持功能到项目中，发布了Ver0.2版本。</p>
<p>剩下的任务就是不断的修正bug而已。春节前几天，我觉得整个项目该有的功能都差不多了，于是这才开始整理feature——生命周期完全颠倒了。也正是这个原因，回头看看之前的代码，已经乱的差不多了。特别是插件的接口我觉得真的很垃圾，况且即便是程序员，也没有几个人愿意通过大量的正则表达式来实现功能，于是索性取消了。</p>
<p>为了给这个项目制作一个像样点的文档，我特地在Mac app store里面买了一套iwork，花了差不多$90大洋。呵呵，也算是开发费用的一部分吧。</p>
<p>然后就是Ver1.0 RC 版本，虽然这个项目没有什么人关注，但很庆幸，有几个人特地找到我，愿意参加测试，在此感谢下！</p>
<p>今天终于咬牙发布了最终的Release版本。希望不要马上推出个什么紧急补丁之类的东西。</p>
<p>说到下一个版本，我觉得应该遵守GAE的习惯，采用整数版本号来表示Release版本，小数表示tag或者snap版本。2版本应该增加一个管理员界面，在后台上多做些文章；重新启用插件部分，当然表达方式越简单越好。Zipsite项目的最终目标是成为一个运行在GAE沙盒内部的一个快速、傻瓜化的网站搭建系统。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2011/03/27/%e5%86%99%e5%9c%a8zipsite1-0%e5%8f%91%e5%b8%83%e4%b9%8b%e5%90%8e/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>重新认识下PHP的输出</title>
		<link>http://www.litrin.net/2011/01/16/%e9%87%8d%e6%96%b0%e8%ae%a4%e8%af%86%e4%b8%8bphp%e7%9a%84%e8%be%93%e5%87%ba/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e9%2587%258d%25e6%2596%25b0%25e8%25ae%25a4%25e8%25af%2586%25e4%25b8%258bphp%25e7%259a%2584%25e8%25be%2593%25e5%2587%25ba</link>
		<comments>http://www.litrin.net/2011/01/16/%e9%87%8d%e6%96%b0%e8%ae%a4%e8%af%86%e4%b8%8bphp%e7%9a%84%e8%be%93%e5%87%ba/#comments</comments>
		<pubDate>Sun, 16 Jan 2011 09:24:47 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1383</guid>
		<description><![CDATA[大概在02 03年或者更早，我第一次接触了PHP lib中的模板类，后来又开始用了众人皆知的Smarty，以及各种框架内自带的模板引擎。这段时间就几乎没有再用过直接的echo输出——临时的脚本除外。似乎现在再去写个网站，一大堆echo嵌入到HTML里，会被同行笑话的，至少会被认为是学生作品吧。本文就重新捡回这个命令，重新认识下。 PHP程序员的逻辑： 数据经过加工，传输给模板引擎，模板引擎选择合适的模板填充，输出为页面通过www服务器传输给用户。 再此之前，请自查手册，熟悉ob_end_clean() flush()以及相关联的输出控制函数。 如果能看懂如下伪代码，并认为这是一团糟糕的死循环的话请继续： &#60;?php set_time_limit(300); header('Cache-control: Private'); ob_end_clean(); while (true) { if ($Count % 2 == 0){ echo "&#60;div&#62;"; echo time(); echo "&#60;/div&#62;"; flush(); } $Count ++;  sleep(1); } echo优势之一——用户体验的改善 尽管现在手机已经进入了3G时代，网速已经接近了有线宽带的水平，但作为手机网络而言，最令人头疼的就是它的不稳定。用传统的方式载入模板，所有的输出必须先行等待数据完全准备好之后才能一次性输出，这可能会需要等待很长时间。API要时刻考虑到可能会出现的断网问题，特别是在大段文件之后，较长时间的等待响应时间之中如果一旦出现网络间断（不是中断）就会让手机端白屏，影响用户体验。 利用此段代码正是n久之前，在没有大规模使用前端缓冲或者ajax技术的时候，很多网站经常看得到的那种分段式载入的效果。程序的逻辑是：step1完成，立即输出给www服务器，在用户等待数据下载的同时准备step2，依次类推。确保用户先“有东西看”。即便中途网络间断，照样可以获取网页内容。 echo优势之二——HTTP长链接的实现 现在很多网站都在搞微博或者网页版本的IM，山寨版本Kik之类的东西。特别是后两种，设置一个定时刷新的js如果间隔很大，等于没有意义；间隔很小的话，请自行领略F5卡住对服务器的影响。要知道对于www服务器而言，建立/中断一个HTTP连接的操作同样是一个高资源耗费的过程。 利用此段代码的逻辑就是将http通常使用的短连接方式模拟一个长链接，每一个用户开一个http通道后不再立刻关闭而是建立一个持久的信道，后台执行一个轮询，一旦有新的数据则第一时间通过此信道传输给客户端。这种方式配合Json可以完成相当一部分数据刷新的需求。或者可以尝试用这种方式简单实现一些BS之间的心跳效果。 其他一些稀奇古怪的用法 Firefox这类的Mozilla系浏览器可以通过自有的header命令撤销之前的输出，通过定时的发送这个命令就可以达到不刷新而刷新的效果。具体header命令请google。 利用此模型带来的问题： set_time_limit(300);只是防止php超时罢了，对于www服务器而言，它也有自己的时效性，请注意不要被www踢出来。特别是apache有个keep_alive的设置要注意。 通用性，有前端缓冲的网站就暂时别想了，就算能成功也不保险啊。对于近期比较风光的轻量级http服务器而言，由于是单进程的，长链接对他们来说也不是擅长的。 浏览器缓冲的问题。有些“智能型”浏览器会认为你的服务器响应不及时而自说自话的启用了n年前的缓冲页面。而且对于IE这类有一大堆“安全策略”的浏览器而言这样的结果反而会白屏（IE7以后256个字节以内的信息不会显示）所以header一定要写，上面的那段仅仅只是伪代码，我不愿保证这个可以在任何浏览器上调试通过。 代码的复杂度，不用我说了吧。All in 1的代码反正我不想读。]]></description>
			<content:encoded><![CDATA[<p>大概在02 03年或者更早，我第一次接触了PHP lib中的模板类，后来又开始用了众人皆知的Smarty，以及各种框架内自带的模板引擎。这段时间就几乎没有再用过直接的echo输出——临时的脚本除外。似乎现在再去写个网站，一大堆echo嵌入到HTML里，会被同行笑话的，至少会被认为是学生作品吧。本文就重新捡回这个命令，重新认识下。</p>
<p>PHP程序员的逻辑：</p>
<p>数据经过加工，传输给模板引擎，模板引擎选择合适的模板填充，输出为页面通过www服务器传输给用户。<br />
再此之前，请自查手册，熟悉ob_end_clean() flush()以及相关联的输出控制函数。</p>
<p>如果能看懂如下伪代码，并认为这是一团糟糕的死循环的话请继续：</p>
<p><span id="more-1383"></span></p>
<pre class="php" name='code'>
&lt;?php

set_time_limit(300);
header('Cache-control: Private');
ob_end_clean();

while (true) {
    if ($Count % 2 == 0){
        echo "&lt;div&gt;";
        echo time();
        echo "&lt;/div&gt;";

        flush();
    }

    $Count ++; 

    sleep(1);
}
</pre>
<p><strong>echo优势之一——用户体验的改善</strong></p>
<p>尽管现在手机已经进入了3G时代，网速已经接近了有线宽带的水平，但作为手机网络而言，最令人头疼的就是它的不稳定。用传统的方式载入模板，所有的输出必须先行等待数据完全准备好之后才能一次性输出，这可能会需要等待很长时间。API要时刻考虑到可能会出现的断网问题，特别是在大段文件之后，较长时间的等待响应时间之中如果一旦出现网络间断（不是中断）就会让手机端白屏，影响用户体验。</p>
<p>利用此段代码正是n久之前，在没有大规模使用前端缓冲或者ajax技术的时候，很多网站经常看得到的那种分段式载入的效果。程序的逻辑是：step1完成，立即输出给www服务器，在用户等待数据下载的同时准备step2，依次类推。确保用户先“有东西看”。即便中途网络间断，照样可以获取网页内容。<br />
<strong>echo优势之二——HTTP长链接的实现</strong></p>
<p>现在很多网站都在搞微博或者网页版本的IM，山寨版本Kik之类的东西。特别是后两种，设置一个定时刷新的js如果间隔很大，等于没有意义；间隔很小的话，请自行领略F5卡住对服务器的影响。要知道对于www服务器而言，建立/中断一个HTTP连接的操作同样是一个高资源耗费的过程。</p>
<p>利用此段代码的逻辑就是将http通常使用的短连接方式模拟一个长链接，每一个用户开一个http通道后不再立刻关闭而是建立一个持久的信道，后台执行一个轮询，一旦有新的数据则第一时间通过此信道传输给客户端。这种方式配合Json可以完成相当一部分数据刷新的需求。或者可以尝试用这种方式简单实现一些BS之间的心跳效果。<br />
<strong>其他一些稀奇古怪的用法</strong></p>
<p>Firefox这类的Mozilla系浏览器可以通过自有的header命令撤销之前的输出，通过定时的发送这个命令就可以达到不刷新而刷新的效果。具体header命令请google。</p>
<p><strong>利用此模型带来的问题：</strong><strong><br />
</strong>set_time_limit(300);只是防止php超时罢了，对于www服务器而言，它也有自己的时效性，请注意不要被www踢出来。特别是apache有个keep_alive的设置要注意。</p>
<ol>
<li>通用性，有前端缓冲的网站就暂时别想了，就算能成功也不保险啊。对于近期比较风光的轻量级http服务器而言，由于是单进程的，长链接对他们来说也不是擅长的。</li>
<li>浏览器缓冲的问题。有些“智能型”浏览器会认为你的服务器响应不及时而自说自话的启用了n年前的缓冲页面。而且对于IE这类有一大堆“安全策略”的浏览器而言这样的结果反而会白屏（IE7以后256个字节以内的信息不会显示）所以header一定要写，上面的那段仅仅只是伪代码，我不愿保证这个可以在任何浏览器上调试通过。</li>
<li>代码的复杂度，不用我说了吧。All in 1的代码反正我不想读。</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2011/01/16/%e9%87%8d%e6%96%b0%e8%ae%a4%e8%af%86%e4%b8%8bphp%e7%9a%84%e8%be%93%e5%87%ba/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Gtalk通知机器人脚本</title>
		<link>http://www.litrin.net/2010/12/02/gtalk%e9%80%9a%e7%9f%a5%e6%9c%ba%e5%99%a8%e4%ba%ba%e8%84%9a%e6%9c%ac/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=gtalk%25e9%2580%259a%25e7%259f%25a5%25e6%259c%25ba%25e5%2599%25a8%25e4%25ba%25ba%25e8%2584%259a%25e6%259c%25ac</link>
		<comments>http://www.litrin.net/2010/12/02/gtalk%e9%80%9a%e7%9f%a5%e6%9c%ba%e5%99%a8%e4%ba%ba%e8%84%9a%e6%9c%ac/#comments</comments>
		<pubDate>Thu, 02 Dec 2010 06:08:53 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[网络和安全]]></category>
		<category><![CDATA[GAE]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1356</guid>
		<description><![CDATA[服务器每天都有这样那样的信息需要实时推送。最初一直是Mail，最容易实现，成本也低。后来有了短信接口，就有了相对实时但要花钱的方式推送。 知道GAE可以直接跟gtalk挂钩推送消息，可能一直挂Gtalk的人远没有挂QQ和MSN的多（密之声：Gtalk又没有星星和钻石，挂也没劲），但有了Android + 3G的实时在线，这种方式却可以直接替代SMS这类花钱的服务，何乐不为？ 大致上完成了原型，制作了一个基于Python的命令行程序，不敢独享，分享给大家。希望大家能够一起加入这个项目完善它。 在此之前，请现将 server-say @@@ appspot.com （你懂的！）加为好友 #!/usr/bin/python # -*- coding:utf-8 -*- import sys import re import urllib class ServerSaid: ApiURL = 'http://server-say.appspot.com/api' # #请到http://server-said.appspot.com/注册您的Ip地址。没有注册的Ip每小时最多10条消息。 # Account = '' MessageBody = '' DefaultEncode = 'utf-8' def SendMessage(self): cleanMessage = self.MessageBody.encode(self.DefaultEncode) if (self.checkEmail(self.Account) and self.checkMessage(cleanMessage)): cleanMessage = self.MessageBody.encode('utf-8') url = self.ApiURL + '?account='+ [...]]]></description>
			<content:encoded><![CDATA[<p>服务器每天都有这样那样的信息需要实时推送。最初一直是Mail，最容易实现，成本也低。后来有了短信接口，就有了相对实时但要花钱的方式推送。</p>
<p>知道GAE可以直接跟gtalk挂钩推送消息，可能一直挂Gtalk的人远没有挂QQ和MSN的多（密之声：Gtalk又没有星星和钻石，挂也没劲），但有了Android + 3G的实时在线，这种方式却可以直接替代SMS这类花钱的服务，何乐不为？</p>
<p>大致上完成了原型，制作了一个基于Python的命令行程序，不敢独享，分享给大家。希望大家能够一起加入这个项目完善它。<br />
在此之前，请现将 server-say @@@ appspot.com （你懂的！）加为好友</p>
<p><span id="more-1356"></span></p>
<pre class="python" name='code'>
#!/usr/bin/python
# -*- coding:utf-8 -*-
import sys
import re
import urllib

class ServerSaid:

	ApiURL = 'http://server-say.appspot.com/api'
	#
	#请到http://server-said.appspot.com/注册您的Ip地址。没有注册的Ip每小时最多10条消息。
	#
	Account = ''
	MessageBody = ''
	DefaultEncode = 'utf-8'

	def SendMessage(self):
		cleanMessage = self.MessageBody.encode(self.DefaultEncode)
		if (self.checkEmail(self.Account) and self.checkMessage(cleanMessage)):
			cleanMessage = self.MessageBody.encode('utf-8')
			url = self.ApiURL + '?account='+ urllib.quote(self.Account) +'&#038;message=' + urllib.quote(cleanMessage)
		#	print url
			query = urllib.urlopen(url)
			if ( query.read() == '200' ):
				print 'Message be sent!\\n'

			else:
				print self.Error(0)
		else :
			print self.Error(1)

	def Error(self, code=0):
		return  'Message can\\'t be send! \\n'

	def checkEmail(self, Email):
		RegexString = r'^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w{2,5}$'

		return re.match(RegexString, Email)

	def checkMessage(self, Message):

		RegexString = r'^.{1,140}$'
		return re.match(RegexString, Message)

if __name__ == '__main__':

	if (len(sys.argv)>1):
		handler = ServerSaid()
		handler.Account = sys.argv[1]

		Message = sys.argv[2]

		handler.MessageBody = Message.decode(handler.DefaultEncode)

		handler.SendMessage()
	else:
		print 'serversaid.py YOUR_GTALK MESSAGE '
</pre>
<p>这里为了防止消息的滥发，使用前最好先到 <a rel="nofollow" target="_blank" href="http://server-say.appspot.com/">http://server-say.appspot.com/ </a>注册一下。没有注册的IP每小时仅能发送10条消息，我想大部分情况下也够用了。注册了的用户可以无限制的发送消息了，除非我在GAE注册的那张信用卡里的钱花光——我会哭的。</p>
<p>啥也不说了，PHP接口也搞定了！</p>
<pre name='code' class='php'>
< ?php
/*
 *    Gtalk 通告机器人 Ver 0.1
 *
 *    Litrin Jiang 2010/12/02
 *
 * --------------------------------
 *  1. 2010/12: v0.1 原型实现
 *
 */

Class Gtalk{

	private $APIUrl = 'Http://server-say.appspot.com/api?';
	//Api 的地址，不要修改

	public $mothod = 'get';
	//默认的提交方式，现阶段仅支持get
	public $account = '';
	//接收人的Gtalk账户
	public $messageBody = '';
	//消息主体，最多支持140个字符

	public $Charset = 'UTF-8';

	public function Send($account=null, $messageBody=null){
		if (is_array($account)){
			foreach ($account as $key) {
				$this->Send($key);
			}
		}

		if ($account === null){
			$account = $this.account;
		}
		$this->CheckAccount($account);

		if($messageBody === null){
			$messageBody = $this->messageBody;
		}
		if ($this->Charset != 'UTF-8'){
			$messageBody = iconv("UTF-8", $this->Charset.'//IGNORE', $messageBody);
		}
		$this->CheckMessage($messageBody);

		$url = $this->APIUrl .'account=' . urlencode($account) . '&#038;message=' .  urlencode($messageBody);

		$query = file($url);
		/*
		if($query == '200'){
			return true;
		}else{
			return false;
		}*/

	}

	private function CheckAccount($account){
		$regexString = '^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w{2,5}$';
		echo ereg($regexString, $account);
		if (ereg($regexString, $account) != true){
			#throw new Exception("Value not a Email account! ");
		}

	}

	private function CheckMessage($messageBody){
		$regexString = '^.{1,140}';
		if (ereg($regexString, $messageBody) != true){
			#throw new Exception("Message not a allowed format! ");
		}
	}
}

?>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2010/12/02/gtalk%e9%80%9a%e7%9f%a5%e6%9c%ba%e5%99%a8%e4%ba%ba%e8%84%9a%e6%9c%ac/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>突破GAE文件数量的限制</title>
		<link>http://www.litrin.net/2010/11/26/%e7%aa%81%e7%a0%b4gae%e6%96%87%e4%bb%b6%e6%95%b0%e9%87%8f%e7%9a%84%e9%99%90%e5%88%b6/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e7%25aa%2581%25e7%25a0%25b4gae%25e6%2596%2587%25e4%25bb%25b6%25e6%2595%25b0%25e9%2587%258f%25e7%259a%2584%25e9%2599%2590%25e5%2588%25b6</link>
		<comments>http://www.litrin.net/2010/11/26/%e7%aa%81%e7%a0%b4gae%e6%96%87%e4%bb%b6%e6%95%b0%e9%87%8f%e7%9a%84%e9%99%90%e5%88%b6/#comments</comments>
		<pubDate>Fri, 26 Nov 2010 08:50:21 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[GAE]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1343</guid>
		<description><![CDATA[这些天有个项目是需要一部分Android开发。本想去官网看文档，众所周知的原因，官网无法连接。下载了本地的文件，由于是做的整站镜像，搜索功能无法正常使用，这对于经常要使用此功能的我来说很是麻烦。于是想到了把文件上传到GAE，再利用Google的本地功能来实现。 想的很简单，写好了app.yaml就要上传。谁知道试了几次也不成功。帮助手册上说的很明确： 限制 值 请求大小 10 兆字节 响应大小 10 兆字节 请求持续时间 30 秒 同时动态请求 30 * 应用程序文件的最大数目 1,000 静态文件的最大数目 1,000 应用程序文件的最大大小 10 兆字节 静态文件的最大大小 10 兆字节 所有应用程序和静态文件的最大总大小 150 兆字节 看了看要上传的文件夹，5000多个文件，小气的google根本满足不了我。 想到Python的灵活性，在手册中翻了到了zipfile类库。有门！就上传zip文档，边解压边浏览。Google要用时间来换空间那就累死他的CPU 写个GAE应用loadzip.py： #!/bin/env python ## ## This is a project for Google App Engine ## that support create a webisite by ZIP packages! ## ## [...]]]></description>
			<content:encoded><![CDATA[<p>这些天有个项目是需要一部分Android开发。本想去官网看文档，众所周知的原因，官网无法连接。下载了本地的文件，由于是做的整站镜像，搜索功能无法正常使用，这对于经常要使用此功能的我来说很是麻烦。于是想到了把文件上传到GAE，再利用Google的本地功能来实现。 想的很简单，写好了app.yaml就要上传。谁知道试了几次也不成功。<a rel="nofollow" target="_blank" href="http://code.google.com/intl/zh-CN/appengine/docs/python/runtime.html" target="_blank">帮助手册</a>上说的很明确：</p>
<table>
<tbody>
<tr>
<th>限制</th>
<th>值</th>
</tr>
<tr>
<td>请求大小</td>
<td>10 兆字节</td>
</tr>
<tr>
<td>响应大小</td>
<td>10 兆字节</td>
</tr>
<tr>
<td>请求持续时间</td>
<td>30 秒</td>
</tr>
<tr>
<td>同时动态请求</td>
<td>30 *</td>
</tr>
<tr>
<td>应用程序文件的最大数目</td>
<td><span style="color: #ff0000;">1,000</span></td>
</tr>
<tr>
<td>静态文件的最大数目</td>
<td><span style="color: #ff0000;">1,000</span></td>
</tr>
<tr>
<td>应用程序文件的最大大小</td>
<td>10 兆字节</td>
</tr>
<tr>
<td>静态文件的最大大小</td>
<td>10 兆字节</td>
</tr>
<tr>
<td>所有应用程序和静态文件的最大总大小</td>
<td>150 兆字节</td>
</tr>
</tbody>
</table>
<p><span id="more-1343"></span>看了看要上传的文件夹，5000多个文件，小气的google根本满足不了我。 想到Python的灵活性，在手册中翻了到了zipfile类库。有门！就上传zip文档，边解压边浏览。Google要用时间来换空间那就累死他的CPU <img src='http://www.litrin.net/wp-includes/images/smilies/icon_evil.gif' alt=':evil:' class='wp-smiley' />  写个GAE应用loadzip.py：</p>
<pre class="python" name='code'>#!/bin/env python
##
##	This is a project for Google App Engine
##		that support create a webisite by ZIP packages!
##
##	By Litrin J. 2010/11
##	Website: www.litrin.net
##	Example: android-sdk.appspot.com
##

import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.api import memcache
from zipfile import ZipFile
import os
import logging
import mimetypes
import re

class MainHandler(webapp.RequestHandler):
	'''
This is a project for Google App Engine that support create a webisite by ZIP packages!
	'''
	URL = ''
	CACHEDTIME = 60*60*24*30
	#setting memcaching time (seconds)

	def get(self):

		self.URL=self.request.path

		if ( self.URL[-1:] == '/'):
		#Get the defaule file for the path
			self.URL+='index.html'

		sRealFileName = os.getcwd() + self.URL

		if (os.path.exists(sRealFileName)):
		#If the file not be ziped, read it drictory
			fNoZipedFile = open(sRealFileName,'r')
			Entry = fNoZipedFile.read()
			fNoZipedFile.close()

		else:
			Entry = self.loadFile()

		self.buildMineType()
		#Set the Mine type in header

		if (self.response.headers['Content-Type'] == 'text/html'):

			Entry = self.regex(Entry)

		self.response.out.write(Entry)
		#Response building finished!

	def loadZipFile(self):
	#Load the file from zip files. This is the core function!
		lFilename = self.URL.split('/')

		iPathLevel = 1
		#Loop count, The dir layers
		bLoaded = False
		#Sucessful marks

		while(iPathLevel &lt; = len(lFilename)):
		#Get the zip file name and the filename from the URL, support muli-layer
			sFilePath = os.getcwd()
			sFileName = ''
			iElementCount = 1

			for sElement in lFilename:
			#Set or reset the Zip filename
				if ( iElementCount &lt;= iPathLevel):
					sFilePath += sElement + '/'

				if ( iElementCount &gt;= iPathLevel ):
					sFileName += sElement + '/'

				iElementCount += 1

			sFileName = sFileName[0:-1]
			sZipFilename = sFilePath[0:-1] + '.zip'

			if (os.path.exists(sZipFilename)):
			#Found the Zip file
				ZipFileHandle = ZipFile(sZipFilename)
				Entry = ZipFileHandle.read(sFileName)
				bLoaded = True
				ZipFileHandle.close()

				if (Entry is not None):
					logging.info(sFileName + " in " + sZipFilename + " Loaded!")
					return Entry
					break

			iPathLevel +=1

		if (bLoaded==False):
		#Can't file the file in Zip packages
			logging.error('No found ' + self.request.path + '' +sZipFilename+' !')
			self.error(404)

			return None

	def loadFile(self):
	#Load in from memcache if cached
		Entry = self.loadFromMemcach()

		if Entry is None:
		#If not cached, cache it
			Entry = self.loadZipFile()
			if(Entry is not None):
				self.writeToMemcache(Entry)

		return Entry

	def loadFromMemcach(self):
		memcachkey = self.URL
		#The cache key is URL

		Entry = memcache.get(memcachkey)
		if Entry is not None:
			return Entry

		else:
			return None

	def writeToMemcache(self, data):
		memcachkey = self.URL
		#set the URL as the key
		memcache.add(memcachkey, data, self.CACHEDTIME)

		logging.info(memcachkey + ' cached!')
		return True

	def buildMineType(self):
	#Building the MineType in Http header
		sFilename = os.path.basename(self.URL)
		lFileName = sFilename.split(".")
		sExFilename = lFileName.pop()
		#Get the file ex-filename

		mimetypes.init()
		sMineType = mimetypes.types_map['.'+sExFilename]

		if (sMineType == ''):
		#Others can't be idented, set to HTML
			sMineType = 'text/html'

		self.response.headers['Content-Type'] = sMineType
		#Send Content-type

	def regex(self, Entry):

		lRegGroup=(
			('\n+', '\n'),
			#
			('\t+', '\t'),
			#
		)

		for sRegCell in lRegGroup:
			(sSource, sTarget) = sRegCell
			rInfo = re.compile(sSource)
			Entry = rInfo.sub(sTarget, Entry)

		return Entry

def main():
	application = webapp.WSGIApplication([('.*', MainHandler)], debug=True)
	wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
	main()</pre>
<p>比较简单，我也没有写太多注释。考虑到Google一直被这么搞，挂掉也不好，于是启用了memcache。 重写app.yaml</p>
<pre>application: android-sdk
version: 1
runtime: python
api_version: 1

handlers:

- url: .*
 script: zipload.py

#- url: /
# static_files: index.html
# upload: index.html</pre>
<p>准备就绪，把手册中每一个子目录压缩成zip，极限大小也无所谓，然后把手册根目录下的所有文件和这两个文件放在同一目录下。上传至GAE便可。 捎带说下，由于GAE还有对文件大小的限制。用此方法还无法完全解决Adroid手册上传的问题，后来我用了分级的目录结构搞定的，原理上跟上面是一致的，在此不再累述了。 把我的Android手册网站共享给大家 <a rel="nofollow" target="_blank" href="http://android-sdk.appspot.com/" target="_blank">http://android-sdk.appspot.com/index.html</a> PS:</p>
<ol>
<li>Linux下的Idle工具真烂，远不及以及够烂的windows版。好在SPE这个工具很好用。</li>
<li>GAE的sdk for linux没有桌面工具，只能用命令行方式，在此代表Linux用户表示抗议！</li>
</ol>
<p style="text-align: center;"><span style="font-size: large;"><span style="color: #ff6600;">本项目的后续版本已经通过google code开源，敬请访问：</span></span></p>
<p style="text-align: center;"><a rel="nofollow" target="_blank" href="http://code.google.com/p/zipsite/"><span style="font-size: large;"><span style="color: #ff6600;">http://code.google.com/p/zipsite/</span></span></a><span style="font-size: large;"><span style="color: #ff6600;"> </span></span></p>
<p style="text-align: center;"><span style="font-size: large;"><span style="color: #ff6600;">获取最新版本！</span></span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2010/11/26/%e7%aa%81%e7%a0%b4gae%e6%96%87%e4%bb%b6%e6%95%b0%e9%87%8f%e7%9a%84%e9%99%90%e5%88%b6/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Ubuntu下安装Nginx+php(FastCGI)</title>
		<link>http://www.litrin.net/2010/10/27/ubuntu%e4%b8%8b%e5%ae%89%e8%a3%85nginxphpfastcgi/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=ubuntu%25e4%25b8%258b%25e5%25ae%2589%25e8%25a3%2585nginxphpfastcgi</link>
		<comments>http://www.litrin.net/2010/10/27/ubuntu%e4%b8%8b%e5%ae%89%e8%a3%85nginxphpfastcgi/#comments</comments>
		<pubDate>Wed, 27 Oct 2010 04:46:13 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[www]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[服务器]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1330</guid>
		<description><![CDATA[Nginx是近期最火的www服务器软件，它的种种优势这里就不在累述了，直接进入正题吧。 安装nginx 老一套的把戏 sudo apt-get install nginx 安装好之后sudo /etc/init.d/nginx restart 启动服务，正常情况下，你应该可以访问它获得经典的Welcome to nginx!欢迎界面。 安装php5 不同于apache的mod方式，nginx只限于fastcgi模式调用php。说道fastcgi，之前一直有兄弟问过我CGI和FastCGI的区别。其实FastCGI就是一个预先启动的进程，减少了CGI方式每次启动解释器的资源耗用。当然，这个方式带来的缺陷就是很容易因为解释器的挂死而失效。 sudo apt-get install php5-cgi 修改nginx的配置，使其支持php5 sudo /etc/nginx/sites-available/default location ~ \.php$ { fastcgi_pass   127.0.0.1:9000; fastcgi_index  index.php; fastcgi_param  SCRIPT_FILENAME  /var/www/nginx-default$fastcgi_script_name; include fastcgi_params; } 需要注意的是，默认nginx默认配置中注释掉的那一段中对于fastcgi_param的那段配置是有问题的，需要设置正确的路径。 如果你重启nginx的话，这里是无法正常使用php的，原理是php的fastcgi进程还没有启动。 sudo php-cgi -b 127.0.0.1:9000  &#38; //与上面的fastcgi_pass设置是同一端口 正常情况下，nginx已经支持了php。 由于这种方式容易出现问题导致php失效。这里我建议使用spawn-fcgi守护进程。 sudo apt-get install spawn-fcgi sudo spawn-fcgi -a 127.0.0.1 -p [...]]]></description>
			<content:encoded><![CDATA[<p>Nginx是近期最火的www服务器软件，它的种种优势这里就不在累述了，直接进入正题吧。</p>
<p><strong>安装nginx</strong><br />
老一套的把戏 sudo apt-get install nginx</p>
<p>安装好之后sudo /etc/init.d/nginx restart 启动服务，正常情况下，你应该可以访问它获得经典的Welcome to nginx!欢迎界面。</p>
<p><span id="more-1330"></span><strong>安装php5</strong></p>
<p>不同于apache的mod方式，nginx只限于fastcgi模式调用php。说道fastcgi，之前一直有兄弟问过我CGI和FastCGI的区别。其实FastCGI就是一个预先启动的进程，减少了CGI方式每次启动解释器的资源耗用。当然，这个方式带来的缺陷就是很容易因为解释器的挂死而失效。</p>
<p>sudo apt-get install php5-cgi</p>
<p><strong>修改nginx的配置，使其支持php5</strong></p>
<p>sudo /etc/nginx/sites-available/default</p>
<pre>location ~ \.php$ {
 fastcgi_pass   127.0.0.1:9000;
 fastcgi_index  index.php;
 fastcgi_param  SCRIPT_FILENAME  /var/www/nginx-default$fastcgi_script_name;
 include fastcgi_params;
 }
</pre>
<p>需要注意的是，默认nginx默认配置中注释掉的那一段中对于fastcgi_param的那段配置是有问题的，需要设置正确的路径。</p>
<p>如果你重启nginx的话，这里是无法正常使用php的，原理是php的fastcgi进程还没有启动。</p>
<p>sudo php-cgi -b 127.0.0.1:9000  &amp; //与上面的fastcgi_pass设置是同一端口</p>
<p>正常情况下，nginx已经支持了php。</p>
<p>由于这种方式容易出现问题导致php失效。这里我建议使用spawn-fcgi守护进程。</p>
<p>sudo apt-get install spawn-fcgi</p>
<p>sudo spawn-fcgi -a 127.0.0.1 -p 9000 -c 5 -f /usr/bin/php5-cgi //其中的 -c意味着同时同时启动5个进程</p>
<p>为了更加方便，不妨修改下nginx的启动脚本</p>
<p>sudo nano /etc/init.d/nginx //修改</p>
<pre>start)
 echo -n "Starting $DESC: "
 test_nginx_config
 start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \
 --exec $DAEMON -- $DAEMON_OPTS || true
 echo -n "Starting Php FastCGI"
 /usr/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -c 5 -f /usr/bin/php5-cgi
 echo "$NAME."
</pre>
<pre>stop)
 echo -n "Stopping $DESC: "
 start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \
 --exec $DAEMON || true
 echo -n "Stopping Php FastCGI"
 killall php5-cgi
 echo "$NAME."
 ;;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2010/10/27/ubuntu%e4%b8%8b%e5%ae%89%e8%a3%85nginxphpfastcgi/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>WordPress google-syntax-highlighter 插件的问题</title>
		<link>http://www.litrin.net/2010/06/12/wordpress-google-syntax-highlighter-%e6%8f%92%e4%bb%b6%e7%9a%84%e9%97%ae%e9%a2%98/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=wordpress-google-syntax-highlighter-%25e6%258f%2592%25e4%25bb%25b6%25e7%259a%2584%25e9%2597%25ae%25e9%25a2%2598</link>
		<comments>http://www.litrin.net/2010/06/12/wordpress-google-syntax-highlighter-%e6%8f%92%e4%bb%b6%e7%9a%84%e9%97%ae%e9%a2%98/#comments</comments>
		<pubDate>Sat, 12 Jun 2010 09:16:08 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[站长的blog]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[服务器]]></category>

		<guid isPermaLink="false">/?p=1251</guid>
		<description><![CDATA[小站上一直使用Google Syntax Highlighter for WordPress 插件实现代码高亮，就在上一篇文章发布之后，我发现了这个插件中存在的一个bug：windows + IE 8无法正常加亮python代码。 查阅了官方网站，发掘Google Syntax Highlighter for WordPress插件用的1.5版已经停止更新，只能使用2.0以上的版本，但2.0版本和之前的版本又存在兼容的问题，直接替换是不行的。不想每篇文章的修改&#60;pre&#62;标记，那就改插件喽。 下载最新版本的Syntax Highlighter，解压。 将原来/wp-content/plugins/google-syntax-highlighter目录下的Scripts和Styles目录清空后，分别用新版本的对应路径中的文件替换，注意大小写方式请不要变动。 修改google_syntax_highlighter.php文件，我用了minify的插件，这里就直接调用了。 &#60;?php function insert_header() {  $current_path = get_option('siteurl') .'/wp-content/plugins/' . basename(dirname(__FILE__)) .'/';  ?&#62;  &#60;link href="&#60;?php echo $current_path; ?&#62;Styles/shCore.css" type="text/css" rel="stylesheet" /&#62;  //可根据自己的需要选择不同的Styles &#60;link href="&#60;?php echo $current_path; ?&#62;Styles/shThemeEclipse.css" type="text/css" rel="stylesheet" /&#62;  &#60;?php } function insert_footer(){ $current_path = 'wp-content/plugins/' . [...]]]></description>
			<content:encoded><![CDATA[<p>小站上一直使用Google Syntax Highlighter for WordPress 插件实现代码高亮，就在上一篇文章发布之后，我发现了这个插件中存在的一个bug：windows + IE 8无法正常加亮python代码。</p>
<p>查阅了官方网站，发掘Google Syntax Highlighter for WordPress插件用的1.5版已经停止更新，只能使用2.0以上的版本，但2.0版本和之前的版本又存在兼容的问题，直接替换是不行的。不想每篇文章的修改&lt;pre&gt;标记，那就改插件喽。</p>
<p><span id="more-1251"></span></p>
<ol>
<li>下载<a rel="nofollow" target="_blank" href="http://alexgorbatchev.com/downloads/grab.php?name=sh" target="_blank">最新版本的Syntax Highlighter</a>，解压。</li>
<li>将原来/wp-content/plugins/google-syntax-highlighter目录下的Scripts和Styles目录清空后，分别用新版本的对应路径中的文件替换，注意大小写方式请不要变动。</li>
<li>修改google_syntax_highlighter.php文件，我用了minify的插件，这里就直接调用了。</li>
</ol>
<pre class="php" name='code'>&lt;?php

function insert_header() {
 $current_path = get_option('siteurl') .'/wp-content/plugins/' . basename(dirname(__FILE__)) .'/';
 ?&gt;
 &lt;link href="&lt;?php echo $current_path; ?&gt;Styles/shCore.css" type="text/css" rel="stylesheet" /&gt;
 //可根据自己的需要选择不同的Styles
 &lt;link href="&lt;?php echo $current_path; ?&gt;Styles/shThemeEclipse.css" type="text/css" rel="stylesheet" /&gt;
 &lt;?php
}

function insert_footer(){
$current_path = 'wp-content/plugins/' . basename(dirname(__FILE__)) .'/';
 ?&gt;
&lt;?php
/*
&lt;script src="&lt;?php echo $current_path; ?&gt;Scripts/shCore.js"&gt;&lt;/script&gt;

&lt;script src="&lt;?php echo $current_path; ?&gt;Scripts/shLegacy.js"&gt;&lt;/script&gt;
&lt;script src="&lt;?php echo $current_path; ?&gt;Scripts/shBrushCSharp.js"&gt;&lt;/script&gt;
&lt;script src="&lt;?php echo $current_path; ?&gt;Scripts/shBrushPhp.js"&gt;&lt;/script&gt;
&lt;script src="&lt;?php echo $current_path; ?&gt;Scripts/shBrushJScript.js"&gt;&lt;/script&gt;
&lt;script src="&lt;?php echo $current_path; ?&gt;Scripts/shBrushJava.js"&gt;&lt;/script&gt;
&lt;script src="&lt;?php echo $current_path; ?&gt;Scripts/shBrushVb.js"&gt;&lt;/script&gt;
&lt;script src="&lt;?php echo $current_path; ?&gt;Scripts/shBrushSql.js"&gt;&lt;/script&gt;
&lt;script src="&lt;?php echo $current_path; ?&gt;Scripts/shBrushXml.js"&gt;&lt;/script&gt;
&lt;script src="&lt;?php echo $current_path; ?&gt;Scripts/shBrushDelphi.js"&gt;&lt;/script&gt;
&lt;script src="&lt;?php echo $current_path; ?&gt;Scripts/shBrushPython.js"&gt;&lt;/script&gt;
&lt;script src="&lt;?php echo $current_path; ?&gt;Scripts/shBrushRuby.js"&gt;&lt;/script&gt;
&lt;script src="&lt;?php echo $current_path; ?&gt;Scripts/shBrushCss.js"&gt;&lt;/script&gt;
&lt;script src="&lt;?php echo $current_path; ?&gt;Scripts/shBrushCpp.js"&gt;&lt;/script&gt;
*/
&lt;script src="/wp-content/plugins/wp-minify/min/?b=&lt;?php echo $current_path; ?&gt;Scripts&amp;f=shCore.js,shLegacy.js,shBrushPhp.js,shBrushSql.js,shBrushPython.js"&gt;&lt;/script&gt;
&lt;script&gt;
dp.SyntaxHighlighter.ClipboardSwf = '&lt;?php echo $current_path; ?&gt;Scripts/clipboard.swf';
SyntaxHighlighter.all();
dp.SyntaxHighlighter.HighlightAll('code');
&lt;/script&gt;
&lt;?php
}
add_action('wp_head','insert_header');
add_action('wp_footer','insert_footer');
?&gt;</pre>
<p>测试，通过！</p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2010/06/12/wordpress-google-syntax-highlighter-%e6%8f%92%e4%bb%b6%e7%9a%84%e9%97%ae%e9%a2%98/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>GAE的数据导入问题</title>
		<link>http://www.litrin.net/2010/05/26/gae%e7%9a%84%e6%95%b0%e6%8d%ae%e5%af%bc%e5%85%a5%e9%97%ae%e9%a2%98/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=gae%25e7%259a%2584%25e6%2595%25b0%25e6%258d%25ae%25e5%25af%25bc%25e5%2585%25a5%25e9%2597%25ae%25e9%25a2%2598</link>
		<comments>http://www.litrin.net/2010/05/26/gae%e7%9a%84%e6%95%b0%e6%8d%ae%e5%af%bc%e5%85%a5%e9%97%ae%e9%a2%98/#comments</comments>
		<pubDate>Wed, 26 May 2010 01:58:28 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[GAE]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[服务器]]></category>

		<guid isPermaLink="false">/?p=1242</guid>
		<description><![CDATA[又是关于Google App Engine的问题。 根据GAE的官方文档，向GAE的存储区导入数据，导入了数次均出现问题，翻来覆去的无法成功。 CVS结构示例：1300428,浙江省嘉兴市,联通130卡 （一个手机号码的对应归属地列表） configfile: from google.appengine.ext import db from google.appengine.tools.bulkloader  import Loader class MobilePhone(db.Model):   Number = db.StringProperty()   Area = db.StringProperty()   BrandType = db.StringProperty() class MPLoader(Loader):   def __init__(self):     Loader.__init__(self, 'MobilePhone',                     [('Number', unicode),                      ('Area', unicode),                      ('BrandType', unicode),                      ]) loaders = [MPLoader] 主要分析了一下，问题分为几类。 1.验证不能通过的问题： 这个问题很多时候是一个低级错误，之前我曾经犯过类似的错误。在app.yaml中的handlers handlers: [...]]]></description>
			<content:encoded><![CDATA[<p>又是关于Google App Engine的问题。<br />
根据<a rel="nofollow" target="_blank" href="http://code.google.com/intl/zh-CN/appengine/docs/python/tools/uploadingdata.html" target="_blank">GAE的官方文档</a>，向GAE的存储区导入数据，导入了数次均出现问题，翻来覆去的无法成功。</p>
<p>CVS结构示例：1300428,浙江省嘉兴市,联通130卡 （一个手机号码的对应归属地列表）</p>
<p>configfile:</p>
<pre class="py" name="code" >
from google.appengine.ext import db
from google.appengine.tools.bulkloader  import Loader

class MobilePhone(db.Model):
  Number = db.StringProperty()
  Area = db.StringProperty()
  BrandType = db.StringProperty()

class MPLoader(Loader):
  def __init__(self):

    Loader.__init__(self, 'MobilePhone',
                    [('Number', unicode),
                     ('Area', unicode),
                     ('BrandType', unicode),
                     ])

loaders = [MPLoader]</pre>
<p>主要分析了一下，问题分为几类。</p>
<p><span id="more-1242"></span></p>
<p>1.验证不能通过的问题：</p>
<p>这个问题很多时候是一个低级错误，之前我曾经犯过类似的错误。在app.yaml中的handlers</p>
<p>handlers:<br />
- url: /remote_api<br />
  script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py<br />
  login: admin<br />
- url: /*<br />
  script: main.py</p>
<p>这个/*的配置必须放在最后。否则所有的访问都会直接匹配main.py自然出现问题。</p>
<p>2.跟编码有关的错误：</p>
<p>这个错误是GAE SDK的一个bug，恐怕对于ASCII系统的用户没什么关系，对于我们UTF-8，GB2312，ASCII已经昏倒的用户来说，这是个严重的问题。</p>
<p>在GAE的安装目录下 google\appengine\tools\bulkloader.py文件中2521行</p>
<pre class="python" name="code">
    for (name, converter), val in zip(self.__properties, values):
      if converter is bool and val.lower() in ('0', 'false', 'no'):
        val = False
      properties[name] = converter(val)</pre>
<p>修改为：</p>
<pre class="python" name="code" >    for (name, converter), val in zip(self.__properties, values):
        if converter is bool and val.lower() in ('0', 'false', 'no'):
            val = False
        if isinstance(val,str) and not isinstance(val, unicode):
            val=unicode(val,'utf-8')
        properties[name] = converter(val)</pre>
<p>系统会将unicode类型的变量转换。</p>
<p>3.对于大容量数据的删除</p>
<p>GAE的限制，一次最多载入1000行的数据，每次最多删除500行数据，这对于像我制作的手机号码归属之类的大数据库是远远不够的，多次删除又会导致500报错，我的方法是使用task queue。<br />
Task Queue在官方的中文文档中没有提及，在英文文档中介绍的也不够详细。个人的理解就是让系统定期的去访问一个页面而已，我的例子：</p>
<pre class="python" name="code">from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.ext import db
from google.appengine.api.labs import taskqueue

class MobilePhone(db.Model):
    Number = db.StringProperty()
    Area = db.StringProperty()
    BrandType = db.StringProperty()

class Delete(webapp.RequestHandler):
    def get(self):
        q = db.GqlQuery("SELECT * FROM MobilePhone limit 100")
        results = q.fetch(100)
        db.delete(results)
        q = db.GqlQuery("SELECT * FROM MobilePhone limit 100")
        if (q.count() &gt; 0):
            self.addqueue()

        self.response.out.write('Hello world!')

    def addqueue(self):
        taskurl = 'http://ip-and-mp.appspot.com/'
        taskqueue.add( url='/del', params=dict(url=taskurl))
        self.response.out.write('added!')</pre>
<p>每次删除100行，使用默认的default queue，即每秒一次。</p>
<p>4.关于命令行的问题</p>
<p>我按照中文文档的办法始终有问题，添加了&#8211;email=xxxx &#8211;passin的参数才成功。手册已经远远落后了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2010/05/26/gae%e7%9a%84%e6%95%b0%e6%8d%ae%e5%af%bc%e5%85%a5%e9%97%ae%e9%a2%98/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>GAE的镜像</title>
		<link>http://www.litrin.net/2010/05/06/gae%e7%9a%84%e9%95%9c%e5%83%8f/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=gae%25e7%259a%2584%25e9%2595%259c%25e5%2583%258f</link>
		<comments>http://www.litrin.net/2010/05/06/gae%e7%9a%84%e9%95%9c%e5%83%8f/#comments</comments>
		<pubDate>Thu, 06 May 2010 05:30:58 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[站长的blog]]></category>
		<category><![CDATA[GAE]]></category>
		<category><![CDATA[服务器]]></category>

		<guid isPermaLink="false">/?p=1238</guid>
		<description><![CDATA[本想将LAMP中的PHP弄成Python，研究下Zope或者索性的Python CGI。想到目前推Python最起劲的公司正是Google，况且Google的App Engine又是免费资源，顺道着就申请了。 GAE对于很多人来说只不过是当作撑杆来翻墙的。其实不管从哪个方面来说，GAE的理念就是一个搭载了Java或Python页面框架的虚拟主机，而且支持一大堆的扩展（google wave上的机器人）。借助了Microblog，我在GAE上架设了本站的镜像站点。 相对来说Google提供的SDK目前只能说是一个上传工具而已。很多功能，例如Xmpp，数据存储还没有办法完全模拟，只有页面框架和Memcache还算能用。相信google也会不断的加以完善。 说起Microblog，首先是一个不错的CMS系统。但可能是版本较低，加之GAE程序的调试比较麻烦，功能和结构上只能实现简单的blog功能。相比现成的blog空间反而麻烦不少。相信随着逐步的升级，这个系统将成为一个优秀的CMS。 想用Wordpress的导出工具直接生成一个新站，发现此功能还有问题，至少本站无法成功。于是只能顺手写一个扩展实现两地同步，尽管问题也不少。 说起Python，个人觉得是一个效率很高的语言，缺点就是产生这高效率的扩展太多，太混乱，初学起来很迷糊。顺手之后你就会觉得它作为高级语言，可以操纵的资源丰富的惊人。不过在此插一句，python3的转变真是太突兀了，大部分2.6之前的代码都要重新写过，对于GAE的目标环境还是用2.6以下搭建吧。]]></description>
			<content:encoded><![CDATA[<p>本想将LAMP中的PHP弄成Python，研究下Zope或者索性的Python CGI。想到目前推Python最起劲的公司正是Google，况且Google的App Engine又是免费资源，顺道着就申请了。</p>
<p>GAE对于很多人来说只不过是当作撑杆来翻墙的。其实不管从哪个方面来说，GAE的理念就是一个搭载了Java或Python页面框架的虚拟主机，而且支持一大堆的扩展（google wave上的机器人）。借助了Microblog，我在GAE上架设了<a rel="nofollow" target="_blank" href="http://litrinblog.appspot.com" target="_blank">本站的镜像站点</a>。</p>
<p><span id="more-1238"></span></p>
<p>相对来说Google提供的SDK目前只能说是一个上传工具而已。很多功能，例如Xmpp，数据存储还没有办法完全模拟，只有页面框架和Memcache还算能用。相信google也会不断的加以完善。</p>
<p>说起Microblog，首先是一个不错的CMS系统。但可能是版本较低，加之GAE程序的调试比较麻烦，功能和结构上只能实现简单的blog功能。相比现成的blog空间反而麻烦不少。相信随着逐步的升级，这个系统将成为一个优秀的CMS。<br />
想用Wordpress的导出工具直接生成一个新站，发现此功能还有问题，至少本站无法成功。于是只能顺手写一个扩展实现两地同步，尽管问题也不少。</p>
<p>说起Python，个人觉得是一个效率很高的语言，缺点就是产生这高效率的扩展太多，太混乱，初学起来很迷糊。顺手之后你就会觉得它作为高级语言，可以操纵的资源丰富的惊人。不过在此插一句，python3的转变真是太突兀了，大部分2.6之前的代码都要重新写过，对于GAE的目标环境还是用2.6以下搭建吧。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2010/05/06/gae%e7%9a%84%e9%95%9c%e5%83%8f/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>flash播放MP3的问题</title>
		<link>http://www.litrin.net/2010/04/19/flash%e6%92%ad%e6%94%bemp3%e7%9a%84%e9%97%ae%e9%a2%98/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=flash%25e6%2592%25ad%25e6%2594%25bemp3%25e7%259a%2584%25e9%2597%25ae%25e9%25a2%2598</link>
		<comments>http://www.litrin.net/2010/04/19/flash%e6%92%ad%e6%94%bemp3%e7%9a%84%e9%97%ae%e9%a2%98/#comments</comments>
		<pubDate>Mon, 19 Apr 2010 09:45:39 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1232</guid>
		<description><![CDATA[最近碰到了一个奇异的问题：apache的服务器，一个Flash调Mp3进行播放，用IE和chromium打开后启动时正常，一旦暂停后，再次播放就无效。但对于Firefox则完全正常。 反复检查代码，无果。将文件上传到另外一台机器后IE也正常。反复修改KeepAlive，disk-cache之类的设置，完全没有效果。 真是叫人郁闷。 检查了apache的配置，感觉问题应该跟mod_deflate模块有关，相应的代码如下：     &#60;IfModule deflate_module&#62;         SetOutputFilter DEFLATE         BrowserMatch ^Mozilla/4 gzip-only-text/html         BrowserMatch ^Mozilla/4\.0[678] no-gzip         BrowserMatch \bMSIE !no-gzip !gzip-only-text/html         SetEnvIfNoCase Request_URI \                 \.(?:gif&#124;jpe?g&#124;png)$ no-gzip dont-vary     &#60;/IfModule&#62; 关闭后IE恢复正常，考虑到一般情况下，mp3和swf压缩不了多少，但是却经常需要断点续传，重新修改相应配置：     &#60;IfModule deflate_module&#62;         SetOutputFilter DEFLATE         BrowserMatch ^Mozilla/4 gzip-only-text/html         BrowserMatch ^Mozilla/4\.0[678] no-gzip         BrowserMatch \bMSIE !no-gzip !gzip-only-text/html         SetEnvIfNoCase [...]]]></description>
			<content:encoded><![CDATA[<p>最近碰到了一个奇异的问题：apache的服务器，一个Flash调Mp3进行播放，用IE和chromium打开后启动时正常，一旦暂停后，再次播放就无效。但对于Firefox则完全正常。</p>
<p>反复检查代码，无果。将文件上传到另外一台机器后IE也正常。反复修改KeepAlive，disk-cache之类的设置，完全没有效果。</p>
<p>真是叫人郁闷。</p>
<p><span id="more-1232"></span></p>
<p>检查了apache的配置，感觉问题应该跟mod_deflate模块有关，相应的代码如下：</p>
<pre>    &lt;IfModule deflate_module&gt;

        SetOutputFilter DEFLATE
        BrowserMatch ^Mozilla/4 gzip-only-text/html
        BrowserMatch ^Mozilla/4\.0[678] no-gzip
        BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
        SetEnvIfNoCase Request_URI \
                \.(?:gif|jpe?g|png)$ no-gzip dont-vary

    &lt;/IfModule&gt;</pre>
<p>关闭后IE恢复正常，考虑到一般情况下，mp3和swf压缩不了多少，但是却经常需要断点续传，重新修改相应配置：</p>
<pre>    &lt;IfModule deflate_module&gt;

        SetOutputFilter DEFLATE
        BrowserMatch ^Mozilla/4 gzip-only-text/html
        BrowserMatch ^Mozilla/4\.0[678] no-gzip
        BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
        SetEnvIfNoCase Request_URI \
                \.(?:gif|jpe?g|mp3|swf|png)$ no-gzip dont-vary

    &lt;/IfModule&gt;</pre>
<p>完全正常！</p>
<p>分析下来大致原理如下（个人分析，无依据）：<br />
首先假设的是我没有踩中任何一个bug。这个问题似乎跟原先出现过deflate的文件不能断点续传为同一个问题。</p>
<p>mp3采用流格式传输，没有完全下载结束就被暂停下载，由于系统启用了mod_deflat的压缩传输模块，本地保存的实际上是一个gzip过的mp3文件。</p>
<p>由于目前主流浏览器都支持断点续传，而对于断点续传的处理却各有不同，IE和chromium可能会先对于gzip之前的文件进行校验，发觉校验失败，认为这是一个错误的文件，不再进行续传；FF则是先不进行校验，或者校验失败后重新下载续传。</p>
<p>尚为猜测，希望各位朋友给个确凿的解释。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2010/04/19/flash%e6%92%ad%e6%94%bemp3%e7%9a%84%e9%97%ae%e9%a2%98/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>将Apache日志实时写入mysql</title>
		<link>http://www.litrin.net/2010/02/24/%e5%b0%86apache%e6%97%a5%e5%bf%97%e5%ae%9e%e6%97%b6%e5%86%99%e5%85%a5mysql/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e5%25b0%2586apache%25e6%2597%25a5%25e5%25bf%2597%25e5%25ae%259e%25e6%2597%25b6%25e5%2586%2599%25e5%2585%25a5mysql</link>
		<comments>http://www.litrin.net/2010/02/24/%e5%b0%86apache%e6%97%a5%e5%bf%97%e5%ae%9e%e6%97%b6%e5%86%99%e5%85%a5mysql/#comments</comments>
		<pubDate>Wed, 24 Feb 2010 09:54:15 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[www]]></category>
		<category><![CDATA[数据库应用]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[服务器]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1176</guid>
		<description><![CDATA[貌似 站长Litrin已经很久没有关注过LAMP的东西了。 作为网站运行来说，日志分析是一个很重要的工作。当一个网站的日志到了一定程度，或者一个网站同时有多台服务器的时候，传统的文本日志分析总会遇到瓶颈。 这个时候我就会想起强大的sql语句。看了网上很多人的帖子，都是将日志转成sql语句再导入的，搞得有点复杂。本方案不需要任何多余的软件和操作，一切全是实时、自动，供各位参考。 主机至少需要预安装apache(废话！)和mysql的客户端。如果访问量不大且服务器的资源管够的话可以将mysql装在本地，否则建议一台独立的mysql服务器。 在apache配置文件或者虚拟主机配置文件中面添加 LogFormat INSERT INTO apacheLog (ID, dateTime, IP, URL, code, referer, userAgent, size, request) VALUES ( NULL , \"%{%Y-%m-%d %H:%M:%S}t\", \"%a\", \"%U\", \"%&#62;s\", \" %{Referer}i\", \"%{User-Agent}i\", %b, %T);" sqlcomm CustomLog "&#124; /usr/local/Mysql --host=HOST --user=USER --password=PASSWORD --database=LITRIN_NET" sqlcomm 建立数据库apacheLog。 CREATE TABLE ` apacheLog ` ( `ID` int(11) NOT NULL auto_increment, `dateTime` datetime NOT [...]]]></description>
			<content:encoded><![CDATA[<p>貌似 站长Litrin已经很久没有关注过LAMP的东西了。</p>
<p>作为网站运行来说，日志分析是一个很重要的工作。当一个网站的日志到了一定程度，或者一个网站同时有多台服务器的时候，传统的文本日志分析总会遇到瓶颈。</p>
<p>这个时候我就会想起强大的sql语句。看了网上很多人的帖子，都是将日志转成sql语句再导入的，搞得有点复杂。本方案不需要任何多余的软件和操作，一切全是实时、自动，供各位参考。</p>
<p><span id="more-1176"></span></p>
<p>主机至少需要预安装apache(废话！)和mysql的客户端。如果访问量不大且服务器的资源管够的话可以将mysql装在本地，否则建议一台独立的mysql服务器。</p>
<p>在apache配置文件或者虚拟主机配置文件中面添加</p>
<pre>LogFormat INSERT INTO apacheLog (ID, dateTime, IP, URL, code, referer, userAgent, size, request) VALUES ( NULL , \"%{%Y-%m-%d %H:%M:%S}t\", \"%a\", \"%U\", \"%&gt;s\", \" %{Referer}i\", \"%{User-Agent}i\", %b, %T);" sqlcomm
CustomLog "| /usr/local/Mysql --host=HOST --user=USER --password=PASSWORD --database=LITRIN_NET" sqlcomm</pre>
<p>建立数据库apacheLog。</p>
<pre name=code class=sql>
CREATE TABLE ` apacheLog ` (
  `ID` int(11) NOT NULL auto_increment,
  `dateTime` datetime NOT NULL,
  `IP` varchar(15) NOT NULL,
  `URL` varchar(256) NOT NULL,
  `code` varchar(3) NOT NULL,
  `referer` varchar(256) NOT NULL,
  `userAgent` varchar(256) NOT NULL,
  `size` int(11) NOT NULL,
  `request` float NOT NULL,
  PRIMARY KEY  (`ID`),
  KEY `dateTime` (`dateTime`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
SET character_set_client = @saved_cs_client;
</pre>
<p> 确认无误后重启apache生效！</p>
<p>其实整个流程很简单：先是利用LogFormat设置将日志转换为接近sql的格式，再通过CustomLog 调用pipe实现mysql写入。你也可以参照<a rel="nofollow" target="_blank" href="http://httpd.apache.org/docs/2.2/mod/mod_log_config.html#formats">http://httpd.apache.org/docs/2.2/mod/mod_log_config.html#formats</a>的格式设置写入自己关心的数据。<br />
此外，你也可以使用<code>SetEnvIf配合正则表达式过滤掉诸如图片、CSS、JS之类相对不重要的信息。</code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2010/02/24/%e5%b0%86apache%e6%97%a5%e5%bf%97%e5%ae%9e%e6%97%b6%e5%86%99%e5%85%a5mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>用SSH Tunnel穿越防火墙</title>
		<link>http://www.litrin.net/2010/02/09/%e7%94%a8ssh-tunnel%e7%a9%bf%e8%b6%8a%e9%98%b2%e7%81%ab%e5%a2%99/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e7%2594%25a8ssh-tunnel%25e7%25a9%25bf%25e8%25b6%258a%25e9%2598%25b2%25e7%2581%25ab%25e5%25a2%2599</link>
		<comments>http://www.litrin.net/2010/02/09/%e7%94%a8ssh-tunnel%e7%a9%bf%e8%b6%8a%e9%98%b2%e7%81%ab%e5%a2%99/#comments</comments>
		<pubDate>Tue, 09 Feb 2010 02:17:06 +0000</pubDate>
		<dc:creator>Litrin</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[www]]></category>
		<category><![CDATA[网络和安全]]></category>
		<category><![CDATA[桌面应用]]></category>

		<guid isPermaLink="false">http://www.litrin.net/?p=1164</guid>
		<description><![CDATA[这次接上篇。 很多企业对互联网的访问进行了限制，如何突破防火墙的限制成了一个问题。本文就是利用了SSH tunnel搭建了socket5代理。 首先，申请一个外网的ssh帐户，个人建议使用http://www.unix-center.net/提供的免费资源，该网站还提供多种平台主机可供测试之用，非常不错！当然，如果可以使用密钥方式登录那就完美了。 申请好账户之后，依然在本地安装ssh的客户端，windows推荐putty。 安装好后执行putty，在hostname里面输入主机名，或者用户名@主机名。我选了FreeBSD的那台，有兴趣的话还有ubuntu，Sun，甚至龙芯一系列主机可供挑选 选择ssh-&#62;tunnels，在source栏里添加你要提供的端口号，一般随便填一个未使用的端口就可，我用了8080端口。选择Dynamic，点击add。这里需要说明的是，如果你的主机允许别人使用的话，请勾选最上面的Local ports accept connection from other hosts。 点击Open确认连接，在接下来的窗口中输入用户名和密码。最小化窗口，不要关闭。 打开命令行，输入命令netstat -an，如果出现了你刚才输入的那个端口号的侦听，说明搭建成功！socket服务已经完成。 浏览器设置，IE:internet选项，连接，局域网设置，勾选“为LAN使用代理服务器”，高级，套接字选择127.0.0.1并填写端口号。完成设置。 对应的命令行： plink : plink -C -N -f -D 端口号 用户名@主机名 ssh: ssh  -C -N -f -D 端口号 用户名@主机名 PS: 话两头说，如果你负责一个企业局域网的话，稍大点的企业如果要限网，SSH端口是绝对要加以限制使用的。伟大的防火墙亦是如此。]]></description>
			<content:encoded><![CDATA[<p>这次接<a href="http://www.litrin.net/2010/02/03/%e7%94%a8ssh-tunnel%e6%89%93%e9%80%a0%e5%ae%89%e5%85%a8%e9%82%ae%e4%bb%b6%e7%b3%bb%e7%bb%9f/" target="_blank">上篇</a>。</p>
<p>很多企业对互联网的访问进行了限制，如何突破防火墙的限制成了一个问题。本文就是利用了SSH tunnel搭建了socket5代理。</p>
<p>首先，申请一个外网的ssh帐户，个人建议使用<a rel="nofollow" target="_blank" href="http://www.unix-center.net/">http://www.unix-center.net/</a>提供的免费资源，该网站还提供多种平台主机可供测试之用，非常不错！当然，如果可以使用密钥方式登录那就完美了。</p>
<p><span id="more-1164"></span></p>
<p>申请好账户之后，依然在本地安装ssh的客户端，windows推荐<a rel="nofollow" target="_blank" href="http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html" target="_blank">putty</a>。</p>
<p>安装好后执行putty，在hostname里面输入主机名，或者用户名@主机名。我选了FreeBSD的那台，有兴趣的话还有ubuntu，Sun，甚至龙芯一系列主机可供挑选</p>
<div id="attachment_1166" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.litrin.net/wp-content/uploads/2010/02/ssh_proxy_putty_main.png"><img class="size-medium wp-image-1166" title="ssh_proxy_putty_main" src="http://www.litrin.net/wp-content/uploads/2010/02/ssh_proxy_putty_main-300x288.png" alt="Putty 主界面" width="300" height="288" /></a><p class="wp-caption-text">Putty 主界面</p></div>
<p>选择ssh-&gt;tunnels，在source栏里添加你要提供的端口号，一般随便填一个未使用的端口就可，我用了8080端口。选择Dynamic，点击add。这里需要说明的是，如果你的主机允许别人使用的话，请勾选最上面的Local ports accept connection from other hosts。</p>
<div id="attachment_1167" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.litrin.net/wp-content/uploads/2010/02/ssh_proxy_putty_tunnel_setting.png"><img class="size-medium wp-image-1167" title="ssh_proxy_putty_tunnel_setting" src="http://www.litrin.net/wp-content/uploads/2010/02/ssh_proxy_putty_tunnel_setting-300x288.png" alt="Putty ssh tunnel界面" width="300" height="288" /></a><p class="wp-caption-text">Putty ssh tunnel界面</p></div>
<p>点击Open确认连接，在接下来的窗口中输入用户名和密码。最小化窗口，不要关闭。</p>
<p>打开命令行，输入命令netstat -an，如果出现了你刚才输入的那个端口号的侦听，说明搭建成功！socket服务已经完成。</p>
<div id="attachment_1165" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.litrin.net/wp-content/uploads/2010/02/ssh_proxy_netstat.png"><img class="size-medium wp-image-1165" title="ssh_proxy_netstat" src="http://www.litrin.net/wp-content/uploads/2010/02/ssh_proxy_netstat-300x195.png" alt="netstat -an " width="300" height="195" /></a><p class="wp-caption-text">netstat -an </p></div>
<p>浏览器设置，IE:internet选项，连接，局域网设置，勾选“为LAN使用代理服务器”，高级，套接字选择127.0.0.1并填写端口号。完成设置。</p>
<div id="attachment_1168" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.litrin.net/wp-content/uploads/2010/02/ssh_proxy_IE.png"><img class="size-medium wp-image-1168" title="ssh_proxy_IE" src="http://www.litrin.net/wp-content/uploads/2010/02/ssh_proxy_IE-300x300.png" alt="IE 设置" width="300" height="300" /></a><p class="wp-caption-text">IE 设置</p></div>
<p>对应的命令行：</p>
<ol>
<li>plink : plink -C -N -f -D 端口号 用户名@主机名</li>
<li>ssh: ssh  -C -N -f -D 端口号 用户名@主机名</li>
</ol>
<p>PS:<br />
话两头说，如果你负责一个企业局域网的话，稍大点的企业如果要限网，SSH端口是绝对要加以限制使用的。伟大的防火墙亦是如此。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.litrin.net/2010/02/09/%e7%94%a8ssh-tunnel%e7%a9%bf%e8%b6%8a%e9%98%b2%e7%81%ab%e5%a2%99/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

