O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

How Node.js Bootstraps Itself: 2019 Edition (Node+JS Interactive 2019)

162 visualizações

Publicada em

By Joyee Cheung.

In this talk we are going to take a deep dive into the Node.js bootstrap process. We will cover the recent effort of bootstrap refactoring and V8 snapshot integration in Node.js core that have significantly improved the startup performance, and will also look into new approaches of Node.js application distribution that can be enabled by this effort.


(c) Node+JS Interactive 2019
December 11-12, 2019
Palais des Congrès de Montréal, Canada
https://events19.linuxfoundation.org/events/nodejs-interactive-2019/

Publicada em: Tecnologia
  • Seja o primeiro a comentar

How Node.js Bootstraps Itself: 2019 Edition (Node+JS Interactive 2019)

  1. 1. How Node.js Bootstraps Itself: 2019 Edition Joyee Cheung, Igalia
  2. 2. About me • Joyee Cheung (Cantonese) / Qiuyi Zhang (Mandarin) • Compilers @ Igalia • Node.js TSC & V8 Committer • Champion of the startup performance initiative in Node.js • https://github.com/nodejs/TSC/blob/master/Strategic-Initiatives.md • @joyeecheung on GitHub & Twitter Slides of this talk: https://github.com/joyeecheung/talks/blob/master/node_js_interactive_2019/how- node-js-bootstraps-itself-2019-edition.pdf
  3. 3. Overview of a Node.js process Main Instance SIGUSR1 watchdog node::Environment v8::Context v8::Isolate uv_loop_t V8 Platform thread pool libuv thread pool Task Scheduler node::inspector::Agent
  4. 4. Creating a Worker Main Instance V8 Platform thread pool libuv thread pool SIGUSR1 watchdog Task Scheduler node::Environment v8::Context v8::Isolate uv_loop_t node::inspector::Agent
  5. 5. Creating a Worker Main Instance V8 Platform thread pool libuv thread pool Worker SIGUSR1 watchdog Task Scheduler node::Environment v8::Context v8::Isolate uv_loop_t node::inspector::Agent
  6. 6. Creating a child process
  7. 7. Creating a child process
  8. 8. Overview of the bootstrap C++ JS Initialize V8 isolate Initialize V8 context Initialize Node.js Environment Load main script Start the event loopInitialize event loop Initialize runtime- independent states Initialize V8 inspector Initialize runtime- dependent states Initialize the process
  9. 9. Setting up the process Initialize V8 isolate Initialize V8 context Initialize Node.js Environment Load main script Start the event loopInitialize event loop Initialize runtime- independent states Initialize V8 inspector Initialize runtime- dependent states Initialize the process C++ JS
  10. 10. Setting up the process • Setup signal handlers • Parse CLI arguments (strings to C++ structures) • Initialize ICU, OpenSSL
  11. 11. Setting up the process Main Instance Task Scheduler V8 Platform thread pool• Setup signal handlers • Parse CLI arguments (strings to C++ structures) • Initialize ICU, OpenSSL • Initialize the V8 platform
  12. 12. Setting up the process Main Instance Task Scheduler V8 Platform thread pool• Setup signal handlers • Parse CLI arguments (string to C++ structures) • Initialize ICU, OpenSSL • Initialize the V8 platform For the main instance • Initialize the default libuv event loop
  13. 13. Setting up the V8 isolate Initialize V8 context Initialize Node.js Environment Load main script Start the event loopInitialize event loop Initialize runtime- independent states Initialize V8 inspector Initialize runtime- dependent states Initialize the process Initialize V8 isolate C++ JS
  14. 14. Setting up the V8 isolate Main Instance Task Scheduler V8 Platform thread poolWhat’s a V8 isolate? • v8::Isolate is the instance of the v8 JavaScript engine • Encapsulates the JS heap, microtask queue, pending exceptions…
  15. 15. Setting up the V8 isolate Main Instance Task Scheduler V8 Platform thread pool• Configure resource constraints (e.g. memory) • Create array buffer allocator • Deserialize from the V8 isolate snapshot
  16. 16. Setting up the V8 isolate Main Instance Task Scheduler V8 Platform thread poolSetup various per-isolate callbacks in C++ • GC callbacks • Uncaught exception listeners • Promise rejection callbacks • etc.
  17. 17. Setting up the V8 context Initialize Node.js Environment Load main script Start the event loopInitialize event loop Initialize runtime- independent states Initialize V8 inspector Initialize runtime- dependent states Initialize the process Initialize V8 isolate C++ JS Initialize V8 context
  18. 18. Setting up the V8 context Main Instance Task Scheduler V8 Platform thread poolWhat’s a V8 context? • A sandboxed execution context • Encapsulates JavaScript builtins (primordials) e.g. globalThis, Array, Object… • What’s inside the returned result of vm.createContext()
  19. 19. Creating a vm Context Main Instance node::Environment v8::Isolate uv_loop_t V8 Platform thread pool libuv thread pool v8::Context SIGUSR1 watchdog Task Scheduler node::inspector::Agent
  20. 20. Main Instance node::Environment v8::Isolate uv_loop_t V8 Platform thread pool libuv thread pool v8::Context SIGUSR1 watchdog Task Scheduler Creating a vm Context node::inspector::Agent v8::Context
  21. 21. Setting up the V8 context Main Instance Task Scheduler V8 Platform thread poolWhat’s inside a Node.js’s V8 context? • Immutable copy of primordials • DOMException for Web APIs (surprise!)
  22. 22. Setting up the V8 context Main Instance Task Scheduler V8 Platform thread poolMain contexts & vm contexts • Each Node.js instance has a main context (and potentially contexts created with vm). • Main contexts contain a pointer to its associated Node.js Environment (not yet created) • VM contexts do not have that pointer and are not bootstrapped further
  23. 23. V8 snapshot: before Array Object String ... primordials Runs through per- context scripts Array Object String ... Node.js process DOMException
  24. 24. V8 snapshot: after Array Object String ... primordials DOMException Runs through per- context scripts Array Object String ... node_mksnapshot process (build time) Snapshot Blob Node.js executable primordials DOMException Array Object String ... Node.js process (run time) Serialize and compile Deserialize
  25. 25. Setting up the Environment Load main script Start the event loopInitialize event loop Initialize runtime- independent states Initialize V8 inspector Initialize runtime- dependent states Initialize the process Initialize V8 isolate C++ JS Initialize V8 context Initialize Node.js Environment
  26. 26. Setting up the Environment What’s a Node.js Environment? • Encapsulation of the Node.js instance • Associated with • One V8 inspector agent (for JS debugging) • One main V8 context • One V8 isolate • One libuv event loop node::Environment v8::Context v8::Isolate uv_loop_t node::inspector::Agent
  27. 27. Setting up the Environment Load main script Start the event loopInitialize event loop Initialize V8 inspector Initialize runtime- dependent states Initialize the process Initialize V8 isolate C++ JS Initialize V8 context Initialize Node.js Environment Initialize runtime- independent states
  28. 28. Setting up the Environment What needs to be initialized? • Components independent of runtime states • Internal JavaScript module and C++ binding loaders • The process object and other globals • JavaScript callbacks that C++ hooks invoke
  29. 29. Environment: internal loaders C++ bindings internalBinding() linear search NativeModule (intenral require()) Internal JavaScript modules map lookup process.binding() process._linkedBinding()
  30. 30. V8 code cache: before Source code of JS native modules V8 Compilation data Parse and compile Native modules Node.js executable Node.js process (run time) Execute
  31. 31. V8 code cache: after Source code of JS native modules V8 Compilation data Parse and compile Serialize Native modules Node.js executable Deserialize and execute mkcodecache process (build time) Node.js process (run time)
  32. 32. Environment: globals • Most globals are implemented in internal JavaScript with access to internal C++ bindings (glue to dependencies) • Created and attached to global or process during bootstrap • global is a legacy alias to the ECMAScript stage 4 globalThis
  33. 33. Environment: globals process.nextTick = require('internal/process/task_queues’).setupTaskQueue().nextTick; ... Object.defineProperty(global, ‘process’, { value: process, ...}); Object.defineProperty(global, ‘global’, { value: global, ...}); Object.defineProperty(global, ‘setTimeout’, { value: require(‘timers’).setTimeout), ... };
  34. 34. Environment: initialize hooks Example of hooks • Async hooks (per-Environment) • User callbacks need to be invoked at different stages of async operations • process.nextTick (per-Environment) • Queued user callbacks need to be invoked when async operations are done • Error.prepareStackTrace (per-Isolate) • User hook needs to be invoked when error.stack is accessed • process.on(‘uncaughtException’) / process.on(‘unhandledRejection’) (per-Isolate) • Need to be invoked when there are uncaught exceptions/unhandled rejections
  35. 35. Setting up the Environment Load main script Start the event loop Initialize V8 inspector Initialize runtime- dependent states Initialize the process Initialize V8 isolate C++ JS Initialize V8 context Initialize Node.js Environment Initialize runtime- independent states Initialize event loop
  36. 36. Environment: initialize the event loop • Some handles are initialized at bootstrap • Some are activated immediately, some are activated on demand • More handles (e.g. poll handles) can be added on-demand (e.g. for I/O)
  37. 37. Environment: initialize the event loop timers idle preparepending pollcheckclose Loop alive? End Yes No
  38. 38. Environment: initialize the event loop timers idle preparepending pollcheckclose Loop alive? End Yes No setTimeout() / setInterval()
  39. 39. Environment: initialize the event loop timers idle preparepending pollcheckclose Loop alive? End Yes No setImmediate()
  40. 40. Environment: initialize the event loop timers idle preparepending pollcheckclose Loop alive? End Yes No --prof
  41. 41. Environment: initialize the event loop timers idle preparepending pollcheckclose Loop alive? End Yes No --prof setImmediate()
  42. 42. Setting up the Environment Load main script Start the event loop Initialize runtime- dependent states Initialize the process Initialize V8 isolate C++ JS Initialize V8 context Initialize Node.js Environment Initialize runtime- independent states Initialize event loop Initialize V8 inspector
  43. 43. Environment: inspector node::Environment v8::Context v8::Isolate uv_loop_t • Initialize inspector agent Main Node.js instance
  44. 44. Environment: inspector node::Environment v8::Context v8::Isolate uv_loop_t node::inspector::Agent • Initialize inspector agent Main Node.js instance
  45. 45. Environment: inspector node::Environment v8::Context v8::Isolate uv_loop_t node::inspector::Agent • Initialize inspector agent • Start the SIGUSR1 watchdog thread (main instance only) Main Node.js instance SIGUSR1 watchdog
  46. 46. Environment: inspector node::Environment v8::Context v8::Isolate uv_loop_t node::inspector::Agent • Initialize inspector agent • Start the SIGUSR1 watchdog thread • Create more threads for listening on the inspector port and/or profiling, depending on CLI flags • --inspect-brk, --cpu-prof, --heap-prof Main Node.js instance SIGUSR1 watchdog
  47. 47. Setting up the Environment Load main script Start the event loop Initialize the process Initialize V8 isolate C++ JS Initialize V8 context Initialize Node.js Environment Initialize runtime- independent states Initialize event loop Initialize V8 inspector Initialize runtime- dependent states
  48. 48. Pre-execution • Handle various runtime configurations • CLI flags: e.g. --no-warnings , --experimental-policy, --experimental- report • Environment variables: e.g. NODE_DEBUG, NODE_V8_COVERAGE const { onWarning } = require('internal/process/warning'); if (!getOptionValue('--no-warnings') && process.env.NODE_NO_WARNINGS !== '1') { process.on('warning’, onWarning); }
  49. 49. Pre-execution • Handle various runtime configurations • Initialize IPC channel for clusters and child_process • Initialize user-land module loaders: CJS ( require() ) and ESM ( import ) • Load --require modules
  50. 50. Start execution Start the event loop Initialize the process Initialize V8 isolate C++ JS Initialize V8 context Initialize Node.js Environment Initialize runtime- independent states Initialize event loop Initialize V8 inspector Initialize runtime- dependent states Load main script
  51. 51. Start execution • Choose a main script according to the CLI args, etc. • lib/internal/main/*.js • Compiled into the binary at build time (similar to internal JS modules)
  52. 52. Start execution: from CLI Create and initialize Environment Load run_main_module.js Read and compile ${cwd}/index.js as CJS Start event loop Select a main script Detect module type $ node index.js
  53. 53. Start execution: Worker Create and initialize Environment Load worker_thread.js Compile and run the script sent from the port Start event loop Select a main script Setup message port and start listening From user code on the main thread const { Worker } = require(‘worker_threads’); const script = `console.log(‘hello’)`; new worker_threads .Worker(script, { eval: true }); From the worker_thread.js on the worker thread evalScript('[worker eval]', script);
  54. 54. Start execution • Kick off the event loop and run until nothing keeps it open • The libuv thread pool will be created if any asynchronous file system operation is used
  55. 55. Start execution Initialize the process Initialize V8 isolate C++ JS Initialize V8 context Initialize Node.js Environment Initialize runtime- independent states Initialize event loop Initialize V8 inspector Initialize runtime- dependent states Load main script Start the event loop
  56. 56. Summary C++ JS Initialize V8 isolate Initialize the process Initialize V8 context Initialize Node.js Environment Load main script Start the event loopInitialize event loop Initialize runtime- independent states Initialize V8 inspector Initialize runtime- dependent states
  57. 57. Ongoing work • V8 startup snapshot integration: • Including the runtime-independent part of Environment bootstrap into Nodejs’s snapshot • Startup performance initiative • https://github.com/nodejs/TSC/blob/master/Strategic-Initiatives.md
  58. 58. If it’s a per-Isolate hook (set by V8)… 1. Inside the callback, get a pointer to the v8::Isolate via e.g. callback arguments How do hooks typically work?
  59. 59. If it’s a per-Isolate hook… 1. Inside the callback, get a pointer to the v8::Isolate via e.g. callback arguments 2. Get the v8::Context that the isolate enters into How do hooks typically work?
  60. 60. If it’s a per-Isolate hook… 1. Inside the callback, get a pointer to the v8::Isolate via e.g. callback arguments 2. Get the v8::Context that the isolate enters into 3. Get the pointer to the node::Environment embedded in a slot of the context How do hooks typically work?
  61. 61. If it’s a per-Isolate hook… 1. Inside the callback, get a pointer to the v8::Isolate via e.g. callback arguments 2. Get the v8::Context that the isolate enters into 3. Get the pointer to the node::Environment embedded in a slot of the context How do hooks typically work? If it’s a per-Environment hook (set by node)… Get access to the node::Environment directly via the callback arguments.
  62. 62. How do hooks typically work? After gaining access to Environment 1. Get the internal JavaScript function stored in the node::Environment during bootstrap 2. Invoke the internal JavaScript function that calls user-provided callbacks
  63. 63. How do hooks typically work? After gaining access to Environment 1. Get the internal JavaScript function stored in the node::Environment during bootstrap 2. Invoke the internal JavaScript function that calls user-provided callbacks How are JS functions stored? internalBinding('errors’) .setPrepareStackTraceCallback( require('internal/errors’) .prepareStackTrace );

×