2016-11-15 60 views
3

我試圖建立一個服務器。除了像普通服務器那樣接受來自客戶端的連接外,我的服務器也會將其他服務器作爲客戶端連接。扭曲:使用connectProtocol連接端點導致內存泄漏?

我給自己定的協議和端點象下面這樣:

p = FooProtocol() 
client = TCP4ClientEndpoint(reactor, '127.0.0.1' , 8080) # without ClientFactory 

然後,呼叫reactor.run()後,服務器會聽/接受新的套接字連接。當新的套接字連接時(在connectionMade),該服務器將調用connectProtocol(client, p),其作用像下面的僞代碼:

while server accept new socket: 
    connectProtocol(client, p) 
    # client.client.connect(foo_client_factory) --> connecting in this way won't 
    #             cause memory leak 

由於連接到客戶端製成,記憶正在逐漸被消耗(顯式調用gc沒有按沒有工作)。

我是否以錯誤的方式使用Twisted?

----- ----- UPDATE

我的測試PROGRAME:服務器等待客戶端連接。當從客戶端連接時,服務器將創建到其他服務器

這50個連接是代碼:

#! /usr/bin/env python 

import sys 
import gc 

from twisted.internet import protocol, reactor, defer, endpoints 
from twisted.internet.endpoints import TCP4ClientEndpoint, connectProtocol 

class MyClientProtocol(protocol.Protocol): 
    def connectionMade(self): 
     self.transport.loseConnection() 

class MyClientFactory(protocol.ClientFactory): 
    def buildProtocol(self, addr): 
     p = MyClientProtocol() 
     return p 

class ServerFactory(protocol.Factory): 
    def buildProtocol(self, addr): 
     p = ServerProtocol() 
     return p 

client_factory = MyClientFactory() # global 
client_endpoint = TCP4ClientEndpoint(reactor, '127.0.0.1' , 8080) # global 

times = 0 

class ServerProtocol(protocol.Protocol): 
    def connectionMade(self): 
     global client_factory 
     global client_endpoint 
     global times 

     for i in range(50): 
      # 1) 
      p = MyClientProtocol() 
      connectProtocol(client_endpoint, p) # cause memleak 

      # 2) 
      #client_endpoint.connect(client_factory) # no memleak 

     times += 1 
     if times % 10 == 9: 
      print 'gc' 
      gc.collect() # doesn't work 

     self.transport.loseConnection() 

if __name__ == '__main__': 
    server_factory = ServerFactory() 
    serverEndpoint = endpoints.serverFromString(reactor, "tcp:8888") 
    serverEndpoint.listen(server_factory) 
    reactor.run() 
+1

這聽起來像是它可能是Twisted中的一個錯誤,但是您沒有在這裏附加足夠的代碼來告訴。你可以附加一個完整的程序嗎? – Glyph

+0

感謝回覆!我的測試代碼已經做了更新。 –

+0

這確實似乎有泄漏。實際上,我用*兩個*例子得到一個泄漏,儘管基於connectProtocol的例子有點快。這絕對是Twisted中的一個錯誤,我們需要進行調查。 – Glyph

回答

4

這個程序沒有做任何扭曲的日誌初始化。這意味着它在整個運行過程中都與「日誌初學者」一起運行。日誌初學者記錄它在LimitedHistoryLogObserver中觀察到的所有日誌事件(最大可配置)。

日誌初學者保持2 ** 16(_DEFAULT_BUFFER_MAXIMUM)事件,然後開始拋出舊的,大概是爲了避免消耗所有可用內存,如果一個程序從未配置另一名觀察員。

如果你破解扭源設置_DEFAULT_BUFFER_MAXIMUM較小的值 - 例如10 - 那麼程序不再是「泄漏」。當然,它實際上只是一個對象泄漏而不是內存泄漏,它受2 ** 16限制Twisted限制。

但是,每次調用connectProtocol時都會創建一個新工廠。每創建一個新工廠時,它都會記錄一條消息。應用程序代碼爲每個日誌消息生成一個新的Logger日誌代碼將新的Logger置於日誌消息中。這意味着保存這些日誌消息的內存成本是相當可觀的(相比之下,僅僅泄漏一小段文本或者甚至包含一些簡單對象的字典)。

我想說Twisted中的代碼表現得和預期的一樣......但也許有人沒有想到完成這種行爲的後果。

而且,當然,如果你配置你自己的日誌觀察者,那麼「日誌初學者」將被從圖片中取出並且沒有問題。期望所有嚴肅的程序能夠快速記錄並避免此問題似乎是合理的。但是,很多簡短的丟棄或示例程序通常不會初始化日誌記錄,而是依賴打印來代替,從而使其受到此行爲的影響。

注意此問題報道#8164並固定在4acde626如此扭曲17不會有這種行爲。