简体   繁体   English

使用带有数据库插入的Twisted AMP

[英]Using Twisted AMP with Database insertion

I am learning how to use Twisted AMP. 我正在学习如何使用Twisted AMP。 I am developing a program that sends data from a client to a server and inserts the data in a SQLite3 DB. 我正在开发一个程序,将数据从客户端发送到服务器并将数据插入SQLite3数据库。 The server then sends back a result to the client which indicates success or error (try and except might not be the best way to do this but it is only a temporary solution while I work out the main problem). 然后服务器将结果发送回客户端,指示成功或错误(尝试和除了可能不是最好的方法,但它只是一个临时的解决方案,而我解决了主要问题)。 In order to do this I modified an example I found that originally did a sum and returned the result, so I realize that this might not be the most efficient way to do what I am trying to do. 为了做到这一点,我修改了一个例子,我发现最初做了一个总和并返回了结果,所以我意识到这可能不是我做的事情最有效的方法。 In particular I am trying to do some timings on multiple insertions (ie send the data to the server multiple times for multiple insertions) and I have included the code I have written. 特别是我试图在多次插入时做一些计时(即多次将数据发送到服务器进行多次插入),并且我已经包含了我编写的代码。 It works but clearly it is not a good way to send multiple data for insertion since I am performing multiple connections before running the reactor. 它工作但很明显它不是一个发送多个数据插入的好方法,因为我在运行reactor之前执行多个连接。

I have tried several ways to get around this including passing the ClientCreator to reactor.callWhenRunning() but you cannot do this with a deferred. 我已经尝试了几种方法来解决这个问题,包括将ClientCreator传递给reactor.callWhenRunning(),但是你不能用延迟来做。

Any suggestions, advice or help with how to do this would be much appreciated. 任何建议,建议或如何做到这一点的帮助将不胜感激。 Here is the code. 这是代码。

Server: 服务器:

from twisted.protocols import amp
from twisted.internet import reactor
from twisted.internet.protocol import Factory
import sqlite3, time

class Insert(amp.Command):
    arguments = [('data', amp.Integer())]
    response = [('insert_result', amp.Integer())]

class Protocol(amp.AMP):
    def __init__(self):     
       self.conn = sqlite3.connect('biomed1.db')
       self.c =self.conn.cursor()
       self.res=None

    @Insert.responder
    def dbInsert(self, data):
        self.InsertDB(data) #call the DB inserter
        result=self.res     # send back the result of the insertion
        return {'insert_result': result}

    def InsertDB(self,data):
      tm=time.time()
      print "insert time:",tm
      chx=data
      PID=2
      device_ID=5
      try:
        self.c.execute("INSERT INTO btdata4(co2_data, patient_Id, sensor_Id) VALUES ('%s','%s','%s')" % (chx, PID, device_ID))    
      except Exception, err:
             print err
             self.res=0
      else:
             self.res=1

      self.conn.commit()


pf = Factory()
pf.protocol = Protocol
reactor.listenTCP(1234, pf) 
reactor.run()

Client: 客户:

from twisted.internet import reactor
from twisted.internet.protocol import ClientCreator
from twisted.protocols import amp
import time

class Insert(amp.Command):
    arguments = [('data', amp.Integer())]
    response = [('insert_result', amp.Integer())]

def connected(protocol):
    return protocol.callRemote(Insert, data=5555).addCallback(gotResult)

def gotResult(result):
    print 'insert_result:', result['insert_result']
    tm=time.time()
    print "stop", tm    

def error(reason):
    print "error", reason

tm=time.time()
print "start",tm
for i in range (10): #send data over ten times
  ClientCreator(reactor, amp.AMP).connectTCP(
     '127.0.0.1', 1234).addCallback(connected).addErrback(error)

reactor.run()

End of Code. 代码结束。

Thank you. 谢谢。

Few things which will improve your Server code. 几乎没有什么能改善您的服务器代码。

First and foremost: The use of direct database access functions is discouraged in twisted, as they normally causes block. 首要的是:不鼓励使用直接数据库访问功能,因为它们通常会导致阻塞。 Twisted has nice abstraction for database access which provides twisted approach to db connection - twisted.adbapi Twisted为数据库访问提供了很好的抽象,它为数据库连接提供了扭曲的方法 - twisted.adbapi

Now on to reuse of db connection: If you want to reuse certain assets (like database connection) across a number of Protocol instances, you should initialize those in constructor of Factory or if you dont fancy initiating such things at a launch time, create an resource access method, which will initiate resource upon first method call then assign it to class variable and return that on subsequent calls. 现在开始重用数据库连接:如果你想在多个协议实例中重用某些资产(比如数据库连接),你应该在工厂的构造函数中初始化那些,或者你不想在启动时启动这些东西,创建一个资源访问方法,它将在第一次方法调用时启动资源,然后将其分配给类变量并在后续调用时返回该变量。

