# Python aiohttp 异步代理接入示例

使用 aiohttp ClientSession、代理认证和异步超时完成并发代理请求。

aiohttp 适合异步任务和受控并发。示例显式传入代理与认证信息，不依赖运行机器的系统代理配置。

## 前置条件

- 调用方项目自行安装并锁定 `aiohttp`。
- 通过受控环境注入 `PROXY_HOST`、`PROXY_PORT`、`PROXY_USERNAME` 和 `PROXY_PASSWORD`。
- 先根据套餐并发能力设置应用侧并发上限。

## 完整示例

```python
import asyncio
import os

import aiohttp

async def main() -> None:
    """通过显式 HTTP 代理异步读取公开出口检测结果。"""
    proxy_url = (
        f"http://{os.environ['PROXY_HOST']}"
        f":{os.environ['PROXY_PORT']}"
    )
    proxy_auth = aiohttp.BasicAuth(
        os.environ["PROXY_USERNAME"],
        os.environ["PROXY_PASSWORD"],
    )
    timeout = aiohttp.ClientTimeout(total=30, connect=10)

    async with aiohttp.ClientSession(timeout=timeout) as session:
        async with session.get(
            "https://<public-test-host>/ip",
            proxy=proxy_url,
            proxy_auth=proxy_auth,
        ) as response:
            response.raise_for_status()
            print(await response.json())

asyncio.run(main())
```

## 预期结果

- HTTPS 请求通过 HTTP CONNECT 建立隧道并返回代理出口。
- `ClientSession` 在上下文结束时释放连接资源。
- 多个任务共享会话时仍需要使用信号量限制并发。

## 错误处理

- `ClientProxyConnectionError` 优先检查主机、端口和网络可达性。
- 407 优先检查代理账密，不要误改目标网站认证。
- 超时或连接重置应进入有限退避，不应无限并发重试。

## 安全提示

不要把 `proxy_auth`、完整异常请求或环境变量输出到日志。不同代理身份应使用独立会话或连接池，避免跨任务复用认证状态。

html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}
