一个项目要做进程管理, supevisor 这块做得比较棒,最终采用方案用的supevisor, 但是里面的控制supervisorctl 对程序调用并不是很友好, 所有对 supervisorctl 程序进行了修改, 这里只是做了个demo版本, supervisor 是 4.2.3 版本, 比 4.2.2 运行有点问题, 个人目前每去做优化, 供大家参考, 下面是代码.

#!/usr/bin/env python -u
"""supervisorctl -- control applications run by supervisord from the cmd line.Usage: %s [options] [action [arguments]]Options:
-c/--configuration FILENAME -- configuration file path (searches if not given)
-h/--help -- print usage message and exit
-i/--interactive -- start an interactive shell after executing commands
-s/--serverurl URL -- URL on which supervisord server is listening(default "http://localhost:9001").
-u/--username USERNAME -- username to use for authentication with server
-p/--password PASSWORD -- password to use for authentication with server
-r/--history-file -- keep a readline history (if readline is available)action [arguments] -- see belowActions are commands like "tail" or "stop".  If -i is specified or no action is
specified on the command line, a "shell" interpreting actions typed
interactively is started.  Use the action "help" to find out about available
actions.
"""
import os
import cmd
import errno
import getpass
import socket
import sys
import threading
from flask import Flask
from flask import jsonifyfrom supervisor.compat import xmlrpclib
from supervisor.compat import urlparse
from supervisor.compat import unicode
from supervisor.compat import raw_input
from supervisor.compat import as_stringfrom supervisor.medusa import asyncore_25 as asyncore# from supervisor.options import ClientOptions
from supervisor.options import Options
from supervisor.options import make_namespec
from supervisor.options import split_namespec
from supervisor.options import normalize_path
from supervisor.options import UnhosedConfigParser
from supervisor import loggers
from supervisor import xmlrpc
from supervisor import states
from supervisor import http_client# app = Flask(__name__, template_folder='html', static_folder='html')
class LSBInitExitStatuses:SUCCESS = 0GENERIC = 1INVALID_ARGS = 2UNIMPLEMENTED_FEATURE = 3INSUFFICIENT_PRIVILEGES = 4NOT_INSTALLED = 5NOT_RUNNING = 7class LSBStatusExitStatuses:NOT_RUNNING = 3UNKNOWN = 4DEAD_PROGRAM_FAULTS = (xmlrpc.Faults.SPAWN_ERROR,xmlrpc.Faults.ABNORMAL_TERMINATION,xmlrpc.Faults.NOT_RUNNING)class fgthread(threading.Thread):""" A subclass of threading.Thread, with a kill() method.To be used for foreground output/error streaming.http://mail.python.org/pipermail/python-list/2004-May/260937.html"""def __init__(self, program, ctl):threading.Thread.__init__(self)self.killed = Falseself.program = programself.ctl = ctlself.listener = http_client.Listener()self.output_handler = http_client.HTTPHandler(self.listener,self.ctl.options.username,self.ctl.options.password)self.error_handler = http_client.HTTPHandler(self.listener,self.ctl.options.username,self.ctl.options.password)def start(self): # pragma: no cover# Start the threadself.__run_backup = self.runself.run = self.__runthreading.Thread.start(self)def run(self): # pragma: no coverself.output_handler.get(self.ctl.options.serverurl,'/logtail/%s/stdout' % self.program)self.error_handler.get(self.ctl.options.serverurl,'/logtail/%s/stderr' % self.program)asyncore.loop()def __run(self): # pragma: no cover# Hacked run function, which installs the tracesys.settrace(self.globaltrace)self.__run_backup()self.run = self.__run_backupdef globaltrace(self, frame, why, arg):if why == 'call':return self.localtraceelse:return Nonedef localtrace(self, frame, why, arg):if self.killed:if why == 'line':raise SystemExit()return self.localtracedef kill(self):self.output_handler.close()self.error_handler.close()self.killed = Trueclass Controller(cmd.Cmd):def __init__(self, options, completekey='tab', stdin=None,stdout=None):self.options = optionsself.prompt = self.options.prompt + '> 'self.options.plugins = []self.vocab = ['help']self._complete_info = Noneself.exitstatus = LSBInitExitStatuses.SUCCESScmd.Cmd.__init__(self, completekey, stdin, stdout)for name, factory, kwargs in self.options.plugin_factories:print('plugins:', name, factory, kwargs)plugin = factory(self, **kwargs)for a in dir(plugin):if a.startswith('do_') and callable(getattr(plugin, a)):self.vocab.append(a[3:])self.options.plugins.append(plugin)plugin.name = namedef emptyline(self):# We don't want a blank line to repeat the last command.returndef default(self, line):self.output('*** Unknown syntax: %s' % line)self.exitstatus = LSBInitExitStatuses.GENERICdef exec_cmdloop(self, args, options):try:import readlinedelims = readline.get_completer_delims()delims = delims.replace(':', '')  # "group:process" as one worddelims = delims.replace('*', '')  # "group:*" as one worddelims = delims.replace('-', '')  # names with "-" as one wordreadline.set_completer_delims(delims)if options.history_file:try:readline.read_history_file(options.history_file)except IOError:passdef save():try:readline.write_history_file(options.history_file)except IOError:passimport atexitatexit.register(save)except ImportError:passtry:self.cmdqueue.append('status')self.cmdloop()except KeyboardInterrupt:self.output('')passdef set_exitstatus_from_xmlrpc_fault(self, faultcode, ignored_faultcode=None):if faultcode in (ignored_faultcode, xmlrpc.Faults.SUCCESS):passelif faultcode in DEAD_PROGRAM_FAULTS:self.exitstatus = LSBInitExitStatuses.NOT_RUNNINGelse:self.exitstatus = LSBInitExitStatuses.GENERICdef onecmd(self, line):""" Override the onecmd method to:- catch and print all exceptions- call 'do_foo' on plugins rather than ourself"""cmd, arg, line = self.parseline(line)if not line:return self.emptyline()if cmd is None:return self.default(line)self._complete_info = Noneself.lastcmd = lineif cmd == '':return self.default(line)else:do_func = self._get_do_func(cmd)if do_func is None:return self.default(line)try:try:return do_func(arg)except xmlrpclib.ProtocolError as e:if e.errcode == 401:if self.options.interactive:self.output('Server requires authentication')username = raw_input('Username:')password = getpass.getpass(prompt='Password:')self.output('')self.options.username = usernameself.options.password = passwordreturn self.onecmd(line)else:self.output('Server requires authentication')self.exitstatus = LSBInitExitStatuses.GENERICelse:self.exitstatus = LSBInitExitStatuses.GENERICraisedo_func(arg)except Exception:(file, fun, line), t, v, tbinfo = asyncore.compact_traceback()error = 'error: %s, %s: file: %s line: %s' % (t, v, file, line)self.output(error)self.exitstatus = LSBInitExitStatuses.GENERICdef _get_do_func(self, cmd):func_name = 'do_' + cmdfunc = getattr(self, func_name, None)if not func:for plugin in self.options.plugins:func = getattr(plugin, func_name, None)if func is not None:breakreturn funcdef output(self, message):if isinstance(message, unicode):message = message.encode('utf-8')self.stdout.write(message + '\n')def get_supervisor(self):return self.get_server_proxy('supervisor')def get_server_proxy(self, namespace=None):proxy = self.options.getServerProxy()if namespace is None:return proxyelse:return getattr(proxy, namespace)def upcheck(self):try:supervisor = self.get_supervisor()api = supervisor.getVersion() # deprecatedfrom supervisor import rpcinterfaceif api != rpcinterface.API_VERSION:self.output('Sorry, this version of supervisorctl expects to ''talk to a server with API version %s, but the ''remote version is %s.' % (rpcinterface.API_VERSION, api))self.exitstatus = LSBInitExitStatuses.NOT_INSTALLEDreturn Falseexcept xmlrpclib.Fault as e:if e.faultCode == xmlrpc.Faults.UNKNOWN_METHOD:self.output('Sorry, supervisord responded but did not recognize ''the supervisor namespace commands that supervisorctl ''uses to control it.  Please check that the ''[rpcinterface:supervisor] section is enabled in the ''configuration file (see sample.conf).')self.exitstatus = LSBInitExitStatuses.UNIMPLEMENTED_FEATUREreturn Falseself.exitstatus = LSBInitExitStatuses.GENERICraiseexcept socket.error as e:if e.args[0] == errno.ECONNREFUSED:self.output('%s refused connection' % self.options.serverurl)self.exitstatus = LSBInitExitStatuses.INSUFFICIENT_PRIVILEGESreturn Falseelif e.args[0] == errno.ENOENT:self.output('%s no such file' % self.options.serverurl)self.exitstatus = LSBInitExitStatuses.NOT_RUNNINGreturn Falseself.exitstatus = LSBInitExitStatuses.GENERICraisereturn Truedef complete(self, text, state, line=None):"""Completer function that Cmd will register with readline usingreadline.set_completer().  This function will be called by readlineas complete(text, state) where text is a fragment to complete andstate is an integer (0..n).  Each call returns a string with a newcompletion.  When no more are available, None is returned."""if line is None: # line is only set in testsimport readlineline = readline.get_line_buffer()matches = []# blank line completes to action listif not line.strip():matches = self._complete_actions(text)else:words = line.split()action = words[0]# incomplete action completes to action listif len(words) == 1 and not line.endswith(' '):matches = self._complete_actions(text)# actions that accept an action nameelif action in ('help'):matches = self._complete_actions(text)# actions that accept a group nameelif action in ('add', 'remove', 'update'):matches = self._complete_groups(text)# actions that accept a process nameelif action in ('clear', 'fg', 'pid', 'restart', 'signal','start', 'status', 'stop', 'tail'):matches = self._complete_processes(text)if len(matches) > state:return matches[state]def _complete_actions(self, text):"""Build a completion list of action names matching text"""return [ a + ' ' for a in self.vocab if a.startswith(text)]def _complete_groups(self, text):"""Build a completion list of group names matching text"""groups = []for info in self._get_complete_info():if info['group'] not in groups:groups.append(info['group'])return [ g + ' ' for g in groups if g.startswith(text) ]def _complete_processes(self, text):"""Build a completion list of process names matching text"""processes = []for info in self._get_complete_info():if ':' in text or info['name'] != info['group']:processes.append('%s:%s' % (info['group'], info['name']))if '%s:*' % info['group'] not in processes:processes.append('%s:*' % info['group'])else:processes.append(info['name'])return [ p + ' ' for p in processes if p.startswith(text) ]def _get_complete_info(self):"""Get all process info used for completion.  We cache this betweencommands to reduce XML-RPC calls because readline may callcomplete() many times if the user hits tab only once."""if self._complete_info is None:self._complete_info = self.get_supervisor().getAllProcessInfo()return self._complete_infodef do_help(self, arg):if arg.strip() == 'help':self.help_help()else:for plugin in self.options.plugins:plugin.do_help(arg)return Truedef help_help(self):self.output("help\t\tPrint a list of available actions")self.output("help <action>\tPrint help for <action>")def do_EOF(self, arg):self.output('')return 1def help_EOF(self):self.output("To quit, type ^D or use the quit command")def get_names(inst):names = []classes = [inst.__class__]while classes:aclass = classes.pop(0)if aclass.__bases__:classes = classes + list(aclass.__bases__)names = names + dir(aclass)return namesclass ControllerPluginBase:name = 'unnamed'def __init__(self, controller):self.ctl = controllerdef _doc_header(self):return "%s commands (type help <topic>):" % self.namedoc_header = property(_doc_header)def do_help(self, arg):if arg:# XXX check arg syntaxtry:func = getattr(self, 'help_' + arg)except AttributeError:try:doc = getattr(self, 'do_' + arg).__doc__if doc:self.ctl.output(doc)returnexcept AttributeError:passself.ctl.output(self.ctl.nohelp % (arg,))returnfunc()else:names = get_names(self)cmds_doc = []cmds_undoc = []help = {}for name in names:if name[:5] == 'help_':help[name[5:]]=1names.sort()# There can be duplicates if routines overriddenprevname = ''for name in names:if name[:3] == 'do_':if name == prevname:continueprevname = namecmd=name[3:]if cmd in help:cmds_doc.append(cmd)del help[cmd]elif getattr(self, name).__doc__:cmds_doc.append(cmd)else:cmds_undoc.append(cmd)self.ctl.output('')self.ctl.print_topics(self.doc_header, cmds_doc, 15, 80)return Truedef not_all_langs():enc = getattr(sys.stdout, 'encoding', None) or ''return None if enc.lower().startswith('utf') else sys.stdout.encodingdef check_encoding(ctl):problematic_enc = not_all_langs()if problematic_enc:ctl.output('Warning: sys.stdout.encoding is set to %s, so Unicode ''output may fail. Check your LANG and PYTHONIOENCODING ''environment settings.' % problematic_enc)class LanhuiControllerPlugin(ControllerPluginBase):name = 'lanhui_ctrl'listener = None # for unit testsdef _tailf(self, path):check_encoding(self.ctl)self.ctl.output('==> Press Ctrl-C to exit <==')username = self.ctl.options.usernamepassword = self.ctl.options.passwordhandler = Nonetry:# Python's urllib2 (at least as of Python 2.4.2) isn't up# to this task; it doesn't actually implement a proper# HTTP/1.1 client that deals with chunked responses (it# always sends a Connection: close header).  We use a# homegrown client based on asyncore instead.  This makes# me sad.if self.listener is None:listener = http_client.Listener()else:listener = self.listener # for unit testshandler = http_client.HTTPHandler(listener, username, password)handler.get(self.ctl.options.serverurl, path)asyncore.loop()except KeyboardInterrupt:if handler:handler.close()self.ctl.output('')returndef do_tail(self, arg):if not self.ctl.upcheck():returnargs = arg.split()if len(args) < 1:self.ctl.output('Error: too few arguments')self.ctl.exitstatus = LSBInitExitStatuses.GENERICself.help_tail()returnelif len(args) > 3:self.ctl.output('Error: too many arguments')self.ctl.exitstatus = LSBInitExitStatuses.GENERICself.help_tail()returnmodifier = Noneif args[0].startswith('-'):modifier = args.pop(0)if len(args) == 1:name = args[-1]channel = 'stdout'else:if args:name = args[0]channel = args[-1].lower()if channel not in ('stderr', 'stdout'):self.ctl.output('Error: bad channel %r' % channel)self.ctl.exitstatus = LSBInitExitStatuses.GENERICreturnelse:self.ctl.output('Error: tail requires process name')self.ctl.exitstatus = LSBInitExitStatuses.GENERICreturnbytes = 1600if modifier is not None:what = modifier[1:]if what == 'f':bytes = Noneelse:try:bytes = int(what)except:self.ctl.output('Error: bad argument %s' % modifier)self.ctl.exitstatus = LSBInitExitStatuses.GENERICreturnsupervisor = self.ctl.get_supervisor()if bytes is None:return self._tailf('/logtail/%s/%s' % (name, channel))else:check_encoding(self.ctl)try:if channel == 'stdout':output = supervisor.readProcessStdoutLog(name,-bytes, 0)else:output = supervisor.readProcessStderrLog(name,-bytes, 0)except xmlrpclib.Fault as e:self.ctl.exitstatus = LSBInitExitStatuses.GENERICtemplate = '%s: ERROR (%s)'if e.faultCode == xmlrpc.Faults.NO_FILE:self.ctl.output(template % (name, 'no log file'))elif e.faultCode == xmlrpc.Faults.FAILED:self.ctl.output(template % (name,'unknown error reading log'))elif e.faultCode == xmlrpc.Faults.BAD_NAME:self.ctl.output(template % (name,'no such process name'))else:raiseelse:self.ctl.output(output)return Truedef help_tail(self):self.ctl.output("tail [-f] <name> [stdout|stderr] (default stdout)\n""Ex:\n""tail -f <name>\t\tContinuous tail of named process stdout\n""\t\t\tCtrl-C to exit.\n""tail -100 <name>\tlast 100 *bytes* of process stdout\n""tail <name> stderr\tlast 1600 *bytes* of process stderr")def do_maintail(self, arg):if not self.ctl.upcheck():returnargs = arg.split()if len(args) > 1:self.ctl.output('Error: too many arguments')self.ctl.exitstatus = LSBInitExitStatuses.GENERICself.help_maintail()returnelif len(args) == 1:if args[0].startswith('-'):what = args[0][1:]if what == 'f':path = '/mainlogtail'return self._tailf(path)try:what = int(what)except:self.ctl.output('Error: bad argument %s' % args[0])self.ctl.exitstatus = LSBInitExitStatuses.GENERICreturnelse:bytes = whatelse:self.ctl.output('Error: bad argument %s' % args[0])self.ctl.exitstatus = LSBInitExitStatuses.GENERICreturnelse:bytes = 1600supervisor = self.ctl.get_supervisor()try:output = supervisor.readLog(-bytes, 0)except xmlrpclib.Fault as e:self.ctl.exitstatus = LSBInitExitStatuses.GENERICtemplate = '%s: ERROR (%s)'if e.faultCode == xmlrpc.Faults.NO_FILE:self.ctl.output(template % ('supervisord', 'no log file'))elif e.faultCode == xmlrpc.Faults.FAILED:self.ctl.output(template % ('supervisord','unknown error reading log'))else:raiseelse:self.ctl.output(output)return Truedef help_maintail(self):self.ctl.output("maintail -f \tContinuous tail of supervisor main log file"" (Ctrl-C to exit)\n""maintail -100\tlast 100 *bytes* of supervisord main log file\n""maintail\tlast 1600 *bytes* of supervisor main log file\n")def do_quit(self, arg):return self.ctl.do_EOF(arg)def help_quit(self):self.ctl.output("quit\tExit the supervisor shell.")do_exit = do_quitdef help_exit(self):self.ctl.output("exit\tExit the supervisor shell.")def _show_statuses(self, process_infos):namespecs, maxlen = [], 30for i, info in enumerate(process_infos):namespecs.append(make_namespec(info['group'], info['name']))if len(namespecs[i]) > maxlen:maxlen = len(namespecs[i])template = '%(namespec)-' + str(maxlen+3) + 's%(state)-10s%(desc)s'for i, info in enumerate(process_infos):line = template % {'namespec': namespecs[i],'state': info['statename'],'desc': info['description']}self.ctl.output(line)# @app.route('/status', methods=['Get'])def do_status(self, arg):# XXX In case upcheck fails, we override the exitstatus which# should only return 4 for do_status# TODO review thisif not self.ctl.upcheck():self.ctl.exitstatus = LSBStatusExitStatuses.UNKNOWNreturnsupervisor = self.ctl.get_supervisor()all_infos = supervisor.getAllProcessInfo()# print(all_infos)# names = as_string(arg).split()names = Noneif not names or "all" in names:matching_infos = all_infoselse:matching_infos = []for name in names:bad_name = Truegroup_name, process_name = split_namespec(name)for info in all_infos:matched = info['group'] == group_nameif process_name is not None:matched = matched and info['name'] == process_nameif matched:bad_name = Falsematching_infos.append(info)if bad_name:if process_name is None:msg = "%s: ERROR (no such group)" % group_nameelse:msg = "%s: ERROR (no such process)" % nameself.ctl.output(msg)self.ctl.exitstatus = LSBStatusExitStatuses.UNKNOWNself._show_statuses(matching_infos)for info in matching_infos:if info['state'] in states.STOPPED_STATES:self.ctl.exitstatus = LSBStatusExitStatuses.NOT_RUNNING# return jsonify(all_infos)# print('matching_infos: ', type(matching_infos), matching_infos)return matching_infosdef help_status(self):self.ctl.output("status <name>\t\tGet status for a single process")self.ctl.output("status <gname>:*\tGet status for all ""processes in a group")self.ctl.output("status <name> <name>\tGet status for multiple named ""processes")self.ctl.output("status\t\t\tGet all process status info")def do_pid(self, arg):supervisor = self.ctl.get_supervisor()if not self.ctl.upcheck():returnnames = arg.split()if not names:pid = supervisor.getPID()self.ctl.output(str(pid))elif 'all' in names:for info in supervisor.getAllProcessInfo():self.ctl.output(str(info['pid']))else:for name in names:try:info = supervisor.getProcessInfo(name)except xmlrpclib.Fault as e:self.ctl.exitstatus = LSBInitExitStatuses.GENERICif e.faultCode == xmlrpc.Faults.BAD_NAME:self.ctl.output('No such process %s' % name)else:raiseelse:pid = info['pid']self.ctl.output(str(pid))if pid == 0:self.ctl.exitstatus = LSBInitExitStatuses.NOT_RUNNINGreturn Truedef help_pid(self):self.ctl.output("pid\t\t\tGet the PID of supervisord.")self.ctl.output("pid <name>\t\tGet the PID of a single ""child process by name.")self.ctl.output("pid all\t\t\tGet the PID of every child ""process, one per line.")def _startresult(self, result):name = make_namespec(result['group'], result['name'])code = result['status']template = '%s: ERROR (%s)'if code == xmlrpc.Faults.BAD_NAME:return template % (name, 'no such process')elif code == xmlrpc.Faults.NO_FILE:return template % (name, 'no such file')elif code == xmlrpc.Faults.NOT_EXECUTABLE:return template % (name, 'file is not executable')elif code == xmlrpc.Faults.ALREADY_STARTED:return template % (name, 'already started')elif code == xmlrpc.Faults.SPAWN_ERROR:return template % (name, 'spawn error')elif code == xmlrpc.Faults.ABNORMAL_TERMINATION:return template % (name, 'abnormal termination')elif code == xmlrpc.Faults.SUCCESS:return '%s: started' % name# assertionraise ValueError('Unknown result code %s for %s' % (code, name))def do_start(self, arg):if not self.ctl.upcheck():returnnames = arg.split()supervisor = self.ctl.get_supervisor()if not names:self.ctl.output("Error: start requires a process name")self.ctl.exitstatus = LSBInitExitStatuses.INVALID_ARGSself.help_start()returnif 'all' in names:results = supervisor.startAllProcesses()for result in results:self.ctl.output(self._startresult(result))self.ctl.set_exitstatus_from_xmlrpc_fault(result['status'], xmlrpc.Faults.ALREADY_STARTED)else:for name in names:group_name, process_name = split_namespec(name)if process_name is None:try:results = supervisor.startProcessGroup(group_name)for result in results:self.ctl.output(self._startresult(result))self.ctl.set_exitstatus_from_xmlrpc_fault(result['status'], xmlrpc.Faults.ALREADY_STARTED)except xmlrpclib.Fault as e:if e.faultCode == xmlrpc.Faults.BAD_NAME:error = "%s: ERROR (no such group)" % group_nameself.ctl.output(error)self.ctl.exitstatus = LSBInitExitStatuses.INVALID_ARGSelse:self.ctl.exitstatus = LSBInitExitStatuses.GENERICraiseelse:try:result = supervisor.startProcess(name)except xmlrpclib.Fault as e:error = {'status': e.faultCode,'name': process_name,'group': group_name,'description': e.faultString}self.ctl.output(self._startresult(error))self.ctl.set_exitstatus_from_xmlrpc_fault(error['status'], xmlrpc.Faults.ALREADY_STARTED)else:name = make_namespec(group_name, process_name)self.ctl.output('%s: started' % name)return Truedef help_start(self):self.ctl.output("start <name>\t\tStart a process")self.ctl.output("start <gname>:*\t\tStart all processes in a group")self.ctl.output("start <name> <name>\tStart multiple processes or groups")self.ctl.output("start all\t\tStart all processes")def _signalresult(self, result, success='signalled'):name = make_namespec(result['group'], result['name'])code = result['status']fault_string = result['description']template = '%s: ERROR (%s)'if code == xmlrpc.Faults.BAD_NAME:return template % (name, 'no such process')elif code == xmlrpc.Faults.BAD_SIGNAL:return template % (name, 'bad signal name')elif code == xmlrpc.Faults.NOT_RUNNING:return template % (name, 'not running')elif code == xmlrpc.Faults.SUCCESS:return '%s: %s' % (name, success)elif code == xmlrpc.Faults.FAILED:return fault_string# assertionraise ValueError('Unknown result code %s for %s' % (code, name))def _stopresult(self, result):return self._signalresult(result, success='stopped')def do_stop(self, arg):if not self.ctl.upcheck():returnnames = arg.split()supervisor = self.ctl.get_supervisor()if not names:self.ctl.output('Error: stop requires a process name')self.ctl.exitstatus = LSBInitExitStatuses.GENERICself.help_stop()return Falseif 'all' in names:results = supervisor.stopAllProcesses()for result in results:self.ctl.output(self._stopresult(result))self.ctl.set_exitstatus_from_xmlrpc_fault(result['status'], xmlrpc.Faults.NOT_RUNNING)else:for name in names:group_name, process_name = split_namespec(name)if process_name is None:try:results = supervisor.stopProcessGroup(group_name)for result in results:self.ctl.output(self._stopresult(result))self.ctl.set_exitstatus_from_xmlrpc_fault(result['status'], xmlrpc.Faults.NOT_RUNNING)except xmlrpclib.Fault as e:self.ctl.exitstatus = LSBInitExitStatuses.GENERICif e.faultCode == xmlrpc.Faults.BAD_NAME:error = "%s: ERROR (no such group)" % group_nameself.ctl.output(error)else:raiseelse:try:supervisor.stopProcess(name)except xmlrpclib.Fault as e:error = {'status': e.faultCode,'name': process_name,'group': group_name,'description':e.faultString}self.ctl.output(self._stopresult(error))self.ctl.set_exitstatus_from_xmlrpc_fault(error['status'], xmlrpc.Faults.NOT_RUNNING)return errorelse:name = make_namespec(group_name, process_name)self.ctl.output('%s: stopped' % name)return Truedef help_stop(self):self.ctl.output("stop <name>\t\tStop a process")self.ctl.output("stop <gname>:*\t\tStop all processes in a group")self.ctl.output("stop <name> <name>\tStop multiple processes or groups")self.ctl.output("stop all\t\tStop all processes")def do_signal(self, arg):if not self.ctl.upcheck():returnargs = arg.split()if len(args) < 2:self.ctl.output('Error: signal requires a signal name and a process name')self.help_signal()self.ctl.exitstatus = LSBInitExitStatuses.GENERICreturnsig = args[0]names = args[1:]supervisor = self.ctl.get_supervisor()if 'all' in names:results = supervisor.signalAllProcesses(sig)for result in results:self.ctl.output(self._signalresult(result))self.ctl.set_exitstatus_from_xmlrpc_fault(result['status'])else:for name in names:group_name, process_name = split_namespec(name)if process_name is None:try:results = supervisor.signalProcessGroup(group_name, sig)for result in results:self.ctl.output(self._signalresult(result))self.ctl.set_exitstatus_from_xmlrpc_fault(result['status'])except xmlrpclib.Fault as e:if e.faultCode == xmlrpc.Faults.BAD_NAME:error = "%s: ERROR (no such group)" % group_nameself.ctl.output(error)self.ctl.exitstatus = LSBInitExitStatuses.GENERICelse:raiseelse:try:supervisor.signalProcess(name, sig)except xmlrpclib.Fault as e:error = {'status': e.faultCode,'name': process_name,'group': group_name,'description':e.faultString}self.ctl.output(self._signalresult(error))self.ctl.set_exitstatus_from_xmlrpc_fault(error['status'])return errorelse:name = make_namespec(group_name, process_name)self.ctl.output('%s: signalled' % name)return Truedef help_signal(self):self.ctl.output("signal <signal name> <name>\t\tSignal a process")self.ctl.output("signal <signal name> <gname>:*\t\tSignal all processes in a group")self.ctl.output("signal <signal name> <name> <name>\tSignal multiple processes or groups")self.ctl.output("signal <signal name> all\t\tSignal all processes")def do_restart(self, arg):if not self.ctl.upcheck():returnnames = arg.split()if not names:self.ctl.output('Error: restart requires a process name')self.ctl.exitstatus = LSBInitExitStatuses.GENERICself.help_restart()returnself.do_stop(arg)self.do_start(arg)return Truedef help_restart(self):self.ctl.output("restart <name>\t\tRestart a process")self.ctl.output("restart <gname>:*\tRestart all processes in a group")self.ctl.output("restart <name> <name>\tRestart multiple processes or ""groups")self.ctl.output("restart all\t\tRestart all processes")self.ctl.output("Note: restart does not reread config files. For that,"" see reread and update.")def do_shutdown(self, arg):if arg:self.ctl.output('Error: shutdown accepts no arguments')self.ctl.exitstatus = LSBInitExitStatuses.GENERICself.help_shutdown()returnif self.ctl.options.interactive:yesno = raw_input('Really shut the remote supervisord process ''down y/N? ')really = yesno.lower().startswith('y')else:really = 1if really:supervisor = self.ctl.get_supervisor()try:supervisor.shutdown()except xmlrpclib.Fault as e:if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:self.ctl.output('ERROR: already shutting down')else:self.ctl.exitstatus = LSBInitExitStatuses.GENERICraiseexcept socket.error as e:self.ctl.exitstatus = LSBInitExitStatuses.GENERICif e.args[0] == errno.ECONNREFUSED:msg = 'ERROR: %s refused connection (already shut down?)'self.ctl.output(msg % self.ctl.options.serverurl)elif e.args[0] == errno.ENOENT:msg = 'ERROR: %s no such file (already shut down?)'self.ctl.output(msg % self.ctl.options.serverurl)else:raiseelse:self.ctl.output('Shut down')return Truedef help_shutdown(self):self.ctl.output("shutdown \tShut the remote supervisord down.")def do_reload(self, arg):if arg:self.ctl.output('Error: reload accepts no arguments')self.ctl.exitstatus = LSBInitExitStatuses.GENERICself.help_reload()returnif self.ctl.options.interactive:yesno = raw_input('Really restart the remote supervisord process ''y/N? ')really = yesno.lower().startswith('y')else:really = 1if really:supervisor = self.ctl.get_supervisor()try:supervisor.restart()except xmlrpclib.Fault as e:self.ctl.exitstatus = LSBInitExitStatuses.GENERICif e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:self.ctl.output('ERROR: already shutting down')else:raiseelse:self.ctl.output('Restarted supervisord')return Truedef help_reload(self):self.ctl.output("reload \t\tRestart the remote supervisord.")def _formatChanges(self, added_changed_dropped_tuple):added, changed, dropped = added_changed_dropped_tuplechangedict = {}for n, t in [(added, 'available'),(changed, 'changed'),(dropped, 'disappeared')]:changedict.update(dict(zip(n, [t] * len(n))))if changedict:names = list(changedict.keys())names.sort()for name in names:self.ctl.output("%s: %s" % (name, changedict[name]))else:self.ctl.output("No config updates to processes")def _formatConfigInfo(self, configinfo):name = make_namespec(configinfo['group'], configinfo['name'])formatted = { 'name': name }if configinfo['inuse']:formatted['inuse'] = 'in use'else:formatted['inuse'] = 'avail'if configinfo['autostart']:formatted['autostart'] = 'auto'else:formatted['autostart'] = 'manual'formatted['priority'] = "%s:%s" % (configinfo['group_prio'],configinfo['process_prio'])template = '%(name)-32s %(inuse)-9s %(autostart)-9s %(priority)s'return template % formatteddef do_avail(self, arg):if arg:self.ctl.output('Error: avail accepts no arguments')self.ctl.exitstatus = LSBInitExitStatuses.GENERICself.help_avail()returnsupervisor = self.ctl.get_supervisor()try:configinfo = supervisor.getAllConfigInfo()except xmlrpclib.Fault as e:self.ctl.exitstatus = LSBInitExitStatuses.GENERICif e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:self.ctl.output('ERROR: supervisor shutting down')else:raiseelse:for pinfo in configinfo:self.ctl.output(self._formatConfigInfo(pinfo))return Truedef help_avail(self):self.ctl.output("avail\t\t\tDisplay all configured processes")def do_reread(self, arg):if arg:self.ctl.output('Error: reread accepts no arguments')self.ctl.exitstatus = LSBInitExitStatuses.GENERICself.help_reread()returnsupervisor = self.ctl.get_supervisor()try:result = supervisor.reloadConfig()except xmlrpclib.Fault as e:self.ctl.exitstatus = LSBInitExitStatuses.GENERICif e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:self.ctl.output('ERROR: supervisor shutting down')elif e.faultCode == xmlrpc.Faults.CANT_REREAD:self.ctl.output("ERROR: %s" % e.faultString)else:raiseelse:self._formatChanges(result[0])return Truedef help_reread(self):self.ctl.output("reread \t\t\tReload the daemon's configuration files without add/remove")def do_add(self, arg):names = arg.split()supervisor = self.ctl.get_supervisor()for name in names:try:supervisor.addProcessGroup(name)except xmlrpclib.Fault as e:if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:self.ctl.output('ERROR: shutting down')self.ctl.exitstatus = LSBInitExitStatuses.GENERICelif e.faultCode == xmlrpc.Faults.ALREADY_ADDED:self.ctl.output('ERROR: process group already active')elif e.faultCode == xmlrpc.Faults.BAD_NAME:self.ctl.output("ERROR: no such process/group: %s" % name)self.ctl.exitstatus = LSBInitExitStatuses.GENERICelse:self.ctl.exitstatus = LSBInitExitStatuses.GENERICraiseelse:self.ctl.output("%s: added process group" % name)return Truedef help_add(self):self.ctl.output("add <name> [...]\tActivates any updates in config ""for process/group")def do_remove(self, arg):names = arg.split()supervisor = self.ctl.get_supervisor()for name in names:try:supervisor.removeProcessGroup(name)except xmlrpclib.Fault as e:self.ctl.exitstatus = LSBInitExitStatuses.GENERICif e.faultCode == xmlrpc.Faults.STILL_RUNNING:self.ctl.output('ERROR: process/group still running: %s'% name)elif e.faultCode == xmlrpc.Faults.BAD_NAME:self.ctl.output("ERROR: no such process/group: %s" % name)else:raiseelse:self.ctl.output("%s: removed process group" % name)return Truedef help_remove(self):self.ctl.output("remove <name> [...]\tRemoves process/group from ""active config")def do_update(self, arg):def log(name, message):self.ctl.output("%s: %s" % (name, message))supervisor = self.ctl.get_supervisor()try:result = supervisor.reloadConfig()except xmlrpclib.Fault as e:self.ctl.exitstatus = LSBInitExitStatuses.GENERICif e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:self.ctl.output('ERROR: already shutting down')returnelse:raiseadded, changed, removed = result[0]valid_gnames = set(arg.split())# If all is specified treat it as if nothing was specified.if "all" in valid_gnames:valid_gnames = set()# If any gnames are specified we need to verify that they are# valid in order to print a useful error message.if valid_gnames:groups = set()for info in supervisor.getAllProcessInfo():groups.add(info['group'])# New gnames would not currently exist in this set so# add those as well.groups.update(added)for gname in valid_gnames:if gname not in groups:self.ctl.output('ERROR: no such group: %s' % gname)self.ctl.exitstatus = LSBInitExitStatuses.GENERICfor gname in removed:if valid_gnames and gname not in valid_gnames:continueresults = supervisor.stopProcessGroup(gname)log(gname, "stopped")fails = [res for res in resultsif res['status'] == xmlrpc.Faults.FAILED]if fails:self.ctl.output("%s: %s" % (gname, "has problems; not removing"))self.ctl.exitstatus = LSBInitExitStatuses.GENERICcontinuesupervisor.removeProcessGroup(gname)log(gname, "removed process group")for gname in changed:if valid_gnames and gname not in valid_gnames:continuesupervisor.stopProcessGroup(gname)log(gname, "stopped")supervisor.removeProcessGroup(gname)supervisor.addProcessGroup(gname)log(gname, "updated process group")for gname in added:if valid_gnames and gname not in valid_gnames:continuesupervisor.addProcessGroup(gname)log(gname, "added process group")return Truedef help_update(self):self.ctl.output("update\t\t\tReload config and add/remove as necessary, and will restart affected programs")self.ctl.output("update all\t\tReload config and add/remove as necessary, and will restart affected programs")self.ctl.output("update <gname> [...]\tUpdate specific groups")def _clearresult(self, result):name = make_namespec(result['group'], result['name'])code = result['status']template = '%s: ERROR (%s)'if code == xmlrpc.Faults.BAD_NAME:return template % (name, 'no such process')elif code == xmlrpc.Faults.FAILED:return template % (name, 'failed')elif code == xmlrpc.Faults.SUCCESS:return '%s: cleared' % nameraise ValueError('Unknown result code %s for %s' % (code, name))def do_clear(self, arg):if not self.ctl.upcheck():returnnames = arg.split()if not names:self.ctl.output('Error: clear requires a process name')self.ctl.exitstatus = LSBInitExitStatuses.GENERICself.help_clear()returnsupervisor = self.ctl.get_supervisor()if 'all' in names:results = supervisor.clearAllProcessLogs()for result in results:self.ctl.output(self._clearresult(result))self.ctl.set_exitstatus_from_xmlrpc_fault(result['status'])else:for name in names:group_name, process_name = split_namespec(name)try:supervisor.clearProcessLogs(name)except xmlrpclib.Fault as e:error = {'status': e.faultCode,'name': process_name,'group': group_name,'description': e.faultString}self.ctl.output(self._clearresult(error))self.ctl.set_exitstatus_from_xmlrpc_fault(error['status'])return errorelse:name = make_namespec(group_name, process_name)self.ctl.output('%s: cleared' % name)return Truedef help_clear(self):self.ctl.output("clear <name>\t\tClear a process' log files.")self.ctl.output("clear <name> <name>\tClear multiple process' log files")self.ctl.output("clear all\t\tClear all process' log files")def do_open(self, arg):url = arg.strip()parts = urlparse.urlparse(url)if parts[0] not in ('unix', 'http'):self.ctl.output('ERROR: url must be http:// or unix://')self.ctl.exitstatus = LSBInitExitStatuses.GENERICreturnself.ctl.options.serverurl = url# TODO review thisold_exitstatus = self.ctl.exitstatusself.do_status('')self.ctl.exitstatus = old_exitstatusreturn Truedef help_open(self):self.ctl.output("open <url>\tConnect to a remote supervisord process.")self.ctl.output("\t\t(for UNIX domain socket, use unix:///socket/path)")def do_version(self, arg):if arg:self.ctl.output('Error: version accepts no arguments')self.ctl.exitstatus = LSBInitExitStatuses.GENERICself.help_version()returnif not self.ctl.upcheck():returnsupervisor = self.ctl.get_supervisor()self.ctl.output(supervisor.getSupervisorVersion())return Truedef help_version(self):self.ctl.output("version\t\t\tShow the version of the remote supervisord ""process")def do_fg(self, arg):if not self.ctl.upcheck():returnnames = arg.split()if not names:self.ctl.output('ERROR: no process name supplied')self.ctl.exitstatus = LSBInitExitStatuses.GENERICself.help_fg()returnif len(names) > 1:self.ctl.output('ERROR: too many process names supplied')self.ctl.exitstatus = LSBInitExitStatuses.GENERICreturnname = names[0]supervisor = self.ctl.get_supervisor()try:info = supervisor.getProcessInfo(name)except xmlrpclib.Fault as e:if e.faultCode == xmlrpc.Faults.BAD_NAME:self.ctl.output('ERROR: bad process name supplied')self.ctl.exitstatus = LSBInitExitStatuses.GENERICelse:self.ctl.output('ERROR: ' + str(e))returnif info['state'] != states.ProcessStates.RUNNING:self.ctl.output('ERROR: process not running')self.ctl.exitstatus = LSBInitExitStatuses.GENERICreturnself.ctl.output('==> Press Ctrl-C to exit <==')a = Nonetry:# this thread takes care of the output/error messagesa = fgthread(name, self.ctl)a.start()# this takes care of the user inputwhile True:inp = raw_input() + '\n'try:supervisor.sendProcessStdin(name, inp)except xmlrpclib.Fault as e:if e.faultCode == xmlrpc.Faults.NOT_RUNNING:self.ctl.output('Process got killed')else:self.ctl.output('ERROR: ' + str(e))self.ctl.output('Exiting foreground')a.kill()returninfo = supervisor.getProcessInfo(name)if info['state'] != states.ProcessStates.RUNNING:self.ctl.output('Process got killed')self.ctl.output('Exiting foreground')a.kill()returnexcept (KeyboardInterrupt, EOFError):self.ctl.output('Exiting foreground')if a:a.kill()return Truedef help_fg(self,args=None):self.ctl.output('fg <process>\tConnect to a process in foreground mode')self.ctl.output("\t\tCtrl-C to exit")class Dummy:passclass ClientOptions(Options):positional_args_allowed = 1interactive = Noneprompt = Noneserverurl = Noneusername = Nonepassword = Nonehistory_file = Nonedef __init__(self):Options.__init__(self, require_configfile=False)self.configroot = Dummy()self.configroot.supervisorctl = Dummy()self.configroot.supervisorctl.interactive = Noneself.configroot.supervisorctl.prompt = 'supervisor'self.configroot.supervisorctl.serverurl = Noneself.configroot.supervisorctl.username = Noneself.configroot.supervisorctl.password = Noneself.configroot.supervisorctl.history_file = Nonedefault_factory = ('default', LanhuiControllerPlugin, {})# we always add the default factory. If you want to a supervisorctl# without the default plugin, please write your own supervisorctl.self.plugin_factories = [default_factory]self.add("interactive", "supervisorctl.interactive", "i","interactive", flag=1, default=0)self.add("prompt", "supervisorctl.prompt", default="supervisor")self.add("serverurl", "supervisorctl.serverurl", "s:", "serverurl=",'http://', default="http://localhost:9001")self.add("username", "supervisorctl.username", "u:", "username=")self.add("password", "supervisorctl.password", "p:", "password=")self.add("history", "supervisorctl.history_file", "r:", "history_file=")def realize(self, *arg, **kw):Options.realize(self, *arg, **kw)# if not self.args:# self.interactive = 0format = '%(levelname)s: %(message)s\n'logger = loggers.getLogger()loggers.handle_stdout(logger, format)self._log_parsing_messages(logger)def read_config(self, fp):section = self.configroot.supervisorctlneed_close = Falseif not hasattr(fp, 'read'):self.here = os.path.dirname(normalize_path(fp))if not self.exists(fp):raise ValueError("could not find config file %s" % fp)try:fp = self.open(fp, 'r')need_close = Trueexcept (IOError, OSError):raise ValueError("could not read config file %s" % fp)parser = UnhosedConfigParser()parser.expansions = self.environ_expansionsparser.mysection = 'supervisorctl'try:parser.read_file(fp)except AttributeError:parser.readfp(fp)if need_close:fp.close()self.read_include_config(fp, parser, parser.expansions)sections = parser.sections()if not 'supervisorctl' in sections:raise ValueError('.ini file does not include supervisorctl section')serverurl = parser.getdefault('serverurl', 'http://localhost:9001',expansions={'here': self.here})if serverurl.startswith('unix://'):path = normalize_path(serverurl[7:])serverurl = 'unix://%s' % pathsection.serverurl = serverurl# The defaults used below are really set in __init__ (since# section==self.configroot.supervisorctl)section.prompt = parser.getdefault('prompt', section.prompt)section.username = parser.getdefault('username', section.username)section.password = parser.getdefault('password', section.password)history_file = parser.getdefault('history_file', section.history_file,expansions={'here': self.here})if history_file:history_file = normalize_path(history_file)section.history_file = history_fileself.history_file = history_fileelse:section.history_file = Noneself.history_file = Noneself.plugin_factories += self.get_plugins(parser,'supervisor.ctl_factory','ctlplugin:')return section# TODO: not covered by any test, but used by supervisorctldef getServerProxy(self):return xmlrpclib.ServerProxy(# dumbass ServerProxy won't allow us to pass in a non-HTTP url,# so we fake the url we pass into it and always use the transport's# 'serverurl' to figure out what to attach to'http://127.0.0.1',transport = xmlrpc.SupervisorTransport(self.username,self.password,self.serverurl))
# 修改的使用样例
def main(args=None, options=None):if options is None:options = ClientOptions()options.realize(args, doc=__doc__)c = Controller(options)# 调用 status 的样例print(c._get_do_func('status')(None))print(c._get_do_func('status')('all'))# if options.args:#     c.onecmd(" ".join(options.args))#     sys.exit(c.exitstatus)# if options.interactive:#     c.exec_cmdloop(args, options)#     sys.exit(0)  # exitstatus always 0 for interactive modeif __name__ == "__main__":# app.run(host='0.0.0.0', port=10801, threaded=True, use_reloader=False)main()

