PIL MemoryError with uWSGI

星期四, 十一月 3rd, 2011

用PIL库处理图片,作缩略图的时候需要先裁减为正方形,再比例缩小,当裁减时不定报错。

img = img.transform((r,r), Image.EXTENT, area)

#ERROR
File "/usr/local/lib/python2.7/dist-packages/PIL/ImageFile.py", line 231, in load_prepare
    self.im = Image.core.new(self.mode, self.size)
MemoryError

google PIL+MemoryError,主要提到2个可能错误。一个是PIL库本身ctype代码问题,一个是传递裁减范围为负值导致内存问题。仔细检查了一下,都没有异常。后来考虑到上面的代码为创建新图像时内存错误,直接调整了uwsgi参数。

-M -p 2 --no-orphans --logdate --chmod-socket=666 --uid admin --gid admin --limit-as 384 --harakiri 60 --max-requests 2000

[--limit-as]参数从256调整到384,系统恢复正常。

反思查找原因,发现图片文件虽然不大仅3M+,但是该图片像素巨大为4096*5210,将该图片转换为BMP,有60M+。估计库在处理图片裁减时会将jpg文件重新读取为完整位图,再考虑到操作位图消耗和程序本身需要的内存空间,原设定256M限制肯定不足了,需要设定更大。

目前程序运行在一个虚拟机上,内存2G……看来需要尽快切换主机了……

install gevent on macosx lion with macport lib

星期四, 十月 27th, 2011

我的os大部分包使用macport管理,直接pip安装存在问题

sudo pip install gevent

出错提示

command ‘gcc-4.2′ failed with exit status 1 

下载源代码包安装

wget http://pypi.python.org/packages/source/g/gevent/gevent-0.13.6.tar.gz
tar -zxvf gevent-0.13.6.tar.gz
cd gevent-0.13.6
sudo python setup.py install -I /opt/local/include -L /opt/local/lib #指定默认的macport路径

Flask下的session认证和cache header设置

星期六, 九月 17th, 2011

Flask是个非常优秀的python microframework,基于Werkzeug、Jinja 2,使用非常方便,性能也非常不错,最近公司创业新项目一直在用。

这里和大家分享2个简单的decorator,分别用于session认证和响应头cache设置

# -*- coding: utf-8 -*-
"""
    lib/decorator.py
    ~~~~~~~~~~~~~~

    some decorator defined 

    :author: linnchord@gmail.com
    :date:2011-8-4

"""
from functools import wraps
from flask import session, redirect, url_for, flash, request, make_response
from models.user import User
from util import helper

def login_required(f):

    @wraps(f)
    def do(*args, **kwargs):
        if 'userId' not in session:
            #cookie自动登录机制
            token=request.cookies.get('auto_login')
            if token:
                userId, token = token.split('_')
                user = User.get_user_by_id(userId)
                from matrix import app
                if user and helper.md5(str(user.id) + user.password + app.config['SECURIY_KEY'])==token:
                    session['userId']=user.id
                    session['phone']=user.phone
                    session['nickName']=user.nickName
                else:
                    return redirect('/logout')
            else:
                return redirect('/')

        return f(*args, **kwargs)

    return do

def no_cache_header(f):

    @wraps(f)
    def do(*args, **kwargs):
        response = make_response(f(*args, **kwargs))
        response.headers['pragma'] = 'no-cache'
        response.headers['Cache-Control'] = 'no-cache, must-revalidate'
        return response
    return do

使用很简单方便

@app.route('/user/follow/', methods=['GET'])
@login_required
@no_cache_header
def follow_user(userId,appId):
    pass

需要注意的是@no_cache_header修饰应该放在最后,否则逻辑无法正确完成。这个修饰经常用于ajax响应,因为……坑爹的IE总是要缓存第一次请求结果。

这个session auto login机制有个问题,他的auto login cookie始终是不变的,长期使用会有一定安全隐患。更安全的方法是用户每次登录生成一个security key来进行hash,不过这样对于经常换浏览器甚至操作系统的人会造成一定障碍。例如我……经常使用chrome/firefox/ie,还在mac/win下切来切去,某个小说网站每次切了就得重新登录,让我很蛋疼……

