2010年12月31日星期五

网络(非GPS)定位原理

有次在地铁上,跟朱朝卓同学聊到这样一个话题,即可装13又省钱的方法:买个可以做3G 路由的廉价手机(比如华为U8500),再买个ipod touch,用手机打电话和wifi热点,这样不到3000元就可以享受Apple产品,经济实惠!恩,的确是个很不错的途径!但是当时我有过顾虑:ipod touch使用wifi,LBS(物理定位服务)恐怕都用不成了,因为不能用GPS了嘛!但是朱朝卓告诉我是可以的,ucweb通过wifi可以定位!当时我的想法是,难道是通过ip定位?肯定很不准确吧?


今天中午,和双木成林同学聊天得知:用wifi定位,很准确,我还是觉得应该公司ip比较固定的缘故!但是双木成林接着说,在他家里定位也很准确!这就奇怪了,难道Google跟中国运营商有合作?这样需要满足以下条件才能定位:



  1. 当用户连接网络时,运营商给用户分配地址的同时,同事记录了你的连接账号和分配ip的映射关系,通过用户的账号自然知道用户的地理位置,因为用户申请账号的时候都填写家庭或者公司地址

  2. Google可以随时根据ip去访问运行商的接口获知详细地理位置。既然google都可以,那相关部门肯定也可以啊!


如果真是这样,那就危险了?自己在任何地方,都没有隐私可言?被跨省也易如反掌啊!接着认真查看了Google Geolocaion API,发现并不是这样!



Many devices do not have native access to GPS or other location data. Additionally, GPS can take a long time to get an accurate location fix, drains battery, and does not work indoors. Because of these problems, the location API also has the ability to send various signals that the devices has access to (nearby cell sites, wifi nodes, etc) to a third-party location service provider, who can resolve the signals into a location estimate.



大概的过程是这样的:Google是很大型的公司,每天都会分配很多工作人员到各城市满大街乱跑,去收集无线热点信息(包括cell sites和wifi nodes),将那些固定、持久的无线热点以及地理信息保存到Google地理信息库中。当有浏览器或者应用程序请求地理信息时,会收集电脑或者手机附近的无线热点,发送给Google解析,从而获取详细的地址信息。


因此可以说,如果你的电脑或者手机没有无线设备或者禁用了无线设备,单凭ip是获取不到详细地理信息的!


测试:使用支持html5的浏览器(Firefox 或者 chrome)访问http://html5demos.com/geo,点击“允许”浏览器跟踪您的地理信息,如果你当前打开了无线网卡,看看定位是否准确?我的电脑是台式机,不能定位,我让我女朋友测试,不知情的她说:我是特工。


References & Resoucres:


2010年12月19日星期日

Thinking in yield

yield作为python的一个关键字,如果在函数体使用,那么这个调用这个函数返回一个“生成迭代器”(generator iterator),当然在平时都称呼为“生成器”。xrange函数就是我们常见的调用后返回生成器(当然这个是已经经过包装的生成器),xrange与range的区别是什么?常见的回答当然是:xrange省内存,在遍历的时候需要的时候才会生成所需要的时,而不像range,首先把所有的值都生成好保存在一个list中。恩,这的确是他们的最表面的区别。


为了更深入的了解生成器,还是先介绍下生成器有的特性和特点吧:



  • 可以迭代(恩,这个特性是废话,从名字都可以看出来,但是这的确是生成器最基本的一个特性)

  • 通过调用next函数或者send函数(其实next() = send(None) ),会执行到yield语句,就会被冻结,冻结后就返回它的caller;直至调用下次send函数从上次冻结点接着执行

  • 当生成器对象引用计数为0被回收时,如果发现生成器对象仍然被冻结,就会调用close函数,close函数的作用就是抛出一个GeneratorExit的异常

  • 生成器只能被迭代一次(有点惊讶?我看源码的时候,发现这个的时候也有点)


那generator是如何实现的呢?看了源码其实很简单,在初始化一个生成器对象,都需要一个参数: PyFrameObject * f。这个f就是生成器函数的栈帧,当函数被冻结时,记录这个栈帧的栈点stacktop 以及虚拟机字节码位置f_lasti。下次执行的时候直接从这个字节码位置和栈点执行。一切很简单吧!


一切谜底都要从python源码Python/ceval.c中的PyEval_EvalFrameEx开始,初始化代码:


 


    /* An explanation is in order for the next line.

f->f_lasti now refers to the index of the last instruction
executed. You might think this was obvious from the name, but
this wasn't always true before 2.3! PyFrame_New now sets
f->f_lasti to -1 (i.e. the index *before* the first instruction)
and YIELD_VALUE doesn't fiddle with f_lasti any more. So this
does work. Promise. */
next_instr = first_instr + f->f_lasti + 1;
stack_pointer = f->f_stacktop;
assert(stack_pointer != NULL);
f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */

 


那yield或者说生成器都有哪些应用呢:



  • 生成迭代器(靠,又是废话)

  • 与with_statement结合使用

  • 著名的"Trampoline in python"

  • 轻量级任务

  • 其他……


Resouces & References:


2010年12月13日星期一

Git与SVN的不同

以前写过一个blog:Git初学习,现在读来觉得当初对于git的理解真的很肤浅,现在就来说说git与svn的不同:



  • 分布式。这是git最明显的一个特征,分布式SCM。分布式也就要求,分布式中的每台机都保存了整个代码仓库所需要的所有代码和资源,而不仅仅是当前最新版本。分布式也带来很多好处:可以在离线的情况下版本控制;另外与集中式SCM不同,即使服务器挂掉也无妨,可以采用任何一台机进行恢复。

  • 版本。svn的版本有着revision的概念,版本号是递增的。版本号能够递增,也是因为svn是集中式SCM的缘故。当多个用户同时提交时,svn服务器会将用户之间的提交操作串行。git是分布式SCM,没有递增的版本号,git采用的做法是:在保存到git之前,git将所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引,作为版本号。

  • 储存方式。svn每个版本提交时,保存的是当前版本与上个版本之间的diff。而git提交时,则是快照。比如git commit了文件A和B,则提交完成后,至少创建了5个对象,新的文件BLOB对象A和B;一个记录着目录树内容及其各个文件对应BLOB对象索引的tree对象;一个包含指向tree对象(根目录)的索引和其他提交信息元数据的commit对象。如果当前代码仓库还有文件C,由于文件C并没有修改,所以tree对象中保存的便是上个版本文件C的BLOB对象的索引。但是一旦文件被修改,比如A和B,就会创建一个新的BLOB文件对象,而不是仅仅的快照。git这种做法虽然牺牲了存储空间(现在存储空间很廉价的吧),但是当比较两个版本或者合并分支的时候,速度上会有明显的提升。

  • 真正的分支和tag。使用过svn都知道,svn中的分支和tag都是我们认为赋予的概念,都是通过svn copy复制到另外一个目录,通过目录或者人为命名标记为分支或者tag,并且人为的规定:tag是只读的,不能修改,而实际上可以修改的。而git则实现了真正的创建一个分支很简单,仅仅是创建一个branche对象,其中branche对象包含有指定commit对象的校验和(即版本号),当随着开发的进行,提交新版本时,branche对象直接修改版本号即可。创建tag也是如此,新建一个tag对象,包含指定commit对象的校验和和其他数据信息。这也就是为什么在git下,创建分支和标签为什么这么快的根本原因。

openvpn随机启动并自动连接

在写这篇blog前,首先还得感谢郭嘉,因为Google App Engine 解封了,手机可以正常发推了,博客不用翻墙也可以登录了。


