如何识别未同步的实体?客户是生成 ID 的人吗?
Posted
技术标签:
【中文标题】如何识别未同步的实体?客户是生成 ID 的人吗?【英文标题】:How to identify unsynced entities? Is the client the one who generates the Ids? 【发布时间】:2020-10-08 09:17:00 【问题描述】:假设我想同步(使用 HTTP 协议)一个名为 Person
的实体。因此,客户端(移动/桌面/其他)中的人员是服务器数据库中人员的镜像复制。显然,服务器拥有所有的人,而客户端只拥有特定用户的人。
考虑以下情况。
客户端离线。当他离线时,他创建了一个Person
,因为他无法连接到服务器,所以他将此人保存在本地存储中。假设一个本地数据库(SQLite 或其他)。 一旦发生这种情况,如何/应如何识别此人?
在我开始实现整个事情之前,我认为服务器应该是生成来找他的人的 ID 的服务器。但是,当我开始实施时,我开始面临这个问题。
在服务器生成 ID 的情况下,由于服务器从未见过此人,因此客户端必须给它一个 ID 以便能够找到此人,并且显然使用此 ID 将这个人存储到他的本地存储中。现在,当客户端上线时,他会将人发送到服务器。服务器,给它一个 ID 并将其存储在他自己的数据库中。之后,客户端将请求他上次同步后发生的任何类型的人员更改,服务器将返回此特定人员。
让我们举个例子。客户端离线,创建 100 个人并将它们存储到他的本地存储中。第 1 个人、第 2 个人、第 3 个人,等等……现在,他连接到服务器并发送了所有 100 个人。由于连接是通过 HTTP 进行的,因此客户端会向 post-persons
端点发出 post 请求。然后,服务器生成 ID(增量 ID 或 UUID)并且可能还会更改一些其他属性。现在,客户端访问get-persons
端点,他看到 100 个更新的人,每个人都有一个他不知道的新 ID。 客户端如何知道这些人中的哪些人对应于客户端已经拥有的人? 删除旧客户端的 100 个人,并插入 100 个新的带有服务器 ID 的人似乎很不正统。换句话说,客户端知道的Person 1在服务器中存储为Person [uuid],服务器将其返回为Person [uuid]。客户如何知道他的 Person 1 对应于 Person [uuid]?一个解决方案可能是,将客户端的 ID 发送到服务器,服务器将响应 Person [uuid] 1。现在客户端知道,他的 1 就是这个。而对我来说,这似乎更加不正统。
第二个选项是让客户端生成 UUID,它们要么离线,要么在线。在我身边实施时,这个解决方案似乎是“最简单”的方法。客户端创建人 [uuid]。当他上线时,他将其发送到服务器。在该客户端访问get-persons
之后,他得到一个更新人[uuid] 作为响应。他很容易识别并将其存储在他的本地存储中。服务器不会为人员生成任何类型的 ID。
我有什么遗漏吗?到目前为止,我认为服务器是生成可同步实体 ID 的服务器,但我认为第二种方法更容易实现且更全面。但它会为以后引入任何“危险”吗?
对于我将用于客户端或服务器的 ID 类型没有明确要求。但是,我知道使用 UUID 与简单的增量数字之间的权衡。
堆栈(尽管我认为它无关紧要):
Spring boot 作为 Hibernate 和 mysql 之间的服务器 使用 Hibernate 和 H2 独立作为本地存储的客户端 Java 8 的一切【问题讨论】:
【参考方案1】:还有其他可能的解决方案,最好的一个取决于您的具体情况——是否需要维护本地关系等。
第一个解决方案在服务器上继续使用没有逻辑的 ID。
根本不生成 ID 并在没有 ID 的情况下存储人员(如果可能),并且在同步时,服务器会返回 ID,然后您在本地更改它们。
因此,很容易知道哪些人已经与服务器同步(那些有 ID 的人),哪些人还不为服务器所知。
实现 INSERT/UPDATE 逻辑也很简单。
但是,如果您需要维护本地关系,此解决方案可能会出现问题。
当您需要本地关系时,您可以生成临时 ID。假设您需要一个数字 ID,因此所有正 ID 都是已同步的 ID,而负 ID 是临时 ID。
当与服务器同步时,您会从服务器获取新的 ID(正数),并且您只需重写本地数据库中的所有 ID。但是,这有点容易出错,因为您必须确保更新所有关系。
您还可以拥有 localId 和 serverId。从技术上讲,您只需要 serverId 是全球唯一的,就可以在您的服务器上存储人员。您将 serverId 保持为空,并在实体与服务器同步后填充它(服务器返回它)。您可以根据需要生成 localId 并将其用于本地关系。但是,如果您需要同步多个客户端,并且每个客户端都希望生成自己的本地 ID,那么这可能是一个有问题的方法。
第二种方法是使用 ID 池。它需要在服务器上做一些工作并存储分配的池。
您可以为给定的客户端分配一个 ID 池。客户端必须至少在线一次,服务器发送其唯一的偏移量。
根据您的需要,您可以使用例如 8 字节标识符(64 位)。前 36 位可用于偏移量,因此您的服务器可以管理 2^36 个客户端。并且最后 28 位可供客户端免费使用。
因此,客户端只要在需要新 ID 时增加内部计数器并添加唯一偏移量 => 获取全局唯一标识符。
【讨论】:
我已经想到了你所说的大部分内容。我真的不想介绍ID的变化。 Person 可能有子实体。更改一个人的 ID 意味着我也必须更改孩子的外键。这变得更加复杂。 (我认为hibernate甚至不会让我)。不过,我想问一下。为什么您没有提及在客户端中创建 ID(一个也是最终的)?我对此也有疑问。我不知道为什么,但在我看来,它是最“易于理解”和“易于实施”的解决方案。 当然,当您需要维护关系时,更改 ID 可能不是一个好方法。它适用于简单的实体同步。我实际上提到了使用 ID 池方法本地生成的 ID。这基本上是在本地创建 ID,但采用集中维护的策略。我不喜欢完全随机的 ID。这是不可预测的。以上是关于如何识别未同步的实体?客户是生成 ID 的人吗?的主要内容,如果未能解决你的问题,请参考以下文章
grpc proto文件生成java.net实体类以及客户端代码