背景:
最近找到一個異步數據庫驅動,TorMySQL,ab測試後效果是其他幾個異步驅動最好的,遂用之。
因爲儘可能業務邏輯層和數據訪問層分離,並且考慮到單一負責原則,把每一個數據庫的操作簡稱爲service,如user_service,work_service之類的。
很多情況下,每一個邏輯業務都是要N個service合作才完成的。
所以就不在單個service中try except,而是在業務邏輯層中try except。
一是:
少寫很多代碼,不然每一個service都要try except之後,logging.error(ex, exc_info=1)打印traceback,然後再raise ex,多麻煩...
二是:
考慮到很多情況下有用到事務,只能在業務邏輯層捕獲exception,捕獲到就conn.rollback(),沒捕獲到就conn.commit()。
然而,我上面這種設計竟然沒捕獲到traceback,簡直匪夷所思。
好了,背景交代完畢。
版本:
python 2.7
tormysql==0.2.9
tornado==4.2
測試1:
import logging
from tornado.ioloop import IOLoop
from tornado import gen
import tormysql
pool = tormysql.ConnectionPool(
max_connections = 20, #max open connections
idle_seconds = 7200, #conntion idle timeout time, 0 is not timeout
wait_connection_timeout = 3, #wait connection timeout
host = "127.0.0.1",
user = "root",
passwd = "123qwe",
db = "chanzai_dev",
charset = "utf8"
)
@gen.coroutine
def aa(conn):
sql = "SELECT * FROM UserDevice where user_id = %s" % 100002
with conn.cursor() as cursor:
yield cursor.execute(sql)
cursor.fetchone()
# 下面是故意出錯代碼
a = '10'
b = a / 10
print b
@gen.coroutine
def bb(conn):
yield aa(conn)
@gen.coroutine
def cc():
with (yield pool.Connection()) as conn:
try:
yield bb(conn)
except KeyError:
pass
except Exception, ex:
yield conn.rollback()
logging.error(ex, exc_info=1)
ioloop = IOLoop.instance()
ioloop.run_sync(cc)
測試結果:G:\projects\myApp\tmp>a.py
ERROR:root:unsupported operand type(s) for /: 'str' and 'int'
None
測試2:
import logging
from tornado.ioloop import IOLoop
from tornado import gen
import tormysql
pool = tormysql.ConnectionPool(
max_connections = 20, #max open connections
idle_seconds = 7200, #conntion idle timeout time, 0 is not timeout
wait_connection_timeout = 3, #wait connection timeout
host = "127.0.0.1",
user = "root",
passwd = "123qwe",
db = "chanzai_dev",
charset = "utf8"
)
@gen.coroutine
def aa(conn):
sql = "SELECT * FROM UserDevice where user_id = %s" % 100002
with conn.cursor() as cursor:
yield cursor.execute(sql)
cursor.fetchone()
# 下面是故意出錯代碼
a = '10'
b = a / 10
print b
@gen.coroutine
def bb(conn):
yield aa(conn)
@gen.coroutine
def cc():
with (yield pool.Connection()) as conn:
try:
yield bb(conn)
except KeyError:
pass
except Exception, ex:
logging.error(ex, exc_info=1)
yield conn.rollback()
ioloop = IOLoop.instance()
ioloop.run_sync(cc)
測試結果:G:\projects\myApp\tmp>a.py
ERROR:root:unsupported operand type(s) for /: 'str' and 'int'
Traceback (most recent call last):
File "G:\projects\myApp\tmp\a.py", line 40, in cc
yield bb(conn)
File "G:\Python27\lib\site-packages\tornado\gen.py", line 1015, in run
value = future.result()
File "G:\Python27\lib\site-packages\tornado\concurrent.py", line 237, in result
raise_exc_info(self._exc_info)
File "G:\Python27\lib\site-packages\tornado\gen.py", line 1021, in run
yielded = self.gen.throw(*exc_info)
File "G:\projects\myApp\tmp\a.py", line 34, in bb
yield aa(conn)
File "G:\Python27\lib\site-packages\tornado\gen.py", line 1015, in run
value = future.result()
File "G:\Python27\lib\site-packages\tornado\concurrent.py", line 237, in result
raise_exc_info(self._exc_info)
File "G:\Python27\lib\site-packages\tornado\gen.py", line 1024, in run
yielded = self.gen.send(value)
File "G:\projects\myApp\tmp\a.py", line 30, in aa
b = a / 10
TypeError: unsupported operand type(s) for /: 'str' and 'int'
G:\projects\myApp\tmp>
觀察結果:
except Exception, ex:
logging.error(ex, exc_info=1)
yield conn.rollback()
和 except Exception, ex:
yield conn.rollback()
logging.error(ex, exc_info=1)
前者才能得到想要的結果,爲什麼有後者這種寫法呢,是因爲,想着對於數據庫的操作出錯了還是儘早rollback爲好,沒想到就踩了坑。