SSE 响应(C# SseResponse / SseStream),Ring 的写法:处理器返回 (response req opts producer) —— 一个 Ring 响应 map,
正文是 ring.core.protocols/StreamableResponseBody;服务端写正文时调 (producer s),producer 在那条线程上往流里写帧
(每帧冲一次),返回即收尾。帧由 im.ttalk.net.sse(event-frame / comment-frame)或调用方自己拼好。
流(producer 收到的 s):写入串行化(心跳与业务写入互斥);写失败(对端走了)不抛,记为 gone?,后续写入直接丢弃 ——
调用方据此退订。aborted 是一个 CompletableFuture:对端断开(在写的那一刻发现)、服务端停机(宿主经请求里的
:im.ttalk.agent.server/open-streams 通知)或 close! 时完成。
⚠️ 对端断开只在写的时候发现:Jetty 的阻塞式 servlet 输出在不写的时候不报告连接已断(Ring 的正文协议也没有这个通道), 所以闲着的流靠心跳(:heartbeat-ms)发现断连。C# 的 RequestAborted 还盯着套接字的读端。
SSE 响应(C# SseResponse / SseStream),**Ring 的写法**:处理器返回 `(response req opts producer)` —— 一个 Ring 响应 map, 正文是 `ring.core.protocols/StreamableResponseBody`;服务端写正文时调 `(producer s)`,producer 在那条线程上往流里写帧 (每帧冲一次),返回即收尾。帧由 im.ttalk.net.sse(`event-frame` / `comment-frame`)或调用方自己拼好。 流(producer 收到的 `s`):写入串行化(心跳与业务写入互斥);写失败(对端走了)不抛,记为 `gone?`,后续写入直接丢弃 —— 调用方据此退订。`aborted` 是一个 CompletableFuture:对端断开(在写的那一刻发现)、服务端停机(宿主经请求里的 :im.ttalk.agent.server/open-streams 通知)或 `close!` 时完成。 ⚠️ 对端断开**只在写的时候**发现:Jetty 的阻塞式 servlet 输出在不写的时候不报告连接已断(Ring 的正文协议也没有这个通道), 所以闲着的流靠心跳(:heartbeat-ms)发现断连。C# 的 RequestAborted 还盯着套接字的读端。
(aborted s)对端断开、服务端停机或 close! 时完成的 CompletableFuture。
对端断开、服务端停机或 close! 时完成的 CompletableFuture。
(await-aborted s)(await-aborted s timeout-ms)阻塞到对端断开 / 停机 / close!,最多 timeout-ms(nil = 不限)。回 true = 已中止。
阻塞到对端断开 / 停机 / close!,最多 `timeout-ms`(nil = 不限)。回 true = 已中止。
(body req opts producer)SSE 正文(StreamableResponseBody):服务端写正文时开流、调 (producer s)、收尾 close!。
SSE 正文(StreamableResponseBody):服务端写正文时开流、调 `(producer s)`、收尾 close!。
(close! s)停心跳、不再写(⛔ 不关正文流:正文随 producer 返回而结束)。幂等。
停心跳、不再写(⛔ 不关正文流:正文随 producer 返回而结束)。幂等。
SSE 的响应头(与 keel agui/http.ss 的 %sse-headers 同一组,⛔ 不写 Content-Length)。 X-Accel-Buffering:nginx 缺省缓冲上游响应 ⇒ SSE「一次全到」,这个头是关它的开关。
SSE 的响应头(与 keel agui/http.ss 的 %sse-headers 同一组,⛔ 不写 Content-Length)。 X-Accel-Buffering:nginx 缺省缓冲上游响应 ⇒ SSE「一次全到」,这个头是关它的开关。
(response req producer)(response req opts producer)SSE 响应(Ring map):状态码(缺省 200)+ SSE 头(并上 opts 的 :headers)+ 流式正文。
opts::heartbeat-ms(闲够这么久写一帧心跳;nil / <= 0 = 不发)、:heartbeat-frame((fn [] 字节),每次心跳现调;
缺省注释帧 : clj-agent-alive —— EventSource 不把注释交给任何回调,前端看门狗要看得见心跳时给一个数据帧)、:status、:headers。
SSE 响应(Ring map):状态码(缺省 200)+ SSE 头(并上 opts 的 :headers)+ 流式正文。 opts::heartbeat-ms(闲够这么久写一帧心跳;nil / <= 0 = 不发)、:heartbeat-frame((fn [] 字节),每次心跳现调; 缺省注释帧 `: clj-agent-alive` —— EventSource 不把注释交给任何回调,前端看门狗要看得见心跳时给一个数据帧)、:status、:headers。
(write-comment! s text)写一条注释(: text\n\n)—— 客户端解析器不把它当事件。
写一条注释(`: text\n\n`)—— 客户端解析器不把它当事件。
(write-event! s data)(write-event! s data opts)写一个事件(data: …\n\n;opts 同 im.ttalk.net.sse/event-frame::event :id :retry-ms)。
写一个事件(`data: …\n\n`;opts 同 im.ttalk.net.sse/event-frame::event :id :retry-ms)。
(write-frame! s f)写一帧,写完冲出去。回 false = 写不出去了。
写一帧,写完冲出去。回 false = 写不出去了。
(write-frames! s frames)写一批帧(编好的 SSE 字节),末尾冲一次。回 false = 写不出去了。
写一批帧(编好的 SSE 字节),末尾冲一次。回 false = 写不出去了。
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |