分类 Python 下的文章

Twisted application中TCPClient的使用

一般,在Twisted中使用Application的方式启动程序,是这样做:



折叠复制代码




  1. pop_service = internet.TCPServer(...,...)

  2. popService = service.MultiService()

  3. pop_service.setServiceParent(popService)

  4. application= service.Application('popqueueservice')

  5. popService.setServiceParent(application)




通过setServiceParent来把某一个服务运行在application中


如果程序中我们需要生成很多TCPClient对象,去做其它的很多事情,那么我们不能这么做:



折叠复制代码




  1. op_service = internet.TCPClient(popserver,popport,f)

  2. pop_service.setServiceParent(popService)











这样做是有很大问题的,因为每一个client的请求都被加入到了application中去运行

即使client执行完毕,也不会去释放自己对应的Factory实例和Protocol实例

长时间会造成内存中有很多这样的实例对象,内存不断增加,最后内存溢出...




这样的内存泄露,不太好定位,我之前检查了程序的各个地方,把能释放的资源都给释放了

唯独没有考虑到这个地方,最后使用了meliae工具,才定位到这个地方

然后这么修改的:



折叠复制代码




  1. pop_service.startService()

  2. f.deferred.addCallback(handleEnd,id,pop_service)

  3. 。。。。。。

  4. #在每个client执行完毕后的deffer中调用了

  5. s.stopService()




这样修改后,观察了几天,内存一直都比较稳定

利用meliae来监控python进程的内存占用情况

meliae是一个python进程内存占用监控、分析工具,它的安装需要依赖pyrex


meliae会把某个时刻的内存给dump到一个文件中,然后再对该文件进行分析


当我们的某个python程序占用内存很大,可能有内存泄露发生时,可以使用该工具来进行检测分析


安装和使用用都比较简单,在需要dump内存的地方,写上以下代码即可:



折叠复制代码




  1. from meliae import scanner

  2. scanner.dump_all_objects('/opt/log/dump.txt')




这样,我们就可以把当前的内存Objects都导出到了dump.txt,然后再进行分析



折叠复制代码




  1. from meliae import loader

  2. #加载dump文件

  3. om=loader.load('/opt/log/dump.txt')

  4. #计算各Objects的引用关系

  5. om.compute_parents()

  6. #去掉各对象Instance的_dict_属性

  7. om.collapse_instance_dicts()

  8. #分析内存占用情况

  9. om.summarize()




我的某个python程序分析结果如下:



折叠复制代码




  1. Total 333015 objects, 188 types, Totalsize=52.8MiB (55414199 bytes)

  2. Index Count % Size % Cum Max Kind

  3. 0 10620 3 18096480 32 32 1704 POP3ClientProtocol

  4. 1 133004 39 6290033 11 44 31457 str

  5. 2 10628 3 5866656 10 54 552 Connector

  6. 3 10628 3 5866656 10 65 552 POP3ClientFactory

  7. .......




从上面可以看到,共有333015个对象,占用了52M的内存


其中,共有10620个POP3ClientProtocol对象实例,占用了32%约18M的内存


最大的对象也只占用了1.7K的内存,但由于对象很多,所以最终它占用的内存就很大了


那么,我们就大概知道了内存泄露的地方,就是这个POP3ClientProtocol对象,在使用完成之后,没有释放造成的


我们还可以继续分析某个对象,找出它的引用关系



