PWA系列 - Web Workers 线程模型

前言

本文是Chromium官方设计文档Blink Workers主旨内容的翻译,介绍ServiceWorker在内核层面的一些基本概念和线程模型。

一 Worker类型

Web Workers是一个Web平台特性,它提供了后台JS context,和允许任何脚本运行在后台,通常运行在一个独立的线程。Blink实现了几类worker:

  • dedicated worker: 专用worker, 只能被创建它的JS访问。创建它的页面关闭, 它的生命周期就结束了。 一个文档可以有多个dedicated worker。
  • shared worker:共享worker, 可以被同一域名下的JS访问。关联的页面都关闭时, 它的生命周期就结束了。多个文档可以对应同一个shared worker。
  • service worker:事件驱动的worker, 生命周期与页面无关。关联页面未关闭时, 它也可以退出, 没有关联页面时, 它也可以启动。
  • compositor worker:允许JS脚本处理UI的工作,比如,响应输入和更新视觉效果。它运行在非UI线程。

二 基本概念

  • Worker context:worker脚本运行的后台JS上下文。
  • Worker thread:worker context运行的线程,它通常是指blink中的WorkerThread类,对应一个底层平台线程(blink中的WebThread)。
  • Worker object: 通常是指JS的worker对象,关联文档可以通过它与worker交互。它一般在它的parent context进行初始化,它通常运行在parent context的线程中,一般是主线程。
  • Worker global scope:worker JS上下文的global scope(比如, window 是文档JS的global scope)。注意,window scope中APIs与worker global scope的APIs并不是一一对应。

PWA系列 - Web Workers 线程模型

它们之间的关系:

Document 可以new一个Worker object,Worker object会去加载worker script,worker script加载完成后, 会new Worker thread,Worker thread会创建Worker global scope,worker context运行在worker thread。

三 进程模型

Workers可以大致分为in-process workers 和 out-of-process workers。

In-process workers: 与它们对应的document(s)运行在同一进程, 因此,它们基本只是对document(s)增加不同的线程。

Out-of-process workers可以运行在与document(s)不同的进程。通常如果worker需要由不同的documents共享,blink/chromium就会实现为out-of-process worker。

具体实现上,in-process workers可以在renderer进程中worker线程和主线程通过post task来实现交互,而out-of-process workers必须通过IPC进行通信,无论worker是否和文档运行在同一进程。

PWA系列 - Web Workers 线程模型

四 线程模型

大部分worker都运行在它们自己的线程(比如,worker context : worker thread = 1:1),而Compositor Worker例外,它是运行在per-process singleton thread(比如,worker context : worker thread = N:1)。

Out-of-process workers需要使用IPCs与关联的文档进行交互,而IPC的基础设施在browser进程实现,所以它们一部分的代码需要在browser进程实现。

 

Process model

Thread model

Dedicated Worker

In-process

Run on its own thread
(Worker context : thread = 1:1)

Shared Worker

Out-of-process

Run on its own thread
(Worker context : thread = 1:1)

Service Worker

Out-of-process

Run on its own thread
(Worker context : thread = 1:1)

Compositor Worker

In-process

Share a per-process single thread within a process
(worker context : thread = N:1)

Isolated Worker

In-process

May run on the same thread as document thread

