You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用requests监听文件变化?避免高频请求导致网站被Dos

解决方案:替换轮询为服务器推送机制

你的核心问题是短轮询导致请求量过高触发DoS,解决思路是把「客户端主动反复问服务器文件有没有变」改成「服务器主动告诉客户端文件变了」,以下是几种可行方案:


1. 用Server-Sent Events(SSE)实现单向推送

SSE是HTTP协议的扩展,客户端和服务器建立一次长连接,服务器有数据更新时主动推送给客户端,完全替代轮询。

客户端修改示例(Python)

import requests
from sseclient import SSEClient

# 连接到你的SSE服务器端点
messages = SSEClient('https://your-server.com/sun-updates')
for msg in messages:
    if msg.data:
        try:
            val = int(msg.data)
            print(val)
            process.write(sun_pointer, val)
        except ValueError:
            print("无效的数值格式")

服务器端核心实现(以Flask为例)

from flask import Flask, Response
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

app = Flask(__name__)
sun_value = 0
file_path = "/path/to/my_file.txt"

class SunFileHandler(FileSystemEventHandler):
    def on_modified(self, event):
        global sun_value
        if event.src_path == file_path:
            with open(file_path, 'r') as f:
                sun_value = int(f.read().strip())

# 启动本地文件监听
event_handler = SunFileHandler()
observer = Observer()
observer.schedule(event_handler, path='/path/to/', recursive=False)
observer.start()

@app.route('/sun-updates')
def sun_updates():
    def generate():
        prev_val = None
        while True:
            global sun_value
            if sun_value != prev_val:
                prev_val = sun_value
                yield f"data: {sun_value}\n\n"
            time.sleep(1)
    return Response(generate(), mimetype='text/event-stream')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

2. 用WebSocket实现双向通信

如果需要客户端也能主动发送修改请求(比如用户调整阳光的操作),WebSocket更合适,它支持双向实时通信。

客户端示例(Python)

import asyncio
import websockets

async def listen_sun_updates():
    async with websockets.connect('ws://your-server.com/ws') as websocket:
        async for message in websocket:
            try:
                val = int(message)
                print(val)
                process.write(sun_pointer, val)
            except ValueError:
                print("无效的数值格式")

asyncio.get_event_loop().run_until_complete(listen_sun_updates())

服务器端核心逻辑(用websockets库)

import asyncio
import websockets
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

file_path = "/path/to/my_file.txt"
sun_value = 0
connected_clients = set()

class SunFileHandler(FileSystemEventHandler):
    def on_modified(self, event):
        global sun_value
        if event.src_path == file_path:
            with open(file_path, 'r') as f:
                sun_value = int(f.read().strip())
            # 推送给所有在线客户端
            asyncio.create_task(broadcast(sun_value))

async def broadcast(value):
    if connected_clients:
        message = str(value)
        await asyncio.gather(*[client.send(message) for client in connected_clients])

async def handle_client(websocket):
    connected_clients.add(websocket)
    try:
        await websocket.wait_closed()
    finally:
        connected_clients.remove(websocket)

# 启动文件监听
event_handler = SunFileHandler()
observer = Observer()
observer.schedule(event_handler, path='/path/to/', recursive=False)
observer.start()

start_server = websockets.serve(handle_client, "0.0.0.0", 8765)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

3. 优化现有轮询(临时过渡方案)

如果暂时不想重构架构,至少把固定间隔轮询改成指数退避轮询,避免请求过于密集:

import requests
import time

val = int(requests.get("https://link.to/my_file.txt").text)
prevVal = val
backoff = 5  # 初始等待时间
max_backoff = 60

while True:
    try:
        new_val = int(requests.get("https://link.to/my_file.txt").text)
        if new_val != prevVal:
            prevVal = new_val
            print(new_val)
            process.write(sun_pointer, new_val)
            backoff = 5  # 数据更新后重置等待时间
        else:
            time.sleep(backoff)
            backoff = min(backoff * 2, max_backoff)  # 无更新时指数增长等待时间
    except requests.exceptions.ConnectTimeout:
        print("连接超时")
        backoff = min(backoff * 2, max_backoff)
        continue
    except KeyboardInterrupt:
        exit()

关键原理说明

原来的短轮询每5秒发一次请求,不管文件有没有变化,高并发下服务器会被大量请求压垮。换成推送机制后:

  • 每个客户端只保持1个长连接
  • 只有当文件真的变化时,服务器才会发送数据
  • 请求量直接从「N个客户端×每5秒1次」降到「N个长连接+仅变化时的推送包」,完全避免DoS风险

内容的提问来源于stack exchange,提问作者Reinmar Grau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 16:58:57