Gunicorn 是一个Python WSGI UNIX的HTTP服务器。Gunicorn有着不错的性能以及稳定性。Gunicorn对WSGI、Django、Paster提供了很好的支持,使用起来非常的简单。
此前 LBForum 和 似水流年 使用apache+wsgi的方式部署在webfaction上。为了给服务器节约些内存将部署方式切换到Gunicorn。不过似乎效果并不明显。除工作进程外,Gunicorn还会另外启动一个master进程用于管理子进程。master进程加一个子进程的内存耗用量加在一起大概在40M左右。
注:在一些评测里Gunicorn的性能并不是太好。Gunicorn支持多种 worker-class ,默认的worker-class使用的是同步模式。在将worker-class切换到gevent或tornado性能会好出不少。
DPress部署到SAE
将DPress部署到SAE的简要说明。如果你在部署过程中有遇到什么问题,欢迎反馈。
- 在SAE管理后台创建应用,开发语言选择python
- 使用svn将应用代码更新到本地
- 修改配置文件config.yaml
name: 你的应用名称
version: 1
libraries:
- name: django
version: "1.4"
- 将DPress sites目录下所有文件复制到SAE应用目录(如:dpress/1/)。
- 在应用的根目录下创建目录libs(如:dpress/1/libs/)
- 将virtualenv.bundle.zip复制到libs目录
- 注:virtualenv.bundle.zip为python的第三方依赖包
- 下载 virtualenv.bundle
- 在服务管理中启用mysql数据库
- 由于SAE上无法使用Django命令。需要在本地使用syncdb命令在本地mysql数据库中创建好DPress的初始数据,然后使用SAE的phpmyadmin将数据导入到SAE。
- 在服务管理中创建Storage,并将Domain设置为base
- 创建配置文件settings/local.py
# -*- coding: UTF-8 -*-
import sae.const
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'NAME': sae.const.MYSQL_DB, # Or path to database file if using sqlite3.
'USER': sae.const.MYSQL_USER, # Not used with sqlite3.
'PASSWORD': sae.const.MYSQL_PASS, # Not used with sqlite3.
'HOST': sae.const.MYSQL_HOST, # Set to empty string for localhost. Not used with sqlite3.
'PORT': sae.const.MYSQL_PORT, # Set to empty string for default. Not used with sqlite3.
}
}
DEFAULT_FILE_STORAGE = 'saestorage.SaeStorage'
FILEBROWSER_DIRECTORY = ''
STATIC_URL = 'http://vicalloy.sinaapp.com/dpress/'#修改为你静态资源位置
DPRESS_TITLE = u'天地一沙鸥'
DPRESS_SUBTITLE = u'to be continue'
DPRESS_DESCN = ''
DISQUS_SHORTNAME = ''
GOOGLE_ANALYTICS_CODE = ''
#根据你的静态资源位置进行修改
EPIC_JS = 'http://vicalloy.sinaapp.com/dpress/dpress/epiceditor/js/epiceditor.min.js'
EPIC_BASEPATH = 'http://vicalloy.sinaapp.com/dpress/dpress/epiceditor'
- 到这里服务已经可以跑起来了。由于没有对静态资源进行配置,将出现无法找到静态资源的情况。
- 创建一个新的SAE应用,应用类型选择PHP,专门用于管理DPress的静态资源文件(如:dstatic.sinaapp.com)。
- 在本地运行Django的collectestatic命令将DPress的静态资源收集到sites/collectedstatc/目录。
- 将collectedstatc中的内容复制到新创建的站点
- 如:static/dpress/。对应的STATIC_URL配置文件修改为 http://dstatic.sinaapp.com/static/dpress/
DPress配置、定制、模板创建指南
DPress项目地址: https://github.com/vicalloy/DPress
DPress是我用Django写的一个博客系统,支持使用MarkDown语法进行博客的书写。这里将就系统的配置,模板定制修改,以及如何创建自己的模板给个简要的指南。由于系统使用Django开发,使用者最好能有一定的Django基础。
使用Django的开发服务器将系统跑起来
- clone DPress的代码库(git://github.com/vicalloy/DPress.git)
- 运行scripts/create_env.py,该脚本将为你创建python虚拟环境,并自动安装相关依赖包。
- 执行scripts/env.bat(. scripts/env.rc),进入刚创建的python虚拟环境。
- %mg%($mg in linux) 是 “python manage.py ” 的快捷方式,用于方便的执行Django命令。
- %mg% syncdb
- %mg% migrate
- %mg% runserver
- DPress Admin http://127.0.0.1:8000/admin/
- DPress http://127.0.0.1:8000/
配置
- 复制 sites /settings/pre.sample 为 pre.py 。修改其中的SETTINGS设置,确定使用哪个配置(默认可选production/dev)。
- 复制 sites /settings/local.sample 为 local.py 。并修改相关配置。
- DISQUS_SHORTNAME以及GOOGLE_ANALYTICS_CODE这两个参数需要在正确设置后,DISQUS的评论功能以及GOOGLE ANALYTICS才可正常生效使用。
模板的配置与修改
- DPress自带的模板在 sites/dpress/templates/ 目录。自定义模板放在 sites/templates/ 目录。系统将自动优先使用自定义模板目录的模板,实现对模板的重写。
- dpress/include/footer.html 为页面的底部。可重写该文件,修改版权增加定义链接等。
- dpress/include/sidebar.html 页面的侧边栏。重写该文件对侧边栏内容进行配置。
- dpress/widgets/ 该目录为侧边栏小工具的模板。如果你需要创建自定义的侧边栏工具,请遵守约定将小工具模板放到该目录。
- dpress自带的静态资源文件放在 sites /dpress/static/ 目录。自定义的静态资源文件放在 sites/static/ 目录。
- 静态资源目录中 /dpress/themes/ 为主题目录。当前的默认主题为 default 。你可创建一个新文件夹用于存放你的新主题。
- 重写 dpress/include/base_style.html 使用新主题的css文件。
SAE Storage for Django
DPress 修正后想着要部署一个在线的演示程序。问题是我自己虚拟主机的资源已经捉襟见肘,于是只能打SAE的主意了。
为保证文件上传功能的正常使用需要将Django默认的Storage实现替换为SAE的storage实现。SAE的官方并没有提供Django的storage实现,需要自己另外实现。在github:gist上有人贴出了SAE Storage for Django的实现(SAE Storage for Django)。github:gist上的这个SAE Storage实现有些bug,我修正后的版本见saestorage。
注:
- SAE Storage单节点的最大容量为2G(注:SAE Storage 服务限制)。这对于一些小站点来说是没什么问题,但如果是一个大型的商业站点这必然会有些不便。难道你需要配置一个storage列表,让程序自行去寻找可用的storage?
- SAE Storage似乎不太稳定,上传的文件经常会打不开。
“DPress”原地满血复活
GitHub地址: DPress(GitHub)
演示地址: DPress(SAE)
DPress 是我在2009用django写的一个博客系统。此后年久失修,已经跑不起来了。
这两天花了些时间将项目重新修整一下,现在项目又可以重新跑起来了。功能方面新版的DPress有了些变化。
- 提供对 Markdown 的支持(也只支持Markdown)。
- 使用 EpicEditor 实现Markdown的实时预览。
- 使用 DISQUS 实现文章的评论功能。(注:既然已经有了这么好用的评论服务,为什么还要自己写代码呢)
- 支持代码高亮度。相关的语法说明见:Fenced Code Blocks
- 支持Tag
- 支持RSS
- 使用 Django FileBrowser 提供文件上传的支持。
- 使用 flatpages 提供对About等自定义页面的管理。
- 内置GOOGLE ANALYTICS的支持
- 代码量非常的小
- 模板和代码分离的比较清晰,要创建一套自己的模板会是一件比较容易的事。
已知bug:直接从网页复制文本时EpicEditor的编辑框内会自动的添加一些看不见的html标签,将导致文本显示不正常。需要粘贴文本时最好先将文本粘贴到写字板中,再重新复制粘贴。
《旁观者》杂感
豆瓣链接: 《旁观者》
这书看完已经有段时间了,一直想写点什么却又憋不出几个字,还是随便写写吧。
两次世界大战
相比物质的破坏,世界大战对人类精神世界的破坏要更为彻底。在20世纪初,社会主义也曾是社会主流思想的一部分。在世界大战中,无数的社会精英消失在了战火中。本该发生的很多社会主义尝试也并未发生。纳粹主义作为国家社会主义的一种,想必也伤透了广大人民的心。社会主义并不是没有过机会,太多劣迹斑斑的历史让自己失去了机会。
战火之后的世界
在第二次世界大战后,几乎没有人相信这个世界会变好。二战后的一段时间却是全球经济发展最快的一段时间。战后的世界就如被野火焚烧过的草原,一个新的轮回即将开始。
所谓的乱世正是思想最为活跃的时期。面对多如乱麻的问题,众多的天才人物都试图去寻找一个有效的解决方案。这些理论在不断的碰撞过程中逐步完善,只要一有机会就将发光。
乱世是一个思想成型的过程,治世这是乱世产生的思想体系应用并完善的过程。战后的繁荣,在一定程度上是受着战争的馈赠。
真假弗洛伊德
弗洛伊德口中的自己和公众眼中的弗洛伊德有很大的不同。弗洛伊德说自己家境贫寒,实际上他一直生活安逸。弗洛伊德说自己被忽视,实际上不管他的观点一直都被重视,学术接在想法设法的驳斥他的观点。弗洛伊德说自己犹太人的身份给自己带来了很多麻烦,这也并不是事实。
弗洛伊德的现实与他自己的说法严重的不一致,源于他内心的不一致。有些谎言是用来骗别人,有些则是用来骗自己,骗着骗着自己就真的相信了。现代社会有着太多的压力,在压力无法以正常的途径得到排解时,就会以一些让人匪夷所思的方式继续释放。
创业者再向别人推销想法时将未来描绘的天花乱坠,或许他也清楚这个想法还存在很多问题,但如果自己不先说服自己又如何说服别人。有时候前进的步伐太慢,告诉自己慢不是问题,只要向着正确的方向走一定可以到达目标。慢是一个让人无法面对的事实,于是只能忽略。狐狸吃不到葡萄,于是告诉自己葡萄是酸的,从此葡萄就是酸的。
WP7开发初体验 — WP7版“百度.ting”开发记录
项目地址: lb.ting(github)
如果你有WP7手机,可以下载XAP文件体验一下:lb.ting.xap
序
感觉已经有段时间没有接触过什么新技术了,或许也是想看看自己接触一项全新的技术需要多少时间来熟悉。近来想看看WP7开发。
上周在MSDN简单的看过一些WP7开发文章后决定动手做自己的地一个WP7应用。因为只是想简单的体验一下WP7开发,因此跳过系统学习的阶段直接动手。
这次想做的是百度.ting的WP7客户端。虽然虾米电台、豆瓣电台等电台网站/软件已经一大堆了,但百度.ting还是有自己的优势。百度电台的的音乐码率普遍为128k,比其他电台要好出不少。大多音乐电台支持直接手机访问,直接浏览器访问无法实现音乐的后台播放。
C#刚出时大家评价C#是Delphi和Java的杂交产物。我虽没什么C#的开发经验,但相信凭自己的Java与Delphi经验应付一般的C#代码还是没多少问题的。电台软件的功能以及技术点都不多,我很乐观的想周末在家宅个两天,折腾出地一个基础版本。显然,这个计划已经失败。接触一项新技术的遇到的问题远比我想象的要多,中间也走了不少弯路。
目前第一个初步可用的版本已经完成。我会将代码放到github上。我希望在接下来一段时间里对这个应用再进行一些优化,然后上线到WP7的商店。
下面的文章里会我记录下一些我遇到的问题。
准备工作,知识储备
微软在帮助文档方面做的非常的好,你可以很容易找到自己需要的资料。
- Windows Phone 开发者中心 WP7的大本营
- Window Phone 7 开发基础 介绍了WP7最基础的一些概念
- Windows Phone 开发 技术资源库 WP7开发的各类资源。技术文档,代码范例等。特别是里面的范例程序都配有相关的技术文档,非常应用。
- 媒体 – 后台音频播放器示例 后台播放是电台应用最重要的功能之一,这个范例自然是必看的。
- 豆瓣电台WP7客户端(开源) 从里面可以学到如何从网上获取json数据并进行解析。不支持后台播放是这个客户端的一个比较大的缺陷。
开发过程中遇到的相关问题以及解决方案
百度.ting API
直接用FireBug对百度播放器的请求进行分析,可以很轻松的得到大部分API。这些API中唯独缺少电台列表的API。该问题最简单的解决方案是直接分析网页获取到电台列表,然后在程序里写死。另外还可以让程序对电台首页的html代码进行分析,从中提取电台列表。方案一,感觉有些傻。方案二实现起来又过麻烦。于是有了方案三。
百度.ting有官方的Android客户端,而Android程序是比较容易反编译的。使用 Dedexer 反编译后,得到相关API。Android客户端和web版本电台使用的并不是同一套API,而且从接口的参数类型上看服务端似乎用的是Java。
//主要接口
//获取频道列表
http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.radio.getCategoryList&format=json
//获取频道歌曲列表
http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.radio.getChannelSong&format=json
//获取专辑的歌曲列表
http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.radio.getArtistChannelSong&format=json
//获取歌曲的详细信息
http://ting.baidu.com/data/music/links?songIds=
JSON解析
最初我希望JSON能被自动解析成LIST/MAP类型的结构,查了一轮并没有找到比较好的解决方案。在C#中JSON都需要和对象进行绑定。如果手动为所有JSON数据创建相关的数据类,无疑是意见很麻烦的时。我相信我并不是遇到该困扰的第一人。果然网上已经有不少C#的JSON工具。
json2csharp 只要你输入json,改网站会自动帮你生成相关的数据类。
配置文件的存储
大多应用程序都需要保存一些自定义的配置数据。在WP7.1后,本地数据的使用变得非常的简单。本地数据库的使用可以参考 如何使用 MVVM 为 Windows Phone 创建本地数据库应用程序
lb.ting最终方案中并没有使用本地数据库。我采用了直接将JSON数据持久化的做法进行数据存储。直接持久化JSON的方案在数据的可靠性比较差,且配置文件格式的修改会成问题。JSON持久化的方案胜在够简单。
后台音频播放
WP7不支持后台进程,为了保证音频的后台播放需要为应用创建对应的后台音频播放代理。按照通常的理解,动态更新播放列表是一个必须的功能。WP7的后台播放代理还就没有更新播放列表的API。在MSDN的例子中播放列表直接写死在程序里。
去网上转了一圈也并没有找到比较好的解决方案。网上的建议是将播放列表保存在数据库或本地文件中,通过创建一个“中间层”的方式同前台UI进行交互。lb.ting最终采用了将播放列表持久化到本地存储区的方案。
baidu图片获取
专辑封面在浏览器可以正常显示,客户端则无法显示专辑图片。怀疑是代码的问题,于是将图片源换为豆瓣,图片正常显示。一通折腾,发现是百度的防盗链引起。将图片的加载换为WebClient实现。
public static void SetImageUrl(Image img, Uri uri, Uri defaultUri)
{
WebClient wc = new WebClient();
wc.Headers["Referer"] = "http://www.baidu.com";
BitmapImage bi = new BitmapImage();
img.Source = bi;
wc.OpenReadCompleted += (s, e) =>
{
try
{
bi.SetSource(e.Result);
}
catch (Exception)
{
//bi.UriSource = defaultUri;
}
};
wc.OpenReadAsync(uri);
}
CSRF(Cross-site request forgery)以及django中的处理方法
CSRF(Cross-site request forgery) 中文名是跨站请求伪造。
简单的说是用户访问了安全的网站A(比如支付宝)并登录,之后用户又访问了问题网站B。网站B包含恶意代码,在访问网站B的时候,网站B的网页偷偷的向网站A发起post(get)请求进行转账操作。由于你已经登录过网站A,网站A误以为请求是用户自己发起的安全请求。在操作成功后你的钱啥的也不见了。
当前Django的处理方法:
在用户访问网站时为用户生成一个CSRF防范用的token并将token保存到cookie中。每次用户发起请求时需要将token的值作为表单字段一同提交。服务端对用户提交的token值同cookie中的token进行比较,如果相同则认为安全。
由于浏览器对cookie的访问有严格的限制,问题网站B无法访问到网站A的cookie无法正确的设置token。
谈“降级论”
降级论 最近这篇文章转的到处都是。因为太火了,虽然赞同文中的大部分观点,还是免不了有些反感。
文中“降级”更准确的说是换个角度,但“降级”这个说法很好的满足了很多“it”人士的虚荣心。或者说很多“it”人一直自以为是的认为自己高级、高智商、如果混的不好是怀才不遇。
相比其他行业,圈内人的喜欢图个嘴快问题更为明显(我也有这个问题,不然就不写这么多了)。在cnbeta、网易、微薄发表各类为图一时之快的言论。大意都是XX公司做的XX很烂、XX很烂、黑幕XX,总之这世界上几乎没什么好的。在费了很多口水后,要说行动则为零。如果你的脚步都不曾迈出又谈何改变。
作者在文中谈及了自己此前“失败”的经验,但我认为这并算不上失败。尽管商业上是失败的,但将一个网站、一个APP做到他文中的程度,从一定程度上说已经非常成功了。能成功的做出了一个受欢迎的产品,并不是一件容易的事。作者的成功并不是一夜暴富的结果,作者的成功的背后是他前期的积累。如果作者继续一心做“纯互联网”产品,也未必不会成功。
互联网渗透入传统行业,这已经成了必然。现阶段是电商的活跃,接下来必将更深度的渗透下去。互联网人做传统行业,和传统行业的人做互联网其实都是各有利弊的。并不能说互联网人做传统行业就“降级”就容易。互联网有互联网的门槛有互联网的问题,传统行业也是。
就如丁磊的养猪,养了好几年的猪,猪到现在都没有出栏。在线出版也算是传统行业迈向互联网的一个尝试。“印客网”有段时间看上去发展的还不错,也关门了。
做什么都有难度,并不是做互联网就高级了。
有想法就行动,用行动去将想法变成现实。
替换博客的代码高亮插件为“Crayon Syntax Highlighter”
此前一直对博客的代码高亮插件不是很满意。以前用的插件,默认配色方案不是太美观,而且配色方案的修改还不是一件容易的事。
将代码高亮插件换到 Crayon Syntax Highlighter 。现在的这个插件还是让我比较满意的。
插件演示
# -*- coding: UTF-8 -*-
import os
from contextlib import contextmanager as __ctxmgr
from fabric.api import *
from fabric.decorators import runs_once
import fabsettings as cfg
#host settings
env.hosts = ['vicalloy@jstwind.com']
env.password = cfg.PASSWORD
#env.passwords = fabsettings.PASSWORDS
#custom settings
env.deploy_dir = '/home/vicalloy/webapps/jstwind/timeline-site'
env.activate = 'source %s' % os.path.join(env.deploy_dir, 'env/bin/activate')
env.mg = 'python %s' % os.path.join(env.deploy_dir, 'sites', 'manage.py ')
@__ctxmgr
def __virtualenv():
with cd(env.deploy_dir):
with prefix(env.activate):
yield
def reload():
wsgi = os.path.join(env.deploy_dir, 'deploy', 'dj_scaffold.wsgi')
run("touch %s" % wsgi)
def update():
with __virtualenv():
run("git pull")
run('%s syncdb' % env.mg)
run('%s migrate' % env.mg)
run('%s compress --force' % env.mg)
reload()