五 代码目录说明

  • third_party/WebKit/Source/core/workers/*

    • Common worker code (e.g. WorkerThread.*, WorkerGlobalScope.*)

    • Dedicated worker code (e.g. DedicatedWorker*)

    • Shared worker code (e.g. SharedWorker*)

  • third_party/WebKit/Source/modules/serviceworkers/*

    • Service worker blink端的代码

  • third_party/WebKit/Source/modules/compositorworker/*

    • Compositor worker bink端的代码

  • third_party/WebKit/Source/web/*Worker*

    • 实现public/web/的类,browser端使用/交互的类 (e.g. ServiceWorkerGlobalScopeProxy, WebEmbeddedWorkerImpl, WebSharedWorkerImpl)

    • 通过public/web/* 与browser端交互的类 (e.g. WorkerGlobalScopeProxyProviderImpl, ServiceWorkerGlobalScopeClientImpl)

  • third_party/WebKit/public/web/*Worker*

    • 头文件的类,由Blink实现,给browser端使用 (e.g. WebEmbeddedWorker, WebSharedWorker, WebServiceWorkerContextProxy)

    • 头文件的类,由browser端实现,给blink使用 (e.g. WebServiceWorkerContextClient)

  • content/{browser,child,common,renderer}/service_worker/*

    • service worker browser端的代码

  • content/{browser,renderer}/shared_worker/*

    • shared worker browser端的代码

  • content/child/worker_*

    • 处理worker thread的browser端的代码。主要是从worker thread收发IPCs。比如,Worker context中的storage API需要和browser进程进行交互时通常使用这些类。

六 重要类说明

(1)Worker object classes

所有Worker object类都从AbstractWorker类派生,AbstractWorker接口定义为H5 workers的通用基础接口。

PWA系列 - Web Workers 线程模型

(2)Worker thread classes

WorkerThread: 代表一个worker线程,WorkerThread的实例一般会持有一个platform thread(通过WebThreadSupportingGC类持有WebThread,通过WorkerThread::backingThread访问)。

每个 WorkerThread 会使用一个v8::Isolate来完全隔离running worker script和main thread scripts。

WorkerBackingThread:代表workers的一个线程,它持有一个WebThreadSupportingGC和一个v8::Isolate。多个workers可以关联到一个WorkerBackingThread(比如,CompositorWorkers)。

PWA系列 - Web Workers 线程模型

注意:大部分WorkerThread的子类,仅仅重写了createWorkerGlobalScope方法,而CompositorWorkerThread还重写了线程和v8::Isolate相关的一系列方法,从而可以做到多个CompositorWorkerThread共享一个per-process platform thread 和 v8::Isolate。

(3)Worker global scope classes

WorkerGlobalScope:通用的基础类,代码一个worker global scope。

PWA系列 - Web Workers 线程模型

注:前面章节为Blink Workers主旨内容的翻译

七 ServiceWorker进程模型

在多进程模型下, ServiceWorker会同时运行在renderrer进程和browser进程,其中ServiceWorkerThread运行在renderer进程,其他如ServiceWorkerVersion等则运行在browser进程。

打开两个相同域名的ServiceWorker页面,页面里的ServiceWorker会运行在同一renderrer进程中。

打开两个不同域名的ServiceWorker页面,页面里的ServiceWorker会运行在不同renderrer进程中。

如果浏览器已有同一域名的renderrer进程,新建一个ServiceWorker一般不会创建一个新的renderrer进程,而只会使用已有的renderrer进程。

我们看看,ServiceWorker创建renderrer进程的过程:

ServiceWorkerVersion::StartWorker
--> EmbeddedWorkerInstance::Start
--> ServiceWorkerProcessManager::AllocateWorkerProcess(bool can_use_existing_process) 
// 如果ServiceWorkerVersion失败次数超过2,那么EmbeddedWorkerInstance会强制新建一个renderrer进程。
// 这里的失败一般是指ServiceWorker注册过程的失败。
     --> ServiceWorkerProcessManager::FindAvailableProcess // 查找可用的进程
     --> ServiceWorkerProcessManager::SortProcessesForPattern(const GURL& pattern) 
     // 按域名查找是否存在合适的renderrer进程
     --> pattern_processes_.find(pattern) // ServiceWorker注册的流程会将pattern加入pattern_processes_
--> SiteInstance::GetProcess // 如果上面没有找到合适的renderrer进程, 就新建一个renderrer进程

从上面流程可以看到, ServiceWorker会根据一定的算法去决定是否使用新的renderrer进程。其中两个较重要的因素是,按域名分派进程,同一version失败次数不能太多,比如,不超过2(kMaxSameProcessFailureCount)。

参考文档

Blink Workers - Current architecture, implementation and future project ideas for Blink Workers

上一篇:如何调整WINDOWS进程对多核CPU的利用率?


下一篇:养成好的MySQL数据库语句书写习惯