PIL 1.1.7 install on ubuntu

星期一, 八月 29th, 2011

PIL虽然可以通过pip安装,但是不好用,各种调用需要的依赖都没有,需要手动安装。

过程如下

#清除系统上现有库 python版本自行替换
sudo rm -rf /usr/lib/python2.7/site-packages/PIL
sudo rm /usr/lib/python2.7/site-packages/PIL.pth

#安装依赖包
sudo apt-get install libjpeg8 libjpeg8-dev libfreetype6 libfreetype6-dev liblcms1-dev

#下载
wget http://effbot.org/downloads/Imaging-1.1.7.tar.gz
tar -zxvf Imaging-1.1.7.tar.gz
cd Imaging-1.1.7/
python setup.py build_ext -i
sudo python setup.py install

#安装后如果发现安装的包没有正确支持可进行如下操作
sudo ln -s /usr/lib/x86_64-linux-gnu/libfreetype.so /usr/lib/
sudo ln -s /usr/lib/x86_64-linux-gnu/libz.so /usr/lib/
sudo ln -s /usr/lib/x86_64-linux-gnu/libjpeg.so /usr/lib/

参考老外代码写了个用于Flask框架的基于redis的session实现,采用msgpack进行序列化。

# -*- coding:utf-8 -*-
"""

lib/RedisSessionStore.py
~~~~~~~~~~~~~~

Flask, session store in redis

ref by https://gist.github.com/994937

:author: linnchord@gmail.com
:date:2011-08-04

"""
import redis
from flask import Flask, request, session, json
from werkzeug.contrib.sessions import Session, SessionStore
import msgpack

class RedisSessionStore(SessionStore):

    def __init__(self, key_prefix=None, host='127.0.0.1', port=6379, dbindex=1, expire=1800):
        SessionStore.__init__(self)

        self.redis = redis.Redis(host,port,dbindex)
        self.key_prefix = key_prefix
        self.expire = expire

    def save(self,session):
        key = self._get_session_key(session.sid)
        data = msgpack.Packer().pack(dict(session))
        #print "set session {0}:{1}".format(key, data)
        self.redis.setex(key, data, self.expire)

    def delete(self, session):
        key = self._get_session_key(session.sid)
        self.redis.delete(key)

    def get(self, sid):
        key = self._get_session_key(sid)
        data = self.redis.get(key)
        if data is not None:
            self.redis.setex(key, data, self.expire)
            un = msgpack.Unpacker()
            un.feed(data)
            data = un.unpack()
        else:
            data = {}

        return self.session_class(data, sid, False)

    def _get_session_key(self,sid):
        key = self.key_prefix + sid
        if isinstance(key, unicode):
          key = key.encode('utf-8')
        return key

    @staticmethod
    def init_app(app):
        app.session_store = RedisSessionStore(
            app.config['SESSION_KEY_PREFIX'],
            app.config['SESSION_REDIS_HOST'],
            app.config['SESSION_REDIS_PORT'],
            app.config['SESSION_REDIS_DB'],
            app.config['SESSION_LIFETIME']
        )
        app.session_key = app.config['SESSION_KEY']

class SessionMixin(object):

  __slots__ = ('session_key', 'session_store')

  def open_session(self, request):
    sid = request.cookies.get(self.session_key, None)
    if sid is None:
        return self.session_store.new()
    else:
        return self.session_store.get(sid)

  def save_session(self, session, response):
    if session.should_save:
        self.session_store.save(session)
        response.set_cookie(self.session_key, session.sid)
    return response

class MyFlask(SessionMixin, Flask): pass

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

HOW TO USE

app = MyFlask(__name__)
RedisSessionStore.init_app(app)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

APP CONFIG:

SESSION_REDIS_HOST = '127.0.0.1'
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 1

SESSION_KEY = '_myapp_sid'
SESSION_KEY_PREFIX = 'session_myapp_'

SESSION_LIFETIME = 900

postfix with smtp auth on Ubuntu 11.04

星期五, 八月 19th, 2011