When Factory creates a specific Protocol intance, it will add a reference to itself inside the protocol, see line 97 of twisted.internet.protocol 当Factory创建特定的Protocol intance时,它将在协议内添加对自身的引用,参见twisted.internet.protocol的第97行。

Then within your Protocol instance, you can access shared database connection instance like: 然后在您的Protocol实例中,您可以访问共享数据库连接实例,如:

self.factory.whatever_name_for_db_connection.doSomething() 

Reworked Server code (I dont have python, twisted or even decent IDE available, so this is pretty much untested, some errors are to be expected) 重做的服务器代码(我没有python,扭曲甚至可靠的IDE,所以这几乎没有测试,有些错误是预期的)

from twisted.protocols import amp
from twisted.internet import reactor
from twisted.internet.protocol import Factory
import time

class AMPDBAccessProtocolFactory(Factory):
    def getDBConnection(self):
        if 'dbConnection' in dir(self):
            return self.dbConnection
        else:
            self.dbConnection = SQLLiteTestConnection(self.dbURL)
            return self.dbConnection

class SQLLiteTestConnection(object):
    """
    Provides abstraction for database access and some business functions.
    """
    def __init__(self,dbURL):
        self.dbPool =  adbapi.ConnectionPool("sqlite3" , dbURL,  check_same_thread=False)

    def insertBTData4(self,data):
        query = "INSERT INTO btdata4(co2_data, patient_Id, sensor_Id) VALUES (%s,%s,%s)" 
        tm=time.time()
        print "insert time:",tm
        chx=data
        PID=2
        device_ID=5
        dF = self.dbPool.runQuery(query,(chx, PID, device_ID)) 
        dF.addCallback(self.onQuerySuccess,insert_data=data)
        return dF
    def onQuerySuccess(self,insert_data,*r):
        """
        Here you can inspect query results or add any other valuable information to be parsed at client.
        For the test sake we will just return True to a customer if query was a success.
        original data available at kw argument insert_data
        """
        return True


class Insert(amp.Command):
    arguments = [('data', amp.Integer())]
    response = [('insert_result', amp.Integer())]

class MyAMPProtocol(amp.AMP):

    @Insert.responder
    def dbInsert(self, data):
        db = self.factory.getDBConnection()
        dF = db.insertBTData4(data)
        dF.addErrback(self.onInsertError,data)
        return dF

    def onInsertError(self, error, data):
        """
        Here you could do some additional error checking or inspect data 
        which was handed for insert here. For now we will just throw the same exception again
        so that the client gets notified
        """
        raise error

if __name__=='__main__':
    pf = AMPDBAccessProtocolFactory()
    pf.protocol = MyAMPProtocol
    pf.dbURL='biomed1.db'
    reactor.listenTCP(1234, pf) 
    reactor.run()

Now on to the client. 现在转到客户端。 IF AMP follows the overall RPC logic (cant test it currently) it should be able to peruse the same connection across a number of calls. 如果AMP遵循整个RPC逻辑(当前无法测试),它应该能够在多个调用中仔细阅读相同的连接。 So I have created a ServerProxy class which will hold that perusable protocol instance and provide abstraction for calls: 所以我创建了一个ServerProxy类,它将保存该perusable协议实例并为调用提供抽象:

from twisted.internet import reactor
from twisted.internet.protocol import ClientCreator
from twisted.protocols import amp
import time

class Insert(amp.Command):
    arguments = [('data', amp.Integer())]
    response = [('insert_result', amp.Integer())]

class ServerProxy(object):
    def connected(self,protocol):
        self.serverProxy = protocol # assign protocol as instance variable
        reactor.callLater(5,self.startMultipleInsert) #after five seconds start multiple insert procedure

    def remote_insert(self,data):
        return self.serverProxy.callRemote(Insert, data)

    def startMultipleInsert(self):
        for i in range (10): #send data over ten times
            dF = self.remote_insert(i)
            dF.addCallback(self.gotInsertResult)
            dF.addErrback(error)

    def gotInsertResult(self,result):
        print 'insert_result:', str(result)
        tm=time.time()
        print "stop", tm    

def error(reason):
    print "error", reason


def main():
    tm=time.time()
    print "start",tm
    serverProxy = ServerProxy()
    ClientCreator(reactor, amp.AMP).connectTCP('127.0.0.1', 1234).addCallback(serverProxy.connected).addErrback(error)
    reactor.run()    

if __name__=='__main__':
    main()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM