扭曲的应用程序丢失了带有重新连接选项的MySQL连接事件

时间:2018-09-26 07:37:00

标签: python mysql twisted

我曾经在呼吸器IoT TCP服务器中使用过Twisted,现在我迁移了代码以服务于连接的门锁项目。这两个项目运行在同一台云服务器上,并通过两个隔离的MySQL帐户连接到同一台MySQL服务器。但是,后者偶尔会因MySQL连接消失而引发错误。

这是(简体)代码。

#!/usr/bin/env python
# coding: utf-8

from twisted.internet import defer
from twisted.internet import protocol, reactor
from twisted.internet.protocol import Factory, ClientCreator
from twisted.enterprise import adbapi
from twisted.python import log
from twisted.protocols.policies import TimeoutMixin

import txredisapi as redis

sys.path.append("..")   # Add package path, will be installed into python path later
from connector.aescrypt import aescrypt
from connector.epic_config import EpicConf

conf = EpicConf().loadConf()
dbid = string.atoi(conf['Redisdbid'])
rcs = redis.lazyConnection(password=conf['RedisPassword'], dbid=dbid, reconnect=True)
dbpool = adbapi.ConnectionPool("MySQLdb",db=conf['DbName'],user=conf['DbAccount'],\
        passwd=conf['DbPassword'],host=conf['DbHost'],\
        use_unicode=True,charset=conf['DbCharset'],cp_reconnect=True)
# cp_reconnect = True is enabled.

class EpicState(object):
    pass

class AesCryptor(object):
    pass

class PlainTCP(protocol.Protocol, TimeoutMixin):
    def __init__(self):
        pass

    def dataReceived(self, data):
        self.attackFilter(data)
        self.dataDecoded(data)

    def dataDecoded(self, data):
        if "HELO" in data:
            self.onHello(data)
        else:
            print("Invalid Command")

@defer.inlineCallbacks
def onHello(self, data):
    global rcs
    self.epicstate = EpicState.HELO
    try:
        _, self.protocolVersion, self.devId = data.split(',')
    except ValueError, e:
        error = "onHello_err: {}".format(e)
        print error
        self.write(error)
        self.transport.loseConnection()
        defer.returnValue(False)

    if self.protocolVersion<1:
        # replace "NACK,-1"
        self.transport.write(b"HELO,{},{},##############\r\n".format(0xFF,0xFF))

    ctx = "%s@%s"%(self.devId, self.peer.host)
    print "[dbg] isDevConnectedAlready(%s)"%(ctx)
    isConnected = yield rcs.sismember(self.factory.onlineDevices, ctx)
    print "isConnected={}".format(isConnected)

    if isConnected:
        e = "Duplicate connections"
        error = "onHello_err: {}".format(e)
        print error
        # replace "NACK,-2"
        self.transport.write(b"HELO,{},{}\r\n".format(0xFE,0xFE))
        # Reopen for a moment
        self.devId = None
        self.transport.loseConnection()
        defer.returnValue(False)
    else:
        self.appendDevToDbCache(rcs)

    query = "SELECT `uid`,`deviceWriteKey`,`serverAccessKey`,`aesKey`,`aesIV`,`productId` FROM `device` WHERE `id`={} LIMIT 1".format(self.devId)
    row = yield dbpool.runQuery(query)
    if row:
        self.uid = row[0][0]
        self.writekey = row[0][1]
        self.serverkey = row[0][2]
        self.aes_key = row[0][3]
        self.aes_iv = row[0][4]
        self.productId = row[0][5]
        self.start = int(time.time())
        self.nonce = uuid.uuid4().hex.upper()[:16]
        self.transport.write(b"HELO,{},{}\r\n".format(self.start, self.nonce))
        self.epicstate = EpicState.IDLE
    else:
        print "Device {} No Match from DB".format(self.devId)
        self.transport.write(b"NACK\r\n") 
        self.transport.loseConnection()

    query = "SELECT `productId`, `version`,`url` FROM `firmware` WHERE `productId`={} ORDER BY `id` DESC LIMIT 1".format(self.productId)
    row = yield dbpool.runQuery(query)
    if row:
        self.otafile = row[0][2]

