图片解析应用
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.

122 lines
3.8 KiB

  1. """Party
  2. :Maintainer: Ben Bangert <ben@groovie.org>
  3. :Status: Production
  4. A Zookeeper pool of party members. The :class:`Party` object can be
  5. used for determining members of a party.
  6. """
  7. import uuid
  8. from kazoo.exceptions import NodeExistsError, NoNodeError
  9. class BaseParty(object):
  10. """Base implementation of a party."""
  11. def __init__(self, client, path, identifier=None):
  12. """
  13. :param client: A :class:`~kazoo.client.KazooClient` instance.
  14. :param path: The party path to use.
  15. :param identifier: An identifier to use for this member of the
  16. party when participating.
  17. """
  18. self.client = client
  19. self.path = path
  20. self.data = str(identifier or "").encode("utf-8")
  21. self.ensured_path = False
  22. self.participating = False
  23. def _ensure_parent(self):
  24. if not self.ensured_path:
  25. # make sure our parent node exists
  26. self.client.ensure_path(self.path)
  27. self.ensured_path = True
  28. def join(self):
  29. """Join the party"""
  30. return self.client.retry(self._inner_join)
  31. def _inner_join(self):
  32. self._ensure_parent()
  33. try:
  34. self.client.create(self.create_path, self.data, ephemeral=True)
  35. self.participating = True
  36. except NodeExistsError:
  37. # node was already created, perhaps we are recovering from a
  38. # suspended connection
  39. self.participating = True
  40. def leave(self):
  41. """Leave the party"""
  42. self.participating = False
  43. return self.client.retry(self._inner_leave)
  44. def _inner_leave(self):
  45. try:
  46. self.client.delete(self.create_path)
  47. except NoNodeError:
  48. return False
  49. return True
  50. def __len__(self):
  51. """Return a count of participating clients"""
  52. self._ensure_parent()
  53. return len(self._get_children())
  54. def _get_children(self):
  55. return self.client.retry(self.client.get_children, self.path)
  56. class Party(BaseParty):
  57. """Simple pool of participating processes"""
  58. _NODE_NAME = "__party__"
  59. def __init__(self, client, path, identifier=None):
  60. BaseParty.__init__(self, client, path, identifier=identifier)
  61. self.node = uuid.uuid4().hex + self._NODE_NAME
  62. self.create_path = self.path + "/" + self.node
  63. def __iter__(self):
  64. """Get a list of participating clients' data values"""
  65. self._ensure_parent()
  66. children = self._get_children()
  67. for child in children:
  68. try:
  69. d, _ = self.client.retry(
  70. self.client.get, self.path + "/" + child
  71. )
  72. yield d.decode("utf-8")
  73. except NoNodeError: # pragma: nocover
  74. pass
  75. def _get_children(self):
  76. children = BaseParty._get_children(self)
  77. return [c for c in children if self._NODE_NAME in c]
  78. class ShallowParty(BaseParty):
  79. """Simple shallow pool of participating processes
  80. This differs from the :class:`Party` as the identifier is used in
  81. the name of the party node itself, rather than the data. This
  82. places some restrictions on the length as it must be a valid
  83. Zookeeper node (an alphanumeric string), but reduces the overhead
  84. of getting a list of participants to a single Zookeeper call.
  85. """
  86. def __init__(self, client, path, identifier=None):
  87. BaseParty.__init__(self, client, path, identifier=identifier)
  88. self.node = "-".join([uuid.uuid4().hex, self.data.decode("utf-8")])
  89. self.create_path = self.path + "/" + self.node
  90. def __iter__(self):
  91. """Get a list of participating clients' identifiers"""
  92. self._ensure_parent()
  93. children = self._get_children()
  94. for child in children:
  95. yield child[child.find("-") + 1 :]