Python 多进程(进程池)及单例的logger模块

关键词:python2.7 multiprocessing logger 单例

前言

前言,很多人提起python就说性能问题,此类文章网上一搜也是铺天盖地。前两天和公司一个老前辈闲谈的时候,他说了一句话让我感触很深,其实任何代码都会有性能问题,只不过一个人写的可能是在1000并发的时候有问题,而另一个是在1200的时候有问题,深感其然。
于此,对于想深究学习Python性能为什么会有问题的同学,提供一篇文章,是国内童鞋翻译的,讲的很不错。http://www.oschina.net/translate/pythons-hardest-problem
性能问题,在这篇文章里不多做赘述,简述一下为什么选择多进程而不是多线程。多线程共享进程的内存,本人需要实现的功能需要避免资源竞争,用多线程的话就必须要实现队列或者加锁。所以,多进程无可厚非。

进程池

本demo引用http://www.cnblogs.com/kaituorensheng/p/4465768.html

#coding: utf-8
import multiprocessing
import time

def func(msg):
    print "msg:", msg
    time.sleep(3)
    print "end"

if __name__ == "__main__":
    pool = multiprocessing.Pool(processes = 3)
    for i in xrange(4):
        msg = "hello %d" %(i)
        pool.apply_async(func, (msg, ))   #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去

    print "Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~"
    pool.close()
    pool.join()   #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
    print "Sub-process(es) done."

本人基于这个demo扩展,源代码目前不方便展示,后续空闲了会放到github。这里展示的时候还是贴上原作者的代码,以示尊重。注释写的很完善,但是有点描述不清晰。添加新的进程进去 会让人误以为是新创建了一个进程。于是我们在这个基础上稍作改动,来确认一下。

# -*- coding:utf-8 -*-
import multiprocessing
import os
import time


def func(msg):
    print ('current process ID:', os.getpid())
    print ('current parent ID:', os.getppid())
    time.sleep(3)


if __name__ == "__main__":
    pool = multiprocessing.Pool(processes=2)
    for i in xrange(10):
        msg = "hello %d" % (i)
        pool.apply_async(func, (msg,))

    pool.close()
    pool.join()  
    print "Sub-process(es) done."

这里我把原来的打印都去掉了,进程个数改为了2,for循环改为了10,在func里面打印当前子进程及其父进程的ID(os.getpid()函数只能在linux系统使用)。我们来看下打印的结果:

Paste_Image.png

可以清楚的看到,程序启动的时候创建了一个主进程ID为9133,之后创建了两个子进程9134和9135。任何一个进程执行完了func之后,都会再次领取任务。直到10个任务都执行完毕,进程结束释放。所以,进程池一旦创建好了一定数目的进程,是不会在额外创建进程的。

单例模式的logger

很多人可能要有疑问,logger根据官网的内容初始化就好了,和单例有什么关系。楼主是从.net,java过来的,对于整个系统在用,但是每次用都要初始化,而且每次初始化还都是一样内容的东西,不做个单例心里很不爽。从字面上来讲单例就是只有一个实例化对象,大家无论谁来调用,都是同一个对象。那么好处也就是减少了内存的消耗(其实就这一个类即使实例化100次占用的内存也是可以忽略的,但是在很大的项目里,很多这样的类的时候还是比较可观的,养成一个好习惯还是很必要的)。

import logging
import os
from logging.handlers import TimedRotatingFileHandler


def singleton(cls, *args, **kw):
    instances = {}

    def _singleton():
        if cls not in instances:
            instances[cls] = cls(*args, **kw)
        return instances[cls]

    return _singleton


@singleton
class LogHelper(object):
    logfile = os.path.join(os.getcwd() + '/', 'log/log.log')
    if not os.path.exists(os.path.dirname(logfile)):
        os.makedirs(os.path.dirname(logfile))

   logger = logging.getLogger()
   logger.setLevel(logging.ERROR)
   file_handler = TimedRotatingFileHandler(logfile, 'midnight', backupCount=15)
   file_handler.setFormatter(
       logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
   # file_handler.suffix = "%Y%m%d.log"
   logger.addHandler(file_handler)

注意这里注释了一行代码,# file_handler.suffix = "%Y%m%d.log"。很多文章都会写,那这句话为什么不能写呢?我们进这个类TimedRotatingFileHandler 看一下init函数,有一段如下:

    if self.when == 'S':
        self.interval = 1 # one second
        self.suffix = "%Y-%m-%d_%H-%M-%S"
        self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(\.\w+)?$"
    elif self.when == 'M':
        self.interval = 60 # one minute
        self.suffix = "%Y-%m-%d_%H-%M"
        self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}(\.\w+)?$"
    elif self.when == 'H':
        self.interval = 60 * 60 # one hour
        self.suffix = "%Y-%m-%d_%H"
        self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}(\.\w+)?$"
    elif self.when == 'D' or self.when == 'MIDNIGHT':
        self.interval = 60 * 60 * 24 # one day
        self.suffix = "%Y-%m-%d"
        self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
    elif self.when.startswith('W'):
        self.interval = 60 * 60 * 24 * 7 # one week
        if len(self.when) != 2:
            raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when)
        if self.when[1] < '0' or self.when[1] > '6':
            raise ValueError("Invalid day specified for weekly rollover: %s" % self.when)
        self.dayOfWeek = int(self.when[1])
        self.suffix = "%Y-%m-%d"
        self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
    else:
        raise ValueError("Invalid rollover interval specified: %s" % self.when)
    self.extMatch = re.compile(self.extMatch, re.ASCI

也就是说,根据你选的when ,会自动给suffix赋值。假如那行注释代码和这里面赋值的一样,是不是冗余了?假如不一样,问题就严重了,TimedRotatingFileHandler模块的作用就是把日志按照指定的日期分片,然后根据指定的备份数据自动维护日志的个数,在不一样的情况下你会发现删除历史日志文件的功能完全就废掉了。我们来看一下自动获取需要被删除日志的逻辑

def getFilesToDelete(self):
    """
    Determine the files to delete when rolling over.

    More specific than the earlier method, which just used glob.glob().
    """
    dirName, baseName = os.path.split(self.baseFilename)
    fileNames = os.listdir(dirName)
    result = []
    prefix = baseName + "."
    plen = len(prefix)
    for fileName in fileNames:
        if fileName[:plen] == prefix:
            suffix = fileName[plen:]
            if self.extMatch.match(suffix):
                result.append(os.path.join(dirName, fileName))
    result.sort()
    if len(result) < self.backupCount:
        result = []
    else:
        result = result[:len(result) - self.backupCount]
    return result

这段代码就是把目录下的文件名都读取出来,然后用正则匹配。那么正则哪里来的?就在上一段代码的最后一句。于是我们就知道了,匹配的正则是根据suffix来的,你自作聪明改了一个suffix,然后程序就无法匹配,自然无法删除需要删除的日志。

谢谢观赏

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容