JS 멀티스레드 런타임 라이브러리 Nexus.js 사용에 대한 자세한 설명
이번에는 JS 멀티스레드 런타임 라이브러리 Nexus.js를 사용하는 방법에 대해 자세히 설명하겠습니다. JS 멀티스레드 런타임 라이브러리 Nexus.js를 사용할 때 주의사항은 무엇인가요? 보세요.
우선, 이 프로젝트에 대해 잘 모르신다면 이전에 쓴 일련의 기사를 읽어 보시기를 권장합니다. 이 글을 읽고 싶지 않다면 걱정하지 마세요. 해당 내용도 여기에서 다루겠습니다.
이제 시작해 보겠습니다.
작년에 저는 Webkit/JavaScript 커널을 기반으로 하는 다중 스레드 서버측 JavaScript 런타임인 Nexus.js를 구현하기 시작했습니다. 나는 한동안 그 일을 포기했습니다. 제가 통제할 수 없는 이유로 여기서는 논의하지 않겠습니다. 주로: 오랫동안 일을 할 수 없었습니다.
이제 Nexus의 아키텍처와 작동 방식에 대해 논의하는 것부터 시작하겠습니다.
이벤트 루프
이벤트 루프 없음
(잠금 없는) 작업 개체가 있는 스레드 풀이 있습니다
setTimeout 또는 setImmediate가 호출되거나 Promise가 생성될 때마다 작업은 작업 대기열에 추가됩니다.
작업이 예약될 때마다 사용 가능한 첫 번째 스레드가 작업을 선택하고 실행합니다.
Promise는 CPU 코어에서 처리됩니다. Promise.all()을 호출하면 Promise가 병렬로 해결됩니다.
ES6
async/await를 지원하며 사용을 권장합니다
대기(...)
지원 해체 지원
비동기 try/catch/finally 지원
모듈
CommonJS는 지원되지 않습니다. (require(...) 및 module.exports)
모든 모듈은 ES6 가져오기/내보내기 구문을 사용합니다
import('file-or-packge').then(...)
을 통해 동적 가져오기 지원 import.meta.filename 및 import.meta.dirname 등과 같은 import.meta를 지원합니다.
추가 기능: URL에서 직접 가져오기 지원(예:
import { h } from 'https://unpkg.com/preact/dist/preact.esm.js';
) EventEmitter
Nexus는 Promise 기반 EventEmitter 클래스를 구현합니다
이벤트 처리프로그램은 모든 스레드에서 순서대로 실행되며 병렬로 실행됩니다.
EventEmitter.emit(...)의 반환 값은 이벤트 핸들러에서 반환 값 배열로 구문 분석될 수 있는 Promise입니다.
예:
class EmitterTest extends Nexus.EventEmitter { constructor() { super(); for(let i = 0; i < 4; i++) this.on('test', value => { console.log(`fired test ${i}!`); console.inspect(value); }); for(let i = 0; i < 4; i++) this.on('returns-a-value', v => `${v + i}`); } } const test = new EmitterTest(); async function start() { await test.emit('test', { payload: 'test 1' }); console.log('first test done!'); await test.emit('test', { payload: 'test 2' }); console.log('second test done!'); const values = await test.emit('returns-a-value', 10); console.log('third test done, returned values are:'); console.inspect(values); } start().catch(console.error);
I/O
모든 입력/출력은 Device, Filter 및 Stream의 세 가지 기본 요소를 통해 수행됩니다.
모든 입력/출력 프리미티브는 EventEmitter 클래스를 구현합니다
Device를 사용하려면 Device
위에 ReadableStream 또는 WritableStream을 생성해야 합니다. 데이터를 조작하기 위해 ReadableStream 또는 WritableStream에 필터를 추가할 수 있습니다.
마지막으로 source.pipe(...destinationStreams)를 사용하고 source.resume()이 데이터를 처리할 때까지 기다립니다.
모든 입력/출력 작업은 ArrayBuffer 개체를 사용하여 수행됩니다.
필터는 데이터 처리를 위해 process(buffer) 방식을 시도했습니다.
예: 2개의 별도 출력 파일을 사용하여 UTF-8을 UTF6으로 변환합니다.
rreeeTCP/UDP
Nexus.js는 IP 주소/포트 바인딩 및 연결 모니터링을 담당하는 Acceptor 클래스를 제공합니다
연결 요청이 수신될 때마다 연결 이벤트가 트리거되고 소켓 장치가 제공됩니다.
각 소켓 인스턴스는 전이중 I/O 장치입니다.
ReadableStream 및 WritableStream을 사용하여 소켓을 작동할 수 있습니다.
가장 기본적인 예: (클라이언트에게 "Hello World" 보내기)
const startTime = Date.now(); try { const device = new Nexus.IO.FilePushDevice('enwik8'); const stream = new Nexus.IO.ReadableStream(device); stream.pushFilter(new Nexus.IO.EncodingConversionFilter("UTF-8", "UTF-16LE")); const wstreams = [0,1,2,3] .map(i => new Nexus.IO.WritableStream(new Nexus.IO.FileSinkDevice('enwik16-' + i))); console.log('piping...'); stream.pipe(...wstreams); console.log('streaming...'); await stream.resume(); await stream.close(); await Promise.all(wstreams.map(stream => stream.close())); console.log(`finished in ${(Date.now() * startTime) / 1000} seconds!`); } catch (e) { console.error('An error occurred: ', e); } } start().catch(console.error);
Http
Nexus는 기본적으로 TCPAcceptor
를 상속하는 Nexus.Net.HTTP.Server 클래스를 제공합니다. 몇 가지 기본 인터페이스
当服务器端完成了对传入连接的基本的Http头的解析/校验时,将使用连接和同样的信息触发connection事件
每一个连接实例都又一个request和一个response对象。这些是输入/输出设备。
你可以构造ReadableStream和WritableStream来操纵request/response。
如果你通过管道连接到一个Response对象,输入的流将会使用分块编码的模式。否者,你可以使用response.write()来写入一个常规的字符串。
复杂例子:(基本的Http服务器与块编码,细节省略)
.... /** * Creates an input stream from a path. * @param path * @returns {Promise<ReadableStream>} */ async function createInputStream(path) { if (path.startsWith('/')) // If it starts with '/', omit it. path = path.substr(1); if (path.startsWith('.')) // If it starts with '.', reject it. throw new NotFoundError(path); if (path === '/' || !path) // If it's empty, set to index.html. path = 'index.html'; /** * `import.meta.dirname` and `import.meta.filename` replace the old CommonJS `dirname` and `filename`. */ const filePath = Nexus.FileSystem.join(import.meta.dirname, 'server_root', path); try { // Stat the target path. const {type} = await Nexus.FileSystem.stat(filePath); if (type === Nexus.FileSystem.FileType.Directory) // If it's a directory, return its 'index.html' return createInputStream(Nexus.FileSystem.join(filePath, 'index.html')); else if (type === Nexus.FileSystem.FileType.Unknown || type === Nexus.FileSystem.FileType.NotFound) // If it's not found, throw NotFound. throw new NotFoundError(path); } catch(e) { if (e.code) throw e; throw new NotFoundError(path); } try { // First, we create a device. const fileDevice = new Nexus.IO.FilePushDevice(filePath); // Then we return a new ReadableStream created using our source device. return new Nexus.IO.ReadableStream(fileDevice); } catch(e) { throw new InternalServerError(e.message); } } /** * Connections counter. */ let connections = 0; /** * Create a new HTTP server. * @type {Nexus.Net.HTTP.Server} */ const server = new Nexus.Net.HTTP.Server(); // A server error means an error occurred while the server was listening to connections. // We can mostly ignore such errors, we display them anyway. server.on('error', e => { console.error(FgRed + Bright + 'Server Error: ' + e.message + '\n' + e.stack, Reset); }); /** * Listen to connections. */ server.on('connection', async (connection, peer) => { // Start with a connection ID of 0, increment with every new connection. const connId = connections++; // Record the start time for this connection. const startTime = Date.now(); // Destructuring is supported, why not use it? const { request, response } = connection; // Parse the URL parts. const { path } = parseURL(request.url); // Here we'll store any errors that occur during the connection. const errors = []; // inStream is our ReadableStream file source, outStream is our response (device) wrapped in a WritableStream. let inStream, outStream; try { // Log the request. console.log(`> #${FgCyan + connId + Reset} ${Bright + peer.address}:${peer.port + Reset} ${ FgGreen + request.method + Reset} "${FgYellow}${path}${Reset}"`, Reset); // Set the 'Server' header. response.set('Server', `nexus.js/0.1.1`); // Create our input stream. inStream = await createInputStream(path); // Create our output stream. outStream = new Nexus.IO.WritableStream(response); // Hook all `error` events, add any errors to our `errors` array. inStream.on('error', e => { errors.push(e); }); request.on('error', e => { errors.push(e); }); response.on('error', e => { errors.push(e); }); outStream.on('error', e => { errors.push(e); }); // Set content type and request status. response .set('Content-Type', mimeType(path)) .status(200); // Hook input to output(s). const disconnect = inStream.pipe(outStream); try { // Resume our file stream, this causes the stream to switch to HTTP chunked encoding. // This will return a promise that will only resolve after the last byte (HTTP chunk) is written. await inStream.resume(); } catch (e) { // Capture any errors that happen during the streaming. errors.push(e); } // Disconnect all the callbacks created by `.pipe()`. return disconnect(); } catch(e) { // If an error occurred, push it to the array. errors.push(e); // Set the content type, status, and write a basic message. response .set('Content-Type', 'text/plain') .status(e.code || 500) .send(e.message || 'An error has occurred.'); } finally { // Close the streams manually. This is important because we may run out of file handles otherwise. if (inStream) await inStream.close(); if (outStream) await outStream.close(); // Close the connection, has no real effect with keep-alive connections. await connection.close(); // Grab the response's status. let status = response.status(); // Determine what colour to output to the terminal. const statusColors = { '200': Bright + FgGreen, // Green for 200 (OK), '404': Bright + FgYellow, // Yellow for 404 (Not Found) '500': Bright + FgRed // Red for 500 (Internal Server Error) }; let statusColor = statusColors[status]; if (statusColor) status = statusColor + status + Reset; // Log the connection (and time to complete) to the console. console.log(`< #${FgCyan + connId + Reset} ${Bright + peer.address}:${peer.port + Reset} ${ FgGreen + request.method + Reset} "${FgYellow}${path}${Reset}" ${status} ${(Date.now() * startTime)}ms` + (errors.length ? " " + FgRed + Bright + errors.map(error => error.message).join(', ') + Reset : Reset)); } }); /** * IP and port to listen on. */ const ip = '0.0.0.0', port = 3000; /** * Whether or not to set the `reuse` flag. (optional, default=false) */ const portReuse = true; /** * Maximum allowed concurrent connections. Default is 128 on my system. (optional, system specific) * @type {number} */ const maxConcurrentConnections = 1000; /** * Bind the selected address and port. */ server.bind(ip, port, portReuse); /** * Start listening to requests. */ server.listen(maxConcurrentConnections); /** * Happy streaming! */ console.log(FgGreen + `Nexus.js HTTP server listening at ${ip}:${port}` + Reset);
基准
我想我已经涵盖了到目前为止所实现的一切。那么现在我们来谈谈性能。
这里是上诉Http服务器的当前基准,有100个并发连接和总共10000个请求:
This is ApacheBench, Version 2.3 <$Revision: 1796539 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient).....done Server Software: nexus.js/0.1.1 Server Hostname: localhost Server Port: 3000 Document Path: / Document Length: 8673 bytes Concurrency Level: 100 Time taken for tests: 9.991 seconds Complete requests: 10000 Failed requests: 0 Total transferred: 87880000 bytes HTML transferred: 86730000 bytes Requests per second: 1000.94 [#/sec] (mean) Time per request: 99.906 [ms] (mean) Time per request: 0.999 [ms] (mean, across all concurrent requests) Transfer rate: 8590.14 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.1 0 1 Processing: 6 99 36.6 84 464 Waiting: 5 99 36.4 84 463 Total: 6 100 36.6 84 464 Percentage of the requests served within a certain time (ms) 50% 84 66% 97 75% 105 80% 112 90% 134 95% 188 98% 233 99% 238 100% 464 (longest request)
每秒1000个请求。在一个老的i7上,上面运行了包括这个基准测试软件,一个占用了5G内存的IDE,以及服务器本身。
voodooattack@voodooattack:~$ cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 60 model name : Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz stepping : 3 microcode : 0x22 cpu MHz : 3392.093 cache size : 8192 KB physical id : 0 siblings : 8 core id : 0 cpu cores : 4 apicid : 0 initial apicid : 0 fpu : yes fpu_exception : yes cpuid level : 13 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm cpuid_fault tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts bugs : bogomips : 6784.18 clflush size : 64 cache_alignment : 64 address sizes : 39 bits physical, 48 bits virtual power management:
我尝试了1000个并发请求,但是APacheBench由于许多套接字被打开而超时。我尝试了httperf,这里是结果:
voodooattack@voodooattack:~$ httperf --port=3000 --num-conns=10000 --rate=1000 httperf --client=0/1 --server=localhost --port=3000 --uri=/ --rate=1000 --send-buffer=4096 --recv-buffer=16384 --num-conns=10000 --num-calls=1 httperf: warning: open file limit > FD_SETSIZE; limiting max. # of open files to FD_SETSIZE Maximum connect burst length: 262 Total: connections 9779 requests 9779 replies 9779 test-duration 10.029 s Connection rate: 975.1 conn/s (1.0 ms/conn, <=1022 concurrent connections) Connection time [ms]: min 0.5 avg 337.9 max 7191.8 median 79.5 stddev 848.1 Connection time [ms]: connect 207.3 Connection length [replies/conn]: 1.000 Request rate: 975.1 req/s (1.0 ms/req) Request size [B]: 62.0 Reply rate [replies/s]: min 903.5 avg 974.6 max 1045.7 stddev 100.5 (2 samples) Reply time [ms]: response 129.5 transfer 1.1 Reply size [B]: header 89.0 content 8660.0 footer 2.0 (total 8751.0) Reply status: 1xx=0 2xx=9779 3xx=0 4xx=0 5xx=0 CPU time [s]: user 0.35 system 9.67 (user 3.5% system 96.4% total 99.9%) Net I/O: 8389.9 KB/s (68.7*10^6 bps) Errors: total 221 client-timo 0 socket-timo 0 connrefused 0 connreset 0 Errors: fd-unavail 221 addrunavail 0 ftab-full 0 other 0
正如你看到的,它任然能工作。尽管由于压力,有些连接会超时。我仍在研究导致这个问题的原因。
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
위 내용은 JS 멀티스레드 런타임 라이브러리 Nexus.js 사용에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











Linux 시스템에서 .sh 파일을 실행하는 방법은 무엇입니까? Linux 시스템에서 .sh 파일은 일련의 명령을 실행하는 데 사용되는 셸 스크립트라는 파일입니다. .sh 파일 실행은 매우 일반적인 작업입니다. 이 기사에서는 Linux 시스템에서 .sh 파일을 실행하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 방법 1: 절대 경로를 사용하여 .sh 파일을 실행합니다. Linux 시스템에서 .sh 파일을 실행하려면 절대 경로를 사용하여 파일 위치를 지정할 수 있습니다. 구체적인 단계는 다음과 같습니다. 터미널을 엽니다.

PyCharm은 매우 인기 있는 Python 통합 개발 환경(IDE)으로 Python 개발을 더욱 효율적이고 편리하게 만들어주는 다양한 기능과 도구를 제공합니다. 이 기사에서는 PyCharm의 기본 작동 방법을 소개하고 독자가 도구 작동을 빠르게 시작하고 능숙하게 사용할 수 있도록 구체적인 코드 예제를 제공합니다. 1. PyCharm 다운로드 및 설치 먼저 PyCharm 공식 웹사이트(https://www.jetbrains.com/pyc)로 이동해야 합니다.

WebSocket 및 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 소개: 지속적인 기술 개발로 음성 인식 기술은 인공 지능 분야의 중요한 부분이 되었습니다. WebSocket과 JavaScript를 기반으로 한 온라인 음성 인식 시스템은 낮은 대기 시간, 실시간, 크로스 플랫폼이라는 특징을 갖고 있으며 널리 사용되는 솔루션이 되었습니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법을 소개합니다.

win7에서 exe 파일을 실행할 수 없는 이유는 무엇입니까? Windows7 운영 체제를 사용할 때 많은 사용자가 exe 파일을 실행할 수 없는 일반적인 문제에 직면할 수 있습니다. exe 파일은 Windows 운영 체제에서 일반적으로 사용되는 실행 파일로 다양한 응용 프로그램을 설치하고 실행하는 데 사용됩니다. 그러나 일부 사용자는 exe 파일을 실행하려고 할 때 시스템이 응답하지 않거나 오류 메시지를 표시하는 것을 발견할 수 있습니다. 이 문제에는 여러 가지 이유가 있습니다. 다음은 몇 가지 일반적인 원인과 해당 해결 방법입니다.

win7에서 bat 파일을 실행할 수 없는 이유는 무엇입니까? 최근 Windows7 운영 체제를 사용하는 많은 사용자가 .bat 파일을 실행할 수 없다고 보고했습니다. 이는 광범위한 논의와 혼란을 불러일으켰습니다. 잘 작동하는 운영 체제에서 간단한 .bat 파일을 실행할 수 없는 이유는 무엇입니까? 먼저 .bat 파일의 배경을 이해해야 합니다. 배치 파일이라고도 하는 .bat 파일은 Windows 명령 해석기(cmd.ex)에서 사용할 수 있는 일련의 명령이 포함된 일반 텍스트 파일입니다.

matlab에서 m 파일을 실행하는 방법을 아시나요? 아래에서 matlab에서 m 파일을 실행하는 방법에 대한 튜토리얼을 가져오겠습니다. 1. 먼저 matlab을 열어보세요. 소프트웨어를 선택하고 아래 그림과 같이 왼쪽 상단 모서리 "열기"를 선택합니다. 2. 그리고 아래 그림과 같이 실행할 m 파일을 선택하고 엽니다. 3. 아래 그림과 같이 창에서 F5 키를 눌러 프로그램을 실행합니다. 4. 아래 그림과 같이 명령줄 창과 작업 공간에서 실행 결과를 볼 수 있습니다. 5. 아래 그림과 같이 "실행"을 직접 클릭하여 파일을 실행할 수도 있습니다. 6. 마지막으로 아래 그림과 같이 명령줄 창과 작업 공간에서 m 파일의 실행 결과를 볼 수 있습니다. 위는 편집자가 가져온 MATLAB 방법입니다.

WebSocket과 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 서론: 인터넷 기술의 급속한 발전과 함께 실시간 모니터링 시스템이 다양한 분야에서 널리 활용되고 있다. 실시간 모니터링을 구현하는 핵심 기술 중 하나는 WebSocket과 JavaScript의 조합입니다. 이 기사에서는 실시간 모니터링 시스템에서 WebSocket 및 JavaScript의 적용을 소개하고 코드 예제를 제공하며 구현 원칙을 자세히 설명합니다. 1. 웹소켓 기술

Microsoft의 새로운 시스템인 Windows 10과 관련하여 친구들은 어떤 버전의 Windows 10 운영 체제가 가장 빠르고 원활하게 실행되는지 알고 싶어합니다. 버전 업데이트는 실제로 시스템 콘텐츠 및 기능에 대한 업데이트 및 결함 수리입니다. 가장 빠르게 실행되는 win10 버전은 무엇입니까? 1. win10의 각 버전 간의 차이점은 주로 해당 기능에 있습니다. 2. 다른 기능을 제외하고 다른 측면은 동일합니다. 3. win10의 다양한 버전 간에는 큰 차이가 없습니다. 실행 속도 측면에서 가장 큰 차이점은 자신의 컴퓨터 구성을 살펴보십시오. ~ win10 Home Edition: 1. Win10 Home Edition은 보급형 시스템 버전인 win8.1의 핵심 버전과 동일합니다. 2. win10 홈 버전의 국가별 버전은 win8.1의 OEM 중국어 버전과 동일합니다.