公司网站注册需要向用户发送邮件,原有的QQ企业邮箱自然不能用来搞这个了,迫不得已自己来配置邮件服务器。网上有很多文章,到处查找搞了一晚上,现在来记录一下……估计过两天换主机还用得着……悲剧……

sudo -i #切到root方便点吧
apt-get install postfix libsasl2-2 sasl2-bin libsasl2-modules db4.6-util procmail
dpkg-reconfigure postfix

需要配置一大批,注意凡是需要填写host什么的都只填写主域domain.com,不必填邮件服务器2级前缀。

sudo apt-get install bsd-mail #很多网上文章写的mailx,不好用了

设置Maildir

vim /etc/postfix/main.cf

添加

home_mailbox = Maildir/

给系统需要收发邮件的用户创建Maildir目录

cd /home/{{user}}  #{{user}}替换为用户名
maildirmake Maildir
chown -R {{user}} Maildir

安装pop和imap

sudo apt-get install courier-pop
sudo apt-get install courier-imap

添加SMTP认证

vim /etc/postfix/main.cf

添加

smtpd_recipient_restrictions = permit_mynetworks,
    permit_sasl_authenticated,
    reject_unauth_destination,
    reject_invalid_hostname,
    reject_non_fqdn_sender,
    reject_non_fqdn_recipient,
    reject_non_fqdn_hostname,

smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname
broken_sasl_auth_clients = yes

修改

vim /etc/default/saslauthd

添加

START=yes
PWDIR="/var/spool/postfix/var/run/saslauthd"
PARAMS="-m ${PWDIR} -r"
PIDFILE="${PWDIR}/saslauthd.pid"
MECHANISMS="pam"
OPTIONS="-c -m ${PWDIR}"

然后运行

mkdir -p /var/spool/postfix/var/run/saslauthd
echo 'pwcheck_method: saslauthd' >> /etc/postfix/sasl/smtpd.conf
echo 'mech_list: plain login' >> /etc/postfix/sasl/smtpd.conf

最后重启

/etc/init.d/postfix restart
/etc/init.d/saslauthd restart

最后测试出现如下错误

authentication failed: generic failure

在日志(/var/log/mail.log)中可以看见错误信息

SASL authentication failure: cannot connect to saslauthd server: Permission denied
SASL authentication failure: Password verification failed
SASL PLAIN authentication failed: generic failure

经历了一个漫长刺激痛苦的google旅程,最后解决方案如下

最后发现是saslauthd运行时目录权限问题

dpkg-statoverride --add root sasl 710 /var/spool/postfix/var/run/saslauthd
adduser postfix sasl

重启

/etc/init.d/postfix restart
/etc/init.d/saslauthd restart

OK 搞定

参考文章:


后记:2011-08-25

  • 如果新增加用户,imap不能即时生效,必须重启。
  • 另外要注意用户/home下的用户目录权限,必须是username.username,我曾在添加用户时用了其他的group,结果失败,会报错-uid -gid不对。

ubuntu下安装最新nginx+uWSGI

星期二, 八月 9th, 2011

先通过apt-get remove删除系统下原有nginx,再添加源安装

sudo apt-get install python-software-properties
sudo add-apt-repository ppa:nginx/stable
sudo add-apt-repository ppa:uwsgi/release
sudo apt-get update
sudo apt-get install uwsgi nginx

过程中遇到问题,最后install无法完成,报错

dpkg: warning: files list file for package `nginx-common' missing, assuming package has no files currently installed.
dpkg: warning: files list file for package `nginx-full' missing, assuming package has no files currently installed.

在网上查了很久,没有完全的解决方案,最后通过如下方式解决

sudo find / | grep nginx

查找系统所有的nginx残留文件并手动删除,然后运行

sudo apt-get clean
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install nginx  --reinstall

ok,nginx成功更新。

更新:2011-09-17

没两天,uwsgi的默认安装部署发生了变化,改了个名字,需要如下安装

sudo apt-get install uwsgi-python

安装后各路径

sudo /etc/init.d/uwsgi-python restart
/var/log/uwsgi-python/uwsgi.log
/etc/uwsgi-python/apps-available/uwsgi.xml #默认配置文件
/etc/default/uwsgi-python #默认命令行启动参数设置

我的参数配置如下


	
	
	
	

/etc/default/uwsgi-python

DAEMON_OPTS=" \
  --master \
  --no-orphans \
  --processes 2 \
  --logdate \
  --chmod-socket=666 \
  --uid www-data \
  --gid www-data \
  --limit-as 256 \
  --harakiri 60 \
  --max-requests 1000 \
"

具体参数含义可参考 http://projects.unbit.it/uwsgi/wiki/Doc

clean & auto save default grub boot of ubuntu

星期一, 三月 21st, 2011

ubuntu用了一段时间,更新了好几个版本,引导菜单拉的老长,需要清理一下。

删除多余的引导项很简单

cd /boot/grub
sudo rm -i *-2.6.35-25-*

填入你想要删除的版本号即可,安全起见,会逐个让你确认。

刚开始用ubuntu的时候,可以自动选择上一次选择的启动项为默认启动,很方便的功能。后来这个功能不知道为什么默认没有了,现在想重新搞起来,google之,都是大批copy如何指定具体启动项的。干脆查看了一下/boot/grub/grub.cfg源文件,里面有如下函数。

function savedefault {
  if [ -z "${boot_once}" ]; then
    saved_entry="${chosen}"
    save_env saved_entry
  fi
}

看上去只要在选择启动项后调用这个函数就ok,不过不太赶立马动手,又google了一下grub的参考,得到这个grub2的guide http://ubuntuforums.org/showthread.php?t=1195275,ok,里面有如何配置savedefault的说明。

修改/etc/default/grub

GRUB_DEFAULT=saved  #原为GRUB_DEFAULT=0
GRUB_SAVEDEFAULT=true  #新增

然后运行

sudo update-grub

系统重新生成grub.cfg,更新后查看下该文件,多余的启动项取消了,每个启动项中添加了savedefault的调用。

linux(ubuntu)软件安装实践

星期二, 六月 15th, 2010

准备在ubuntu上搞搞java,整个环境还是不太习惯,以前零零碎碎看了些操作片段,总是感觉很模糊,几天不用就忘一干二净了。

这次准备系统的搞搞清楚,在每遇到问题时就问题涉及范围内的知识要点先拟一个提纲,然后查资料、整理、描述、记录,这样有利于系统的提升。

这部分是关于ubuntu下软件安装的相关要点,部分自撰,部分来自网络。

常见方式

  • 自动安装

    通过软件管理 器或apt-get自动安装软件,软件会按照默认设置编译部署配置。

    优点:方便、快捷、易升级

    缺点:一般来说文件会按照标准分散在各目录,不易维护,因此如果需要经常对软件配置或其他内容进行维护,则不宜采用此方式。

    常用命令:

    • apt-get install {soft}
    • apt-get remove {soft}
    • apt-get update
    • apt-get autoremove 自动删除已经无效的包
  • 下载包解压安装

    下载发行商已经 编译好的压缩包进行安装,需自行解压,并进行相关配置。

    优点:可自定义文件部署路径,方便配置维护。

    缺点:部署、升级、删除需手工操作,需关注依赖。

    常用命令:

    • wget http://{package url} 获取包
    • curl -o http://{package url}  获取包
    • tar zxvf {package.gz} -C {target dir. etc /opt} 将包gzip解压到指定路径
  • 手工编译安装

    下载源代码包,手工编译安装。

    优点:可对针对具体情况对软件编译参数进行设置。

    缺点:需要了解软件编译参数,需要编译环境,手工操作容易出错,依赖问题。

    常用命令:

    • sudo ./configure {–options}
    • make
    • make install
  • 常用工具

    aptitude:apt终端图形界面工具

    dpkg:deb包管理工具

    • dpkg -i {pkg.deb} 安装包
    • dpkg -r {pkg} 删除包
    • dpkg -L {pkg} 列出与该包关联文件
    • dpkg -l | grep {keyword}  列出当前已安装指定关键字包

    dpkg指令参考/apt指令参考

