예제 #1
0
def run_worker(context):
    poller = Poller()

    liveness = HEARTBEAT_LIVENESS
    interval = INTERVAL_INIT

    heartbeat_at = time.time() + HEARTBEAT_INTERVAL

    worker = yield worker_socket(context, poller)
    cycles = 0
    while True:
        socks = yield poller.poll(HEARTBEAT_INTERVAL * 1000)
        socks = dict(socks)

        # Handle worker activity on backend
        if socks.get(worker) == zmq.POLLIN:
            #  Get message
            #  - 3-part envelope + content -> request
            #  - 1-part HEARTBEAT -> heartbeat
            frames = yield worker.recv_multipart()
            if not frames:
                break    # Interrupted

            if len(frames) == 3:
                # Simulate various problems, after a few cycles
                cycles += 1
                if cycles > 3 and randint(0, 5) == 0:
                    print("I: Simulating a crash")
                    break
                if cycles > 3 and randint(0, 5) == 0:
                    print("I: Simulating CPU overload")
                    yield gen.sleep(3)
                print("I: Normal reply")
                yield worker.send_multipart(frames)
                liveness = HEARTBEAT_LIVENESS
                yield gen.sleep(1)  # Do some heavy work
            elif len(frames) == 1 and frames[0] == PPP_HEARTBEAT:
                print("I: Queue heartbeat")
                liveness = HEARTBEAT_LIVENESS
            else:
                print("E: Invalid message: %s" % frames)
            interval = INTERVAL_INIT
        else:
            liveness -= 1
            if liveness == 0:
                print("W: Heartbeat failure, can't reach queue")
                print("W: Reconnecting in %0.2fs..." % interval)
                yield gen.sleep(interval)

                if interval < INTERVAL_MAX:
                    interval *= 2
                poller.unregister(worker)
                worker.setsockopt(zmq.LINGER, 0)
                worker.close()
                worker = yield worker_socket(context, poller)
                liveness = HEARTBEAT_LIVENESS
        if time.time() > heartbeat_at:
            heartbeat_at = time.time() + HEARTBEAT_INTERVAL
            print("I: Worker heartbeat")
            yield worker.send(PPP_HEARTBEAT)
예제 #2
0
def run_client(context):
    print("I: Connecting to server...")
    client = context.socket(zmq.REQ)
    client.connect(SERVER_ENDPOINT)
    poll = Poller()
    poll.register(client, zmq.POLLIN)
    sequence = 0
    retries_left = REQUEST_RETRIES
    while retries_left:
        sequence += 1
        request = str(sequence)
        print("I: Sending (%s)" % request)
        yield client.send_string(request)
        expect_reply = True
        while expect_reply:
            socks = yield poll.poll(REQUEST_TIMEOUT)
            socks = dict(socks)
            if socks.get(client) == zmq.POLLIN:
                reply = yield client.recv()
                if not reply:
                    break
                if int(reply) == sequence:
                    print("I: Server replied OK (%s)" % reply)
                    retries_left = REQUEST_RETRIES
                    expect_reply = False
                else:
                    print("E: Malformed reply from server: %s" % reply)
            else:
                print("W: No response from server, retrying...")
                # Socket is confused. Close and remove it.
                print('W: confused')
                client.setsockopt(zmq.LINGER, 0)
                client.unbind(SERVER_ENDPOINT)
                #client.close()
                poll.unregister(client)
                retries_left -= 1
                if retries_left == 0:
                    print("E: Server seems to be offline, abandoning")
                    return
                print("I: Reconnecting and resending (%s)" % request)
                # Create new connection
                client = context.socket(zmq.REQ)
                client.connect(SERVER_ENDPOINT)
                poll.register(client, zmq.POLLIN)
                yield client.send_string(request)
예제 #3
0
파일: lpclient.py 프로젝트: lw000/zmq_guide
def run_client(context):
    print("I: Connecting to server...")
    client = context.socket(zmq.REQ)
    client.connect(SERVER_ENDPOINT)
    poll = Poller()
    poll.register(client, zmq.POLLIN)
    sequence = 0
    retries_left = REQUEST_RETRIES
    while retries_left:
        sequence += 1
        request = str(sequence)
        print("I: Sending (%s)" % request)
        yield client.send_string(request)
        expect_reply = True
        while expect_reply:
            socks = yield poll.poll(REQUEST_TIMEOUT)
            socks = dict(socks)
            if socks.get(client) == zmq.POLLIN:
                reply = yield client.recv()
                if not reply:
                    break
                if int(reply) == sequence:
                    print("I: Server replied OK (%s)" % reply)
                    retries_left = REQUEST_RETRIES
                    expect_reply = False
                else:
                    print("E: Malformed reply from server: %s" % reply)
            else:
                print("W: No response from server, retrying...")
                # Socket is confused. Close and remove it.
                print('W: confused')
                client.setsockopt(zmq.LINGER, 0)
                client.unbind(SERVER_ENDPOINT)
                #client.close()
                poll.unregister(client)
                retries_left -= 1
                if retries_left == 0:
                    print("E: Server seems to be offline, abandoning")
                    return
                print("I: Reconnecting and resending (%s)" % request)
                # Create new connection
                client = context.socket(zmq.REQ)
                client.connect(SERVER_ENDPOINT)
                poll.register(client, zmq.POLLIN)
                yield client.send_string(request)
예제 #4
0
class TornadoRdsBusClient(object):
    ASC = 1
    DESC = -1

    def __init__(self,
                 url: str,
                 logger,
                 request_timeout: int = None,
                 database: str = None):
        self._logger = logger
        self._database = database
        self._context = Context.instance()
        self._poller = Poller()
        self._request = self._context.socket(zmq.DEALER)
        self._request_timeout = request_timeout or 60
        self._rds_bus_url = url
        self._request.connect(self._rds_bus_url)
        self._request_dict = dict()
        self._io_loop = ioloop.IOLoop.current()
        self._io_loop.add_callback(self.start)

    @classmethod
    def pack(cls,
             database: str,
             key: str,
             parameter: dict,
             is_query: bool = False,
             order_by: list = None,
             page_no: int = None,
             per_page: int = None,
             found_rows: bool = False):
        """
        打包请求数据
        :param database: RDS-BUS的数据库类名
        :param key: 数据库类所持有的实例名
        :param parameter: 参数字典
        :param is_query: 是否为查询操作
        :param order_by: 排序信息 [{"column": "字段名", "order": TornadoRdsBusClient.ASC/TornadoRdsBusClient.DESC}]
        :param page_no: 当前页(范围[0-n) n指第n页)
        :param per_page: 每页记录数
        :param found_rows: 是否统计总数
        :return:
        """
        if is_query:
            amount = int(per_page) if per_page else None
            offset = int(page_no) * amount if page_no else None
            limit = (dict(amount=amount, offset=offset) if offset else dict(
                amount=amount)) if amount else None
            result = dict(command="{}/{}".format(database, key),
                          data=dict(var=parameter,
                                    order_by=order_by,
                                    limit=limit,
                                    found_rows=found_rows))
        else:
            result = dict(command="{}/{}".format(database, key),
                          data=dict(var=parameter))
        return result

    @gen.coroutine
    def query(self,
              key: str,
              parameter: dict,
              order_by: list = None,
              page_no: int = None,
              per_page: int = None,
              found_rows: bool = False,
              database: str = None,
              execute: bool = True):
        """
        查询接口
        :param database: RDS-BUS的数据库类名
        :param key: 数据库类所持有的语句实例名
        :param parameter: 参数字典
        :param order_by: 排序信息 [{"column": "字段名", "order": TornadoRdsBusClient.ASC/TornadoRdsBusClient.DESC}]
        :param page_no: 当前页(范围[0-n) n指第n页)
        :param per_page: 每页记录数
        :param found_rows: 是否统计总数
        :param execute: 是否执行
        :return:
        """
        _database = database or self._database
        argument = self.pack(database=_database,
                             key=key,
                             parameter=parameter,
                             is_query=True,
                             order_by=order_by,
                             page_no=page_no,
                             per_page=per_page,
                             found_rows=found_rows)
        if execute:
            response = yield self._send(operation=OperationType.QUERY,
                                        argument=argument)
            result = RdsData(response)
        else:
            result = argument
        return result

    @gen.coroutine
    def insert(self,
               key: str,
               parameter: dict,
               database: str = None,
               execute: bool = True):
        """
        新增接口
        :param database: RDS-BUS的数据库类名
        :param key: 数据库类所持有的语句实例名
        :param parameter: 参数字典
        :param execute: 是否执行
        :return:
        """
        _database = database or self._database
        argument = self.pack(database=_database, key=key, parameter=parameter)
        if execute:
            response = yield self._send(operation=OperationType.INSERT,
                                        argument=argument)
            result = RdsData(response)
        else:
            result = argument
        return result

    @gen.coroutine
    def update(self,
               key: str,
               parameter: dict,
               database: str = None,
               execute: bool = True):
        """
        更新接口
        :param database: RDS-BUS的数据库类名
        :param key: 数据库类所持有的语句实例名
        :param parameter: 参数字典
        :param execute: 是否执行
        :return:
        """
        _database = database or self._database
        argument = self.pack(database=_database, key=key, parameter=parameter)
        if execute:
            response = yield self._send(operation=OperationType.UPDATE,
                                        argument=argument)
            result = RdsData(response)
        else:
            result = argument
        return result

    @gen.coroutine
    def delete(self,
               key: str,
               parameter: dict,
               database: str = None,
               execute: bool = False):
        """
        删除接口
        :param database: RDS-BUS的数据库类名
        :param key: 数据库类所持有的语句实例名
        :param parameter: 参数字典
        :param execute: 是否执行
        :return:
        """
        _database = database or self._database
        argument = self.pack(database=_database, key=key, parameter=parameter)
        if execute:
            response = yield self._send(operation=OperationType.DELETE,
                                        argument=argument)
            result = RdsData(response)
        else:
            result = argument
        return result

    @gen.coroutine
    def transaction(self, data: list, database: str = None):
        """
        事务接口
        :param database: RDS-BUS的数据库类名
        :param data: 操作列表
        :return:
        """
        _database = database or self._database
        result = yield self._send(
            operation=OperationType.TRANSACTION,
            argument=dict(command="{}/transaction".format(_database),
                          data=data))
        return RdsListData(result)

    @gen.coroutine
    def batch(self, data: list, database: str = None):
        """
        批量接口
        :param database: RDS-BUS的数据库类名
        :param data: 操作列表
        :return:
        """
        _database = database or self._database
        result = yield self._send(operation=OperationType.BATCH,
                                  argument=dict(
                                      command="{}/batch".format(_database),
                                      data=data))
        return RdsListData(result)

    @gen.coroutine
    def start(self):
        self._poller.register(self._request, zmq.POLLIN)
        while True:
            events = yield self._poller.poll()
            if self._request in dict(events):
                response = yield self._request.recv_json()
                self._logger.debug("received {}".format(response))
                if response["id"] in self._request_dict:
                    future = self._request_dict.pop(response["id"])
                    if HttpResult.is_duplicate_data_failure(response["code"]):
                        future.set_exception(
                            DuplicateDataException.new_exception(
                                response["desc"]))
                    elif HttpResult.is_failure(response["code"]):
                        future.set_exception(
                            CallServiceException(method="ZMQ",
                                                 url=self._rds_bus_url,
                                                 errmsg=response["desc"]))
                    else:
                        future.set_result(response["data"])
                else:
                    self._logger.warning(
                        "unknown response {}".format(response))

    def stop(self):
        self._poller.unregister(self._request)

    def shutdown(self):
        self.stop()
        self._request.close()

    def _send(self, operation, argument):
        """

        :param operation:
        :param argument:
        :return:
        """
        request_id = get_unique_id()
        self._request_dict[request_id] = Future()
        self._io_loop.call_later(60, self._session_timeout, request_id)
        self._request.send_multipart([
            json.dumps(
                dict(id=request_id,
                     operation=operation.value,
                     argument=argument)).encode("utf-8")
        ])
        return self._request_dict[request_id]

    def _session_timeout(self, request_id):
        if request_id in self._request_dict:
            future = self._request_dict.pop(request_id)
            future.set_exception(
                ServerTimeoutException(method="ZMQ", url=self._rds_bus_url))