supervisor的程序控制修改参考方案相关推荐

  1. linux镜像修改密码,OpenStack 镜像修改密码方案

    现在各大linux厂商,其实已经有专门给openStack提供的镜像,不过国内的朋友,不太习惯老外做镜像的方式,经常问密码是多少.本博客提供几种修改密码方案,仅供参考. 前言 对OpenStack云主 ...

  2. 【matlab】绘制云图 + 修改配色方案 + 保存配色方案

    一.绘制云图 假设 x.y 为坐标向量,f 为各个坐标点所对应的函数值 的向量 1.需要根据 x.y 的范围进行划分,可以理解为建立坐标系或比例尺 X1和Y1 X1 = linspace(min(x) ...

  3. Gazebo中针对Gazebo软件或生成模型出现process has died问题的参考方案

    在基于ROS机器人仿真时,打开Gazebo经常出现一些问题,比如Gazebo窗口打开了,终端却显示gazebo进程已死.本文记录了一次遇到的类似问题,并给出了在部分情况下能够适用的参考方案. 问题描述 ...

  4. 【运筹学】表上作业法 ( 最优解判别 | 初始基可行解 | 运费修改可行性方案 | 闭回路法 )

    文章目录 一.最优解判别 二.初始基可行解 三.运费修改可行性方案 四.闭回路法 一.最优解判别 在上两篇博客 [运筹学]表上作业法 ( 求初始基可行解 | 最小元素法 ) , [运筹学]表上作业法 ...

  5. web2.0网站的配色参考方案

    web2.0网站的配色参考方案 Shiny silver [#EEEEEE] Reddit white [#FFFFFF] Magnolia Mag.nolia [#F9F7ED] Interacti ...

  6. 英飞凌电动汽车参考方案,包含原理图,和Bom清单

    英飞凌电动汽车参考方案,包含原理图,和Bom清单,说明文档和代码,基于英飞凌TC27xC平台. ID:57200634941592563微微就不笑哈

  7. TFmini__Plus的I²C接口电脑单机测试参考方案

    一.方案概述 本方案可以实现一种利用一款市面上可买到的USB-I²C转换器连接电脑单机测试TFmini_Plus的I²C接口的简单方法. 方案中使用的USB转I²C转换器非北醒公司产品,有需要请自行购 ...

  8. 网易云网络部署参考方案-网易云网络服务研发实践|网易云

    本系列以私有云为例,将为大家讲述网易云网络服务的研发实践. 作者:张晓龙 浙江大学计算机学院本科.博士毕业.网易专业技术委员会委员.网易云计算基础设施研发负责人.专注于云计算.虚拟化.软件自定义网络( ...

  9. 英飞凌电动汽车参考方案,包含原理图,和Bom清单,和代码,基于英飞凌TC27xC平台

    英飞凌电动汽车参考方案,包含原理图,和Bom清单,和代码,基于英飞凌TC27xC平台

最新文章

  1. 一、mysql分表简单介绍
  2. 缓存Cookie、session、localStorage的区别
  3. C++ Primer 5th笔记(chap 19 特殊工具与技术)链接指示: extern “C“
  4. sklearn应用—高斯混合
  5. maven中scope属性的
  6. JDK源码解析之 Java.lang.Double
  7. spring 集成mybatis——多数据源切换(附带定时器的配置)
  8. apache web_Web发明家预测文化将发生变化,Apache推动一半的互联网发展,等等
  9. Codeforce 1600Difficulty Graphs 20 questions
  10. 软件工程毕业设计选题c语言,经典软件工程专业论文选题 软件工程专业论文题目选什么比较好...
  11. IT 必备电脑快捷键
  12. 单片机复位电路是怎么工作的?
  13. 豆瓣api不能访问了的解决办法
  14. 遗传算法(三)——基本遗传算法
  15. 宝塔php漏洞,宝塔面板 phpmyadmin 未授权访问漏洞 BUG ip:888/pma的问题分析
  16. IO流原理及流的分类
  17. oracle工资第二高怎么查询,求各部门第二高的工资
  18. GPIO 配置之ODR, BSRR, BRR 详解
  19. 开放科研:数据科学场景下如何让研究更加开放?
  20. 联想服务器维修单据,联想ThinkServer SR650服务器故障维修

热门文章

  1. 真的,我现在特讨厌 Safari 浏览器!
  2. 关于基线版本、基线那点事儿
  3. android手机备份恢复出厂设置密码,手机恢复出厂设置 如何让安卓手机恢复出厂设置经验分享...
  4. Excel中F4键的作用
  5. 【Opencv】【C++】 Opencv之calcHist() 计算直方图
  6. 加班调休被拒的程序员,怒怼996是行规的HR,你的996真的值吗?
  7. linux http status404,解决问题:HTTP Status 404(The requested resource is not avail
  8. 区块链学习:区块链分类
  9. sqlserver字符串转日期
  10. 吉他谱Guitar pro是什么软件及功能作用介绍