我正在使用Python的Flask模块并通过apache2 httpd的WSGI模块运行它。配置如下所示。
httpd.conf:
...
WSGISocketPrefix <somedir>/wsgisock
include sites-enabled
然后在我的python代码中,我以这种方式调用Flask:
from flask import Flask, jsonify, abort, request
我的python程序接收有效负载,作为响应,执行长任务,通常需要超过30分钟。最后它返回一个响应主体。但是,那时我在错误输出文件中看到了这条消息:
[reqtimeout:info] [pid 7036] [client xxx.xx.xxx.xx:37038] AH01382: Request body read timeout
我在httpd进程上运行strace并发现:
brk(NULL) = 0x7fd2b961a000
brk(0x7fd2b963b000) = 0x7fd2b963b000
write(6, "[Sat Oct 21 15:02:21.771995 2017"..., 127) = 127
writev(10, [{"HTTP/1.1 201 CREATED\r\nDate: Sat,"..., 257}, {"{\n
\"outcomeDetails\": \"Successfu"..., 2873}, {"", 0}], 3) = 3130
write(8, "xxx.xx.xxx.xx - - [21/Oct/2017:1"..., 183) = 183
times({tms_utime=0, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 714623894
close(11) = 0
shutdown(10, SHUT_WR) = 0
poll([{fd=10, events=POLLIN}], 1, 2000) = 0 (Timeout)
close(10) = 0
read(4, 0x7fff5156c92f, 1) = -1 EAGAIN (Resource temporarily
unavailable)
accept4(3,
很明显,httpd进程确实写入了套接字。但是,我没有看到任何客户端(Java servlet)。任何人都知道如何解决这个问题?
这是我的python代码:
#!usr/bin/python
from flask import Flask, jsonify, abort, request
import threading, myClass, time, inspect, sys, pprint, os
app = Flask(__name__)
# Dictionary to store threadname, output and returnvalue of task being executed
threads_n_outputs = {'task_string' : {'threadName' : 'thread1', 'logFile' : 'logFile', 'executionResult' : {}}}
tasks = {
'myTask1': {
'Description': u'blah blah',
'inputParams': {'var1': u'','var2': u''},
'outputParams': {'taskOutcome': u'', 'outcomeDetails': u'', 'reversePayload': {}}
},
'myTask2': {
'Description': u'blah blah blah',
'inputParams': {'var1': u'','var2': u''},
'outputParams': {'taskOutcome': u'', 'outcomeDetails': u'', 'reversePayload': {}}
}
}
@app.route('/some/path')
def get_tasks():
return jsonify(tasks)
# Get method to list specific tasks
@app.route('/some/path/<task_name>')
def get_task(task_name):
if tasks[task_name]:
return jsonify(tasks[task_name])
else:
abort(404)
# Get method to show status of a running task
@app.route('/some/path/status/<task_string>')
def showStatus(task_string):
global threads_n_outputs
if task_string == 'allRunningJobs': # Show all running jobs
temp_dict = dict(threads_n_outputs)
del temp_dict['task_string']
if temp_dict:
return jsonify(temp_dict.keys())
else:
return 'No running jobs as of now.'
if task_string in threads_n_outputs: # show status for specific job
with open(threads_n_outputs[task_string]['logFile'], 'r') as fileHandle:
return jsonify(fileHandle.read().splitlines())
else:
return 'No running job named ' + task_string + ' as of now.'
class runTaskClass(threading.Thread):
global threads_n_outputs
def __init__(self, functionName, kwargs, task_string, logFile):
super(runTaskClass, self).__init__(name = task_string)
self.functionName = functionName
self.kwargs = kwargs
self.task_string = task_string
self.logFile = logFile
def run(self):
try:
# Write to logfile instead of stdout
with open(self.logFile, "w", 1) as fileHandle:
with fileHandle as sys.stdout:
print '\n' + 'Starting ' + self.task_string + ' at ' + str(time.ctime()) + '\n'
threads_n_outputs[self.task_string]['executionResult'] = self.functionName(**self.kwargs)
print '\n' + 'Task ' + self.task_string + ' completed at ' + str(time.ctime()) + '\n'
sys.stdout = sys.__stdout__ # Reset stdout to normal
except Exception, inst:
fileHandle.close() # Close file if exception occured within 'with' statement
sys.stdout = sys.__stdout__ # Reset stdout to normal
print '\n' + 'runTaskClass : Error While running ' + str(self.task_string) + ' - ' + str(inst)
# Post method to execute a task
@app.route('/some/path/<task_string>', methods=['POST']) ## task_string comprises of taskname and querytstring to get status of task
def execute_task(task_string):
print request.get_data() # print raw input data
sys.stdout.flush()
requestData = request.get_json()
if not requestData:
print '\n' + 'ERROR : Posted data is not valid' + '\n'
abort(400)
outputParams={'taskOutcome': u'', 'outcomeDetails': u'', 'reversePayload': {}}
def updateOutputParams(taskOutcome, outcomeDetails, reversePayload):
outputParams.update(dict(zip(['taskOutcome', 'outcomeDetails', 'reversePayload'],[taskOutcome, outcomeDetails, reversePayload])))
return jsonify(outputParams), 201
if len(task_string.split('_')) < 2: ## If task_string is not in desired format exit with error
return updateOutputParams('Failure','Error: task_string format should be taskname_queryString', None)
task_name = task_string.split('_')[0]
task_details=task_name + ' with arguments ' + ",".join(["%s=%s" % (k, v) for k, v in requestData.items()])
print '\n' + 'Executing '+ task_details + '\n'
task_instance=<someclass>.<someclass>()
task_instance.logon()
functionName='task_instance.' + task_name
logFile=task_instance.logDirectory + '/td_' + task_string + '.log' # logfile for this job
if os.path.isfile(logFile):
newlogFile = logFile + time.strftime("%d%b%Y_%Hh%Mm%Ss")
os.rename(logFile, newlogFile)
#Verify if task domain function named task_name exists
try:
if callable(eval(functionName)):
print '\n' + 'Verified that '+ functionName +' is a valid function. Proceeding with validation of arguments.'
else:
return updateOutputParams('Failure', 'Error:' + functionName + ' is not a valid function.', None)
except:
return updateOutputParams('Failure', 'Exception:' + functionName + ' is not a valid function.', None)
#Verify if arguments count provided for task_name function is valid
if len(requestData.items()) + 1 == len(inspect.getargspec(eval(functionName))[0]): # +1 for first argument 'this'
print '\n' + 'Argument count for ' + functionName + ' is valid. Proceeding with execution of this function now.' + '\n'
else:
return updateOutputParams('Failure','Error:Argument count for ' + functionName + ' does not match with definition.', None)
#Initialize the placeholder variable for this thread
global threads_n_outputs
#Never start a thread if another thread with same name is running at the moment
if task_string in threads_n_outputs:
return updateOutputParams('Failure','Error: Job named ' + task_string + ' is already running. Please submit with distinct identifier', None)
else:
threads_n_outputs[task_string] = {'thread': None, 'logFile' : logFile, 'executionResult' : {}}
#Call task_name function
try:
print '\n' + 'Initializing Thread for job ' + str(task_string) + ' ...'
threads_n_outputs[task_string]['thread'] = runTaskClass(eval(functionName), requestData, task_string, logFile)
threads_n_outputs[task_string]['thread'].start()
threads_n_outputs[task_string]['thread'].join()
print '\n' + 'Thread for job ' + str(task_string) + ' completed.'
executionResult=threads_n_outputs[task_string]['executionResult']
print '\n' + 'Execution result for Thread ' + str(task_string) + ' :'
pprint.pprint(executionResult)
# handle exception of executionResult['outcomeDetails'] being None when outcome of successful jobs like startVM result None object
if executionResult['outcomeDetails'] is None:
executionResult['outcomeDetails'] = 'None'
outcomeDetails='Successfully executed ' + task_details + ', Outcome details = ' + executionResult['outcomeDetails'] if executionResult['taskOutcome'] == 'Success' else 'Execution failed for ' + task_details + ':Error message is - ' + executionResult['outcomeDetails']
# Delete current thread's info from threads_n_outputs
del threads_n_outputs[task_string]
return updateOutputParams(executionResult['taskOutcome'], outcomeDetails, executionResult['reversePayload'])
except Exception, inst:
del threads_n_outputs[task_string]
print '\n' + 'While running ' + str(task_string) + ' : ERROR ' + str(inst)
return updateOutputParams('Failure', 'While running ' + str(task_string) + ' : ERROR ' + str(inst), None)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5011, debug=True)