折叠复制代码




  1. #得到所有的POP3ClientProtocol对象  

  2. p = om.get_all('POP3ClientProtocol')  

  3. #查看第一个对象  

  4. p[0]  

  5. 说明该对象的地址为2803894924,占用了1.7K内存,引用了51个对象,它被1个对象所引用  

  6. >>>POP3ClientProtocol(2803894924 1704B 51refs
     1par)  

  7. #可以查看该对象的所有引用  

  8. p[0].c


  9. >>>[str(3079323384 33B 10647par 'popuserid'), str(2814724096 45B 1par 'fuzimiao2000@sohu.com'), str(3079036128 31B 10624par 'mailnum'), int(165090672 12B 29par '75'),

  10. #查看谁引用了这个对象

  11. p[0].p

  12. >>>[POP3ClientFactory(2803893100 552B 15refs 3par)]



python中的全局变量




Post方式的Http请求

在使用python的httplib2后台发送http请求时
如果采用get方式
一般不会有什么问题,因为get本身不需要什么额外的header
但如果采用post方式
有时候经常会忘掉Content-Type和Content-Length这2个headers
结果会造成无法成功请求

对于Content-Type:
如果发送的是name-value pairs类型的数据,则指定application/x-www-form-urlencoded
如果发送的是二进制的数据,则指定application/octet-stream

当然,如果在java里,我们使用了httpclient来发送的话
Content-Type是不需要我们手工指定的
但Content-Length最好还是指定一下

webpy的session

web.py已经支持session功能了
但现在只支持文件session和数据库session
对于并发量大的系统,用db方式不太可行
只能选择文件session了
也许将来webpy会加入memcached的session支持?

大概看了下其源码
它是将每个用户的session文件都统一存放到一个目录下
这样,当访问用户量很大时
对应目录下的session文件数目会很多
而且,其对过期的session处理似乎也不太好
是在每个请求过来时,判断是否需要清理过期session
然后遍历其下面所有的session文件
根据其访问时间来清理

不过,对于并发量小的应用,倒是可以试试
准备打算在vip里面试试这个session

具体代码如下:


session = web.session.Session(app, web.session.DiskStore('/tmp'))
web.config['_session'] = session


在初始化session后,将它放到一个全局的config里面
这样后续的应用就可以直接用了


setattr(self, '_session', web.config['_session'])
self._session.user = user



twisted中factory和protocol的结合

twisted中的factory和对应的protocol结合大概有2种方式:

1.在factory中定义:
  protocol = POP3ClientProtocol
   这种方式默认是在twisted的prtocol.py中的buildProtocol方法中来生成对应的protocol实例,代码如下:
  def buildProtocol(self, addr):
         p = self.protocol()
         p.factory = self
         return p
   这样,在protocol的init方法中是无法得到当前factory对象的,只能是在protocol的某个事件回调函数里去拿到factory
   经常,我们需要在factory和protocol之间传递一些参数,这种方式就比较麻烦,因为不确定在什么时间才能拿到那些参数的值。

2.在factory中重写buildProtocol方法:
  def buildProtocol(self, addr):
        pr = POP3ClientProtocol(self)
        self.p = pr
        return pr
   这样,在生成protocol实例时,就可以把当前的factory给传递过去,因此,在protocol的init方法中是能得到当前factory对象
   从而,也能在init时就顺利的拿到factory传递的一些参数了


第二种方式其实更灵活一下吧

twisted的application运行模式

这2天写一个基于twisted的socket应用
开发是在Windows机器上,最后的运行是在Linux机器上

由于Windows的机器不支持Application的方式运行
所以在开发时,就使用了reactor.run的模式来运行
写了:
if __name__ == '__main__':
      main()

然后在Linux上想以Application的模式运行、于是想当然的又写了一个函数
main_application(),在这个函数里面实现了application的方法
但最后用twistd运行时一直报错
Failed to load application:'application'

后来才发现,application的实现不能写在一个function里
只能是在py的最外层,无奈改成了
if __name__ == "__main__":
    main_reactor()
else:
    bdb = BDBServer()
    db = bdb.db
    from twisted.application import internet,service
    application = service.Application('bdb_server')
    bservices = service.MultiService()
    bdb_service = internet.TCPServer(constants.LISTENPORT,SocketServerFactory(SocketProtocol,bdb,db))
    adm_service = internet.TCPServer(constants.ADMINPORT,SocketServerFactory(SocketAdminProtocol,bdb,db))
    bdb_service.setServiceParent(bservices)
    adm_service.setServiceParent(bservices)
    bservices.setServiceParent(application)


编译和反编译python程序

当我们运行一个单独的py文件,则python是不会编译该文件为pyc的,而是直接解释运行它


但如果我们的一个py文件作为一个module被其它的py文件import,那么python默认会编译它为pyc文件


pyc文件是python的字节码文件,其实还有一种pyo文件,是优化后的字节码文件


 


如果想编译成pyc文件,可以使用py_comile或compileall模块



折叠复制代码




  1. #编译单个文件

  2. importpy_compile

  3. py_compile.compile('/opt/ysz/python/test.py')


  4. #编译整个目录

  5. importcompileall

  6. compileall.compile_dir('/opt/ysz/python')




或者也可以用如下方式编译



折叠复制代码




  1. #编译pyc

  2. python -m py_compile test.py


  3. #编译pyo

  4. python -O -m py_compile test.py




 有一个网站,可以实现在线反编译pyc,据说能支持到python2.6


http://www.crazy-compilers.com/decompyle/


 

python中导入子目录中的py

当我们开发的项目比较大时,源文件也许是按照目录结构来存放的
这样,就类似于java的package
如果我们需要下级目录的py文件,可以有以下方法:
1.sys.path.append('folder')
   然后直接import folder.test
2.在下级目录folder下放一个__init__.py文件,表明把目录作为一个module来对待
  然后直接import folder.test


如果需要导入上层目录的py文件,则:
sys.path.append("..")
然后再import test

python的yield

yield语句是从Python2.2开始引入的
它只能用在一个generator(生成器)中,作为一个语句来执行并返回结果
当调用改generator的next方法时,会从上次的yield语句开始继续执行....

在python2.5中对yield进行了增强:
1.yield不再是一个语句,而是一个expression(表达式)了,这样我们就可以这样来使用了:  
  y = yield 6
2.增加了一个send(msg)函数:
  当执行send(msg)函数后,msg参数将会替代整个yield x表达式的值
  而next方法则相当于是执行了send(None)函数
  send和next函数其实都是有返回值的,是下个yield的参数值(注意:不是整个yield表达式的值)。
3.增加了throw GeneratorExit方法,可以用来中断该generator方法
4.增加了close()方法,其实和throw的作用差不多。


通过下面这个列子,大概能明白yield的用法了:
# -*- coding: cp936 -*-

def gen():
    for i in xrange(5):
        print 'i: %d' % i
        m = yield i
        print 'm: ', m

g = gen()
n = g.next()
print 'g: %d' % n
n = g.next()
print 'g: %d' % n
n = g.send(6)
print 'g: %d' % n
n = g.send(None)
print 'g: %d' % n
g.throw(GeneratorExit)
n = g.next()



最新文章

最近回复

  • feifei435:这两个URI实际是不一样的
  • zsy: git push origin 分支 -f 给力!
  • 冼敏兵:简单易懂,good fit
  • Jack:无需改配置文件,看着累! # gluster volume se...
  • Mr.j:按照你的方法凑效了,折腾死了。。。。
  • zheyemaster:补充一句:我的网站路径:D:\wamp\www ~~菜鸟站长, ...
  • zheyemaster:wamp2.5(apache2.4.9)下局域网访问403错误的...
  • Git中pull对比fetch和merge | 炼似春秋:[…] 首先,我搜索了git pull和git fe...
  • higkoo:总结一下吧, 性能调优示例: gluster volume s...
  • knowaeap:请问一下博主,你维护的openyoudao支持opensuse吗

分类

归档

其它