res.php'yi her 5 saniyede bir yoklamak işe yarar, ancak isteklerin boşa gitmesine ve gecikmenin artmasına neden olur. Sunucu Tarafından Gönderilen Etkinlikler (SSE), sunucunuzun CAPTCHA çözümlerini bağlı istemcilere geldikleri anda iletmesine olanak tanır; boşa giden istekler sıfır, saniyeden kısa teslimat.
SSE, CAPTCHA İş Akışına Nasıl Uyuyor?
[Client] ← SSE stream ← [Your Server] ← Callback ← [CaptchaAI]
↓ ↑
Submit task → [CaptchaAI] ──┘ (pingback URL points to your server)
- İstemci SSE uç noktanıza bağlanır (kalıcı HTTP bağlantısı)
- İstemci,
pingbacksunucunuzu işaret ederek CaptchaAI'ye bir CAPTCHA görevi gönderir - CaptchaAI çözer ve sonucu geri arama uç noktanıza gönderir
- Sunucunuz sonucu SSE akışı aracılığıyla istemciye iletir
Tam Uygulama – Python (Flask)
Sunucu
import os
import queue
import threading
import requests
from flask import Flask, Response, request, jsonify
app = Flask(__name__)
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
# Per-client event queues: client_id -> Queue
client_queues = {}
queues_lock = threading.Lock()
@app.route("/events/<client_id>")
def sse_stream(client_id):
"""SSE endpoint — clients connect here for real-time results."""
q = queue.Queue()
with queues_lock:
client_queues[client_id] = q
def generate():
try:
while True:
# Block until a result arrives (timeout for keepalive)
try:
data = q.get(timeout=30)
yield f"event: captcha-solved\ndata: {data}\n\n"
except queue.Empty:
# Send keepalive comment to prevent connection timeout
yield ": keepalive\n\n"
finally:
with queues_lock:
client_queues.pop(client_id, None)
return Response(
generate(),
mimetype="text/event-stream",
headers={
"Cache-Control": "no-cache",
"X-Accel-Buffering": "no" # Disable nginx buffering
}
)
@app.route("/submit", methods=["POST"])
def submit_captcha():
"""Submit a CAPTCHA task with callback to this server."""
data = request.json
client_id = data["client_id"]
sitekey = data["sitekey"]
pageurl = data["pageurl"]
callback_url = f"{request.host_url}callback?client_id={client_id}"
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"pingback": callback_url,
"json": 1
})
result = resp.json()
if result.get("status") == 1:
return jsonify({"task_id": result["request"]})
return jsonify({"error": result.get("request")}), 400
@app.route("/callback")
def captcha_callback():
"""Receive CaptchaAI callback and push to SSE stream."""
client_id = request.args.get("client_id")
task_id = request.args.get("id")
solution = request.args.get("code")
import json
message = json.dumps({
"task_id": task_id,
"solution": solution
})
with queues_lock:
q = client_queues.get(client_id)
if q:
q.put(message)
return "OK", 200
if __name__ == "__main__":
app.run(port=5000, threaded=True)
Tarayıcı İstemcisi
<!DOCTYPE html>
<html>
<body>
<button onclick="submitCaptcha()">Solve CAPTCHA</button>
<div id="results"></div>
<script>
const clientId = crypto.randomUUID();
const resultsDiv = document.getElementById("results");
// Connect SSE stream
const eventSource = new EventSource(`/events/${clientId}`);
eventSource.addEventListener("captcha-solved", (event) => {
const data = JSON.parse(event.data);
resultsDiv.innerHTML += `<p>Task ${data.task_id}: ${data.solution.substring(0, 30)}...</p>`;
});
eventSource.onerror = () => {
console.log("SSE connection lost, reconnecting...");
};
async function submitCaptcha() {
const response = await fetch("/submit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
client_id: clientId,
sitekey: "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
pageurl: "https://example.com"
})
});
const result = await response.json();
resultsDiv.innerHTML += `<p>Submitted: ${result.task_id}</p>`;
}
</script>
</body>
</html>
Tam Uygulama – JavaScript (Express)
Sunucu
const express = require("express");
const axios = require("axios");
const app = express();
app.use(express.json());
const API_KEY = process.env.CAPTCHAAI_API_KEY;
const BASE_URL = process.env.BASE_URL || "http://localhost:3000";
// Per-client SSE connections: clientId -> Response object
const clients = new Map();
// SSE endpoint
app.get("/events/:clientId", (req, res) => {
const clientId = req.params.clientId;
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
"X-Accel-Buffering": "no",
});
clients.set(clientId, res);
// Keepalive every 30 seconds
const keepalive = setInterval(() => {
res.write(": keepalive\n\n");
}, 30000);
req.on("close", () => {
clearInterval(keepalive);
clients.delete(clientId);
});
});
// Submit CAPTCHA
app.post("/submit", async (req, res) => {
const { client_id, sitekey, pageurl } = req.body;
const callbackUrl = `${BASE_URL}/callback?client_id=${client_id}`;
try {
const resp = await axios.post("https://ocr.captchaai.com/in.php", null, {
params: {
key: API_KEY,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: pageurl,
pingback: callbackUrl,
json: 1,
},
});
if (resp.data.status === 1) {
return res.json({ task_id: resp.data.request });
}
res.status(400).json({ error: resp.data.request });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// CaptchaAI callback → push to SSE
app.get("/callback", (req, res) => {
const clientId = req.query.client_id;
const taskId = req.query.id;
const solution = req.query.code;
const clientRes = clients.get(clientId);
if (clientRes) {
const data = JSON.stringify({ task_id: taskId, solution: solution });
clientRes.write(`event: captcha-solved\ndata: ${data}\n\n`);
}
res.sendStatus(200);
});
app.listen(3000, () => console.log("SSE server running on :3000"));
SSE, WebSocket ve Yoklama
| Özellik | SSE | WebSocket | Oylama |
|---|---|---|---|
| Yön | Sunucu – İstemci | Çift yönlü | İstemci – Sunucu |
| Protokol | HTTP/1.1+ | WS/WSS | HTTP |
| Otomatik yeniden bağlan | Yerleşik | Manuel | N/A |
| Tarayıcı desteği | Hepsi modern | Hepsi modern | Hepsi |
| Karmaşıklık | Düşük | Orta | Düşük |
| Boşa giden istekler | Yok | Yok | Birçok |
| CAPTCHA sonuçları için en iyisi | Evet | Aşırı öldürme | Çalışıyor ama israf |
SSE, CAPTCHA sonuçları için idealdir çünkü veriler yalnızca sunucudan istemciye akar.
Üretimle İlgili Hususlar
Birden Çok Sunucu Örneğiyle Ölçeklendirme
SSE bağlantıları durum bilgilidir; sunucunuzun bir yük dengeleyicinin arkasında birden fazla örneği varsa, geri arama, istemcinin SSE bağlantısını tutandan farklı bir örneğe çarpabilir.
Çözüm: Redis Pub/Sub'yi mesaj veri yolu olarak kullanın:
# Callback handler publishes to Redis
import redis
r = redis.Redis()
r.publish(f"captcha:{client_id}", json.dumps(message))
# SSE handler subscribes to Redis
pubsub = r.pubsub()
pubsub.subscribe(f"captcha:{client_id}")
for msg in pubsub.listen():
if msg["type"] == "message":
yield f"data: {msg['data'].decode()}\n\n"
Bağlantı Sınırları
Tarayıcılar SSE bağlantılarını etki alanı başına 6 bağlantıyla (HTTP/1.1) sınırlandırır. Daha yüksek sınırlar için HTTP/2 kullanın veya istemci başına tek bir SSE bağlantısı aracılığıyla birden fazla görev sonucunu çoğaltın.
Sorun giderme
| Sorun | Sebep | Düzeltme |
|---|---|---|
| SSE bağlantısı her 30 saniyede bir düşüyor | Proxy/load dengeleyici zaman aşımı | Canlı tutan yorumlar gönderin; proxy zaman aşımını artır |
| Sonuçlar gelmiyor | Farklı sunucu örneğine isabet eden geri arama | Geri arama ve SSE işleyicileri arasına Redis Pub/Sub ekleyin |
| Tarayıcı konsolda hatalar gösteriyor | CORS başlıkları eksik | SSE uç noktasına Access-Control-Allow-Origin başlığını ekleyin |
| Çoklu yeniden bağlantı | Sunucu hatalı biçimlendirilmiş SSE gönderiyor | \n\n'nin her olayı sonlandırdığından emin olun; veri formatını doğrula |
SSS
SSE Cloudflare'in arkasında çalışıyor mu?
Evet, ancak Cloudflare yanıtları arabelleğe alabilir. X-Accel-Buffering: no başlığıyla yanıt arabelleğe almayı devre dışı bırakın veya Cloudflare'in akış modunu kullanın.
Bir sunucu kaç eşzamanlı SSE bağlantısını yönetebilir?
Node.js, her biri hafif ve canlı tutulan bir HTTP bağlantısı olduğundan, 10.000'den fazla eşzamanlı SSE bağlantısını kolayca yönetir. İş parçacığı içeren Python daha sınırlıdır; yüksek eşzamanlılık için eşzamansız bir çerçeve (asyncio ile FastAPI) kullanın.
Tarayıcı olmayan istemciler için SSE kullanmalı mıyım?
CLI araçları veya arka uç hizmetleri için doğrudan geri arama yönetimi veya kuyruk tabanlı yaklaşımlar daha basittir. SSE, sonuçları tarayıcı tabanlı kontrol panellerine veya web uygulamalarına aktarırken en kullanışlıdır.
Sonraki Adımlar
CAPTCHA çözümlerini gerçek zamanlı olarak yayınlayın —CaptchaAI API anahtarınızı alınve SSE'yi geri arama hattınıza bağlayın.
İlgili kılavuzlar: