grpc-libp2p-client 0.0.39 → 0.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dc-http2/frame.cjs.js +51 -19
- package/dist/dc-http2/frame.cjs.js.map +1 -1
- package/dist/dc-http2/frame.d.ts +1 -1
- package/dist/dc-http2/frame.esm.js +51 -19
- package/dist/dc-http2/frame.esm.js.map +1 -1
- package/dist/dc-http2/hpack.cjs.js +43 -13
- package/dist/dc-http2/hpack.cjs.js.map +1 -1
- package/dist/dc-http2/hpack.esm.js +43 -13
- package/dist/dc-http2/hpack.esm.js.map +1 -1
- package/dist/dc-http2/parser.cjs.js +281 -189
- package/dist/dc-http2/parser.cjs.js.map +1 -1
- package/dist/dc-http2/parser.d.ts +21 -2
- package/dist/dc-http2/parser.esm.js +281 -189
- package/dist/dc-http2/parser.esm.js.map +1 -1
- package/dist/dc-http2/stream.cjs.js +97 -70
- package/dist/dc-http2/stream.cjs.js.map +1 -1
- package/dist/dc-http2/stream.d.ts +2 -0
- package/dist/dc-http2/stream.esm.js +97 -70
- package/dist/dc-http2/stream.esm.js.map +1 -1
- package/dist/grpc.js +810 -579
- package/dist/grpc.js.map +1 -1
- package/dist/grpc.min.js +1 -1
- package/dist/grpc.min.js.map +1 -1
- package/dist/index.cjs.js +633 -411
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.esm.js +633 -411
- package/dist/index.esm.js.map +1 -1
- package/dist/stats.html +1 -1
- package/package.json +1 -1
- package/src/dc-http2/frame.ts +11 -9
- package/src/dc-http2/hpack.ts +43 -13
- package/src/dc-http2/parser.ts +219 -196
- package/src/dc-http2/stream.ts +84 -79
- package/src/index.ts +240 -183
package/src/dc-http2/stream.ts
CHANGED
|
@@ -46,6 +46,8 @@ export class StreamWriter {
|
|
|
46
46
|
private lastBytesDrainedSeen = 0
|
|
47
47
|
private lastBpWarnAt = 0
|
|
48
48
|
private isHandlingError = false // 防止重复错误处理
|
|
49
|
+
/** drain 事件驱动等待者,替代 flush() 中的 setInterval 轮询 */
|
|
50
|
+
private drainWaiters: Array<() => void> = []
|
|
49
51
|
|
|
50
52
|
private log?: { trace?: (...args: unknown[]) => void }
|
|
51
53
|
|
|
@@ -169,8 +171,9 @@ export class StreamWriter {
|
|
|
169
171
|
// 使用 stream.send() 发送数据,返回 false 表示需要等待 drain
|
|
170
172
|
const canContinue = this.stream.send(chunk)
|
|
171
173
|
if (!canContinue) {
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
+
// 传入 abort signal,当流被 abort 时 onDrain() 会立即 reject,
|
|
175
|
+
// 避免在 abort 路径下永久挂住
|
|
176
|
+
await this.stream.onDrain({ signal: this.abortController.signal })
|
|
174
177
|
}
|
|
175
178
|
} catch (err: unknown) {
|
|
176
179
|
// Gracefully handle stream closing errors - 不要传递到 handleError
|
|
@@ -185,6 +188,9 @@ export class StreamWriter {
|
|
|
185
188
|
throw err
|
|
186
189
|
}
|
|
187
190
|
}
|
|
191
|
+
// pipeline 正常结束(stream 关闭或 pushable 耗尽)—— 确保资源清理
|
|
192
|
+
// 若已通过 abort() 触发则 cleanup() 内部幂等处理
|
|
193
|
+
this.cleanup()
|
|
188
194
|
}
|
|
189
195
|
|
|
190
196
|
private createTransform() {
|
|
@@ -207,6 +213,11 @@ export class StreamWriter {
|
|
|
207
213
|
self.lastDrainEventAt = now
|
|
208
214
|
self.dispatchEvent(new CustomEvent('drain', { detail: { drained: self.bytesDrained, queueSize: self.queueSize } }))
|
|
209
215
|
}
|
|
216
|
+
// 唤醒所有在等 flush() 或背压解除 的 drainWaiters(队列降低时就可唤醒)
|
|
217
|
+
if (self.drainWaiters.length > 0) {
|
|
218
|
+
const ws = self.drainWaiters.splice(0);
|
|
219
|
+
for (const fn of ws) { try { fn(); } catch { /* ignore */ } }
|
|
220
|
+
}
|
|
210
221
|
// 记录本次已消耗字节,用于看门狗判断是否前进
|
|
211
222
|
self.lastBytesDrainedSeen = self.bytesDrained
|
|
212
223
|
} catch (err) {
|
|
@@ -291,10 +302,11 @@ export class StreamWriter {
|
|
|
291
302
|
}
|
|
292
303
|
|
|
293
304
|
private async writeChunks(buffer: ArrayBuffer) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
|
|
305
|
+
const src = new Uint8Array(buffer);
|
|
306
|
+
for (let offset = 0; offset < src.byteLength; offset += this.options.chunkSize!) {
|
|
307
|
+
const end = Math.min(offset + this.options.chunkSize!, src.byteLength)
|
|
308
|
+
// subarray 创建视图,不拷贝内存。pushable.push 不修改内容,安全。
|
|
309
|
+
const chunk = src.subarray(offset, end)
|
|
298
310
|
|
|
299
311
|
await this.retryableWrite(chunk)
|
|
300
312
|
this.updateProgress(chunk.byteLength)
|
|
@@ -321,16 +333,11 @@ export class StreamWriter {
|
|
|
321
333
|
throw new Error('Stream aborted during backpressure monitoring')
|
|
322
334
|
}
|
|
323
335
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
resolve()
|
|
331
|
-
})
|
|
332
|
-
} catch (err) {
|
|
333
|
-
if (attempt < this.options.retries!) {
|
|
336
|
+
// push 是同步操作,直接调用即可
|
|
337
|
+
this.p.push(chunk)
|
|
338
|
+
} catch (err) {
|
|
339
|
+
// aborted 时不重试,立即抛出
|
|
340
|
+
if (!this.abortController.signal.aborted && attempt < this.options.retries!) {
|
|
334
341
|
const delay = this.calculateRetryDelay(attempt)
|
|
335
342
|
await new Promise(r => setTimeout(r, delay))
|
|
336
343
|
return this.retryableWrite(chunk, attempt + 1)
|
|
@@ -339,71 +346,48 @@ export class StreamWriter {
|
|
|
339
346
|
}
|
|
340
347
|
}
|
|
341
348
|
|
|
342
|
-
private async monitorBackpressure(): Promise<void> {
|
|
343
|
-
const
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
if (currentSize < baseThreshold) {
|
|
349
|
+
private async monitorBackpressure(): Promise<void> {
|
|
350
|
+
const baseThreshold = this.options.bufferSize! * 0.7
|
|
351
|
+
const criticalThreshold = this.options.bufferSize! * 0.9
|
|
352
|
+
|
|
353
|
+
// 快速路径
|
|
354
|
+
if (this.queueSize < baseThreshold) {
|
|
349
355
|
if (this.isBackpressure) {
|
|
350
356
|
this.isBackpressure = false
|
|
351
|
-
this.dispatchBackpressureEvent({
|
|
352
|
-
currentSize,
|
|
353
|
-
averageSize: this.getAverageQueueSize(),
|
|
354
|
-
threshold: baseThreshold,
|
|
355
|
-
waitingTime: 0
|
|
356
|
-
})
|
|
357
|
+
this.dispatchBackpressureEvent({ currentSize: this.queueSize, averageSize: this.getAverageQueueSize(), threshold: baseThreshold, waitingTime: 0 })
|
|
357
358
|
}
|
|
358
359
|
return
|
|
359
360
|
}
|
|
360
|
-
|
|
361
|
-
// 进入背压状态
|
|
361
|
+
|
|
362
362
|
if (!this.isBackpressure) {
|
|
363
363
|
this.isBackpressure = true
|
|
364
|
-
this.dispatchBackpressureEvent({
|
|
365
|
-
currentSize,
|
|
366
|
-
averageSize: this.getAverageQueueSize(),
|
|
367
|
-
threshold: baseThreshold,
|
|
368
|
-
waitingTime: 0
|
|
369
|
-
})
|
|
364
|
+
this.dispatchBackpressureEvent({ currentSize: this.queueSize, averageSize: this.getAverageQueueSize(), threshold: baseThreshold, waitingTime: 0 })
|
|
370
365
|
}
|
|
371
|
-
|
|
372
|
-
//
|
|
373
|
-
const
|
|
374
|
-
let
|
|
375
|
-
|
|
376
|
-
if (currentSize >= criticalThreshold) {
|
|
377
|
-
// 临界状态:长时间等待
|
|
378
|
-
waitTime = 50 + Math.min(200, pressure * 100)
|
|
379
|
-
} else {
|
|
380
|
-
// 轻度背压:短时间等待
|
|
381
|
-
waitTime = Math.min(20, pressure * 30)
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// 使用指数退避,但最多等待3次
|
|
385
|
-
let retryCount = 0
|
|
386
|
-
const maxRetries = 3
|
|
387
|
-
|
|
388
|
-
while (this.queueSize >= baseThreshold && retryCount < maxRetries) {
|
|
366
|
+
|
|
367
|
+
// 事件驱动等待:每轮等到 drain 触发或超时,最多 3 轮
|
|
368
|
+
const maxRounds = 3
|
|
369
|
+
for (let i = 0; i < maxRounds; i++) {
|
|
389
370
|
if (this.abortController.signal.aborted) break
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
371
|
+
if (this.queueSize < baseThreshold) break
|
|
372
|
+
|
|
373
|
+
const isCritical = this.queueSize >= criticalThreshold
|
|
374
|
+
const waitMs = isCritical ? 100 : 30
|
|
375
|
+
|
|
376
|
+
await new Promise<void>(resolve => {
|
|
377
|
+
let done = false
|
|
378
|
+
const timer = setTimeout(() => { if (!done) { done = true; resolve() } }, waitMs)
|
|
379
|
+
this.drainWaiters.push(() => { if (!done) { done = true; clearTimeout(timer); resolve() } })
|
|
380
|
+
})
|
|
396
381
|
}
|
|
397
|
-
|
|
398
|
-
// 如果仍然背压但达到最大重试次数,记录警告但继续执行
|
|
382
|
+
|
|
399
383
|
if (this.queueSize >= baseThreshold) {
|
|
400
384
|
const now = Date.now()
|
|
401
|
-
if (now - this.lastBpWarnAt > 1000) {
|
|
385
|
+
if (now - this.lastBpWarnAt > 1000) {
|
|
402
386
|
this.lastBpWarnAt = now
|
|
403
387
|
console.warn(`Stream writer: High backpressure detected (${this.queueSize} bytes), continuing anyway`)
|
|
404
388
|
}
|
|
405
389
|
}
|
|
406
|
-
}
|
|
390
|
+
}
|
|
407
391
|
|
|
408
392
|
|
|
409
393
|
private calculateRetryDelay(attempt: number): number {
|
|
@@ -505,11 +489,17 @@ export class StreamWriter {
|
|
|
505
489
|
this.abortController.abort()
|
|
506
490
|
}
|
|
507
491
|
|
|
508
|
-
//
|
|
492
|
+
// 执行所有待处理的写入任务:它们会检查 signal.aborted 并立即 resolve,
|
|
493
|
+
// 不执行的话调用方的 Promise 会永远挂住
|
|
509
494
|
const pendingTasks = this.writeQueue.splice(0)
|
|
510
|
-
pendingTasks
|
|
511
|
-
|
|
512
|
-
}
|
|
495
|
+
for (const task of pendingTasks) {
|
|
496
|
+
task().catch(() => { /* already aborted, ignore */ })
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// 唤醒所有 drainWaiters(flush / monitorBackpressure 中的等待者),
|
|
500
|
+
// 让它们检查 signal.aborted 并立即 resolve,不必等到各自的超时
|
|
501
|
+
const ws = this.drainWaiters.splice(0)
|
|
502
|
+
for (const fn of ws) { try { fn() } catch { /* ignore */ } }
|
|
513
503
|
|
|
514
504
|
try {
|
|
515
505
|
this.p.end()
|
|
@@ -523,19 +513,34 @@ export class StreamWriter {
|
|
|
523
513
|
// 等待内部队列被下游完全消费(用于在结束前确保尽量发送完数据)
|
|
524
514
|
// 默认超时 10s,避免无限等待
|
|
525
515
|
async flush(timeoutMs: number = 10000): Promise<void> {
|
|
526
|
-
const start = Date.now()
|
|
527
516
|
// 快速路径
|
|
528
517
|
if (this.queueSize <= 0 && !this.isProcessingQueue && this.writeQueue.length === 0) return
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
if (
|
|
534
|
-
|
|
535
|
-
return
|
|
518
|
+
if (this.abortController.signal.aborted) return
|
|
519
|
+
|
|
520
|
+
await new Promise<void>((resolve) => {
|
|
521
|
+
// 已经清空
|
|
522
|
+
if (this.queueSize <= 0 && !this.isProcessingQueue && this.writeQueue.length === 0) {
|
|
523
|
+
resolve(); return
|
|
536
524
|
}
|
|
537
|
-
|
|
538
|
-
|
|
525
|
+
let done = false
|
|
526
|
+
const timer = setTimeout(() => {
|
|
527
|
+
if (!done) {
|
|
528
|
+
done = true
|
|
529
|
+
console.warn(`Stream writer: flush timeout with ${this.queueSize} bytes still queued`)
|
|
530
|
+
resolve()
|
|
531
|
+
}
|
|
532
|
+
}, timeoutMs)
|
|
533
|
+
// 由 createTransform 在每个 chunk 被下游消耗后唤醒
|
|
534
|
+
const check = () => {
|
|
535
|
+
if (this.abortController.signal.aborted || (this.queueSize <= 0 && !this.isProcessingQueue && this.writeQueue.length === 0)) {
|
|
536
|
+
if (!done) { done = true; clearTimeout(timer); resolve() }
|
|
537
|
+
} else {
|
|
538
|
+
// 下次 drain 时再检查
|
|
539
|
+
this.drainWaiters.push(check)
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
this.drainWaiters.push(check)
|
|
543
|
+
})
|
|
539
544
|
}
|
|
540
545
|
|
|
541
546
|
// 事件系统
|