目录结构

  • 标准目录

    linux系统的标准目录规范,如果通过系统软件管理器自动安装软件,则通常遵循此规范要求。

    • /bin 系统执行文件,最常使用命令,这些文件可以被普通用户使用。
    • /sbin 系统执行文件,这些文件不打算被普通用户使用,普通用户使用要指定目录。
    • /root 系统管理员(也叫超级用户或根用户)的Home目录。
    • /dev 设备文件目录。
    • /boot 用于自举加载程序(LILO或GRUB)的文件。
    • /lost+found 在文件系统修复时恢复的文件
    • /lib 系统核心共享库目录,几乎所有的应用程序都会用到该目录下的共享库。
    • /opt 可选应用程序目录,很多第三方软件在安装时默认会找这个目录。
    • /tmp 用来存放不同程序执行时产生的临时文件,该目录会被系统自动清理干净。
    • /var 存放在正常操作中被修改的文件,包括各种日志、加锁、临时文件、数据文件等。
    • /var/log 系统日志目录,通常也将其他系统的日志置于此目录或子目录。
    • /etc 存放各种配置文件和子目录,例如网络配置文件、文件系统、用户配置信息等。
    • /home/{user} 用户目录
    • /usr 用户的应用程序和文件几乎都存放在该目录下。
    • /usr/share 独立与你计算机结构的数据,譬如,字典中的词。
    • /usr/(s)bin 类似根目录下对应的目录(/bin和/sbin),但不用于基本的启动(譬如,在紧急维护中)。大多数命令在这个目录下。
    • /usr/local 本地安装的应用程序,这个目录下的内容在重安装或升级操作系统后应该存在。
    • /usr/local/bin 可能是用户安装的小的应用程序,和一些在/usr/local目录下大应用程序的符号连接。
    • /usr/src Linux开放的源代码目录。
  • 常见规划

    • 桌面用途

      用户自有应用和文件置于/home/{user}中。

      系统共享应用可置于/usr/local。

      纯自用无共享主机也可全部置于/home/{user}中。

      /home可独立划分磁盘分区方便管理升级维护。

    • 服务器用途

      无固定模式,根据实际需求,通常不会严格按照规范。

      常见模式

      根据文件更新频繁度、破碎、重要性、IO读写等关键指标,将文件分类存储于不同目录、分区或磁盘,方便维护管理,提高性能和安全性。

环境设置

  • /etc/enviroment 系统环境变量配置,系统启动时读取。
  • /etc/profile 用户全局环境变量配置,用户登录启用时读取。
  • /etc/bashrc 开启shell时为每个bash用户执行此环境配置。
  • ~/.profile 具体用户环境变量配置。
  • ~/.bashrc 具体用户开启shell时环境配置。
  • ~/.bash_profile 交互式、login方式进入bash时运行,通常和.bashrc内容相同,因此通常会调用.bashrc。
  • ~/.bash_logout 当每次退出系统(退出bash shell)时,执行该文件。

服务

最后,某些软件我们希望把他设置为服务,或者简单的说希望 他开机启动,很简单。

将启动脚本部署到/etc/init.d/,然后安装一个软件rcconf。

sudo apt-get install rcconf

运行rcconf即可设置启动选项,很怀旧的dos风格gui界面。

双浮动web布局

星期五, 五月 28th, 2010

很久没写html了,最近写个东西,需要一个经典的双浮动布局。以前的经典做法是在末尾提供一个元素,并附加clear:both样式,这个附加的东东很显然是没有语义的,纯粹为了外观而添加,一个bad smell。想来web前端发展迅猛,应该有更优雅的解决方案,于是在twitter上提个问,得到2位专业前端童鞋的回答,记之。

linnchord: @b3inside 请教下,现在web布局div左右双浮动,下面还是要插一个div做clear:both吗? 有没啥更优雅的办法?

b3inside: @linnchord 在需要清浮动的div样式里加:after。.yn-fix-float:after{content:”.”; display:block; height:0; clear:both; visibility:hidden;} 使用时div里引用这个class

b3inside: @linnchord <div id=”main” class=”yn-fix-float”>,最好样式里加个.yn-fix-float{zoom:1;}

terranc: @linnchord 父元素如果没有高度的话,加overflow:hidden;zoom:1;可以了

1 of 712345...last »