> ## Documentation Index
> Fetch the complete documentation index at: https://docs.squarecloud.app/llms.txt
> Use this file to discover all available pages before exploring further.

# 实时

> 使用 client.apps.realtime() 以流的形式获取应用的实时日志、状态和系统事件。它是一个迭代器（使用 AsyncSquareCloud 时为异步迭代器），会合并状态帧并自动重新连接。

`client.apps.realtime(app_id)` 打开一个包含应用日志、状态和系统事件的实时流。使用 `SquareCloud` 时，它是一个用 `for` 消费的**迭代器**；使用 `AsyncSquareCloud` 时，它是一个用 `async for` 消费的**异步迭代器**。请在 `with` / `async with` 代码块中使用它，这样离开代码块时就会关闭连接。

<Tabs>
  <Tab title="同步">
    ```python theme={"system"}
    with client.apps.realtime(app_id) as stream:
        for event in stream:
            if event["event"] == "logs":
                print(event["stream"], event["line"])  # "stdout" | "stderr"
            elif event["event"] == "status":
                print(event["status"].get("cpu"), event["status"].get("ram"))  # always the full, merged state
            else:
                print(event["event"], event["data"])  # "system" or "error"
    ```
  </Tab>

  <Tab title="异步">
    ```python theme={"system"}
    async with client.apps.realtime(app_id) as stream:  # no await: realtime() is not a coroutine
        async for event in stream:
            if event["event"] == "logs":
                print(event["stream"], event["line"])  # "stdout" | "stderr"
            elif event["event"] == "status":
                print(event["status"].get("cpu"), event["status"].get("ram"))  # always the full, merged state
            else:
                print(event["event"], event["data"])  # "system" or "error"
    ```
  </Tab>
</Tabs>

使用 `AsyncSquareCloud` 时，`apps.realtime(app_id)` 直接返回一个 `AsyncRealtime`：**不要**对它使用 `await`。一个后台读取线程会将事件传递给你的事件循环，因此在等待期间不会占用任何执行器线程。

## 事件

每个事件都是一个字典，包含 `event`、`data`（原始帧文本）和 `id`（帧中没有时为 `None`）。

| `event`   | 额外字段             | 说明                                                                                                                       |
| --------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `logs`    | `stream`, `line` | 一行日志。`data` 的第一个字符表示所属的流（`\u0001` 为 stdout，`\u0002` 为 stderr）；SDK 会将其去除后放入 `line`，并将 `stream` 设为 `"stdout"` 或 `"stderr"` |
| `status`  | `status`         | 容器的实时指标。第一帧是完整的，后续帧只携带发生变化的内容：SDK 会将它们合并，因此 `status` 始终是完整状态                                                             |
| `system`  |                  | 连接事件，例如 `REALTIME_CONNECTING \| <id>`、`REALTIME_TIMEOUT` 或 `REALTIME_DISCONNECTED`                                       |
| `error`   |                  | 错误代码，例如 `CONTAINER_NOT_FOUND`                                                                                            |
| `message` |                  | 没有 `event` 名称的帧                                                                                                          |

### `status` 对象

| 字段         | 说明                              |
| ---------- | ------------------------------- |
| `cpu`      | CPU 使用率（%）                      |
| `cpuLimit` | 分配的 CPU 核心数                     |
| `ram`      | `[usedMB, limitMB]`             |
| `status`   | 容器状态，例如 `running`               |
| `netIO`    | 网络 `{ i, o }` 总计，`new` 表示最近一个间隔 |
| `bIO`      | 磁盘 `{ i, o }` 总计                |
| `uptime`   | 容器启动时的 Unix 毫秒时间                |

## 结束流

在以下情况下，循环会自行结束：

* 服务器正常关闭连接（每个连接最长持续 **10 分钟**）；
* 服务器发送 `REALTIME_DISCONNECTED`；
* 你使用 `break` 跳出循环（随后离开 `with` 代码块，从而关闭连接）；
* 你从任意线程调用 `stream.close()`，即使循环正在等待数据或重新连接（循环直接结束，不会抛出异常）。

`close()` 在 `Realtime` 和 `AsyncRealtime` 上都是同步的。如需在 10 分钟后继续观察，请在循环结束时打开一个新的流。

```python theme={"system"}
import threading

stream = client.apps.realtime(app_id)
threading.Timer(60, stream.close).start()  # stop after one minute

for event in stream:
    print(event["event"], event["data"])
```

## 重新连接

连接断开，或服务器将重新连接交由客户端处理（`REALTIME_RECONNECT`）时，流会自行重新打开：

* 最多**连续 3 次**；任何 `logs` 或 `status` 事件都会重置计数；
* 每次重新打开距上一次打开至少 **5.5 秒**，以保持在 API 每个应用每 5 秒打开一次的速率之内。

重新连接失败超过 3 次后，循环会抛出带有 `NETWORK_ERROR` 的 `SquareCloudAPIError`。每次打开都是一个 `GET`，因此打开时发生的网络错误也会被[重试](/zh/sdks/py/errors#重试)，最多 `max_retries` 次；仍然失败的打开会抛出异常。

## 限制和错误

* 每个账户 **5** 个并发连接，每个应用 **30** 个（涵盖所有用户）。超出时返回 429 `REALTIME_MAX_CONNECTIONS` 或 `REALTIME_MAX_CONNECTIONS_APP`。
* HTTP 状态会在开始流式传输之前检查，因此这些错误（以及未知应用的 404）会在第一次迭代时抛出。
* 超时只覆盖打开阶段，直到响应头到达为止。流本身没有超时：半开的连接（例如从睡眠中恢复的笔记本电脑）会一直等待，直到你调用 `close()`。
