在RMDB中,由于不存在select-test-insert的原子操作,因此要想达到不存在则插入的效果,就必须在SELECT语句中加锁。但往往对于复杂的查询条件来说,由于涉及锁的类型(Record/Gap/Next-key),加锁操作会带来很大的性能隐患,因此采用容错检查的方式可能会是一个比较好的实现方案(另一种做法是用INSERT...WHERE NOT EXISTS)。以MySQL与SQLAlchemy为例:

SQLAlchemy get-or-create method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from sqlalchemy.exc import IntegrityError
from sqlalchemy.sql.expression import ClauseElement

def get_or_create(session, model, defaults={}, **kwargs):
"""
插入新数据, 若数据已存在则返回已有记录. 若数据库存在唯一约束, 保证不会重复插入

:param session: SQLAlchemy `Session`
:param model: 数据表映射
:param defaults: 新数据的参数默认值
:param kwargs: 查询条件
"""

try:
query = session.query(model).filter_by(**kwargs)

instance = query.first()

if instance:
return instance, True
else:
session.begin(nested=True)
try:
params = dict((k, v) for k, v in kwargs.iteritems() if not isinstance(v, ClauseElement))
params.update(defaults)

instance = model(**params)

session.add(instance)
session.commit()

return instance, False
except IntegrityError as e:
session.rollback()
instance = query.one()

return instance, True
except Exception as e:
session.rollback()
raise e
阅读全文 »

近年来前端技术可谓发展迅猛,受益于计算机与浏览器内核的性能提升,如今网站前端架构可以设计得更加庞大,许多原本服务器端处理的业务逻辑都挪到了前端进行,因此也相应地出现了许多MVC(Backbone)、MVVM(KnockoutJS)等前端框架和CommonJSRequireJS等接口规范。而为了更好的用户体验,许多现代的站点都采用了B/S只通过数据接口通信的策略,用户一次加载,随后的浏览与操作中网页内容都由JS代码控制加载与渲染完成。这带来的挑战是,一个页面请求将带来更多的代码网络传输(若是没有进行压缩合并那影响就更明显了),同时在一个页面容器内浏览器必须解析更多的JS代码,这二者都严重地影响着页面加载时间。对此,一个比较好的解决办法是将前端结构模块化,然后在初始载入中只读取核心功能部分的代码,随后根据用户的操作加载对应的模块,也就是代码动态加载技术。除了通过Ajax配合eval实现外,一般脚本的动态加载都是通过生成Script节点完成,下面是一个简单的实现:

Dynamic Script Loading
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
;(function () {
"use strict"

var doc = document
var head = doc.head ||
doc.getElementsByTagName('head')[0] ||
doc.documentElement

function scriptOnload(node, callback) {
node.onload = node.onerror = node.onreadystatechange = function () {
if (/loaded|complete|undefined/.test(node.readyState)) {
node.onload = node.onerror = node.onreadystatechange = null
if (node.parentNode) { head.removeChild(node) }
node = undefined
if (typeof callback !== 'undefined') { callback() }
}
}
}

window.scriptOnload = scriptOnload
})()

其中在回调方法中head.removeChild(node)等移除节点的语句是为了避免IE8以下浏览器中的内存泄露。以加载jQuery为例,使用方式为:

Demo
1
2
3
4
5
6
7
8
9
var ns = {}

var jqueryNode = doc.createElement('script')
jqueryNode.async = 'async'
jqueryNode.src = '/path/to/jquery.js'
window.scriptOnload(jqueryNode, function () {
ns.$ = jQuery.noConflict(true)
})
head.appendChild(jqueryNode)

项目中需要实现一个通过iframe嵌入到其它网站中的辅助工具,由于两个域间需要大量的数据交互操作,因此最近研究了一下比较可行的跨域通信方案,其中postMessage目测是最好的一个。虽然只有现代的浏览器才支持这个方法,但考虑到在其他需求中我使用addEventListener的Capture模式已经把IE6什么的给排除掉了,因此兼容性反倒不成问题。随手给个例子:

Page A postMessageA.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>

<body>
<p></p>
<div style="border: 1px solid whiteSmoke">
<iframe src="./postMessageB.html" frameborder="0" style="width: 100%;"></iframe>
</div>
</body>

<script>
;(function () {

var theSong = '白山兮高高,黑水兮滔滔,有此山川之伟大,故生民质朴而雄豪,地所产者丰且美,俗所习者勤与劳,愿以此为基础,应世界进化之洪潮,沐三民主义之圣化,仰青天白日之昭昭,痛国难之未已,恒怒火之中烧,东夷兮狡诈,北虏兮矫骁,灼灼兮其目,霍霍兮其刀,苟捍卫之不力,宁宰割之能逃,惟卧薪而尝胆,庶雪耻于一朝,唯知行合一方为责,无取乎空论之滔滔,唯积学养气可致用,无取乎狂热之呼号,其自迩以行远,其自卑以登高,爱校、爱乡、爱国、爱人类,期终达于世界大同之目标,使命如此其重大,能不奋勉乎吾曹,能不奋勉乎吾曹!'
var lyrics = theSong.split(',')

var iframe = document.getElementsByTagName('iframe')[0]

var sing = function ( song ) {
document.getElementsByTagName('p')[0].innerHTML = song || '...'
}

var chorus = function () {
if (lyrics.length) {
sing(lyrics.shift())
iframe.contentWindow.postMessage(lyrics.shift(), '*')
}
}

var receiver = function ( event ) {
setTimeout(function () {
chorus()
}, 2000)
}
window.addEventListener('message', receiver, false)

window.onload = chorus
})()
</script>

</html>
阅读全文 »