我在python中的套接字编程和线程方面经验不足。我正在研究将GPS位置数据发送到服务器的设备。设备可以在长连接模式下工作,这意味着一旦设备连接到服务器,它就会使连接保持活动状态(很多)。
当我检查Ubuntu系统资源使用情况(使用top或htop)时,我看到我的进程增加了虚拟和常驻内存使用量。进程从~55 MB虚拟内存和~12 MB驻留内存开始。 3天后,虚拟内存增加到~14 GB,而Resident内存使用量约为90 MB。
这个统计数据对我来说似乎不正常,虚拟和常驻内存使用率从未下降,这让我怀疑内存泄漏。由于我对线程编程和套接字编程不是很有经验,所以我很难检查可能存在的问题。
我的代码如下:
_log = Logger("Orion_EasyTrac").create_logger()
server = Server("", 9985, logger=_log)
server.start()
Server
上课
class Server(object):
def __init__(self, host, sock_port, buffsize=8192, logger=None):
self.hostname = host
self.sock_port = sock_port
self.buffsize = buffsize
self.socket = None
self.log = logger or _log
def start(self):
self.log.info("Listening: {prt}".format(prt=self.sock_port))
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((self.hostname, self.sock_port))
self.socket.listen(5)
while True:
conn, address = self.socket.accept()
thread.start_new_thread(EasyTrackProHandler(conn=conn, buff_size=self.buffsize, log=self.log).handle_data, ())
EasyTrackPro
class EasyTrackPro(object):
def __init__(self, conn, buff_size, log):
self.conn = conn
self.buff_size = buff_size
self.log = log
self.db_conn = None
self.cursor = None
self.db_connector = None
self.ack_parameter = None
self.device_id = None
self.user_id = None
self.beaconID = None
self.error_count = 0
...
...
def handle_data(self):
while True:
try:
_veri = self.conn.recv(self.buff_size)
if not _veri:
# We do not recieve any data...
raise NoIncomingDataException()
except NoIncomingDataException:
# No need to log anything in here...
break
except Exception as h_e:
self.log.exception(self._log_msg("Socket exception {h_e}".format(h_e=h_e)))
break
else:
self.control_data(_veri)
self.conn.close()
self.control_data(_veri)
是完成这项工作的一部分,但由于它很长很复杂,分享那部分似乎不合逻辑。
Logger
是一个使用python logging
模块并记录到文本文件的类。
我的猜测是,垃圾收集器无法收集旧对象,或者由于设备使用长连接,服务器端永远不会关闭打开的套接字。但不确定其中任何一个。
这种内存泄漏可能是什么问题?
更新:我的记录器类
class Logger:
def __init__(self, log_name, level='INFO', log_dir='logs', log_format=None, handler=None):
"""
log_name: log file name
level: log levels: CRITICAL, ERROR, WARNING, INFO, DEBUG
log_dir: log folder. default <file_path>/logs/
log_format: logging format
handler: TODO (incomplete)
"""
self.__log_name = log_name
self.__formatter = None
self.__handler = handler
self.__level = None
self.__log_format = u"%(asctime)s %(levelname)s %(name)s %(process)d %(threadName)s %(module)s: " \
u"%(lineno)d %(funcName)s(): %(message)s\r\n"
self.loger = None
self.__configure_level(level.upper())
self.__configure_format(log_format)
self.__configure_handler(log_dir)
def __configure_level(self, level):
if level and (level not in ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG']):
raise Exception(u"{lvl} is not a valid log level".format(lvl=level))
self.__level = level or 'INFO'
def __configure_format(self, log_format):
if log_format:
self.__log_format = log_format
self.formatter = logging.Formatter(self.__log_format)
def __configure_handler(self, log_dir):
_dir = '{b_dr}/{l_dr}'.format(b_dr=os.path.dirname(os.path.abspath(__file__)), l_dr=log_dir)
if not os.path.exists(_dir):
os.mkdir(_dir)
_filename = "{dr}/{lnf}.log".format(dr=_dir, lnf=self.__log_name)
self.handler = handlers.WatchedFileHandler(_filename, mode="a", encoding="utf-8")
self.handler.setFormatter(self.formatter)
def create_logger(self):
_loger = logging.getLogger(self.__log_name)
_loger.setLevel(getattr(logging, self.__level))
_loger.addHandler(self.handler)
self.loger = _loger
return _loger