如果是脱机优先应用程序,如何与远程数据库同步数据?

day*_*mer 7 javascript rest offline service-worker progressive-web-apps

  • 我正在构建一个"TODO"应用程序,它使用Service Workers来缓存请求的响应,如果用户处于脱机状态,则会向用户显示缓存的数据.
  • 服务器公开一个REST-ful端点,该端点具有为资源公开的POST,PUT,DELETE和GET端点.
  • 考虑到当用户离线并提交TODO项目时,我将其保存到本地IndexedDB,但由于没有网络连接,我无法发送此服务器的POST请求.对于用户更新或删除现有TODO项的PUT,DELETE请求也是如此

问题

  • 当连接重新联机时,正在使用哪些模式将待处理请求与REST-ful服务器同步?

pmb*_*ugo 6

连接重新联机时,正在使用什么模式将待处理请求与REST-ful服务器同步?

后台同步API将适用于这种情况。它使Web应用程序能够在后台同步数据。这样,它可以将操作推迟到用户建立可靠连接之前,从而确保实际上发送了用户要发送的任何内容。即使用户导航或关闭浏览器,也会执行该操作,并且您可以在需要时通知用户。

由于要保存到IndexDB,因此可以在用户添加,删除或更新TODO项目时注册同步事件

function addTodo(todo) {
  return addToIndeDB(todo).then(() => {
    // Wait for the scoped service worker registration to get a
    // service worker with an active state
    return navigator.serviceWorker.ready;
  }).then(reg => {
    return reg.sync.register('add-todo');
  }).then(() => {
    console.log('Sync registered!');
  }).catch(() => {
    console.log('Sync registration failed :(');
  });
}
Run Code Online (Sandbox Code Playgroud)

您已经注册了一个类型的同步事件add-todo,您将在服务工作者中侦听该事件,然后在获取此事件时,从IndexDB中检索数据,然后对您的Restful API进行POST。

self.addEventListener('sync', event => {
if (event.tag == 'add-todo') {
      event.waitUntil(
      getTodo().then(todos => {
        // Post the messages to the server
        return fetch('/add', {
          method: 'POST',
          body: JSON.stringify(todos),
          headers: { 'Content-Type': 'application/json' }
        }).then(() => {
          // Success!
        });
      })
      })
    );
   }
});
Run Code Online (Sandbox Code Playgroud)

这只是如何使用Background Sync实现此示例。请注意,您必须在服务器上处理冲突解决方案。

您可以在客户端上使用PouchDB,在服务器上使用Couchbase或CouchDB。使用客户端上的PouchDB,您可以将数据保存在客户端上,并将其设置为在用户在线时自动同步/复制数据。当数据库同步并且有冲突的更改时,CouchDB将检测到这一点,并使用特殊属性标记受影响的文档"_conflicts":true。它确定将哪个版本用作最新版本,并将其他版本另存为该记录的先前版本。它不会尝试合并冲突的修订版。由您决定如何在应用程序中进行合并。它与Couchbase也没有太大区别。有关冲突解决的更多信息,请参见下面的链接。

我曾经使用过pouchDB和couchbase / couchdb / IBM cloudant,但是我已经通过Hoodie做到了这一点。它具有开箱即用的用户身份验证,处理冲突管理等功能。将其视为您的后端。在您的TODO应用程序中,Hoodie非常适合。我已经写了一些有关使用Hoodie的信息,请参见下面的链接:


小智 5

目前,我可以想到两种方法,这取决于您在后端使用的存储选项。

如果您正在使用RDBMS备份所有数据:

使用这种方法的离线优先系统的问题在于,发布新数据或更新现有数据时可能会遇到冲突。

作为避免冲突发生的首要措施,您将必须为客户端中的所有对象生成唯一的ID,以使它们在服务器上发布并保存在数据库中时保持唯一。为此,您可以安全地依靠UUID为对象生成唯一的ID。UUID保证了分布式系统中各个系统之间的唯一性,并且根据实现语言的不同,您将拥有无需任何麻烦即可生成UUID的方法。

设计本地数据库,以便可以将UUID用作本地数据库中的主键。在服务器端,可以同时具有自动递增和索引的整数类型,主键和用于保存UUID的VARCHAR类型。服务器上的主键唯一地标识该表中的对象,而UUID唯一地标识跨表和数据库的记录。

因此,在同步时将对象发布到服务器时,您只需检查是否已存在带有UDID的任何对象,然后从那里采取适当的措施即可。从服务器获取对象时,请同时发送表中对象的主键和对象的UDID。这就是为什么当您在模型对象中序列化响应或将其保存在本地数据库中时,您可以告诉已同步对象与未同步对象的原因,因为需要同步的对象在本地数据库中将没有主键, UUID。

在某些情况下,服务器可能会出现故障,并且在同步时拒绝保存数据。在这种情况下,您可以在对象中保留一个整数变量,该变量将对尝试同步它的次数进行计数。如果此数字超出某个值(例如3),则继续进行下一个对象的同步。现在,您要对未同步的对象进行处理,这取决于您对此类对象的策略,作为解决方案,您可以丢弃它们或将它们保留在本地。

如果您不使用RDBMS

作为一种替代方法,可以保留每个客户端在服务器本地执行的事务,而不是保留所有对象。每个客户端仅同步事务,而在获取时,您将通过自下而上地处理所有事务来获取当前状态。这与Git使用的非常相似。它以事务的形式(例如,添加(或删除)的内容以及由谁进行的事务)的形式将更改保存在存储库中。每个用户的存储库的当前状态都是通过事务处理的。这种方法不会导致冲突,但是如您所见,开发它有些棘手。