# 缩放和多租户

将 Copilot SDK 部署设计为可为多个用户提供服务、处理并发会话，并可在整个基础设施中实现横向扩展。 本指南介绍会话隔离模式、缩放拓扑和生产最佳做法。

<!-- markdownlint-disable GHD046 GHD005 -->

<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->

有关 SDK 级别的选项和模式，请参阅 [多租户与服务器部署](/zh/copilot/how-tos/copilot-sdk/setup/multi-tenancy)。

**最适合：** 平台开发人员、SaaS 生成器，任何为多个并发用户提供服务的部署。

## 核心概念

在选择模式之前，请了解缩放的三个维度：

![图示：显示所述过程的流程图。](/assets/images/help/copilot/copilot-sdk/setup-scaling-diagram-0.png)

## 会话隔离模式

### 模式 1：每位用户一个独立的 CLI

每个用户获取自己的 CLI 服务器实例。 最强的隔离 — 用户的会话、内存和进程完全分离。

![图示：显示所述过程的流程图。](/assets/images/help/copilot/copilot-sdk/setup-scaling-diagram-1.png)

**何时使用**：

* 数据隔离要求极高的多租户 SaaS
* 具有不同身份验证凭据的用户
* 合规性要求 （SOC 2， HIPAA）

```typescript
// CLI pool manager — one CLI per user
class CLIPool {
    private instances = new Map<string, { client: CopilotClient; port: number }>();
    private nextPort = 5000;

    async getClientForUser(userId: string, token?: string): Promise<CopilotClient> {
        if (this.instances.has(userId)) {
            return this.instances.get(userId)!.client;
        }

        const port = this.nextPort++;

        // Spawn a dedicated CLI for this user
        await spawnCLI(port, token);

        const client = new CopilotClient({
            cliUrl: `localhost:${port}`,
        });

        this.instances.set(userId, { client, port });
        return client;
    }

    async releaseUser(userId: string): Promise<void> {
        const instance = this.instances.get(userId);
        if (instance) {
            await instance.client.stop();
            this.instances.delete(userId);
        }
    }
}
```

### 模式 2：支持会话隔离的共享式 CLI

多个用户共享一个 CLI 服务器，但通过唯一会话 ID 隔离会话。 资源较轻，但隔离较弱。

![图示：显示所述过程的流程图。](/assets/images/help/copilot/copilot-sdk/setup-scaling-diagram-2.png)

**何时使用**：

* 具有受信任用户的内部工具
* 资源约束的环境
* 降低隔离要求

```typescript
const sharedClient = new CopilotClient({
    cliUrl: "localhost:4321",
});

// Enforce session isolation through naming conventions
function getSessionId(userId: string, purpose: string): string {
    return `${userId}-${purpose}-${Date.now()}`;
}

// Access control: ensure users can only access their own sessions
async function resumeSessionWithAuth(
    sessionId: string,
    currentUserId: string
): Promise<Session> {
    const [sessionUserId] = sessionId.split("-");
    if (sessionUserId !== currentUserId) {
        throw new Error("Access denied: session belongs to another user");
    }
    return sharedClient.resumeSession(sessionId);
}
```

### 模式 3：共享会话（协作）

多个用户与同一会话进行交互，例如与 Copilot 的共享聊天室。

![图示：显示所述过程的流程图。](/assets/images/help/copilot/copilot-sdk/setup-scaling-diagram-3.png)

**何时使用**：

* 团队协作工具
* 共享代码审查会话
* 配对编程助手

> ⚠️**重要：** SDK 不提供内置会话锁定。 你**必须**将访问串行化，以防止对同一会话的并发写入。

```typescript
import Redis from "ioredis";

const redis = new Redis();

async function withSessionLock<T>(
    sessionId: string,
    fn: () => Promise<T>,
    timeoutSec = 300
): Promise<T> {
    const lockKey = `session-lock:${sessionId}`;
    const lockId = crypto.randomUUID();

    // Acquire lock
    const acquired = await redis.set(lockKey, lockId, "NX", "EX", timeoutSec);
    if (!acquired) {
        throw new Error("Session is in use by another user");
    }

    try {
        return await fn();
    } finally {
        // Release lock (only if we still own it)
        const currentLock = await redis.get(lockKey);
        if (currentLock === lockId) {
            await redis.del(lockKey);
        }
    }
}

// Usage: serialize access to shared session
app.post("/team-chat", authMiddleware, async (req, res) => {
    const result = await withSessionLock("team-project-review", async () => {
        const session = await client.resumeSession("team-project-review");
        return session.sendAndWait({ prompt: req.body.message });
    });

    res.json({ content: result?.data.content });
});
```

## 隔离模式比较

|               | 每个用户的隔离 CLI  | 共享 CLI 与会话隔离 | 共享会话              |
| ------------- | ------------ | ------------ | ----------------- |
| **Isolation** |              |              |                   |
| ✅ 完成          |              |              |                   |
| ⚠️ 逻辑         |              |              |                   |
| ❌ 共享          |              |              |                   |
| **资源使用情况**    | 高（每个用户的 CLI） | 低（一个 CLI）    | 低（一个命令行界面 + 一个会话） |
| **复杂性**       | 中等           | 低            | 高（锁定）             |
| **身份验证灵活性**   | ✅ 每个用户的令牌    |              |                   |
| ⚠️ 服务令牌       |              |              |                   |
| ⚠️ 服务令牌       |              |              |                   |
| **最适用于**      | 多租户软件即服务     | 内部工具         | 协作                |

## 水平扩展

### 负载均衡器后面的多个 CLI 服务器

![图示：显示所述过程的流程图。](/assets/images/help/copilot/copilot-sdk/setup-scaling-diagram-4.png)

**关键要求：** 会话状态必须位于 **共享存储** 上，因此任何 CLI 服务器都可以恢复任何会话。

```typescript
// Route sessions to CLI servers
class CLILoadBalancer {
    private servers: string[];
    private currentIndex = 0;

    constructor(servers: string[]) {
        this.servers = servers;
    }

    // Round-robin selection
    getNextServer(): string {
        const server = this.servers[this.currentIndex];
        this.currentIndex = (this.currentIndex + 1) % this.servers.length;
        return server;
    }

    // Sticky sessions: same user always hits same server
    getServerForUser(userId: string): string {
        const hash = this.hashCode(userId);
        return this.servers[hash % this.servers.length];
    }

    private hashCode(str: string): number {
        let hash = 0;
        for (let i = 0; i < str.length; i++) {
            hash = (hash << 5) - hash + str.charCodeAt(i);
            hash |= 0;
        }
        return Math.abs(hash);
    }
}

const lb = new CLILoadBalancer([
    "cli-1:4321",
    "cli-2:4321",
    "cli-3:4321",
]);

app.post("/chat", async (req, res) => {
    const server = lb.getServerForUser(req.user.id);
    const client = new CopilotClient({ cliUrl: server });

    const session = await client.createSession({
        sessionId: `user-${req.user.id}-chat`,
        model: "gpt-4.1",
    });

    const response = await session.sendAndWait({ prompt: req.body.message });
    res.json({ content: response?.data.content });
});
```

### 粘滞会话与共享存储

![图示：显示所述过程的流程图。](/assets/images/help/copilot/copilot-sdk/setup-scaling-diagram-5.png)

**粘滞会话** 更简单- 将用户固定到特定的 CLI 服务器。 不需要共享存储，但负载分布不均衡。

**共享存储** 允许任何 CLI 处理任何会话。 负载分布更佳，但 `~/.copilot/session-state/` 需要网络存储。

## 纵向扩展

### 优化单个 CLI 服务器

单个 CLI 服务器可以处理多个并发会话。 关键注意事项：

![图示：显示所述过程的流程图。](/assets/images/help/copilot/copilot-sdk/setup-scaling-diagram-6.png)

**会话生命周期管理** 是垂直缩放的关键：

```typescript
// Limit concurrent active sessions
class SessionManager {
    private activeSessions = new Map<string, Session>();
    private maxConcurrent: number;

    constructor(maxConcurrent = 50) {
        this.maxConcurrent = maxConcurrent;
    }

    async getSession(sessionId: string): Promise<Session> {
        // Return existing active session
        if (this.activeSessions.has(sessionId)) {
            return this.activeSessions.get(sessionId)!;
        }

        // Enforce concurrency limit
        if (this.activeSessions.size >= this.maxConcurrent) {
            await this.evictOldestSession();
        }

        // Create or resume
        const session = await client.createSession({
            sessionId,
            model: "gpt-4.1",
        });

        this.activeSessions.set(sessionId, session);
        return session;
    }

    private async evictOldestSession(): Promise<void> {
        const [oldestId] = this.activeSessions.keys();
        const session = this.activeSessions.get(oldestId)!;
        // Session state is persisted automatically — safe to disconnect
        await session.disconnect();
        this.activeSessions.delete(oldestId);
    }
}
```

## 临时会话与永久性会话

![图示：显示所述过程的流程图。](/assets/images/help/copilot/copilot-sdk/setup-scaling-diagram-7.png)

### 临时会话

对于每个请求都是独立的无状态 API 端点：

