mirror of
https://github.com/haiwen/ccnet-server.git
synced 2025-04-28 02:30:08 +00:00
251 lines
8.1 KiB
Python
251 lines
8.1 KiB
Python
import logging
|
|
import libevent
|
|
|
|
from ccnet.client import Client, parse_update, parse_response
|
|
|
|
from ccnet.packet import response_to_packet, parse_header, Packet
|
|
from ccnet.packet import to_response_id, to_master_id, to_slave_id, to_packet_id
|
|
from ccnet.packet import CCNET_MSG_REQUEST, CCNET_MSG_UPDATE, CCNET_MSG_RESPONSE, \
|
|
CCNET_HEADER_LENGTH, CCNET_MAX_PACKET_LENGTH
|
|
|
|
from ccnet.status_code import SC_PROC_DONE, SC_PROC_DEAD, SS_PROC_DEAD, \
|
|
SC_UNKNOWN_SERVICE, SC_PROC_KEEPALIVE, SS_PROC_KEEPALIVE, SC_PERM_ERR
|
|
|
|
from ccnet.status_code import PROC_NO_SERVICE, PROC_PERM_ERR, \
|
|
PROC_BAD_RESP, PROC_REMOTE_DEAD
|
|
|
|
from ccnet.errors import NetworkError
|
|
|
|
from .processor import Processor
|
|
from .sendcmdproc import SendCmdProc
|
|
from .mqclientproc import MqClientProc
|
|
|
|
|
|
__all__ = [
|
|
'AsyncClient',
|
|
]
|
|
|
|
def debug_print(msg):
|
|
print msg
|
|
|
|
class AsyncClient(Client):
|
|
'''Async mode client'''
|
|
def __init__(self, config_dir, event_base, central_config_dir=None):
|
|
Client.__init__(self, config_dir, central_config_dir)
|
|
self.proc_types = {}
|
|
self.procs = {}
|
|
self.register_processors()
|
|
self._bev = None
|
|
|
|
self._evbase = event_base
|
|
|
|
def get_event_base(self):
|
|
return self._evbase
|
|
|
|
def add_processor(self, proc):
|
|
self.procs[proc.id] = proc
|
|
|
|
def remove_processor(self, proc):
|
|
if proc.id in self.procs:
|
|
del self.procs[proc.id]
|
|
|
|
def get_proc(self, id):
|
|
return self.procs.get(id, None)
|
|
|
|
def write_packet(self, pkt):
|
|
outbuf = self._bev.output
|
|
|
|
outbuf.add(pkt.header.to_string())
|
|
outbuf.add(pkt.body)
|
|
|
|
def send_response(self, id, code, code_msg, content=''):
|
|
id = to_response_id(id)
|
|
pkt = response_to_packet(id, code, code_msg, content)
|
|
self.write_packet(pkt)
|
|
|
|
def handle_packet(self, pkt):
|
|
ptype = pkt.header.ptype
|
|
if ptype == CCNET_MSG_REQUEST:
|
|
self.handle_request(pkt.header.id, pkt.body)
|
|
|
|
elif ptype == CCNET_MSG_UPDATE:
|
|
code, code_msg, content = parse_update(pkt.body)
|
|
self.handle_update(pkt.header.id, code, code_msg, content)
|
|
|
|
elif ptype == CCNET_MSG_RESPONSE:
|
|
code, code_msg, content = parse_response(pkt.body)
|
|
self.handle_response(pkt.header.id, code, code_msg, content)
|
|
|
|
else:
|
|
logging.warning("unknown packet type %d", ptype)
|
|
|
|
def handle_request(self, id, req):
|
|
commands = req.split()
|
|
self.create_slave_processor(to_slave_id(id), commands)
|
|
|
|
def create_slave_processor(self, id, commands):
|
|
peer_id = self.peer_id
|
|
if commands[0] == 'remote':
|
|
if len(commands) < 3:
|
|
logging.warning("invalid request %s", commands)
|
|
return
|
|
peer_id = commands[1]
|
|
commands = commands[2:]
|
|
|
|
proc_name = commands[0]
|
|
|
|
if not proc_name in self.proc_types:
|
|
logging.warning("unknown processor type %s", proc_name)
|
|
return
|
|
|
|
cls = self.proc_types[proc_name]
|
|
|
|
proc = cls(proc_name, id, peer_id, self)
|
|
self.add_processor(proc)
|
|
proc.start(*commands[1:])
|
|
|
|
def create_master_processor(self, proc_name):
|
|
id = self.get_request_id()
|
|
|
|
cls = self.proc_types.get(proc_name, None)
|
|
if cls == None:
|
|
logging.error('unknown processor type %s', proc_name)
|
|
return None
|
|
|
|
proc = cls(proc_name, id, self.peer_id, self)
|
|
self.add_processor(proc)
|
|
return proc
|
|
|
|
def handle_update(self, id, code, code_msg, content):
|
|
proc = self.get_proc(to_slave_id(id))
|
|
if proc == None:
|
|
if code != SC_PROC_DEAD:
|
|
self.send_response(id, SC_PROC_DEAD, SS_PROC_DEAD)
|
|
return
|
|
|
|
if code[0] == '5':
|
|
logging.info('shutdown processor %s(%d): %s %s\n',
|
|
proc.name, to_packet_id(proc.id), code, code_msg)
|
|
if code == SC_UNKNOWN_SERVICE:
|
|
proc.shutdown(PROC_NO_SERVICE)
|
|
elif code == SC_PERM_ERR:
|
|
proc.shutdown(PROC_PERM_ERR)
|
|
else:
|
|
proc.shutdown(PROC_BAD_RESP)
|
|
|
|
elif code == SC_PROC_KEEPALIVE:
|
|
proc.send_response(SC_PROC_KEEPALIVE, SS_PROC_KEEPALIVE)
|
|
|
|
elif code == SC_PROC_DEAD:
|
|
logging.info('shutdown processor %s(%d): when peer(%.8s) processor is dead\n',
|
|
proc.name, to_packet_id(proc.id), proc.peer_id)
|
|
proc.shutdown(PROC_REMOTE_DEAD)
|
|
|
|
elif code == SC_PROC_DONE:
|
|
proc.done(True)
|
|
|
|
else:
|
|
proc.handle_update(code, code_msg, content)
|
|
|
|
def handle_response(self, id, code, code_msg, content):
|
|
proc = self.get_proc(to_master_id(id))
|
|
if proc == None:
|
|
if code != SC_PROC_DEAD:
|
|
self.send_update(id, SC_PROC_DEAD, SS_PROC_DEAD)
|
|
return
|
|
|
|
if code[0] == '5':
|
|
logging.info('shutdown processor %s(%d): %s %s\n',
|
|
proc.name, to_packet_id(proc.id), code, code_msg)
|
|
if code == SC_UNKNOWN_SERVICE:
|
|
proc.shutdown(PROC_NO_SERVICE)
|
|
elif code == SC_PERM_ERR:
|
|
proc.shutdown(PROC_PERM_ERR)
|
|
else:
|
|
proc.shutdown(PROC_BAD_RESP)
|
|
|
|
elif code == SC_PROC_KEEPALIVE:
|
|
proc.send_update(id, SC_PROC_KEEPALIVE, SS_PROC_KEEPALIVE)
|
|
|
|
elif code == SC_PROC_DEAD:
|
|
logging.info('shutdown processor %s(%d): when peer(%.8s) processor is dead\n',
|
|
proc.name, to_packet_id(proc.id), proc.peer_id)
|
|
proc.shutdown(PROC_REMOTE_DEAD)
|
|
|
|
else:
|
|
proc.handle_response(code, code_msg, content)
|
|
|
|
def register_processor(self, proc_name, proc_type):
|
|
assert Processor in proc_type.mro()
|
|
|
|
self.proc_types[proc_name] = proc_type
|
|
|
|
def register_processors(self):
|
|
self.register_processor("send-cmd", SendCmdProc)
|
|
self.register_processor("mq-client", MqClientProc)
|
|
|
|
def register_service(self, service, group, proc_type, callback=None):
|
|
self.register_processor(service, proc_type)
|
|
cmd = 'register-service %s %s' % (service, group)
|
|
self.send_cmd(cmd, callback)
|
|
|
|
def send_cmd(self, cmd, callback=None):
|
|
proc = self.create_master_processor("send-cmd")
|
|
if callback:
|
|
proc.set_callback(callback)
|
|
proc.start()
|
|
proc.send_cmd(cmd)
|
|
|
|
def _read_cb(self, bev, cb_data):
|
|
dummy = bev, cb_data
|
|
|
|
inbuf = self._bev.input
|
|
while (True):
|
|
raw = inbuf.copyout(CCNET_HEADER_LENGTH)
|
|
header = parse_header(raw)
|
|
if len(inbuf) < CCNET_HEADER_LENGTH + header.length:
|
|
break
|
|
|
|
inbuf.drain(CCNET_HEADER_LENGTH)
|
|
data = inbuf.copyout(header.length)
|
|
pkt = Packet(header, data)
|
|
|
|
self.handle_packet(pkt)
|
|
|
|
inbuf.drain(header.length)
|
|
|
|
if len(inbuf) < CCNET_HEADER_LENGTH:
|
|
break
|
|
|
|
def _event_cb(self, bev, what, cb_data):
|
|
dummy = bev, cb_data
|
|
logging.warning('libevent error: what = %s' % what)
|
|
if what & libevent.BEV_EVENT_EOF or \
|
|
what & libevent.BEV_EVENT_ERROR or \
|
|
what & libevent.BEV_EVENT_READING or \
|
|
what & libevent.BEV_EVENT_WRITING:
|
|
if self._bev is not None:
|
|
self._bev = None
|
|
raise NetworkError('libevent error: what = %s' % what)
|
|
|
|
def base_loop(self):
|
|
'''Create an event base -> register socket events -> loop'''
|
|
self._bev = libevent.BufferEvent(self._evbase,
|
|
self._connfd.fileno())
|
|
|
|
self._bev.set_watermark(libevent.EV_READ,
|
|
CCNET_HEADER_LENGTH, # low wartermark
|
|
CCNET_MAX_PACKET_LENGTH * 2) # highmark
|
|
|
|
self._bev.set_callbacks(self._read_cb, # read callback
|
|
None, # write callback
|
|
self._event_cb) # event callback
|
|
|
|
self._bev.enable(libevent.EV_READ | libevent.EV_WRITE)
|
|
|
|
self._evbase.loop()
|
|
|
|
def main_loop(self):
|
|
self.base_loop()
|
|
|