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 SessionStore by redis with msgpack serialized
星期五, 八月 19th, 2011
参考老外代码写了个用于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
街舞和中国武术
星期一, 三月 21st, 2011
看了几个街舞视频,感觉和中国武术有异曲同工之妙。
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的调用。