`
liukl
  • 浏览: 31591 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
最近访客 更多访客>>
社区版块
存档分类
最新评论

用Python实现C系统与Java/PHP/Ruby等Web系统的对接

阅读更多

一、问题的提出

因为web和原有系统互通的需要,而原系统已经有基于tcp的长连接API。由于web客户端是并发性的访问,而老系统的API是C2C的对等长连接通信,不允许多个客户端同时连接一个服务器。因此要在原有C系统与web间搭建起互通的适配层,将web的通信协议与内部系统间对接起。

二、解决方案

早先曾考虑用JNI的方式把api包装下,但JNI在处理字符串的时候,经常会有字符集转换的问题。再则,JNI搞定了java,那php、RoR等又得一个个的单独实现,比较费时,因此改用语言无关的方案。

基于这个需求,想来python最合适干这个,原因有三:

1、python标准库里的协议集最全面,随手可得不说,还有标准的doc和一些立即可以run的example,这对于为web提供服务来说,最容易不过;

2、python对c的集成也是非常容易的

3、python代码的简洁性是有目共睹的,包括简洁的语法、大量唾手可得的标志模块,如日志系统、线程等等

 

选定python,就着手开始下一步,选定与web的协议。考虑到web client不能也定死到python,应该允许使用php、java等任意web语言使用本模块的可能性。因此可选的方案必须要符合语言无关的特点(当然也是平台无关的),可选的方案有:webservice、xmlrpc、REST等。因为ws过于复杂而REST还需要自己定义参数格式,最后选择了xmlrpc这种既简洁又具有参数类型的协议(xmlrpc在参数的前面添加类别定义信息,一般的xmlrpc协议栈都会根据该定义将数据转换到与语言对应的类型上来)

至于原有CAPI的包装,已有非常多的文章,可以google下,笔者常用的是Boost.Python,非常简单,差不多是公式性代码,这里不再藉述。

 三、具体实现

接下来就开始实现这个xmlrpcserver,python里就有一个SimpleXMLRPCServer的,在python的帮助里还有示例代码:

服务器侧:
from SimpleXMLRPCServer import SimpleXMLRPCServer

# Create server
server = SimpleXMLRPCServer(("localhost", 8000))
server.register_introspection_functions()

# Register pow() function; this will use the value of 
# pow.__name__ as the name, which is just 'pow'.
server.register_function(pow)

# Register a function under a different name
def adder_function(x,y):
    return x + y
server.register_function(adder_function, 'add')

# Register an instance; all the methods of the instance are 
# published as XML-RPC methods (in this case, just 'div').
class MyFuncs:
    def div(self, x, y): 
        return x // y
    
server.register_instance(MyFuncs())

# Run the server's main loop
server.serve_forever()
客户端侧:
import xmlrpclib

s = xmlrpclib.Server('http://localhost:8000')
print s.pow(2,3)  # Returns 2**3 = 8
print s.add(2,3)  # Returns 5
print s.div(5,2)  # Returns 5//2 = 2

# Print list of available methods
print s.system.listMethods() 
windows下开两个pyshell,先运行服务器侧的代码,然后输入客户端的代码,马上就能看到结果。
一切进展顺利,然后新问题出现了。

三、性能优化与改进

这个适配层很通用,意味着可能被用来做为多个web与多个后台系统间的统一中间层,而这个SimpleServer里没有看到任何并发的概念,因此立即修改了server的一个函数,增加了time.sleep。果然,如我所料,全部调用都是串行的。这在web与后台系统的集成中是会经常发生的。
因此需要解决两个问题:
1、如何把两个请求独立开来处理,不能因为某个调用阻塞了其他所有的请求
2、web有超时的概念,这个xmlrpc该如何处理
先来看第一个问题,google了资料。有人说要改python库的代码,有人说用twisted。
虽然鄙人早先使用VC的时候,曾有过修改MFC代码的时候,但深知这种修改所带来的麻烦。在VC的工程里如果修改了MFC代码,几个人合作的项目要让别人也编译过是多么的麻烦不说,就算是自己过段时间重装了windows后再来修改几个月前的项目,也会遇到不少麻烦,因此不可取。twisted到其网站上查找xmlrpc,得到的答案是这个东西对xmlrpc的支持很勉强。因此还决定采用C++里的常用模式,copy出pyhton里的SimpleXMLRPCServer来,自己修改后重新命名成自己的。其实因为python强大的标准库,修改起来很简单。只需要:
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher, SimpleXMLRPCRequestHandler
from SocketServer import ThreadingTCPServer

try:
    import fcntl
except ImportError:
    fcntl = None
    
class MyXMLRPCServer(ThreadingTCPServer,
                         SimpleXMLRPCDispatcher):
    """Simple XML-RPC server.

    Simple XML-RPC server that allows functions and a single instance
    to be installed to handle requests. The default implementation
    attempts to dispatch XML-RPC calls to the functions or instance
    installed in the server. Override the _dispatch method inhereted
    from SimpleXMLRPCDispatcher to change this behavior.
    """

    allow_reuse_address = True

    def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
                 logRequests=True, allow_none=False, encoding=None):
        self.logRequests = logRequests

        SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
        ThreadingTCPServer.__init__(self, addr, requestHandler)

        # [Bug #1222790] If possible, set close-on-exec flag; if a
        # method spawns a subprocess, the subprocess shouldn't have
        # the listening socket open.
        if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
            flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
            flags |= fcntl.FD_CLOEXEC
            fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
然后把原有代码里的SimpleXMLRPCServer换成MyXMLRPCServer就行了。查看ThreadingTCPServer的代码,可以看到其核心实际上是基类的ThreadingMixIn里的process_request方法,在每个request到来时,都是很简单的启动一个python里的threading:
def process_request(self, request, client_address):
        """Start a new thread to process the request."""
        import threading
        t = threading.Thread(target = self.process_request_thread,
                             args = (request, client_address))
        if self.daemon_threads:
            t.setDaemon (1)
        t.start()
再明显不过,因此多线程问题得到解决。如果实际系统在运行的时候,发现这种“不受控”的开启线程过于“并发”,当然可以自己实现一个ThreadingMixIn,仿照Python库里的方式足组合一把。
遗留问题:
1、API调用的超时问题该如何解决
2、 Java客户端代码
6
2
分享到:
评论
3 楼 54sunboy 2008-02-28  
不错,学习了。前几天刚用JNI做了一个这样的接口。字符的编码问题确实难搞掂。我用的方法是dll+libxml,jni+xstream之间用xml进行数据交换。
2 楼 liukl 2008-02-25  
多谢galaxystar,原来还有这么好的东东。以前用redhat,不能自动升级,很是抓狂,遇到debian后终于了却了烦恼。
1 楼 galaxystar 2008-02-24  
顶下python,本人最喜欢的script language,
看楼主,应该是debian爱好者吧。
哈哈,喜欢python就支持下gentoo吧,gentoo的package manager就是python写的.

相关推荐

Global site tag (gtag.js) - Google Analytics