17.5 设计长时间运行的应用
长时间运行的应用服务会从某种队列中读取请求并生成相应的回复。在许多情况下,会使用HTTP协议并将创建的应用服务添加到网络服务框架中。有关如何基于WSGI设计模式来实现RESTful的网络服务,可参见第12章“传输和共享对象”。
桌面GUI应用与服务有很多共同的功能,它会从队列中读取鼠标和键盘操作的事件,对每种事件进行处理并返回相应的GUI回复。在一些情况中,回复可能为文本组件的更新。在另一些情况中,会打开或关闭一个文件,菜单项的状态可能会发生改变。
对于以上两种情况,应用的核心功能都是无限循环的处理事件或请求。由于这些循环很简单,因此它们通常为框架的一部分。对于GUI应用来说,可能会使用如下代码来实现循环。
root= Tkinter.Tk()
app= Application(root)
root.mainloop()
对于Tkinter应用,最高层组件的mainloop()获得GUI事件并把它们交给框架中的适当组件来处理。当对象完成了事件的处理——最上层组件,像上例中的 root——会执行quit()方法,然后循环会被终止。
对于一个基于WSGI的网络服务框架,可能会使用以下代码来实现循环。
httpd = make_server('', 8080, debug)
httpd.serve_forever()
在这个例子中,服务中的serve _ forever()方法会得到每个请求并交给应用来处理——在本例中为debug。当应用执行了服务的shutdown()方法,循环将被终止。
通常还需要在其他方面来区分长时间运行的应用。
- 健壮性:对于一般情况,这种需求不需要。所有的软件都应该能够运行。然而,当需要与外部OS或网络资源交互时,就可能有超时和其他错误,它们必须被很好地解决。对于插件式设计的应用框架来说,插件和扩展组件中的错误应该能够被全局的框架很好地处理。Python中原生的异常处理就已经足够用来写出健壮的程序。
- 可审计:一个简单的、集中的日志不是在所有情况下都够用的。在第14章“Logging和Warning模块”中,我们介绍了几种技术来创建多种日志用于满足安全或金融审计方面的需求。
- 可调试:原生的单元测试和集成测试降低了复杂调试工具的需要。然而,对于外部资源和软件插件或扩展所带来的复杂性,在不提供调试支持的情况下,它们很难处理。使用更复杂的日志系统或许会有帮助。
- 可配置性:除了要提供简单的补丁,我们还需要能够启用或禁用应用的功能。例如,启用或禁用调试日志,是一种常见的配置。在一些情况中,我们希望在不需要完全终止并重启应用的情况下来完成配置的变更。在第13章“配置文件和持久化”中,我们介绍了一些有关应用配置的技术。在第16章“使用命令行”中,我们扩展了这些技术。
- 可控制:对于简单的长时间运行服务,可以通过重启来完成对不同配置的加载。为了确保缓存被清空并且OS资源被释放,使用信号机制比SIGKILL强行关闭要好一些。Python在signal模块中提供了信号处理的能力。
对于最后两种——动态配置和干净关闭——使得能够区分主事件或请求输入和次要控制的输入。这个控制输入能够为配置或关闭提供额外的请求。
我们有很多方式能够通过一个额外的通道来提供异步输入。
- 最简单的方式之一就是使用multiprocessing模块创建队列。这样一来,就能够使用简单的客户端管理程序来与这个队列进行交互,完成控制或查询服务,或是通过GUI进行其他操作。有关multiprocessing模块的更多信息,可参见第12章“传输和共享对象”。我们能够在客户端和服务之间传递控制或状态对象。
- 在Python标准库的第18章中定义了更底层的技术。这些模块也可用于与长运行的服务或GUI应用进行交互。它们没有像创建队列或通过multiprocessing实现管道那样复杂。
一般地,更多的情况下我们会使用multiprocessing来实现更高层面的可用的API。底层的一些技术(socket、signal、mmap、asyncore和asynchat)相对简单,只会提供少量功能。它们应该用于更高层面中模块的内部支持,例如multiprocessing。
