예제 #1
0
class NatsClient(object):
    'nats client class'

    def __init__(self, **argvs):
        self.subs = {}
        self.buf = None
        self.parse_state = Common.AWAITING_CONTROL_LINE
        self.stat = Stat()
        self.conn = Connector(callback=self.process_data, **argvs)
        self.ping_timer = Heartbeat(self.conn)
        print self.__str__()

    def start(self):
        'start nats client, open the connection, and start the ping timer;'
        self.conn.open()
        self.ping_timer.start()

    def stop(self):
        'stop the nats client, close the connection, and cancel ping timer;'
        #self.conn.close()
        try:
           self.conn.close()
           self.ping_timer.cancel()
        except Exception, ex:
           print 'error exit 1'
           sys.exit(1)
예제 #2
0
class NatsClient(object):
    'nats client class'

    def __init__(self, **argvs):
        self.subs = {}
        self.buf = None
        self.parse_state = Common.AWAITING_CONTROL_LINE
        self.stat = Stat()
        self.conn = Connector(callback=self.process_data, **argvs)
        self.ping_timer = Heartbeat(self.conn)
        print self.__str__()

    def start(self):
        'start nats client, open the connection, and start the ping timer;'
        self.conn.open()
        self.ping_timer.start()

    def stop(self):
        'stop the nats client, close the connection, and cancel ping timer;'
        #self.conn.close()
        try:
            self.conn.close()
            self.ping_timer.cancel()
        except Exception, ex:
            print 'error exit 1'
            sys.exit(1)
예제 #3
0
 def __init__(self, **argvs):
     self.subs = {}
     self.buf = None
     self.parse_state = Common.AWAITING_CONTROL_LINE
     self.stat = Stat()
     self.conn = Connector(callback=self.process_data, **argvs)
     self.ping_timer = Heartbeat(self.conn)
     print self.__str__()
예제 #4
0
 def __init__(self, **argvs):
     self.subs = {}
     self.buf = None
     self.parse_state = Common.AWAITING_CONTROL_LINE
     self.stat = Stat()
     self.conn = Connector(callback=self.process_data, **argvs)
     self.ping_timer = Heartbeat(self.conn)
     print self.__str__()