上周五一冲动还是买了一个ssh代理,虽然自己有个速度很快的免费openvpn,主要还是考虑:



  • vpn的确有着自身的一些软肋:所有境外ip都要走vpn,启动需要手动输入账号密码,修改route比较麻烦

  • 免费申请的openvpn规定不能观看youtube,虽然在技术上并没有做任何限制,但是由于免费申请来的,自己经常看,也不好意思

  • 上周五,免费申请的openvpn突然不给力了,速度突然慢起来

  • 自己免费申请的openvpn速度都很赞,想着如果自己买个岂不是更快


所以就在敏感词上的ssh广告买了一个“45元/366天规格“的ssh服务,当时测试下载速度很快,500kb/s左右,在账号为到之前,我觉得用了这个ssh代理会秒开twitter,facebook,流畅观看youtube。晚上做梦的时候还是翻墙。


可是第二天账号到的时候,才发现自己错了,购买的ssh代理服务只是比gappproxy快点而已,没有自己免费申请的openvpn,开始还以为是自己的客户端软件myEnTunnel的plink核心做了限速,谁知修复了这个问题或者换了Tunnerler都不管用,现在我才意识到下载测试速度跟真是翻墙代理的速度不是一个概念,很有可能是代理服务器对账号做了限速。自己不得不换回免费申请的openvpn。


使用openvpn时,觉得有几点不爽:



  • 每次都要手动输入账号密码,官方认为将账号和密码保存起来不太安全

  • 不能自动连接,虽然可以随机启动


上网google了下,果然得到了解决。


首先说在windows下,虽然官方没有推出保存保存账号和密码的openvpn-GUI版本,但是已经有人为了方便,自己修改了openvpn源码,重新编译好了,供我们下载。使用起来也很方便,直接将client.ovpn中的auth-user-pass改成auth-user-pass mypass.pwd即可。其中mypass.pwd是保存着账号密码的文件,第一行为账号,第二行为密码。在windows下系统启动并自动连接,直接修改注册表即可,即:


[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
$OPENVPN_PATH --connect client.ovpn --sclient-connection 1

在ubuntu下,随机启动并且自动连接也很简单,也是跟上面一样修改clien.ovpn中的auth-user-pass,但是需要在编译安装openvpn的时候,需要指定enable-user-pass。不过幸运的是,如果你采用sudo apt-get install openvpn,已经默认支持保存账户密码了。


Resources & References:


2010年11月30日星期二

MapReduce初探

MapReduce最近很热,是Google提出的一个软件架构,用来大规模的分布式计算。主要思想就是Map和Reduce:



  • Map是将一组键值对转换为一组新的键值对列表

  • Reduce则是根据相同的键,把其值通过一定的函数合并


本来这些概念也通俗易懂(难的地方,比如分布式、可靠性我还没有去深究),但是我也想实现一个简单的MapReduce却遇到了问题。此次实现简单的MapReduce,主要想用python的多线程、列表以及Queue等基本的一些结构和数据类型来实现,具体实现步骤:



  1. 通过parse,将原始数据转换成键值对

  2. 使用map将1)步生成的键值对转换成新的键值对

  3. 将map生成的map进行merge操作,首先对键进行排序,然后将相同键的键值对的值合并一个list

  4. 对list进行reduce


这次想通过python的多线程来模拟并行,通过Queue和自定义的SynchronizeDict来缓存中间数据。但是在实现代码的时候,我发现map worker和reduce worker并不能同时执行,因为reduce worker需要等待,只有当所有的map worker执行完毕,merge完毕,reduce worker才开始自己的工作,否则必然出现不完整的情况。代码实现见:https://github.com/yangjuven/MapReduce


重新读了下《MapReduce: Simplied Data Processing on Large Clusters》的3.1 Execution Overview,有段这样写到:



When a reduce worker has read all intermediate data, it sorts it by the intermediate keys so that all occurrences of the same key are grouped together.



也询问了Google's MapReduce in 98 Lines of Python的作者John Arley Burns。




Juven:

In you mapreduce example, the map workers and reduce workers can’t be processed in parallel. Because reduce workers must wait for the complete of map workers. How is the true MapReduce?

John Arley Burns:

Yes you are correct, the map and reduce steps cannot be in parallel. Instead, all maps run in parallel, then when all are finished, all reduce steps run, depending on algorithm in order, This is inherent to the map reduce algorithm. As you noticed, this limits parallelization. This is perhaps one reason Google has largely abandoned map reduce in favor of Bigtable-based processing, letting the database function as the point of control in the algorithms.



Resouces & References:



  1. MapReduce: Simplied Data Processing on Large Clusters

  2. Google's MapReduce in 98 Lines of Python


 



 


 

2010年11月17日星期三

Alias与WSGIScriptAlias

将在项目托管到igor中,由于Apache这样配置:



Alias /project_name/images/ /home/project/project_name/htdocs/images/
Alias /project_name/css/ /home/project/project_name/htdocs/css/
Alias /project_name/js/ /home/project/project_name/htdocs/js/
AliasMatch ^/project_name/(?!app/)(.*) /home/project/project_name/htdocs/$1
WSGIScriptAlias /project_name/app /home/project/project_name/wsgi_handler.py



就出现以下问题:



  1. http://domain/project_name/app

  2. http://domain/proejct_name/app/


链接1和链接2是不同的,链接1会指向到/home/project/htdocs/app,而链接2则会交由wsgi_handler.py来处理。但是实际上我们希望1和2是等价的。那么这个问题也有两种解决方案:


  1. 修改AliasMatch的正则表达式,修改成"/proect_name/((?!app).*|app(?!/).+)"

  2. 将WSGIScriptAlias 放到AliasMatch前面


方案1是可行的。方案2我原以为是可用性的,原因(from http://httpd.apache.org/docs/2.0/mod/mod_alias.html):





Aliases and Redirects occuring in different contexts are processed like other directives according to standard merging rules. But when multiple Aliases or Redirects occur in the same context (for example, in the same <VirtualHost> section) they are processed in a particular order.

First, all Redirects are processed before Aliases are processed, and therefore a request that matches a Redirect or RedirectMatch will never have Aliases applied. Second, the Aliases and Redirects are processed in the order they appear in the configuration files, with the first match taking precedence.





但是经过验证,不可行,我就怀疑 Alias和WSGIScriptAlias的优先级了,google了下,的确有人说Alias要比WSGIScriptAlias的优先级要高。在官方文档只找到这句话(from http://code.google.com/p/modwsgi/wiki/ConfigurationGuidelines)



 When listing the directives, list those for more specific URLs first. In practice this shouldn't actually be required as the Alias directive should take precedence over WSGIScriptAlias, but good practice all the same.

 
Do note though that if using Apache 1.3, the Alias directive will only take precedence over WSGIScriptAlias if the mod_wsgi module is loaded prior to the mod_alias module. To ensure this, the LoadModule/AddModule directives are used. For more details see section 'Alias Directives And Apache 1.3' in Installation Issues.


2010年11月12日星期五

GRANT与UPDATE mysql.user

上周服务器的项目都要开始托管,很多自己的项目都需要搬迁。在搬迁的时候,进行测试,由于服务器有变化,所以为了让数据库能够访问,就需要修改user的host。(以下都假设用户名为juven)。


当时的host是locahost,为了让所有机器都能访问,为了图简便,直接:


UPDATE msyql.user SET host = "%" WHERE user = "juven" AND host = "localhost"

(说明,这是在测试机上,如果在正式机不建议这样做,请将%改为真是ip)


修改之后,以为其他服务器可以直接连接,但是测试的时候还是不可以。觉得不能啊,登录验证和权限检查不就是依靠表mysql.user吗?查了资料后,才知道,需要使用


flush privileges

原因(来自 http://dev.mysql.com/doc/refman/5.1/en/adding-users.html):



it is necessary to use FLUSH PRIVILEGES to tell the server to reload the grant tables. Otherwise, the changes go unnoticed until you restart the server. With CREATE USER, FLUSH PRIVILEGES is unnecessary.



当时还遇到一个问题,当我执行GRANT的语句会有以下报错:


mysql> grant select on db.* to juven@'%' identified by 'XXXX';
ERROR 1133 (42000): Can't find any matching row in the user table

 


这个问题的确比较诡异,现在才知道,在"traditional sql mode"下,如果没有指定密码或者密码为空,都会提示这个错误!可是我指定密码了啊!为何呢?也有仁兄遇到跟我一样的问题http://bugs.mysql.com/bug.php?id=7000


不过不管怎们说,msyql的错误提示太misread了!

2010年11月4日星期四

AJAX Cross Domain

今天下午忙了半天,才深刻ajax不能跨域带来的影响。众所周知,浏览器为了安全,禁止了ajax跨域。即使host相同,port不同,也一样禁止访问。为了能够ajax能够跨域访问,也有很多方法:



  • JSONP(JSON with padding)。这种方法利用的就是<script>标签的src属性可以引用跨域的文件,从而达到跨域访问js。

  • 在服务器端,写个cgi通过代理的形式来访问。

  • flash也是可以来帮助实现跨域。

  • 另外,如果根域名相同子域名不同,可在同一页面的不同iframe中通过设置相同的document.domain,也能实现js相互访问,从而跨域


我综合比较了下,觉得JSONP相对而言还是比较简单、完美的,jQuery也实现了比较好的封装:




  • $.getJSON(url, paramaters, callback),如果在调用时,url结尾加上"?callback=?"query string便可以实现JSONP的访问。

  • $.ajax(),在option中指定dataType为jsonp,便可以实现,也可以通过jsonp来指定query string的key,通过jsonpCallback指定调用函数的名称。


不过jsonp也是有缺点,如果调用的url返回的是xml就无能为力。不过如果返回结果类型能自己控制的,jsonp真的是一个很不错的ajax跨域解决方案。



 

2010年10月18日星期一

PyListObject与PyDictObject

昨天又接着阅读了PyListObject和PyDictObject两个内建对象的实现。其实在明白了python对象机制,阅读起着两个内建对象,的确简单了很多。其实我认为PyListObject和PyDictObject还是有很多相似之处的。


首先,PyListObject和PyDictObject的缓存池机制是一样的。都有对应的缓存池num_free_lists或者num_free_dicts,刚刚初始化的时候都是为空的数组(PyListObject * 或者PyDictObject *),却在对象销毁的时候,招兵买马。


其次,这两个对象保存基本数据的结构,都是一个数组。PyListObject的是ob_item(各个元素是PyObject *),PyDictObject是ma_smalltable(各个元素是PyDictEntry)。为什么都是数组,而不是链表?都是为了随机访问的时候访问,试想如果ob_item是个链表,那么l[2]如何迅速查询到,遍历链表是何等的慢。但是采用数组,在PyListObject插入或者删除的时候就麻烦了,大量的内存申请或者搬运工作。PyDictObject的ma_smalltable更不用说了,通过hash值以及探测函数,迅速搜索,那是dict的特性之一。


最后,这两个对象在初始化的时候,申请内存的时候,并不是用多少申请多少,而是在节省内存的前提下,多申请了一些,都是让内存管理更加高效。但是具体细节又有不同。


BTW,PyDictObject搜索的实现,完全是大学课程《数据结构》的那一套,有空复习下。

分布式系统工程化

  前天下午参加了珠三角技术沙龙2010Q4,对陈硕老师(@bnu_chensuo)讲的分布式系统的工程化开发方法很是感兴趣,也觉得陈硕老师讲的很入理,虽然我对分布式仅仅处于了解阶段,对于网络编程更是一窍不通。下面就谈谈通过这堂课,我的收获:

  • 分布式的场景:几十到几百台PC
  • 分布式的状况:还处于技术浪潮的前期,没有公认的成熟解决方案,虽有些开源组件但是可靠性有待考虑
  • 分布式的实现技术:勿在浮沙建高台,要是用成熟的技术,借鉴但不是照搬别人的“成功经验”
  • 分布式的设计原则:Desgin For Failure,高可用的关键在于不停机,恰恰在于可以随时重启。
  • 分布式需要实现监控:程序内置Naming Service,对各种异常报警,让人来处理
  • 分布式进程通信:为了能实现重启,所以只是用操作系统能自动回收的IPC(TCP),不用生命期大于进程的IPC(共享内存,mutex),不使用不能重建的IPC(pipe)
  • 心跳协议的设计:进程c依赖进程S,则进程S向进程C发送心跳(什么是依赖?为何这样?我还没有搞懂);不要另起线程发送心跳包,直接在工作线程发送,避免假心跳
  • 消息格式的设计:要想着以后的升级和兼容,不要用C struct或者bit fileds,考虑Google Protocal Buffer

2010年10月16日星期六

python源码初探

在阅读完《Apanche源代码全景分析》前6章后,对Apache体系结构以及模块开发有了比较深的认识,也开发了一个比较简单的Module(实现pv统计,用memcached保存page count,源代码链接),最近开始了新的阅读《python源码剖析》。在阅读完前3章(python对象初探,PyIntObject,PyStringObject),终于揭开python神秘的面纱,拉近我和python之间的距离。


python中一切皆对象,而对象机制的基石便是PyObject


typedef struct _object {
int ob_refcnt;
struct _typeobject *ob_type;
}PyObject;

ob_refcnt便是引用计数,ob_type便是类型信息。那么其他对象都可以在此基础上扩展,添加自身的信息,比如


typedef struct _object {
int ob_refcnt;
struct _typeobject *ob_type;
long ob_ival;
}PyIntObject;


当时阅读到这里,最疑惑的是python中的类型对象和实例对象都是怎么实现?stuct PyTypeObject的一个实例PyType_Type便是类型对象的基础,所有类型对象的ob_type都是指向PyType_Type,包括PyType_type本身,也就是说下面的表达式是成立的:


PyType_Type->ob_type == &PyType_Type

一切很简单,比如


a = 1

a是一个int类型,a便是struct PyIntObject数据,int便是PyInt_Type(PyInt_type并不是一个struct,也是一个struct PyTypeObject数据)。这便是python中“一切皆对象”的完美实现。


另外,还有几点说明,需要警惕下:



  • 在python的各种对象中,类型对象时超越引用计数的。类型对象“跳出三界外,不在五行中”,永远不会被析构。每个对象中指向类型对象的指针不会被视为类型对象的引用,就是说每个对象建立初始化化,并不会对类型对象的引用计数加一;销毁时并不会减一。估计类型对象的引用计数永远是1了。

  • 在python中,PyIntObject和PyStringObject都引用了内存对象池,因为对于这些用经常频繁用到的数据,会有大量的时间浪费是内存的申请和销毁上(如果不适用内存对象池的话)。PyIntObject不仅使用了小整数对象池(范围从NSMALLNEGINTS到NSAMLLPOSINTS),还有专门的PyIntBlock来用于申请大整数对象。因此对于一个整数,在NSAMLLNEGINTS和NSMALLPOSINTS之间,如果数值相等,其id便是相等,但是其他的id就不同了。PyStringObject也有字符串内存池,对于size为0或者1的字符串,都会经过intern机制缓存起来,其id肯定相同,但是大于1的就不一定了,这个或许跟创建PyStrngObject采用的方法有关吧,有可能是PySting_FromString,PyString_InternInPlace,也有可能是PyString_InternFromString。

  • 关于PyStringObject的一个效率问题。对于N个字符串的连接,如果采用直接相加的话,调用string_concat,就会有N-1的内存申请以及内存搬运工作。但是如果采用"".join([a, b, ..]的话,调用string_join,在开始时会一次性计算好内存,然后申请内存,将字符串拼接,生成PyStringObject即可。


 

2010年7月21日星期三

[转]Denial of Service

转载自 http://www.sq120.com/Get/yingjian/232130979.htm


 


拒绝服务,英文为"Distributed Denial of Service",也就是我们常说的DDoS。那什么又是拒绝服务呢?大家可以这样理解,凡是能导致合法用户不能进行正常的网络服务的行为都算是拒绝服务攻击。拒绝服务攻击的目的非常的明确,就是要阻止合法用户对正常网络资源的访问,从而达到攻击者不可告人的目的。www.sq120.com推荐文章


简单的讲,拒绝服务就是用超出被攻击目标处理能力的海量数据包消耗可用系统、带宽资源,致使网络服务瘫痪的一种攻击手段。在早期,拒绝服务攻击主要是针对处理能力比较弱的单机,如个人PC,或是窄带宽连接的网站,对拥有高带宽连接,高性能设备的网站影响不大。


在1999年底,伴随着DDoS的出现,高端网站高枕无忧的局面不复存在。DDoS实现是借助数百,甚至数千台被植入攻击守护进程的攻击主机同时发起的集团作战行为。因此,分布式拒绝服务也被称之为"洪水攻击"。常见的DDoS攻击手法有UDP Flood、SYN Flood、ICMP Flood、TCP Flood、Proxy Foold等等。


曾经案例


在新千年来临之际,全球知名网站雅虎第一个宣告因为遭受分布式拒绝服务攻击而彻底崩溃。UDP Flooder是一款拒绝服务攻击软件,是基于UDP协议对目标服务器进行洪水攻击。


当年雅虎就是遭到来自50台计算机使用此软件攻击而导致瘫痪。紧接着Amazon.com、CNN、ZDNet、Buy.com、Excite和eBay等其他知名网站几乎在同一时间也遭受暴风骤雨般的DDoS攻击,带来了巨大的经济损失。而一些政治团体或非法组织,则通过DDoS攻击对政府门户网站或其它官方网站进行打击,造成巨大的政治影响。


 


简单防御方法


DDoS是一种特殊形式的拒绝服务攻击,所以对付它是一个系统工程,想仅仅依靠某种系统或产品防范DDoS是不现实的。可以肯定的是,要想完全杜绝DDoS目前是不可能的,但是通过适当的措施抵御90%的DDoS攻击是可以做到的。对于此类隐蔽性极好的DDoS攻击的防范,更重要的是用户要加强安全防范意识,提高网络系统的安全性。


(一)通过对系统进行优化,高水平的技术专家,能够根据攻击迅速确定攻击类型,并对各种操作系统核心的配置了如指掌,同时能够根据己方所提供的服务类型和侧重点选择防护和退让策略。


(二)建议网络管理者,在所管辖网络的出入口,配置源路由控制策略,限制非本网的源地址对外访问,这样就能保证至少本网内,不会有发动DoS/DDoS攻击的机器,如果这样做的网络增多了,DoS/DDoS攻击自然减少甚至杜绝。需要得到攻击路径中上级的网络设备服务商支持。


(三)采取退让策略,利用DNS轮循,或者通过负载均衡、Cluster等技术增加响应主机数量,增加系统资源,从而提升抗打击能力。


(四)安装抗DoS能力的防火墙,能够防护一些低规模的拒绝服务攻击,配置和操作相当简单。防火墙作为通用网络安全产品,在防DoS方面不可能达到专业产品的性能和效率,对大规模的DoS攻击是无能为力的,甚至会成为攻击目标。


(五)建立网络安全事件应急响应小组,为所管辖的网络提供应急服务,一旦出现DDoS攻击或者其他的攻击,可以及时启动应急流程,借助有实力安全厂商、CNCERT(国家计算机网络应急技术处理协调中心)的力量,追查攻击的源头。必要时,申请公安部门的协助,诉诸于法律。

2010年7月5日星期一

memcached与iptables

在客户端去连接memcached的服务端,很简单,一个tcp或者udp连接,不需要user和password。貌似很简单,很容易维护,没有了管理帐户的麻烦,但是很不安全。因为如果不加限制,任何一台计算机通过telnet ip port都可以访问memcached服务器,访问和修改数据。这是,就需要iptables对访问加以限制。


什么是iptables(官网)?iptables is the userspace command line program used to configure the Linux 2.4.x and 2.6.x IPv4 packet filtering ruleset. It is targeted towards system administrators. 我认为iptables就是计算机网络数据进出的一个负责人或者说一个门卫,对任何一个数据包,根据数据包的执行阶段(chain)和相关操作(table),执行相关的策略(target),当然需要对数据包进行一定的筛选,可以根据协议(protocal)、端口(port)、地址(ip)、数据包(state)状态等等去判断。


至于这些筛选规则和相关策略都可以设置,但是首先你必须是root。可以通过iptables或者iptables-restore去设置,也可以通过iptables-save或者iptables -L去查看当前的设置。(PS: 如果当前没有设置过规则,使用iptables-save是会报错的,iptables-save v1.4.2: Unable to open /proc/net/ip_tables_names: No such file or directory)


通过这次也终于搞明白了unix中经常提到的bind是何意?一台电脑是可以有多个ip的,比如123.0.0.1和x.x.x.x,在启动memcached的时候,可以bind到其中一个ip。客户端访问时,就必须访问服务器端绑定的ip,不然就会访问出错。


Resoures & References:



  1. netfiler

  2. iptables-save与iptables-restore

  3. iptables指南


 

2010年7月4日星期日

python-memcached连接维护

纵所周知,memcahced client管理了多个与服务器的连接,实现分布式缓存系统。那我们需要担心的是,在执行相关操作时,如果连接断开会出现什么结果?


阅读了python-memcached源码,它是这样处理的:当缓存或者读取数据的时候,连接断开,出现socket.error,client daemon就会标记该连接为dead,在30秒内,不会重新连接。需要注意的是,当出现socket error,除了相关处理外,client daemon并不会抛出错误,仅仅将该次error输出到标准错误,执行相关命名的返回结果也是None或者0(有就是执行失败)。



对于此种处理,我认为是合理的。因为毕竟memcached是缓存,而不是持久化存储,它并不保证你的每次操作(或者说命令)在没有报错的情况下,一定是成功的。

安全有效获取客户端IP

在web开发中,经常需要获取用户的ip地址,以便记录或者添加特定的限制。但是实际生产环境中,web server使用了代理服务器,所以environ中remote_addr的ip地址通常是离web server最近一层代理服务器的ip地址,这样得到的ip就没有太大的意义。但是x-forwarded-for的出现就是想解决这种问题,但是这种方式如何使用以及可靠性如何,都是我们应该考虑的范畴。下面就具体讨论下关于x-forwarded-for和remote_addr的区别。


1. 含义不同


The REMOTE_ADDR variable MUST be set to the network address of the client sending the request to the server.(参考RFC3875 4.1.8 REMOTE_ADDR)


The X-Forwarded-For (XFF) HTTP header is a de facto standard for identifying the originating IP address of a client connecting to a web server through an HTTP proxy or load balancer. This is a non-RFC-standard request header which was introduced by the Squid caching proxy server's developers.(参考)

2. 格式不同


remote_addr的值就是单个ip地址,而x-forwarded-for则是多个ip地址的集合:


The general format of the header is:

X-Forwarded-For: client1, proxy1, proxy2

where the value is a comma+space separated list of IP addresses, the left-most being the farthest downstream client, and each successive proxy that passed the request adding the IP address where it received the request from. In this example, the request passed proxy1, proxy2 and proxy3 (proxy3 appears as remote address of the request).(参考)

3. 来源不同


remote_addr是http server与客户端通过三次握手建立的tcp连接后,根据socket中的ip赋值给remote_addr。而x-forwarded-for则是http server解析http报文头部获取的。


 


总结:通过来源不同,可以看出,environ中的remote_addr是很不容易伪造的,但是x-forwarded-for则相反,极易伪造,伪造一个http 报文头部,轻而易举。所以我们不能简单通过获取x-forwarded-for的ip地址列中的第一项作为客户端ip地址。但是由于代理服务器是我们部署的,这个是可信的,所以x-forwarded-for的内容,我们不能“全信”,也不能“全不信”。至少经过我们的代理服务器,在x-forwarded-for上添加的ip是可信的,所以我们可以通过实际生产环境部署的代理服务器情况,也从x-forwarded-for中选择合适的ip。比如我们只有一层代理服务器,那么x-forwarded-for的最后一个ip就是客户端ip,并且是“可信”的,因为这个ip是代理服务器根据客户端ip加上去的。

memcached学习

许多web应用都是将数据保存在RDBMS中,web apps从RDMS中读取数据并经过渲染后返回给客户端,在浏览器中显示。但是随着数据访问量的增大,访问的集中,RDBMS的负担加重,响应速度变慢、显示延迟等问题都会出现。


但是如果我们采取以下做法:每次从RDBMS读取的数据存放在内存中,并且更新内容时,不仅更改RDBMS并且同时更改内存中的数据。那么等待用户下次读取数据时,先从内存中获取,如果存在直接返回,如果没有再从RDBMS获取返回。


采取这种做法,响应速度就会得到很大改善。因为磁盘I/O的速度跟内存的读写速度不是一个等级的。


memcached是一个实现以上做法的、高性能、分布式的内存对象缓存系统,目的主要是通过减轻数据库负载来使动态web应用程序提速,任意的数据都是采用key-value形式存储的。


1. memcached特点



1.1 协议简单


采用简单的基于文本行的协议,并没有采用二进制协议或者复杂的XML格式。(目前memcahed开发者们正在策划和实现二进制协议)

1.2 基于libevent的事件处理


使用libevent取代了网络服务器所使用的循环检查架构,从而可以省去对网络的处理,达到不错的性能。(参考 对于此点优势,了解还不是特别深入)

1.3 内置内存存储方式


为了提高性能,memcached都将数据保存在内存中。因此如果重启memcahed、重启操作系统或者断电等操作都会造成数据的丢失。所以说memcached不能永久存储数据。

1.4 memcached不互相通信的分布式


memcached虽然是分布式缓存服务器,但是服务端并没有分布式功能,分布式读和写都由客户端采用特定的算法去实现。另外,服务器端并不会通信以达到共享信息。我的看法是:memcached是个缓存系统,并不需要像NoSQL达到“最终一致性”,所以每个服务器保存的内容都是不一样的,都只是部分数据。另外如果一个服务器出现异常,也不影响正常程序,因为我们丢失的仅仅是“缓存”数据。




2 实现memcached的相关理论和方式




2.1 内存存储方式


在使用内存存储数据的时候,就涉及到如何分配内存。如果采用简单的malloc和free,就会导致内存碎片,反而会加重操作系统管理内存的负担。而memcached采用的内存分配机制Slab Allocation,就是为了解决这种问题。具体的工作原理是:按照预先设定的大小,将内存分割成特定长度的块,并且把长度相同的块分成组;这些内存并不会释放,而是重复利用。memcached根据客户端发来数据的大小去选择合适的块去保存,然后将数据缓存其中。


Slab Allocation解决了内存碎片的问题,但是却带来了另外一个问题:由于分配块的大小是固定大小,而客户端发来缓存的数据大小却是随机的,就会造成内存浪费的问题,比如一个100k的数据保存一个128k大小的块中,造成了28k内存空间的浪费。对于这个问题还没有良好的解决方案。但是如果预先知道客户端发送的数据的公用大小,或者仅缓存大小相同的情况下,只要使用适合数据大小的组的列表,就可以减小浪费。

2.2 删除缓存数据方式


内存空间不是无限的,当然有个上限或者一个指定值,所以删除缓存数据是必须的。memcached采用的方式是:LRU(Least Recently Used),也就是说:当memcached内存容量达到指定值后,需要空间来缓存新的数据时,去删除那些近期最少使用的数据,用它的内存空间来保存新的缓存数据。

2.3 客户端分布式算法


memcached分服务端和客户端,服务端仅仅负责存储和读取。至于分布式存储,是客户端需要做的事情。分布式存储,需要保证实现一个特性:良好的伸缩性和扩展性,我对此特性的理解就是:在memcached服务器中,如果新添一台服务器,简简单单通过配置就可以,而不影响其他服务器的正常运行,缓存数据的键也会均匀分散到各个服务器;如果一台memcached服务器因为故障无法连接,也不会影响其他缓存,系统依然能继续运行。


下面就简单介绍下实现该特性比较完善的算法:Consistent Hashing,nosql中需要实现分布的数据库也多采用这种算法。其基本原理是:首先计算各个服务器的哈希值,并将其配置到0 ~ 2^32 的圆上(不是线段哦),然后采用同样的方法求出数据的键的哈希值,映射到圆上,沿着映射的位置顺时针查找,将数据保存在最近的一个服务器上。如果超过2^32任然找不到,就保存到第一台服务器上。读取数据时,也是同样的道理,计算读取数据的键的哈希值,根据哈希值找到对应服务器,读取值。


采取此种算法的好处就是,不管添加服务器或者减少服务器或者服务器故障,都不会影响缓存数据,不影响系统的正常运行。并且,如果我们让一个服务器(物理节点)对应n个虚拟节点,随机分布在0 ~ 2^32的圆上,这样就可以解决分布数据不均匀的问题。


 

2010年7月3日星期六

又遇乱码

身为一个Chinese Web Coder,最烦也最不可避免的就是乱码。在解决乱码之前,必须搞清楚unicode和编码方式的区别,python中执行环境的默认编码。


unicode的诞生就是要显示世界上不同国家、不同地区的文字。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。在我看来,unicode就是一个简单的映射表,key是二进制编码,value就是各个语言的不同字符。而如何高效保存和读取这些二进制编码,就是编码方式所要解决的方法,不同编码方式有着不同的保存读取方式。


在python命令行解释器中:


>>> s = "中国"
>>> s
'\xd6\xd0\xb9\xfa'

我的理解就是,"中"和"国"两个字的unicode码按照python默认的编码方式保存起来了。至于默认编码方式是多少,是sys.getdefaultencoding()或者sys.getfilesystemencoding()。至于这两个的区别:



  • sys.getdefaultencoding(). Return the name of the current default string encoding used by the Unicode implementation. New in version 2.0.

  • sys.getfilesystemencoding(). Return the name of the encoding used to convert Unicode filenames into system file names, or None if the system default encoding is used.


到底用的哪个,我到现在还没有搞清。按照解释,应该是sys.getdefaultencoding(),但是实际测试却是sys.getfilesystemencoding()。


但是在文件中,却有另外一番理论。在文件中,我们需要关心两个编码:文件自身保存的编码以及我们在开始设置的coding。


#! -*- coding: utf-8 -*-

With that declaration, all characters in the source file will be treated as having the encoding encoding, and it will be possible to directly write Unicode string literals in the selected encoding. The list of possible encodings can be found in the Python Library Reference, in the section on codecs.


文件中如果字符串中有汉字,其对应值是以文件保存编码方式编码,因此如果开始设置的coding跟文件保存编码不一样,用设置的coding去解码时,自然会报错。因此让两种编码保证一致,是很有必要的。


Resoucres & References



  1. Using the Python Interpreter

  2. python -> docs-pdf -> library.pdf


 

神秘的__init__.py

Python中的module和package的作用不言而喻:Each module has its own private symbol table, which is used as the global symbol table by all functions defined in the module. Thus, the author of a module can use global variables in the module without worrying about accidental clashes with a user’s global variables.而Packages are a way of structuring Python’s module namespace by using “dotted module names”.


让package称为module那样的访问,__init__ .py 。一个文件夹A如果存在文件__init__.py(哪怕这个文件夹是空的),就表明A不是一个简简单单的文件夹,而是一个包。


另外__init__.py控制着包的导入行为。比如现在存在这样一个pacakge:


Package1/ 


    __init__.py


    Module1.py


    Module2.py


    Package2/ __init__.py


       Module1.py


       Module2.py


如果执行语句:


import Package1
Package1.Module1

肯定会报错


AttributeError: 'module' object has no attribute 'Module1'


为何?因为Module1只是Package1的一个submodule,并不是attribute,通过dir(package)就可以看出。如果想执行正确上面的语句,就必须在__init__.py中执行:


import Module1

另外,语句


from Package1 import *

到底import了那些,完全依靠__init__.py中的__all__属性。


另外,关于from和import需要注意的是:



Note that when using from package import item, the item can be either a submodule (or subpackage) of the package, or some other name defined in the package, like a function, class or variable. The import statement first tests whether the item is defined in the package; if not, it assumes it is a module and attempts to load it. If it fails to find it, an ImportError exception is raised.


Contrarily, when using syntax like import item.subitem.subsubitem, each item except for the last must be a package; the last item can be a module or a package but can’t be a class or function or variable defined in the previous item.



Resouces & References



  1. Modules

  2. Python模块包中__init__.py文件的作用


 


 

2010年6月25日星期五

追忆年华

四年前,德国世界杯如火如荼的进行着。当时我跟茹茹的关系,可以用茹茹的一句经典的话来概括:当时谁认识你啊!月上柳   梢头,人约黄昏后,我们游走于三教和荟园开水房之间的大道,探讨着“我喜欢你,你为什么不喜欢我?你怎么样才能喜欢我 ,做我女朋友”的神圣话题。那时,世界杯的激情,似乎跟我们没有关系。


四年后,南非世界杯也同样如火如荼的举行着。现在,我和茹茹早已相知相爱,我们已经相互理解,我们的性格经过四年的磨合,对于大事上的看法也慢慢趋于一致。可以说,我们现在的人生观、价值观、世界观已经归拢。    


四年,人生最重要的四年,已经过去。这四年,包含着多少美好记忆,承载着许多酸甜苦辣,孕育了我们的爱情,催生了我的日渐成熟。这四年来,我是幸运的,我是成功的。幸运的遇到了人生中的另一半,成功的俘获了她的芳心。


我们拥有令旁人羡慕,自己永生难忘的甜蜜时光:



  • 月光下散步,大学生活动中心前的竹林、南湖边有我们的身影

  • 春天,学校油菜花开了,那黄灿灿的油菜花,加上我们是多么的美丽

  • 南湖边,桃花园,散步感受春意

  • 夏天热了,在草地买个西瓜,跟茹茹品尝

  • 在体育馆前的林荫小道,学习、聊天、休息

  • 篮球场上、一运,当茹茹想保持身材的时候,我们也会在这里滴下汗水

  • 荟一楼下的草地,有多少等待在那里

  • 游泳馆游泳,虽然只有一次,虽然我不会,但是有茹茹的陪伴,一次足以记忆一生

  • 冬天满天雪花,打雪仗自然少不了我们

  • 茹茹的21岁生日,野芷湖旁,我们将21根蜡烛组成心形

  • 我们经常出去吃小吃,常去校门口、司门口、中南财大,酸辣粉、周黑鸭、肉夹馍、馄饨……

  • 动物园、磨山、江滩,有着心爱的人,游玩的感觉无法形容

  • 我头痛时,茹茹越来越唠叨,越来越像我妈时,我知道那是关心体贴

  • 工作了,一到周末,茹茹屁颠屁颠过来找我

  • 不管在学校,还是在广州,虽然我不喜欢逛街,虽然我当时都很不耐烦,但是现在回忆,依然很美好

  • ……


当然我们也经常吵架,我们也有不合,茹茹也经常对我的举动不满意,虽然我们争吵很多,虽然我们有时候争取很凶,但是我们通过争吵更加了解对方。我依然记得在雪地里,茹茹咬着牙,使出浑身力气,将她的粉拳细腿砸向我,但我更记得我们第二天和好时的甜蜜。


虽然我现在不富裕,但我坚信:我能够给茹茹快乐幸福的生活。就像电视剧《媳妇的美好时代》里,余味反驳李若秋一样:“我跟豆豆的生活乐趣,是我们通过自己的努力,共同买辆车,我们这个月在一块存钱,能够买一个车轮子,下个月在存钱就买了方向盘,再下个月我们就买了后备箱,再下个月我们就买了一辆车了,也可能车的钱,不贵,很便宜,只是一辆QQ,这个车就全当练手了,练会了之后,随着以后越来越好,我们会买更好的车,这就是我们两人的快乐。”我想这也是我应该给茹茹的快乐,并且是我能够给茹茹的幸福。


2010年6月23日星期三

排序

在平时项目中,排序很常见,一般情况下,采取的常见方法有:



  • 在数据库查询时order by xx。

  • 采用list的方法sort(), 如list.sort()


但是有写情况,上面两种情况不是那么轻易可以搞定的。内建函数sorted则相当强大。


sorted(iterable, cmp=None, key=None, reverse=False):



  • iterable是一个可迭代的对象,就是我们需要排序的对象

  • cmp只是一个函数,定义了两个元素之间的比较规则。如果这个参数为None,则按照程序默认的排序规则比较iterable的元素对象。如果不为None,则根据cmp来比较。

  • key也是一个函数,但是这个函数和cmp不同,key函数是用来提取iterable对象元素的索引,用这个索引来排序。如果key为None,则直接用iterable对象元素来排序。

  • reverse则是指定排序的顺序,如果为False,则从小到大,为True,则从大到小,默认为False。


 


比如现在有这样一个list:


user = [
{"name": "Juven", "age": 23},
{"name": "fisher", "age": 22}
]

想根据age来降序排序,怎样做呢?一句话搞定:


sorted(user, key=operation.itemgetter("age"), reverse=True)

或许你对itemgetter不熟,看看这些:


itemgetter(item, [args...])


Return a callable object that fetches item from its operand using the operand’s __getitem__() method.


If multiple items are specified, returns a tuple of lookup values.

2010年6月20日星期日

Git初学习

今天细细琢磨了一个git。git很火,慢慢会流行起来。git有什么特点,能够在众多版本管理系统中杀出一条血路呢?这是git官网对于git的介绍:What is git?


Git is a free & open source, distributed version control system designed to handle everything from small to very large projects with speed and efficiency.

Every Git clone is a full-fledged repository with complete history and full revision tracking capabilities, not dependent on network access or a central server. Branching and merging are fast and easy to do.

Git is used for version control of files, much like tools such as Mercurial, Bazaar, Subversion, CVS, Perforce, and Visual SourceSafe.


所以先可以得出git有两个特点:





  • 快,据说比小李飞刀还快。我想git快,其中一个原因还是和git的分布式分不开,因为很多情况下不需要在网络环境中进行,本地就OK。一部分的原因是因为git是建立来用在linux核心上的,这表示git从一开始就必须有效率的处理非常大的repository。此外git是采用c写的,减少了使用其他高级语言在执行器的开销。

  • 分布式。git是分布式SCM,每个开发者本地都有一套git库,每个人都维护自己的版本(或者合并其他人的版本)。这样带来的好处就是:离线情况下也能做好版本控制,不用时刻连网,随时随地coding。另外,分布式的SCM更加健壮,不像SVN,服务器一挂掉,后果不堪设想。


Git当然还有很多优点:





  • 强大的branch和merge的能力

  • 操作更加简洁、方便


bzr最慢,cvs太老,svn主流,git新潮。今天看文档,觉得学习git的确比其他版本控制,更难些。但是这个怎能阻止我的步伐?



参考:


http://hilbert.spaces.live.com/?_c11_BlogPart_BlogPart=blogview&_c=BlogPart&partqs=cat%3Dgit

http://wiki.guoshuang.com/Git_svn_%E6%AF%94%E8%BE%83

http://blog.csdn.net/colorant/archive/2008/10/31/3193820.aspx

http://www.robinlu.com/blog/archives/191

http://markmcb.com/2008/10/18/3-reasons-to-switch-to-git-from-subversion/

2010年6月3日星期四

The Apache Paltform and Architecture

上个星期算是将APUE粗略看了一遍,写了个简易的http server(算是弥补下实习时候的遗憾),写完后发现自己对文件描述符的理解还不是特别透彻,特别是多进程共享的时候(很早以前觉得APUE必须重新精读一遍)。很想知道Apache是如何实现多进程并发,无奈源代码苦涩难懂,不知从而看起(应了linus的那句话Read The Fuck Soucing Code)。进而转向看文档The Apache Modules Book。看完第二章The Apache Paltform and Architecture,我彻底拜服,拜服于apache的架构。下面就谈谈对apache architecture的了解。


Apache HTTP server由APR、Apache HTTPD、mpm、modules、The Third Libraries几部分构成。《Apache 源代码全景分析》中分的更细,将Apache划分为四个大的层次:


  1. 可移植性运行库层(APR)

  2. Apache核心功能层(Apache CORE)

  3. Apache可选功能层(Apache Optional Module)

  4. Apache第三方支持库


《Apache源代码全景分析》为何没有列出mpm?我想是把Apache HTTPD、mpm和一些必要的modules归结成Apache CORE。Apache为何这样设计,下面就详细说说各个层次的用处和这样设计的目的:



  • APR。由于各个操作系统提供的底层API是不同的,甚至存在着很大的差异。如果没有APR,Apache设计者就必须关心各个操作系统API细节之间的不同。采用APR就是将不同操作系统的底层细节封装起来形成操作系统API的适配并将其隐藏起来。这样Apache CORE建立在APR上,能够满足不同操作系统的要求。(说明:APR并不是只为Apache使用,任何想跨平台的软件工程,都可以拿来用)

  • Apache HTTPD的主要功能是启动和停止Apache,处理配置文件,接受和处理HTTP连接,读取HTTP请求并对该请求进行处理、处理HTTP协议等。



Apache HTTPD和modules之间的关系是调用和被调用的关系,所有的模块都直接和核心进行交互。调用是通过Apache HOOK实现的,这样处理的好处是:Apache HTTPD不用关心模块的多少,模块到底如何实现,直接调用即可,用户可以随时加载新的模块或者卸载模块,不仅降低耦合,并且扩展性极好。

2010年6月1日星期二

The zen of python

在python的命令行的交互解释器上,输入import this便可以得到:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!


下面是中文的解释,觉得很赞:


优美胜于丑陋(Python 以编写优美的代码为目标)
明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似)
简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现)
复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁)
扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套)
间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题)
可读性很重要(优美的代码是可读的)
即便假借特例的实用性之名,也不可违背这些规则(这些规则至高无上) 
不要包容所有错误,除非你确定需要这样做(精准地捕获异常,不写 except:pass 风格的代码) 
当存在多种可能,不要尝试去猜测
而是尽量找一种,最好是唯一一种明显的解决方案(如果不确定,就用穷举法)
虽然这并不容易,因为你不是Python之父(这里的 Dutch 是指 Guido )
做也许好过不做,但不假思索就动手还不如不做(动手之前要细思量) 
如果你无法向人描述你的方案,那肯定不是一个好方案;反之亦然(方案测评标准)
命名空间是一种绝妙的理念,我们应当多加利用(倡导与号召)

2010年5月31日星期一

爱上Google

从三年前开始编程时,我就彻底摒弃了百度,开始使用Google。不过由于自己不是科班出身,旁边没有同学或者老师交流,对于Google的认识也就是搜索,仅限于搜索。不过仅仅是搜索,我对Google就爱不释手,因为每次用她搜索都可以得到想要的答复,不像百度,只是洗钱的工具而已。


从一年前开始工作来,随着自己知识面的拓展和与同事的交流,我对于Google的认识逐渐加深。Google Chrome, Google Code, GMail, GTalk, Google App Engine, Google Calendar, 一个个Google应用让我再也离不开Google。今年3月份,也购买G2,使用Google的Android操作系统。


今天下午,在使用Google Calendar,绑定了自己的手机号码,用手机短信来提醒自己的task、plan。更得知,只要加了好友(详细),便可以将2010年世界杯的所有赛程信息加入自己的Google Calendar,惊呼:爽极了!现在使用GTD管理软件都是Google Calendar和Doit.im结合使用,为了使用Doit.im,就是让我不打开Google Chrome的情况下,也能得到提醒。不过我相信再过一段时间,就要甩掉Doit.im了,因为Google Chrome完全开放扩展桌面提醒API


为何钟爱Google,在于:



  • Google的Don't be evil的信仰理念

  • Google的Do anything in web brower的产品思想

  • Google的简洁、朴实无华的界面

  • Google的免费、好用、舒服的服务

  • Google对于将科技变为实用应用的追求

  • Google更带领了我们对于自由的追求

  • ……


