Python:如何将Twisted用作SUDS的传输?
我有一个项目,是基于Twisted来和网络设备进行通信的,现在我想为一个新的供应商(Citrix NetScaler)添加支持,这个供应商的API是SOAP。不过,Twisted对SOAP的支持还是依赖于一个叫SOAPpy
的库,而这个库已经很久没有更新了。实际上,直到我问这个问题的时候(我刚查过),twisted.web.soap
已经有21个月没更新了!
我想问问有没有人愿意分享一下他们在使用Twisted的优秀异步传输功能和SUDS结合方面的经验。看起来把一个自定义的Twisted传输功能放进SUDS的Client.options.transport
里是个不错的选择,但我就是有点搞不清楚怎么操作。
我确实想出了一个方法,可以通过twisted.internet.threads.deferToThread()
异步调用SOAP方法,但我觉得这有点像是变通的办法。
下面是我做的一个例子,给你们一个参考:
# netscaler is a module I wrote using suds to interface with NetScaler SOAP
# Source: http://bitbucket.org/jathanism/netscaler-api/src
import netscaler
import os
import sys
from twisted.internet import reactor, defer, threads
# netscaler.API is the class that sets up the suds.client.Client object
host = 'netscaler.local'
username = password = 'nsroot'
wsdl_url = 'file://' + os.path.join(os.getcwd(), 'NSUserAdmin.wsdl')
api = netscaler.API(host, username=username, password=password, wsdl_url=wsdl_url)
results = []
errors = []
def handleResult(result):
print '\tgot result: %s' % (result,)
results.append(result)
def handleError(err):
sys.stderr.write('\tgot failure: %s' % (err,))
errors.append(err)
# this converts the api.login() call to a Twisted thread.
# api.login() should return True and is is equivalent to:
# api.service.login(username=self.username, password=self.password)
deferred = threads.deferToThread(api.login)
deferred.addCallbacks(handleResult, handleError)
reactor.run()
这个方法按预期工作,能在api.login()
调用完成之前不阻塞返回。但正如我所说的,这种做法让我觉得不太对劲。
提前感谢任何帮助、指导、反馈、批评、侮辱或完整的解决方案。
更新:我找到的唯一解决方案是twisted-suds,这是一个修改过的Suds分支,能够与Twisted一起使用。
1 个回答
在Twisted的上下文中,transport的默认理解可能是实现了twisted.internet.interfaces.ITransport
的东西。在这个层面上,你基本上是在处理通过某种套接字(比如UDP、TCP和SSL,这三种最常用)发送和接收的原始字节。这其实不是SUDS/Twisted集成库所关注的内容。相反,你需要的是一个HTTP客户端,SUDS可以用它来发送必要的请求,并且能够展示所有的响应数据,以便SUDS可以判断结果是什么。也就是说,SUDS并不关心网络上的原始字节,它关心的是HTTP请求和响应。
如果你查看twisted.web.soap.Proxy
的实现(这是Twisted Web SOAP API的客户端部分),你会发现它其实做的事情不多。大约20行代码把SOAPpy
和twisted.web.client.getPage
连接起来。也就是说,它就是按照我上面描述的方式把SOAPpy接入到Twisted中。
理想情况下,SUDS应该提供一些类似于SOAPpy.buildSOAP
和SOAPpy.parseSOAPRPC
的API(也许这些API会复杂一些,或者接受更多参数——我不是SOAP专家,所以不确定SOAPpy的API是否缺少什么重要的东西——但基本思路应该是一样的)。这样你就可以基于SUDS编写类似于twisted.web.soap.Proxy
的东西。如果twisted.web.client.getPage
对请求的控制不够,或者对响应的信息不够,你也可以使用twisted.web.client.Agent
,这是一个最近引入的,提供了对整个请求/响应过程更大的控制。但这其实和现在基于getPage
的代码是同样的思路,只是实现上更灵活、更丰富。
刚刚查看了Client.options.transport
的API文档,听起来SUDS的transport基本上就是一个HTTP客户端。这样的集成问题在于,SUDS想要发送请求后立即获取响应。由于Twisted主要是基于回调的,基于Twisted的HTTP客户端API无法立即返回响应给SUDS。它只能返回一个Deferred
(或等效的东西)。
这就是为什么如果反转这种关系,事情会更好。与其给SUDS一个HTTP客户端,不如把SUDS和HTTP客户端交给第三段代码,让它来协调这些交互。
不过,创建一个基于Twisted的SUDS transport(也就是HTTP客户端)让事情运作并不是不可能。Twisted主要使用Deferred
(也就是回调)来暴露事件,并不意味着这是它唯一的工作方式。通过使用像greenlet
这样的第三方库,可以提供一个基于协程的API,在这个API中,异步操作的请求涉及从一个协程切换到另一个协程,而事件则通过切换回原来的协程来传递。有一个项目叫做corotwine,可以做到这一点。可能可以用这个来为SUDS提供它想要的HTTP客户端API;然而,这并不保证。它依赖于SUDS在突然插入上下文切换时不会崩溃。这是SUDS一个非常微妙和脆弱的特性,未来的版本中SUDS开发者可能会无意中改变它,所以即使现在能让它工作,这也可能不是理想的解决方案(除非你能得到SUDS维护者的合作,承诺在这种配置下测试他们的代码,以确保它继续工作)。
顺便提一下,Twisted Web的SOAP支持仍然基于SOAPpy,并且近两年来没有修改的原因是没有出现明确的SOAPpy替代品。虽然有很多竞争者(Python有哪些SOAP客户端库,它们的文档在哪里?涵盖了其中的一些)。如果情况有所稳定,更新Twisted内置的SOAP支持可能是有意义的。在此之前,我认为分开做这些集成库更有意义,这样它们可以更容易地更新,并且Twisted本身不会变成一堆没人想要的不同SOAP集成(这会比现在的情况更糟,现在只有一个没人想要的SOAP集成模块)。