m2m模型翻译
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

183 lines
6.8 KiB

6 months ago
  1. from __future__ import absolute_import
  2. import collections
  3. import logging
  4. import kafka.errors as Errors
  5. from kafka.protocol.api import RequestHeader
  6. from kafka.protocol.commit import GroupCoordinatorResponse
  7. from kafka.protocol.frame import KafkaBytes
  8. from kafka.protocol.types import Int32
  9. from kafka.version import __version__
  10. log = logging.getLogger(__name__)
  11. class KafkaProtocol(object):
  12. """Manage the kafka network protocol
  13. Use an instance of KafkaProtocol to manage bytes send/recv'd
  14. from a network socket to a broker.
  15. Arguments:
  16. client_id (str): identifier string to be included in each request
  17. api_version (tuple): Optional tuple to specify api_version to use.
  18. Currently only used to check for 0.8.2 protocol quirks, but
  19. may be used for more in the future.
  20. """
  21. def __init__(self, client_id=None, api_version=None):
  22. if client_id is None:
  23. client_id = self._gen_client_id()
  24. self._client_id = client_id
  25. self._api_version = api_version
  26. self._correlation_id = 0
  27. self._header = KafkaBytes(4)
  28. self._rbuffer = None
  29. self._receiving = False
  30. self.in_flight_requests = collections.deque()
  31. self.bytes_to_send = []
  32. def _next_correlation_id(self):
  33. self._correlation_id = (self._correlation_id + 1) % 2**31
  34. return self._correlation_id
  35. def _gen_client_id(self):
  36. return 'kafka-python' + __version__
  37. def send_request(self, request, correlation_id=None):
  38. """Encode and queue a kafka api request for sending.
  39. Arguments:
  40. request (object): An un-encoded kafka request.
  41. correlation_id (int, optional): Optionally specify an ID to
  42. correlate requests with responses. If not provided, an ID will
  43. be generated automatically.
  44. Returns:
  45. correlation_id
  46. """
  47. log.debug('Sending request %s', request)
  48. if correlation_id is None:
  49. correlation_id = self._next_correlation_id()
  50. header = RequestHeader(request,
  51. correlation_id=correlation_id,
  52. client_id=self._client_id)
  53. message = b''.join([header.encode(), request.encode()])
  54. size = Int32.encode(len(message))
  55. data = size + message
  56. self.bytes_to_send.append(data)
  57. if request.expect_response():
  58. ifr = (correlation_id, request)
  59. self.in_flight_requests.append(ifr)
  60. return correlation_id
  61. def send_bytes(self):
  62. """Retrieve all pending bytes to send on the network"""
  63. data = b''.join(self.bytes_to_send)
  64. self.bytes_to_send = []
  65. return data
  66. def receive_bytes(self, data):
  67. """Process bytes received from the network.
  68. Arguments:
  69. data (bytes): any length bytes received from a network connection
  70. to a kafka broker.
  71. Returns:
  72. responses (list of (correlation_id, response)): any/all completed
  73. responses, decoded from bytes to python objects.
  74. Raises:
  75. KafkaProtocolError: if the bytes received could not be decoded.
  76. CorrelationIdError: if the response does not match the request
  77. correlation id.
  78. """
  79. i = 0
  80. n = len(data)
  81. responses = []
  82. while i < n:
  83. # Not receiving is the state of reading the payload header
  84. if not self._receiving:
  85. bytes_to_read = min(4 - self._header.tell(), n - i)
  86. self._header.write(data[i:i+bytes_to_read])
  87. i += bytes_to_read
  88. if self._header.tell() == 4:
  89. self._header.seek(0)
  90. nbytes = Int32.decode(self._header)
  91. # reset buffer and switch state to receiving payload bytes
  92. self._rbuffer = KafkaBytes(nbytes)
  93. self._receiving = True
  94. elif self._header.tell() > 4:
  95. raise Errors.KafkaError('this should not happen - are you threading?')
  96. if self._receiving:
  97. total_bytes = len(self._rbuffer)
  98. staged_bytes = self._rbuffer.tell()
  99. bytes_to_read = min(total_bytes - staged_bytes, n - i)
  100. self._rbuffer.write(data[i:i+bytes_to_read])
  101. i += bytes_to_read
  102. staged_bytes = self._rbuffer.tell()
  103. if staged_bytes > total_bytes:
  104. raise Errors.KafkaError('Receive buffer has more bytes than expected?')
  105. if staged_bytes != total_bytes:
  106. break
  107. self._receiving = False
  108. self._rbuffer.seek(0)
  109. resp = self._process_response(self._rbuffer)
  110. responses.append(resp)
  111. self._reset_buffer()
  112. return responses
  113. def _process_response(self, read_buffer):
  114. recv_correlation_id = Int32.decode(read_buffer)
  115. log.debug('Received correlation id: %d', recv_correlation_id)
  116. if not self.in_flight_requests:
  117. raise Errors.CorrelationIdError(
  118. 'No in-flight-request found for server response'
  119. ' with correlation ID %d'
  120. % (recv_correlation_id,))
  121. (correlation_id, request) = self.in_flight_requests.popleft()
  122. # 0.8.2 quirk
  123. if (recv_correlation_id == 0 and
  124. correlation_id != 0 and
  125. request.RESPONSE_TYPE is GroupCoordinatorResponse[0] and
  126. (self._api_version == (0, 8, 2) or self._api_version is None)):
  127. log.warning('Kafka 0.8.2 quirk -- GroupCoordinatorResponse'
  128. ' Correlation ID does not match request. This'
  129. ' should go away once at least one topic has been'
  130. ' initialized on the broker.')
  131. elif correlation_id != recv_correlation_id:
  132. # return or raise?
  133. raise Errors.CorrelationIdError(
  134. 'Correlation IDs do not match: sent %d, recv %d'
  135. % (correlation_id, recv_correlation_id))
  136. # decode response
  137. log.debug('Processing response %s', request.RESPONSE_TYPE.__name__)
  138. try:
  139. response = request.RESPONSE_TYPE.decode(read_buffer)
  140. except ValueError:
  141. read_buffer.seek(0)
  142. buf = read_buffer.read()
  143. log.error('Response %d [ResponseType: %s Request: %s]:'
  144. ' Unable to decode %d-byte buffer: %r',
  145. correlation_id, request.RESPONSE_TYPE,
  146. request, len(buf), buf)
  147. raise Errors.KafkaProtocolError('Unable to decode response')
  148. return (correlation_id, response)
  149. def _reset_buffer(self):
  150. self._receiving = False
  151. self._header.seek(0)
  152. self._rbuffer = None