2010年5月28日星期五

从招聘看哲学

今天在CPyUG看到一则招聘,吸引我的是Elias Soong发的共事标准,很是喜欢,可以说引起我的共鸣,我觉得以后工作中的处事哲学也应该如此:



  • 你觉得做人、做事应该力求靠谱。

  • 你认为好的自动化会大幅提高工作效率,但也认为有时人工的适当参与可以形成 更为清晰有效的解决方案。

  • 你在写程序的时候总是很在意结构清晰和可读性,会想到三周或三个月以后再维 护这段程序的时候是什么状况,甚至有时这样的考虑超过了对极致性能的追求。

  • 在同等可用性条件下,你更愿意用开源软件/开源组件完成工作,并且曾经直接 与软件作者联系,提供过建议或者代码补丁。

  • 在使用开源软件遇到问题的时候,你不会立即放弃,除查看文档之外,你还会试 着调试或者查看源代码,看能不能自己动手解决,甚至在没有合适解决方案的情况 下还会自己动手编写软件并回馈给开源社区。

  • 你喜欢Python语言的简洁、优美和丰富的类库,但同时认为用其他编程语言也有办法做到Pythonic 。

  • 相比自成体系的Django,你更喜欢TurboGears 2或者Pylons,因为这样可以根据需要选择组件,组成最适合项目状况的编程框架,同时又能在需要时增补替换,与整个Python社区共享公共组件的进步。

  • 如果用Linux的话,你喜欢Debian Linux这样的发行版,因为它既能以简单 直观的方式即装即用,也提供一些好用的自动化机制帮助你管理自己的一些定制修 改,并且这种增补机制和发行版自带的软件包管理方式是完全一致的。

  • 你对新技术总有兴趣,比如尽管在工作中用的是 Subversion 而且觉得公司在很 长一段时间内不太可能去替换它,但还是忍不住在业余时间研究一下Git/Mercurial/Bazaar,并且会在自己的业余项目中试着使用,有机会时也会通过Seminar等方式推荐给同事。