可以肯定的是,如果我使用Python模拟器来测试服务器,则MySQL连接将始终运行良好,并且永远不会丢失连接。但是,如果我使用物理设备连接到服务器,有时会看到此类错误。

2018-09-25 14:21:54+0800 Peer: 73.202.44.233:1769 Conn+: 1:3
2018-09-25 14:21:54+0800 ASC[9]:HELO,1,16
2018-09-25 14:21:54+0800 [dbg] isDevConnectedAlready(16@73.202.44.233)
2018-09-25 14:21:54+0800 isConnected=False
2018-09-25 14:21:54+0800 [dbg] appendDevToDbCache(16@73.202.44.233)
2018-09-25 14:21:54+0800 Rollback failed
    Traceback (most recent call last):
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/python/threadpool.py", line 196, in _worker
        result = context.call(ctx, function, *args, **kwargs)
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/python/context.py", line 118, in callWithContext
        return self.currentContext().callWithContext(ctx, func, *args, **kw)
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/python/context.py", line 81, in callWithContext
        return func(*args,**kw)
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/enterprise/adbapi.py", line 455, in _runInteraction
        conn.rollback()
    --- <exception caught here> ---
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/enterprise/adbapi.py", line 56, in rollback
        self._connection.rollback()
    _mysql_exceptions.OperationalError: (2006, 'MySQL server has gone away')

2018-09-25 14:21:54+0800 Rollback failed
    Traceback (most recent call last):
      File "/usr/lib/python2.7/threading.py", line 504, in run
        self.__target(*self.__args, **self.__kwargs)
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/python/threadpool.py", line 196, in _worker
        result = context.call(ctx, function, *args, **kwargs)
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/python/context.py", line 118, in callWithContext
        return self.currentContext().callWithContext(ctx, func, *args, **kw)
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/python/context.py", line 81, in callWithContext
        return func(*args,**kw)
    --- <exception caught here> ---
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/enterprise/adbapi.py", line 455, in _runInteraction
        conn.rollback()
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/enterprise/adbapi.py", line 70, in rollback
        raise ConnectionLost()
    twisted.enterprise.adbapi.ConnectionLost: 

2018-09-25 14:21:54+0800 Unhandled error in Deferred:
2018-09-25 14:21:54+0800 Unhandled Error
    Traceback (most recent call last):
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/internet/defer.py", line 424, in errback
        self._startRunCallbacks(fail)
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/internet/defer.py", line 491, in _startRunCallbacks
        self._runCallbacks()
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/internet/defer.py", line 578, in _runCallbacks
        current.result = callback(current.result, *args, **kw)
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/internet/defer.py", line 1163, in gotResult
        _inlineCallbacks(r, g, deferred)
    --- <exception caught here> ---
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/internet/defer.py", line 1105, in _inlineCallbacks
        result = result.throwExceptionIntoGenerator(g)
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/python/failure.py", line 389, in throwExceptionIntoGenerator
        return g.throw(self.type, self.value, self.tb)
      File "/home/allankliu/Epic_Connector/GLINK_plus/secure_server/EpicGlinkTcpServer.py", line 414, in onHello
        row = yield dbpool.runQuery(query)
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/python/threadpool.py", line 196, in _worker
        result = context.call(ctx, function, *args, **kwargs)
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/python/context.py", line 118, in callWithContext
        return self.currentContext().callWithContext(ctx, func, *args, **kw)
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/python/context.py", line 81, in callWithContext
        return func(*args,**kw)
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/enterprise/adbapi.py", line 448, in _runInteraction
        result = interaction(trans, *args, **kw)
      File "/usr/local/lib/python2.7/dist-packages/Twisted-15.0.0-py2.7-linux-i686.egg/twisted/enterprise/adbapi.py", line 462, in _runQuery
        trans.execute(*args, **kw)
      File "/usr/lib/python2.7/dist-packages/MySQLdb/cursors.py", line 174, in execute
        self.errorhandler(self, exc, value)
      File "/usr/lib/python2.7/dist-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
        raise errorclass, errorvalue
    _mysql_exceptions.OperationalError: (2006, 'MySQL server has gone away')