```typescript
app.post("/api/analyze", async (req, res) => {
    const session = await client.createSession({
        model: "gpt-4.1",
    });

    try {
        const response = await session.sendAndWait({
            prompt: req.body.prompt,
        });
        res.json({ result: response?.data.content });
    } finally {
        await session.disconnect();  // Clean up immediately
    }
});
```

### 持久会话

对于对话式界面或长期运行的工作流：

```typescript
// Create a resumable session
app.post("/api/chat/start", async (req, res) => {
    const sessionId = `user-${req.user.id}-${Date.now()}`;

    const session = await client.createSession({
        sessionId,
        model: "gpt-4.1",
        infiniteSessions: {
            enabled: true,
            backgroundCompactionThreshold: 0.80,
        },
    });

    res.json({ sessionId });
});

// Continue the conversation
app.post("/api/chat/message", async (req, res) => {
    const session = await client.resumeSession(req.body.sessionId);
    const response = await session.sendAndWait({ prompt: req.body.message });

    res.json({ content: response?.data.content });
});

// Clean up when done
app.post("/api/chat/end", async (req, res) => {
    await client.deleteSession(req.body.sessionId);
    res.json({ success: true });
});
```

## 容器部署

### 具有持久性存储的 Kubernetes

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: copilot-cli
spec:
  replicas: 3
  selector:
    matchLabels:
      app: copilot-cli
  template:
    metadata:
      labels:
        app: copilot-cli
    spec:
      containers:
        - name: copilot-cli
          image: your-registry/copilot-cli:latest  # See backend-services.md for how to build and push this image
          args: ["--headless", "--host", "0.0.0.0", "--port", "4321"]
          env:
            - name: COPILOT_GITHUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: copilot-secrets
                  key: github-token
          ports:
            - containerPort: 4321
          volumeMounts:
            - name: session-state
              mountPath: /root/.copilot/session-state
      volumes:
        - name: session-state
          persistentVolumeClaim:
            claimName: copilot-sessions-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: copilot-cli
spec:
  selector:
    app: copilot-cli
  ports:
    - port: 4321
      targetPort: 4321
```

![图示：显示所述过程的流程图。](/assets/images/help/copilot/copilot-sdk/setup-scaling-diagram-8.png)

### Azure 容器实例

```yaml
containers:
  - name: copilot-cli
    image: your-registry/copilot-cli:latest  # See backend-services.md for how to build and push this image
    command: ["copilot", "--headless", "--host", "0.0.0.0", "--port", "4321"]
    volumeMounts:
      - name: session-storage
        mountPath: /root/.copilot/session-state

volumes:
  - name: session-storage
    azureFile:
      shareName: copilot-sessions
      storageAccountName: myaccount
```

## 生产核对清单

![图示：显示所述过程的流程图。](/assets/images/help/copilot/copilot-sdk/setup-scaling-diagram-9.png)

| 关注          | Recommendation                      |
| ----------- | ----------------------------------- |
| **会话清理**    | 运行定期清理以删除超过 TTL 的会话                 |
| **运行状况检查**  | 定期 Ping CLI 服务器;如果无响应，请重启           |
| **存储**      | 为 `~/.copilot/session-state/` 挂载持久卷 |
| **机密**      | 使用您平台的密钥管理器（Vault、K8s Secrets 等）    |
| **监控**      | 跟踪活动会话计数、响应延迟、错误率                   |
| **Locking** | 使用 Redis 或类似方案实现共享会话访问              |
| 关机\*\*\*\*  | 在停止 CLI 服务器之前清空活动会话                 |

## 局限性

| Limitation    | 详细信息                      |
| ------------- | ------------------------- |
| **无内置会话锁定**   | 实现并发访问的应用程序级锁定            |
| **无内置负载均衡**   | 使用外部 LB 或服务网格             |
| **会话状态基于文件**  | 多服务器部署需要共享文件系统            |
| **30 分钟空闲超时** | 不带活动的会话由 CLI 自动清理         |
| **CLI 是单进程**  | 通过添加更多 CLI 服务器实例而不是线程进行缩放 |

## 后续步骤

* **[会话恢复和持久性](/zh/copilot/how-tos/copilot-sdk/features/session-persistence)**：深入解析可恢复会话
* **[后端服务设置](/zh/copilot/how-tos/copilot-sdk/setup/backend-services)**：核心服务器端设置
* **[GitHub OAuth 设置](/zh/copilot/how-tos/copilot-sdk/setup/github-oauth)**：多用户身份验证
* **[BYOK （自带密钥）](/zh/copilot/how-tos/copilot-sdk/auth/byok)**：使用您自己的模型提供方