作为一个入行不久的*nuxer和pythoer,觉得这样的职业习惯很利于自己的发展,虽然自己有些方面做的比较欠缺。

 

2010年5月27日星期四

reStructured Text使用感悟

上次参加珠江三角洲技术沙龙,社区大妈屡次提到:所见即所得(What you see is what you get),所想即所得(What you think is what you get)。字面意思好理解,但是真正理解还是今天使用reStructure Text。


平时vim书写html,的确不怎么爽,需要记住那么多的标签,手动敲那么多代码;另外对于我这个对css不精的人来说,设置样式简直是噩梦。如果采用DreamWeaver,的确省了不少力气。因为DW的确是个不错的WYSIWYG(跟word算是一类吧)。


不过所见即所得偏重的外观设计,并不是代码。比如我在强调某事的时候,是采用粗体还是红色字体呢,或者其他方法?注意:我仅仅想的是强调,我的目的是强调,粗体或者红色字体仅仅是方法。那么我在编辑代码的时候,只需要输出我的目的,实现方法可以根据不同的场合去选择不同的实现。这便是What you think is what you get。而reStructed Text便是实现WYTIWYT的工具。比如想调用,通过"**强调内容**"来表达自己的目的,具体如何显示强调,转换的时候去设置。


在这篇文章中,我就不再累赘reStructured Text的细节和元素,我所重视的它的一种思想。在使用中,我的确感觉中这种“所见即所得”的思想,的确给我带来了很大的方便。