2018-09-25 14:21:54+0800 [DBG] devId=None, peer=None, cancel checkCommand

从上面的日志文件中,系统抛出

的异常
  • _mysql_exceptions.OperationalError:(2006年,“ MySQL服务器已消失”)
  • twisted.enterprise.adbapi.ConnectionLost
  • 与相同的MySQL 2006错误相关的回滚异常。

都是由

引起的
row = yield dbpool.runQuery(query)

用于从RDBS加载与现有设备相关的所有元数据(AES密钥/ IV /访问/其他密钥),其中大部分用于以后的相互认证。

为了捕获由一条语句引起的异常。我将代码更改如下:

@defer.inlineCallbacks
def onHello(self, data):
    global rcs
    self.epicstate = EpicState.HELO
    try:
        _, self.protocolVersion, self.devId = data.split(',')
    except ValueError, e:
        error = "onHello_err: {}".format(e)
        print error
        self.write(error)
        self.transport.loseConnection()
        defer.returnValue(False)

    if self.protocolVersion<1:
        # replace "NACK,-1"
        self.transport.write(b"HELO,{},{},##############\r\n".format(0xFF,0xFF))

    ctx = "%s@%s"%(self.devId, self.peer.host)
    print "[dbg] isDevConnectedAlready(%s)"%(ctx)
    isConnected = yield rcs.sismember(self.factory.onlineDevices, ctx)
    print "isConnected={}".format(isConnected)

    if isConnected:
        e = "Duplicate connections"
        error = "onHello_err: {}".format(e)
        print error
        # replace "NACK,-2"
        self.transport.write(b"HELO,{},{}\r\n".format(0xFE,0xFE))
        # Reopen for a moment
        self.devId = None
        self.transport.loseConnection()
        defer.returnValue(False)
    else:
        self.appendDevToDbCache(rcs)

    query = "SELECT `uid`,`deviceWriteKey`,`serverAccessKey`,`aesKey`,`aesIV`,`productId` FROM `device` WHERE `id`={} LIMIT 1".format(self.devId)
    try:
        row = yield dbpool.runQuery(query)
    except:
        self.transport.write(b"MySQL error\r\n")
        self.transport.loseConnection()
        defer.returnValue(False)

    if row:
        self.uid = row[0][0]
        self.writekey = row[0][1]
        self.serverkey = row[0][2]
        self.aes_key = row[0][3]
        self.aes_iv = row[0][4]
        self.productId = row[0][5]
        self.start = int(time.time())
        self.nonce = uuid.uuid4().hex.upper()[:16]
        self.transport.write(b"HELO,{},{}\r\n".format(self.start, self.nonce))
        self.epicstate = EpicState.IDLE
    else:
        print "Device {} No Match from DB".format(self.devId)
        self.transport.write(b"NACK\r\n")
        self.transport.loseConnection()
        defer.returnValue(False)

    query = "SELECT `productId`, `version`,`url` FROM `firmware` WHERE `productId`={} ORDER BY `id` DESC LIMIT 1".format(self.productId)
    try:
        row = yield dbpool.runQuery(query)
    except:
        self.transport.write(b"MySQL error\r\n")
        self.transport.loseConnection()
        defer.returnValue(False)

    if row:
        self.otafile = row[0][2]

但是我仍然对adbapi组件的重新连接选项感到困惑。

为什么在同一台云服务器中使用相同的MySQL连接代码时,它为什么不能像以前的版本那样工作?我与我的云供应商进行了双重检查,连接没有任何限制。有任何调试此类问题的线索吗?

除了代码好得足以捕获此类组合异常之外,我是否还在尝试?

0 个答案:

没有答案