参考资料
- 接口定义与参数说明开发文档:weiti.apifox.cn
一、先搞清楚:这里的 WebSocket 指什么
做微信机器人时,你需要和"机器人API服务"通信。API服务通常提供两种协议选项:
- REST API(HTTP + Webhook):发消息你主动调 HTTP POST,收消息 API 服务主动 POST 到你的公网回调地址
- WebSocket:你和 API 服务之间建一条 TCP 长连接,发消息和收消息都在这条连接上双向通信
两种协议解决的是同一个问题(和微信服务器之间通过机器人API服务收发消息),只是通信方式不同。选哪个,取决于你的应用架构和部署条件。
二、REST API 模式详解
通信流程
发消息:你的应用 ──HTTP POST──► API服务 ──► 微信服务器
收消息:API服务 ──HTTP POST──► 你的公网回调地址
两个方向用的是两条独立的 HTTP 通道:
| 方向 | 方式 | 触发方 |
|---|---|---|
| 发送消息 | 你主动调 POST 接口 | 你的应用 |
| 接收消息 | API 服务主动 POST 到你配置的回调 URL | API 服务 |
REST API 的代码骨架
以 WTAPI 为例(HTTP + Webhook 形式),接口细节以文档为准(weiti.apifox.cn):
python
from flask import Flask, request, jsonify
import requests, queue, threading, time, random
app = Flask(__name__)
msg_queue = queue.Queue()
BASE_URL = "https://wx.chuapi.com"
TOKEN = "你的X-finder-TOKEN"
APP_ID = "你的appId"
# ① 发送消息:主动调 HTTP POST
def send_text(to_wxid, content):
resp = requests.post(
f"{BASE_URL}/finder/v2/api/message/postText",
headers={"Content-Type": "application/json", "X-finder-TOKEN": TOKEN},
json={"appId": APP_ID, "toWxid": to_wxid, "content": content},
timeout=10
)
return resp.json().get("ret") == 200
# ② 接收消息:被动等 Webhook 回调
@app.route("/callback", methods=["POST"])
def callback():
msg = request.get_json()
if msg and msg.get("msgType") == 1:
msg_queue.put(msg)
return jsonify({"ret": 200}) # 5秒内必须返回
# ③ 消费者:串行发送 + 限频
def consumer():
while True:
msg = msg_queue.get()
to_wxid = msg.get("chatRoomId") or msg["fromUser"]
send_text(to_wxid, f"已收到:{msg['content']}")
time.sleep(random.uniform(1, 5))
threading.Thread(target=consumer, daemon=True).start()
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)
REST API 的硬性要求
Webhook 回调需要你的应用有公网可达的 URL。本地开发时用 ngrok 做内网穿透:
bash
ngrok http 8080
# 把 https://xxx.ngrok.io/callback 配到 API 服务后台
三、WebSocket 模式详解
通信流程
你的应用 ◄──── TCP 长连接 ────► API服务 ──► 微信服务器
(发和收都在同一条连接上)
你只需要向 API 服务发起一次 WebSocket 连接请求,之后:
- 收消息:API 服务主动往这条连接推送 JSON
- 发消息:你往这条连接发 JSON 请求
WebSocket 的代码骨架
某些 API 服务提供 WebSocket 接口时,代码长这样:
python
import asyncio, json, random
import websockets
WS_URL = "wss://api.example.com/ws?token=你的Token"
async def main():
async with websockets.connect(WS_URL) as ws:
# 发送登录指令(假设 WebSocket 模式需要先认证)
await ws.send(json.dumps({
"action": "login",
"appId": "你的appId"
}))
# 循环收消息 + 发消息
while True:
# ① 接收消息:API 服务主动推过来
raw = await ws.recv()
msg = json.loads(raw)
if msg.get("type") == "message" and msg.get("msgType") == 1:
to_wxid = msg.get("chatRoomId") or msg["fromUser"]
content = f"已收到:{msg['content']}"
# ② 发送消息:往同一条连接发请求
await ws.send(json.dumps({
"action": "postText",
"appId": "你的appId",
"toWxid": to_wxid,
"content": content
}))
# 限频
await asyncio.sleep(random.uniform(1, 5))
asyncio.run(main())
WebSocket 的额外工作
长连接需要处理心跳和断线重连:
python
async def heart_beat(ws):
"""每 30 秒发一次心跳"""
while True:
await asyncio.sleep(30)
try:
await ws.send(json.dumps({"action": "ping"}))
except:
break # 连接断了,交给外层重连
async def connect_with_retry():
"""带指数退避的重连"""
delay = 1
while True:
try:
async with websockets.connect(WS_URL) as ws:
await heart_beat(ws) # 启动心跳
# ... 正常收发逻辑
delay = 1 # 连接成功后重置
except Exception as e:
print(f"连接断开,{delay}秒后重连: {e}")
await asyncio.sleep(delay)
delay = min(delay * 2, 30) # 指数退避,最多 30 秒
四、两种协议横向对比
| 对比项 | REST API(HTTP + Webhook) | WebSocket |
|---|---|---|
| 部署要求 | 需要公网回调 URL | 只要能出网(不需要公网入站) |
| 网络限制 | 如果你的服务器在内网,需要内网穿透或公网服务器 | 内网服务器直接连出去就行 |
| 连接管理 | 无状态,每次请求独立 | 需要维护长连接、心跳、重连 |
| 实时性 | Webhook 推送有毫秒级延迟 | 同一条连接,延迟更低 |
| 断线影响 | 回调断了 API 服务会重推(有重发机制) | 连接断了期间的消息可能丢失(取决于服务端实现) |
| 发送限频 | 靠消费者串行 + 随机间隔控制 | 同样需要控制,但连接层面可以做流控 |
| 防火墙 | 需要开放入站端口(80/443) | 只需要出站权限(几乎所有网络都允许) |
| 多实例 | 负载均衡分发 Webhook,天然支持水平扩展 | 每条 WebSocket 连接绑定单个实例,需要连接粘性 |
| 代码复杂度 | 低(HTTP 请求 + Flask/FastAPI) | 中(需要 asyncio + 心跳 + 重连) |
| 适用场景 | 大多数业务场景 | 内网服务器、出站受限网络、低延迟要求 |
五、选型决策树
你的服务器有公网 IP 吗?
├── 有 → 用 REST API(HTTP + Webhook)
│ 理由:部署简单、有重发机制、天然支持多实例
│
└── 没有(内网/VPS 无公网入站)
├── 能做内网穿透(ngrok/frp)?
│ ├── 能 → 还是用 REST API(穿透后就是公网 URL)
│ └── 不能 → 用 WebSocket
│
└── 必须用 WebSocket(出站受限网络/极低延迟要求)
90% 的场景选 REST API。原因:
- Webhook 有重发机制:你的回调临时挂了,API 服务会重推消息,不会丢;WebSocket 连接断了期间的消息大概率直接丢
- 部署简单:不用管连接管理、心跳、重连,Flask 写个回调接口就行
- 水平扩展友好:多实例 + 负载均衡就能抗高并发,WebSocket 需要连接粘性或服务端消息同步
WebSocket 的真正优势在内网服务器场景------服务器不能被外部访问(没有公网 IP、防火墙封了入站),但能主动连出去。这时候 WebSocket 是唯一选择。
六、混合方案
某些场景下两种协议可以配合使用:
┌─ WebSocket(实时推送前端管理面板)
你的微信机器人服务 ──┤
└─ REST API(和 API 服务交互收发消息)
这里的 WebSocket 不是和机器人 API 服务的通信,而是你自己的应用内部------后端通过 Webhook 收消息后,用 WebSocket 推给前端页面做实时展示。这是两个不同层级的通信,不冲突。
七、小结
微信机器人接口的协议选型:REST API(HTTP + Webhook)是默认选择 ,部署简单、有重发机制、天然支持多实例;WebSocket 适合内网服务器(无公网入站)或极低延迟要求的场景,但需要自己维护心跳和重连。判断标准只有一个:你的服务器能不能被 API 服务访问到(公网可达)? 能就选 REST,不能就选 WebSocket。两种协议的业务逻辑(发消息、处理回调、串行限频)是一样的,换的只是通信层。接口路径和参数以官方文档为准,开发前建议先确认 API 服务提供哪种协议。