开场白
您如何得出结论,系统的某些部分使用另一种语言会更好?您是否遇到性能问题?这些问题有多严重?如果可以更快,那么是否必须更快?
单线程异步
有几个问题和其他Web资源已经在解决单线程异步与多线程并发的优缺点。有趣的是,当I / O是主要瓶颈时,Node.js的单线程异步模型是如何执行的,并且有很多请求可以同时处理。
Twisted,Tornado和其他异步模型充分利用了单线程。由于大量的Web编程具有大量的I / O(网络,数据库等),因此等待远程调用所花费的时间明显增加。那是可以花在做其他事情上的时间,例如启动其他数据库调用,呈现页面和生成数据。该单线程的利用率非常高。
一个单线程异步的最大好处是,它使用多少内存更少。在多线程执行中,每个线程都需要一定数量的保留内存。随着线程数量的增加,仅存在线程所需的内存量也会增加。由于内存是有限的,因此这意味着可以一次创建的线程数是有限的。
例
对于Web服务器,假装每个请求都有自己的线程。假设每个线程需要1MB的内存,并且Web服务器具有2GB的RAM。该Web服务器将能够在没有足够的内存来处理更多时间之前的任何时间点(大约)处理2000个请求。
如果您的负载明显高于此负载,则请求将花费很长时间(在等待较旧的请求完成时),或者您将不得不向群集中放置更多服务器以扩大可能的并发请求数。
多线程并发
相反,多线程并发依赖于同时执行多个任务。这意味着,如果某个线程在等待数据库调用返回时被阻塞,则可以同时处理其他请求。线程利用率较低,但执行的线程数要大得多。
多线程代码也很难推理。存在锁定,同步和其他有趣的并发问题。单线程异步不会遇到相同的问题。
但是,多线程代码在执行CPU密集型任务时性能更高。如果没有机会让线程“让步”(例如通常会阻塞的网络调用),那么单线程模型将不会有任何并发性。
两者可能共存
两者之间当然有重叠;它们不是互斥的。例如,可以以非阻塞方式编写多线程代码,以更好地利用每个线程。
底线
还有许多其他问题需要考虑,但是我喜欢这样考虑两个问题:
- 如果您的程序受I / O约束,那么单线程异步可能会很好地工作。
- 如果您的程序受CPU限制,那么多线程系统可能是最佳选择。
在您的特定情况下,您需要确定完成哪种异步工作以及这些任务的出现频率。
- 它们是否在每次请求时发生?如果是这样,随着请求数量的增加,内存可能会成为问题。
- 这些任务有序吗?如果是这样,如果使用多个线程,则必须考虑同步。
- 这些任务是否占用大量CPU?如果是这样,单线程是否能够跟上负载的需求?
没有简单的答案。您必须考虑用例是什么,并进行相应的设计。有时异步单线程模型会更好。在其他时候,需要使用多个线程来实现大规模并行处理。
其他注意事项
您还需要考虑其他问题,而不仅仅是您选择的并发模型。你知道Erlang或Clojure吗?您认为您可以使用其中一种语言编写安全的多线程代码,从而提高应用程序的性能吗?掌握其中一种语言是否会花费很长时间,并且您将来所学的语言对您有好处吗?
这两个系统之间的通信困难如何?并行维护两个独立的系统是否过于复杂?Erlang系统将如何从Django接收任务?Erlang将如何将这些结果传达回Django?性能是否足够重要,是否值得增加复杂性?
最后的想法
我一直发现Django足够快,并且被一些流量非常大的网站所使用。您可以进行一些性能优化,以增加并发请求的数量和响应时间。诚然,到目前为止,我对Celery并没有做任何事情,因此通常的性能优化可能无法解决这些异步任务可能遇到的任何问题。
当然,总有建议在此问题上投入更多的硬件。供应新服务器的成本是否比全新子系统的开发和维护成本便宜?
在这一点上,我问了太多问题,但这是我的意图。如果没有分析和进一步的细节,答案将不容易。能够分析问题归结为知道要问的问题,所以……希望我在这方面有所帮助。
我的直觉说不需要用另一种语言重写。复杂性和成本可能太大。
编辑
回应跟进
您的后续文章介绍了一些非常有趣的用例。
1. Django在HTTP请求之外工作
您的第一个示例涉及读取NFC标签,然后查询数据库。我认为用另一种语言编写该部分不会对您有用,仅因为查询数据库或LDAP服务器将受到网络I / O(并可能影响数据库性能)的约束。另一方面,并发请求的数量将受服务器本身的约束,因为每个管理命令将作为其自己的进程运行。因为没有将消息发送到已经运行的进程,所以设置和拆卸时间会影响性能。但是,您将能够同时发送多个请求,因为每个请求都是一个独立的过程。
对于这种情况,我发现您可以研究两种途径:
- 确保您的数据库能够使用连接池一次处理多个查询。(例如,Oracle要求您相应地配置Django
'OPTIONS': {'threaded':True}
。)在数据库级别或Django级别可能有类似的配置选项,您可以针对自己的数据库进行调整。无论使用哪种语言编写数据库查询,都必须等待该数据返回后才能点亮LED。不过,查询代码的性能可能会有所不同,并且Django ORM的运行速度并不快(但通常足够快)。
- 最小化设置/拆卸时间。有一个持续运行的过程,并向其发送消息。(如果我错了,请纠正我,但这是您最初的问题实际上关注的问题。)上面介绍了此过程是用Python / Django还是其他语言/框架编写的。我不喜欢这么频繁使用管理命令的想法。是否有可能不断运行一小段代码,将来自NFC读取器的消息推送到消息队列,然后Celery读取并转发给Django?即使是用Python(但不是Django!)编写的小程序,其安装和拆卸也要比启动和停止Django程序(及其所有子系统)更好。
我不确定您要为Django使用哪种Web服务器。mod_wsgi
使用Apache,您可以配置服务请求的进程数和进程中的线程数。确保调整Web服务器的相关配置,以优化可服务请求的数量。
2. Django信号的“消息传递”
您的第二个用例也相当有趣。我不确定是否有答案。如果要删除模型实例,并希望稍后对其进行操作,则可以序列化它们JSON.dumps
,然后反序列化JSON.loads
。稍后将不可能完全重新创建对象图(查询相关模型),因为相关字段是从数据库延迟加载的,并且该链接将不再存在。
另一种选择是以某种方式将对象标记为删除,并且仅在请求/响应周期结束时(在所有信号都得到服务之后)将其删除。可能需要一个自定义信号来实现此目的,而不是依赖post_delete
。