#python #httpserver
#python #httpserver
Вопрос:
У меня был http-сервер python. Этот http-сервер прослушивает входящие post-запросы (100-150 одновременных подключений). Мой сервер python выдает сбой и закрывается через несколько дней работы.
Вот мой код:
import http.server
import socketserver
import queue
import socket
import gzip
from urllib.parse import parse_qs
import json
import time
import _thread as thread
import threading
import globals
from logs import LOG
import conf
from os import getcwd
from DBConnection import DB
mutex = None
MAX_THREADS = 25
class Handler(http.server.BaseHTTPRequestHandler):
def setup(self):
http.server.BaseHTTPRequestHandler.setup(self)
self.request.settimeout(30)
#def do_HEAD(self):
# self.send_response(200)
# self.send_header("Content-type", "text/html")
# self.end_headers()
def do_POST(self):
global mutex
data = {'result': 'error'}
length = int(self.headers['Content-Length'])
print('length:%s' % length)
content = self.headers['Content-Encoding']
print('Content-Encoding:%s' % content)
try:
post_data = self.rfile.read(length)
except socket.error as e:
print(e)
with mutex:
print('active threads:%d' % threading.active_count())
print(threading.current_thread().getName())
#print(post_data)
if content == "gzip":
try:
post_data = gzip.decompress(post_data)
except Exception as e:
LOG.log('gzip error:%s, data:%s' % (e, post_data))
data = json.dumps(data)
self.send_response(200)
self.send_header("Content-type", "application/json")
self.end_headers()
self.wfile.write(data.encode())
return
post_data = parse_qs(post_data.decode('utf-8'))
#parse data
records = []
gpselement = {}
imei = ''
try:
if content == "gzip":
N = len(post_data.get('lat[]'))
else:
N = 1
for i in range(N):
for key in post_data.keys():
if key in ['m', 'imei']:
continue
#if key == 'gpstime[]':
gpselement[key.replace('[]', '')] = post_data.get(key)[i]
io = {}
io[1] = gpselement['status']
gpselement['metrage'] = 0
gpselement['voltage'] = 0
gpselement['io'] = io
records.append(gpselement)
imei = post_data.get('imei')[0]
except Exception as e:
print(e)
LOG.log('error:%s' % e)
with mutex:
# print(records)
s='imei: %s, len:%d , coords:%s' % (imei, len(records), str(records))
#LOG.log(s)
print(s)
db = DB()
#search imei
with mutex:
deviceData = db.getDevice(imei)
#deviceData = None
#insert gps data to base
if deviceData:
with mutex:
if db.addDataRecords(records, deviceData):
data['result'] = 'success'
else:
LOG.log('device not found, imei:%s' % imei)
#send response
try:
data = json.dumps(data)
self.send_response(200)
self.send_header("Content-type", "application/json")
self.end_headers()
self.wfile.write(data.encode())
print('%s closed.' % threading.current_thread().getName())
except Exception as e:
LOG.log('http response error:%s' % e)
class ThreadPoolMixIn(socketserver.ThreadingMixIn):
'''
use a thread pool instead of a new thread on every request
'''
numThreads = MAX_THREADS
allow_reuse_address = True # seems to fix socket.error on server restart
def serve_forever(self):
'''
Handle one request at a time until doomsday.
'''
# set up the threadpool
self.requests = queue.Queue(self.numThreads)
for x in range(self.numThreads):
t = threading.Thread(target=self.process_request_thread)
t.setDaemon(True)
t.start()
# server main loop
while True:
self.handle_request()
self.server_close()
def process_request_thread(self):
'''
obtain request from queue instead of directly from server socket
'''
while True:
socketserver.ThreadingMixIn.process_request_thread(self, *self.requests.get())
def handle_request(self):
'''
simply collect requests and put them on the queue for the workers.
'''
try:
request, client_address = self.get_request()
except socket.error:
return
if self.verify_request(request, client_address):
self.requests.put((request, client_address))
class ThreadedTCPServer(ThreadPoolMixIn, socketserver.TCPServer):
"""Handle requests in a separate thread."""
#pass
def loadIni():
inipath = getcwd() '/' 'conf.ini'
config = conf.loadSettings(inipath)
globals.DB_HOST = str(config['Database']['host'])
globals.DB_USER = str(config['Database']['user'])
globals.DB_PWD = str(config['Database']['pwd'])
globals.DB_NAME = str(config['Database']['db'])
globals.LISTEN_PORT = int(config['PORTS']['listenPort'])
conf.saveSettings(inipath, config)
def checkBase():
#reconnect to database
if not DB.checkConnection():
DB.close()
DB.connect(globals.DB_HOST, globals.DB_USER, globals.DB_PWD, globals.DB_NAME)
time.sleep(600)
#print('check Base')
checkBase()
def start_server():
global mutex
try:
#Загрузка из конфигурации
loadIni()
#start server
if DB.connect(globals.DB_HOST, globals.DB_USER, globals.DB_PWD, globals.DB_NAME):
httpd = ThreadedTCPServer(("", globals.LISTEN_PORT), Handler)
print("Start server at port", globals.LISTEN_PORT)
mutex = thread.allocate_lock()
thread.start_new_thread(checkBase, ())
httpd.serve_forever()
except Exception as e:
print(e)
print('Retrying connect to server.')
time.sleep(5)
start_server()
if __name__ == '__main__':
start_server()
Как решить эту проблему? и есть ли какие-либо другие способы сделать http-сервер?
Комментарии:
1. Можете ли вы предоставить более подробную информацию о сбое
2. Почему бы вам не использовать настоящий HTTP-сервер вместо игрушечного в stdlib?
3. Кажется, вы действительно хотите повторить все проблемы, с которыми столкнулись разработчики других проверенных веб-фреймворков Python. Обработка сокетов напрямую оказывает на меня довольно отталкивающий эффект. Почему бы не выбрать
cherrypy
— вы получите полное решение на чистом Python, илиflask
(требуется несколько интерфейсных серверов), или … что-то еще, что работает.