在具体使用中,使用vim编辑器,使用vst插件。

TCP连接被重置的探究

身在天朝,在访问互联网的时候,总有各种原因被重置,现在就了解了解,探究一番。


在探究之前,先需知道TCP连接的三次握手。


  1. 用户-[SYN, x] --> 服务器{第一次握手}

  2. 用户 <-- [SYN/ACK, y:x+1]-服务器{第二次握手}

  3. 用户-[ACK: y+1] --> 服务器{第三次握手}


需要三次握手的原因:保证客户端和服务器都可以发送和接受信息。



那么我朝的GFW都干了些什么呢?GFW负责监控全国的TCP连接,当发现敏感词时就会介入,将服务器发回的SYN/ACK包改成SYN/ACK, Y:0,这代表TCP连接被重置,用户便主动放弃了连接,提示连接失败。可以看出,其实GFW就是在欺骗用户,让用户误认为服务器拒绝连接,而主动放弃继 续与服务器连接。


  1. 用户-[SYN, x] --> GFW-[SYN, x] --> 服务器{第一次握手}

  2. 用户 <-- [SYN/ACK, y:0]-GFW <-- [SYN/ACK, y:x+1]-服务器{第二次握手}

  3. 用户放弃连接



但是GFW虽然工作在TCP协议层上,但是也留下一个致命的漏洞:GFW只在连接发起时检测第一个TCP连接,这样做就可大大提高GFW的工作性能。下面是骗过GFW的方案:




  1. 用户-[SYN, x] --> GFW-[SYN, x] --> 服务器{第一次握手}

  2. 用户 <-- [SYN/ACK, y:0]-GFW <-- [SYN/ACK, y:x+1]-服务器{第二次握手}

  3. 用户-[FIN, y] --> GFW[认为用户连接结束]-[FIN, y] --> 服务器{忽略}

  4. 用户-[ACK, y] --> GFW[停止监测用户]-[ACK, y] --> 服务器{判断是坏包}

  5. 用户{忽略} <-- [RST, 0]-GFW[认为服务器重置连接] <-- [RST, 0]服务器{重置连接}

  6. 用户-[ACK, y+1] --> GFW[停止监测服务器]-[ACK, y+1] --> 服务器{第三次握手}