예제 #5
0
class NatsClient(object):
    'nats client class'

    def __init__(self, **argvs):
        self.subs = {}
        self.buf = None
        self.parse_state = Common.AWAITING_CONTROL_LINE
        self.stat = Stat()
        self.conn = Connector(callback=self.process_data, **argvs)
        self.ping_timer = Heartbeat(self.conn)
        print self.__str__()  

    def start(self):
        'start nats client, open the connection, and start the ping timer;'
        self.conn.open()
        self.ping_timer.start()

    def stop(self):
        'stop the nats client, close the connection, and cancel ping timer;'
        self.conn.close()
        self.ping_timer.cancel()

    def _on_messsage(self, subject, sid, msg, reply=None):
        '''\
        actions when received messages from nats server;

        Params:
        =====
        subject: message subject;
        sid: subscriber id;
        reply: inbox name if exists
        msg: message body
        '''
        self.stat.msgs_received += 1
        if msg: 
            self.stat.bytes_received += len(msg)
        if sid not in self.subs: 
            return
        sub = self.subs[sid]
        #unsubscribe subscriber if received enough messages; 
        sub["received"] += 1
        if "max" in sub:  
            if sub["received"] > sub["max"]: 
                return self.unsubscribe(sid) 
            if sub["received"] == sub["max"]: 
                del self.subs[sid]
        callback = sub["callback"]
        if callback:
            args, _, _, _ = inspect.getargspec(callback)
            args_len = len(args)
            if args_len == 0: 
                callback()
            elif args_len == 1:
                callback(msg)
            elif args_len == 2:
                callback(msg, reply)
            else:
                callback.call(msg, reply, subject)
        # cancel autounscribe timer, if subscriber request with timeout, 
        # and receive enough messages;
        if "timeout" in sub  \
                    and sub["timeout"] \
                    and sub["received"] >= sub["expected"]:
            sub["timeout"].cancel()
            sub["timeout"] = None
        self.subs[sid] = sub

    def _process_info(self, info):
        '''\
        process server infomation message;
        
        Params:
        =====
        info: nats server information 
        '''
        server_info = json.loads(info)
        print 'process info'
        if server_info["auth_required"]:
            conn_opts = self.conn.get_connection_options()
            self.conn.send_command(Protocol.connect_command(conn_opts), 
                True)

    def publish(self, subject, msg=Protocol.EMPTY_MSG, opt_reply="", blk=None):
        '''\
        Publish a message to a given subject, with optional reply subject and 
        completion block

        Params:
        =====
        subject: message subject;
        msg: message body;
        opt_reply: reply inbox if needs;
        blk: closure called when publish has been processed by the server.     

        '''
        if not self.conn.connected: 
            raise NatsClientException("Connection losted")
        if not subject: 
            return None
        msg = str(msg)
        self.stat.msgs_sent += 1
        self.stat.bytes_sent += len(msg)
        self.conn.send_command("PUB {} {} {}{}{}{}".format(subject, 
            opt_reply, len(msg), Protocol.CR_LF, msg, Protocol.CR_LF))
        if blk: 
            self.ping_timer.queue_server_rt(blk)

    def subscribe(self, subject, callback=None, **opts):
        '''\
        Subscribe to a subject with optional wildcards.
        Messages will be delivered to the supplied callback.
        Callback can take any number of the supplied arguments as defined by the 
        list: msg, reply, sub.

        Params:
        =====
        subject: optionally with wilcards.
        opts:  optional options hash, e.g. "queue", "max".
        callback, called when a message is delivered.

        Returns:
        =====
        sid: Subject Identifier
        Returns subscription id which can be passed to #unsubscribe.
        '''
        if not self.conn.connected: 
            raise NatsClientException("Connection losted")
        if not subject: 
            return None
        sid = Common.get_ssid()
        queue_str = ""
        sub = {"subject" : subject, "received" : 0}
        sub["callback"] = callback
        if "queue" in opts: 
            queue_str = sub["queue"] = opts["queue"]
        if "max" in opts: 
            sub["max"] = opts["max"] 
        self.conn.send_command("SUB {} {} {}{}".format(subject, queue_str, 
            sid, Protocol.CR_LF))
        self.subs[sid] = sub
        # Setup server support for auto-unsubscribe
        if "max" in opts: 
            self.unsubscribe(sid, opts["max"])
        return sid

    def unsubscribe(self, sid, opt_max=None):
        '''\
        Cancel a subscription.

        Params:
        =====
        sid: Subject Identifier
        opt_max: optional number of responses to receive 
        before auto-unsubscribing.      
        '''
        if not self.conn.connected: 
            raise NatsClientException("Connection losted")
        opt_max_str = ""
        if opt_max: 
            opt_max_str = " " + str(opt_max)
        self.conn.send_command("UNSUB {}{}{}".format(sid, 
            opt_max_str, Protocol.CR_LF))
        if not sid in  self.subs: 
            return None
        sub = self.subs[sid]
        sub["max"] = opt_max
        if not (sub["max"] and (sub["received"] < sub["max"])): 
            del self.subs[sid]

    def timeout(self, sid, timeout, callback=None, **opts):
        '''\
        Setup a timeout for receiving messages for the subscription.

        Params:
        =====
        sid: Subject Identifier
        timeout: integer in seconds
        opts: options, :auto_unsubscribe(true), :expected(1)
        '''
        if not self.conn.connected: 
            raise NatsClientException("Connection losted")
        if not sid in self.subs: 
            return None
        sub = self.subs[sid]
        auto_unsubscribe, expected = True, 1
        if  "auto_unsubscribe" in opts:
            auto_unsubscribe = opts["auto_unsubscribe"]
        if "expected" in opts:
            expected = opts["expected"]
        def pblock():
            'closure block for timeout request'
            if auto_unsubscribe: 
                self.unsubscribe(sid)
            if callback: 
                callback(sid)
        sub["timeout"] = Timer(timeout, pblock)
        sub["timeout"].start()
        sub["expected"] = expected
        self.subs[sid] = sub

    def request(self, subject, data=None, blk=None, **opts):
        '''\
        Send a request and have the response delivered to the supplied callback.

        Params:
        =====
        subject: message subject;
        msg: message payload;
        callback: callback if any;

        Returns:
        =====
        sid: Subject Identifier
        '''
        if not self.conn.connected: 
            raise NatsClientException("Connection losted")
        if not subject: 
            return None
        inbox = Common.create_inbox()
        def process_reply(msg, reply):
            'closure block of request'
            args, _, _, _ = inspect.getargspec(blk)
            args_len = len(args)
            if args_len == 0: 
                blk()
            elif args_len == 1:
                blk(msg)
            else:
                blk(msg, reply)
        sid = self.subscribe(inbox, process_reply, **opts)
        self.publish(subject, data, inbox)
        return sid

    def process_data(self, data):
        "process data received from nats server, disptch data to proper handles"

        if not data: 
            return
        if not self.buf:
            self.buf = data
        else:
            self.buf += data
        assert_protocol_type = Protocol.assert_protocol_type
        not_matched = Protocol.not_matched
        matched = Protocol.matched
        while self.buf:
            print "buf.{}".format(self.buf)
            if self.parse_state == Common.AWAITING_CONTROL_LINE:
                if assert_protocol_type(self.buf, 'msg'):
                    print "matched message"
                    sub, sid, _, reply, needed = matched('msg', self.buf)
                    self.buf = not_matched('msg', self.buf)
                    sid = int(sid)
                    needed = int(needed)                     
                    self.parse_state = Common.AWAITING_MSG_PAYLOAD
                elif assert_protocol_type(self.buf, 'ok'): 
                    self.buf = not_matched('ok', self.buf)
                    print "parseed ok"
                elif assert_protocol_type(self.buf, 'err'):
                    self.buf = not_matched('err', self.buf)
                    print "Nats server error."  
                elif assert_protocol_type(self.buf, 'ping'):
                    self.ping_timer.on_ping_request()
                    self.buf = not_matched('ping', self.buf)
                elif assert_protocol_type(self.buf, 'pong'):
                    print "process Pong"
                    self.buf = not_matched('pong', self.buf)
                    self.ping_timer.on_pong_response()
                elif assert_protocol_type(self.buf, 'info'):
                    self._process_info(matched('info', self.buf))
                    self.buf = not_matched('info', self.buf)
                elif assert_protocol_type(self.buf, 'unknown'):
                    self.buf = not_matched('ok', self.buf)
                    raise NatsException("Unknown protocol")
                else:
                    return None
                if self.buf and len(self.buf) == 0: 
                    self.buf = None
            if self.parse_state == Common.AWAITING_MSG_PAYLOAD:
                if not (needed and 
                    len(self.buf) >= (needed + Protocol.CR_LF_SIZE)): 
                    return None 
                self._on_messsage(sub, sid, self.buf[0 : needed], reply)
                cr_lf_size = Protocol.CR_LF_SIZE
                self.buf = self.buf[(needed + cr_lf_size) : len(self.buf)]
                sub = sid = reply = needed = None
                self.parse_state = Common.AWAITING_CONTROL_LINE
                if self.buf and len(self.buf) == 0: 
                    self.buf = None

    def __str__(self):
        return "python-nats-{}".format(Common.VERSION)