加速instagram.com。 第三部分

今天,我们发布了有关instagram.com的一系列材料的第三部分的翻译。 在第一部分中,我们讨论了预加载数据,在第二 部分中,我们讨论了根据服务器的主动性将数据发送到客户端。 这是关于缓存。



工作从缓存开始


我们已经在将数据发送到客户端应用程序,并在页面加载期间尽早进行此操作。 这意味着唯一更快的数据传输方式将是不提供从客户端请求信息或将其发送给客户端的步骤。 可以使用这种页面形成方法来实现,其中缓存才是最重要的。 但是,这意味着我们将不得不(尽管非常简短)向用户显示过时的信息。 使用这种方法,在加载页面后,我们立即向用户显示其提要和故事的缓存副本,然后,在获得最新数据之后,我们将所有这些替换为此类数据。

我们使用Redux管理instagram.com的状态。 结果,上述方案的总体实施方案看起来像这样。 我们将Redux存储库的子集存储在客户端的indexedDB表中,以便在首次加载页面时填充该存储库。 但是,使用indexedDB,从服务器下载数据以及用户与页面的交互是异步过程。 结果,我们可能会遇到问题。 它们包含以下事实:用户正在使用旧的缓存状态,并且当从服务器接收到新状态时,我们需要使用户的操作适用于新状态。

例如,如果我们使用标准机制来处理缓存,则可能会遇到以下问题。 我们正在开始从缓存和网络并行加载数据。 由于来自缓存的数据准备就绪的速度将比网络数据快,因此我们将其显示给用户。 例如,用户然后喜欢该帖子,但是在服务器响应(该消息携带最新信息)到达客户端后,此信息将覆盖有关喜欢的帖子的信息。 在此新数据中,将没有有关用户已设置帖子的缓存版本的喜欢的信息。 这是它的外观。


用户与缓存的数据进行交互时出现的竞争状态(Redux操作以绿色突出显示,状态为灰色)

为了解决这个问题,我们需要根据用户的动作更改缓存状态并保存有关这些动作的信息,这将使我们能够重现它们,并将其应用于从服务器接收到的新状态。 如果您曾经使用过Git或其他版本控制系统,那么您可能似乎很熟悉此任务。 假设磁带的缓存状态是本地存储库分支,而具有最新数据的服务器响应是主分支。 如果是这样,那么我们可以说我们要执行重定位操作,也就是说,我们要获取记录在一个分支中的更改(例如,喜欢,评论等),并将其应用于另一个。

这个想法将我们引向以下系统架构:

  • 页面加载后,我们向服务器发送请求以下载新数据(或等待服务器主动发送)。
  • 创建Redux状态的中间(分段)子集。
  • 在等待服务器中的数据的过程中,我们保存了提交的操作。
  • 从服务器接收到数据后,我们对新数据执行操作,并在新数据上播放存储的操作,并将它们应用于中间状态。
  • 之后,我们提交更改并将当前状态替换为中间状态。


使用中间状态解决由竞争状况引起的问题(Redux操作以绿色突出显示,状态为灰色)

由于处于中间状态,我们可以重用所有现有的reducer。 此外,这还允许您将中间状态(包含最新数据)与当前状态分开存储。 而且,由于使用Redux来实现中间状态的工作,因此我们足以发送使用该状态的操作!

API


中间状态API由两个主要功能组成。 这是stagingActionstagingCommit

 function stagingAction(    key: string,    promise: Promise<Action>, ): AsyncAction<State, Action> function stagingCommit(key: string): AsyncAction<State, Action> 

那里还有其他一些功能,例如,用于取消更改和处理边界情况,但是在此我们将不考虑它们。

stagingAction函数接受对事件的承诺,该事件需要发送到中间状态。 此函数初始化中间状态并监视自初始化以来已发送的动作。 如果将其与版本控制系统进行比较,结果表明我们正在处理本地分支的创建。 新数据到达后,正在进行的操作将排队,并应用于临时状态。

stagingCommit函数用中间状态替换当前状态。 而且,如果期望完成一些在中间状态下执行的异步操作,则系统将在替换它们之前等待这些操作完成。 这类似于重定位操作,将本地更改(来自存储高速缓存的分支)应用于主分支(从服务器接收的新数据之上)时,这导致状态的本地版本是相关的。

为了使工作系统处于中间状态,我们将根减速器包装在减速器的扩展器功能中。 它处理stagingCommit操作并将以前保存的操作应用于新状态。 为了利用所有这些优势,我们只需要发送动作,其他所有动作都会自动完成。 例如,如果我们要加载新的磁带并将其数据置于中间状态,则可以执行以下操作:

 function fetchAndStageFeed() {    return stagingAction(        'feed',        (async () => {            const {data} = await fetchFeedTimeline();            return {                type: FEED_LOADED,                ...data,            };        })(),    ); } //          store.dispatch(fetchAndStageFeed()); //   ,    stagingCommit, //      'feed' //      store.dispatch(stagingCommit('feed')); 

提要和故事的呈现方法的使用(其中缓存非常重要)使材料的输出分别提高了2.5%和11%。 此外,这导致了这样一个事实,即在用户看来,该系统的网络版本更接近iOS和Android的Instagram客户端。

亲爱的读者们! 在处理项目时,是否使用任何方法来优化缓存?


Source: https://habr.com/ru/post/zh-CN472336/


All Articles