这样,一个三次握手的过程被我们改成了六次握手,成功骗过了GFW。


最后想说的是,不得不佩服西厢计划的黑客们,他们对于技术的探索,对于技术的热衷,更加体现出我们对于自由的渴望!最后在说句:Fuck GFW!






参考文献:


  1. 深入理解GFW,http://gfwrev.blogspot.com/2010/03/gfw.html (墙外)

  2.  “西厢计划”原理小解,http://blog.youxu.info/2010/03/14/west-chamber/

  3. 简述TCP三次握手过程,并说明为什么要3次握手,http://hi.baidu.com/it_hawk/blog/item/d053ab346830783e5ab5f54e.html

  4. TCP连接建立和关闭http://hi.baidu.com/layer7/blog/item/cb1243166bd6f11c962b430f.html

  5. 对TCP连接被重置解决方案的探究――跨过GFW通向自由网络的可行途径http://feedproxy.google.com/~r/chinagfwblog/~3/2tFHT1rfons/tcpgfw.html


2010年5月26日星期三

c语言中static和extern的作用

最近学习《unix环境编程》时,对于c语言不熟的我,经常有这样的疑惑:static和extern到底有什么作用,和心中的想法一致吗?Google之。


static的三个作用(来自http://club.it.sohu.com/r-program-76259-0-0-0.html):



  • 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。

  • 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。

  • 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。


我认为这样介绍static的作用比较肤浅,这样介绍更深入(来自http://developer.51cto.com/art/200812/103804.htm):



  • 隐藏

  • 保持变量内容的持久

  • 默认初始化为0


对于static可以保持变量内容的持久和默认初始化为0,都是由于static的变量都是存储在静态存储区中的缘故吧!



对于extern的理解和作用, 我想这样一句话就可以解释吧:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。


 

2010年4月24日星期六

adb初次使用

今天才接触到adb。说来惭愧,买G2这么久了,以前都是使用91软件助手的,现在在windows下突然打不开了,卸载重装都不行,我估计是.NET Framework的缘故吧!windows就是这么麻烦!


闲话少说,Andorid adb(Android Debug Bridge)是Android提供的一个通用调试工具,通过它,我们就可以随心所欲管理Gphone了。


SDK下载地址(adb是Android SDK中的并不可少一个工具):SDK下载主页 For Linux


第一次使用的时候,当然有很多问题:



  • error: device not found (解决方法:请点击)

  • error: insufficient permissions for device(解决方法:sudo adb kill-server / sudo adb start-server >>more)


当然如果你想修改/etc/hosts, 必须得有写权限,可以执行以下代码:


su
mount -o remount,rw -t yaffs2 /dev/block/mtdblock3 /system
cat /sdcard/hosts > /etc/hosts
mount -o remount,ro -t yaffs2 /dev/block/mtdblock3 /system

2010年4月18日星期日

开博了

终于开博了,首先还是得感谢郭嘉(没有屏蔽掉所有google服务),其次是google(Google App Engine),xuming提供的micologSteven Wang主题


至于为什么搭建个人blog,原因如下:




  • 将思想转化为文字。今天(2010年4月18日)去大学城华南理工大学参加珠三角技术沙龙,Zoom Quiet所说:将立体网状的思想转化为线性一维的文字或者语言都是很难的,是沟通能力的一种。所以我开通个人blog,将自己的想法、思想、技术采用文字表达在blog上,来增强自己的表达沟通能力,也希望跟各位技术牛人交流。

  • Personal Knowledge Manage(个人知识管理)。blog记录自己的学习、技术总结,来对自己技术和知识进行管理。对于我来说,新学的东西如果不常用,遗忘是很快的,但是可以通过blog,在最短时间内捡回来。


Just blogging, not writting.记录随意,但是能清楚表达自己的意思,这是自己写blog的原则和目标吧!